summaryrefslogtreecommitdiffstats
path: root/toolkit/components
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--toolkit/components/aboutconfig/content/aboutconfig.js2
-rw-r--r--toolkit/components/aboutconfig/test/browser/browser.toml1
-rw-r--r--toolkit/components/aboutmemory/content/aboutMemory.js4
-rw-r--r--toolkit/components/aboutmemory/tests/test_aboutmemory.xhtml10
-rw-r--r--toolkit/components/aboutmemory/tests/test_aboutmemory2.xhtml2
-rw-r--r--toolkit/components/aboutmemory/tests/test_aboutmemory3.xhtml2
-rw-r--r--toolkit/components/aboutmemory/tests/test_aboutmemory4.xhtml2
-rw-r--r--toolkit/components/aboutmemory/tests/test_aboutmemory7.xhtml2
-rw-r--r--toolkit/components/aboutmemory/tests/test_memoryReporters.xhtml10
-rw-r--r--toolkit/components/aboutmemory/tests/test_memoryReporters2.xhtml2
-rw-r--r--toolkit/components/aboutmemory/tests/xpcshell/test_gpuprocess.js9
-rw-r--r--toolkit/components/aboutprocesses/content/aboutProcesses.js4
-rw-r--r--toolkit/components/aboutthirdparty/nsIAboutThirdParty.idl4
-rw-r--r--toolkit/components/aboutthirdparty/tests/browser/browser_aboutthirdparty.js2
-rw-r--r--toolkit/components/aboutwebauthn/content/aboutWebauthn.js4
-rw-r--r--toolkit/components/aboutwebauthn/tests/browser/browser_aboutwebauthn_credentials.js2
-rw-r--r--toolkit/components/aboutwindowsmessages/tests/browser/browser_aboutwindowsmessages.js2
-rw-r--r--toolkit/components/alerts/nsIAlertsService.idl4
-rw-r--r--toolkit/components/alerts/test/test_alerts_requireinteraction.html4
-rw-r--r--toolkit/components/alerts/test/test_invalid_utf16.html2
-rw-r--r--toolkit/components/alerts/test/test_multiple_alerts.html2
-rw-r--r--toolkit/components/alerts/test/test_principal.html2
-rw-r--r--toolkit/components/antitracking/AntiTrackingUtils.cpp135
-rw-r--r--toolkit/components/antitracking/StorageAccess.h2
-rw-r--r--toolkit/components/antitracking/StorageAccessAPIHelper.cpp8
-rw-r--r--toolkit/components/antitracking/StoragePrincipalHelper.cpp41
-rw-r--r--toolkit/components/antitracking/StoragePrincipalHelper.h8
-rw-r--r--toolkit/components/antitracking/StripOnShareLists/LGPL/LICENSE165
-rw-r--r--toolkit/components/antitracking/StripOnShareLists/LGPL/StripOnShareLGPL.json556
-rw-r--r--toolkit/components/antitracking/StripOnShareLists/MPL2/StripOnShare.json (renamed from toolkit/components/antitracking/data/StripOnShare.json)2
-rw-r--r--toolkit/components/antitracking/URLDecorationStripper.cpp8
-rw-r--r--toolkit/components/antitracking/URLQueryStringStripper.cpp189
-rw-r--r--toolkit/components/antitracking/URLQueryStringStripper.h10
-rw-r--r--toolkit/components/antitracking/URLQueryStrippingListService.sys.mjs56
-rw-r--r--toolkit/components/antitracking/jar.mn5
-rw-r--r--toolkit/components/antitracking/nsIURLQueryStringStripper.idl2
-rw-r--r--toolkit/components/antitracking/nsIURLQueryStrippingListService.idl2
-rw-r--r--toolkit/components/antitracking/test/browser/browser.toml12
-rw-r--r--toolkit/components/antitracking/test/browser/browser_doublyNestedTracker.js2
-rw-r--r--toolkit/components/antitracking/test/browser/browser_partitionedABA.js86
-rw-r--r--toolkit/components/antitracking/test/browser/browser_staticPartition_cache.js4
-rw-r--r--toolkit/components/antitracking/test/browser/browser_staticPartition_saveAs.js4
-rw-r--r--toolkit/components/antitracking/test/browser/browser_storageAccessThirdPartyChecks_alwaysPartition.js1
-rw-r--r--toolkit/components/antitracking/test/browser/file_saveAsPageInfo.html2
-rw-r--r--toolkit/components/antitracking/test/browser/file_video.ogvbin16049 -> 0 bytes
-rw-r--r--toolkit/components/antitracking/test/browser/file_video.webmbin0 -> 17931 bytes
-rw-r--r--toolkit/components/antitracking/test/gtest/TestStoragePrincipalHelper.cpp2
-rw-r--r--toolkit/components/antitracking/test/xpcshell/test_validate_strip_on_share_list.js141
-rw-r--r--toolkit/components/autocomplete/nsAutoCompleteController.cpp104
-rw-r--r--toolkit/components/autocomplete/nsAutoCompleteController.h10
-rw-r--r--toolkit/components/autocomplete/nsIAutoCompleteResult.idl2
-rw-r--r--toolkit/components/autocomplete/nsIAutoCompleteSearch.idl23
-rw-r--r--toolkit/components/autocomplete/tests/unit/head_autocomplete.js8
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_378079.js8
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_393191.js8
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_440866.js8
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_autocomplete_multiple.js8
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_autocomplete_userContextId.js7
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_immediate_search.js174
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_previousResult.js8
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_search_zerotimeout.js58
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_stopSearch.js2
-rw-r--r--toolkit/components/autocomplete/tests/unit/xpcshell.toml4
-rw-r--r--toolkit/components/backgroundhangmonitor/BHRTelemetryService.sys.mjs2
-rw-r--r--toolkit/components/backgroundhangmonitor/nsIHangDetails.idl2
-rw-r--r--toolkit/components/backgroundtasks/BackgroundTask_message.sys.mjs5
-rw-r--r--toolkit/components/backgroundtasks/BackgroundTasksManager.sys.mjs2
-rw-r--r--toolkit/components/backgroundtasks/BackgroundTasksUtils.sys.mjs2
-rw-r--r--toolkit/components/backgroundtasks/tests/BackgroundTask_crash.sys.mjs2
-rw-r--r--toolkit/components/backgroundtasks/tests/BackgroundTask_jsdebugger.sys.mjs2
-rw-r--r--toolkit/components/backgroundtasks/tests/BackgroundTask_shouldprocessupdates.sys.mjs2
-rw-r--r--toolkit/components/backgroundtasks/tests/BackgroundTask_targeting.sys.mjs2
-rw-r--r--toolkit/components/backgroundtasks/tests/xpcshell/test_backgroundtask_experiments.js4
-rw-r--r--toolkit/components/bitsdownload/bits_client/bits/src/wide.rs22
-rw-r--r--toolkit/components/bitsdownload/src/bits_interface/action.rs1
-rw-r--r--toolkit/components/bitsdownload/src/bits_interface/error.rs1
-rw-r--r--toolkit/components/browser/nsIWebBrowserChromeFocus.idl4
-rw-r--r--toolkit/components/browser/nsIWebBrowserPrint.idl9
-rw-r--r--toolkit/components/captivedetect/CaptiveDetect.sys.mjs9
-rw-r--r--toolkit/components/captivedetect/nsICaptivePortalDetector.idl2
-rw-r--r--toolkit/components/captivedetect/test/unit/test_abort.js4
-rw-r--r--toolkit/components/captivedetect/test/unit/test_abort_during_user_login.js2
-rw-r--r--toolkit/components/captivedetect/test/unit/test_abort_ongoing_request.js4
-rw-r--r--toolkit/components/captivedetect/test/unit/test_abort_pending_request.js4
-rw-r--r--toolkit/components/captivedetect/test/unit/test_captive_portal_found.js4
-rw-r--r--toolkit/components/captivedetect/test/unit/test_captive_portal_found_303.js4
-rw-r--r--toolkit/components/captivedetect/test/unit/test_captive_portal_not_found.js2
-rw-r--r--toolkit/components/captivedetect/test/unit/test_captive_portal_not_found_404.js2
-rw-r--r--toolkit/components/captivedetect/test/unit/test_multiple_requests.js4
-rw-r--r--toolkit/components/certviewer/content/certviewer.mjs4
-rw-r--r--toolkit/components/certviewer/tests/browser/browser_checkNonRepeatedCertTabs.js2
-rw-r--r--toolkit/components/certviewer/tests/browser/browser_openTabAndSendCertInfo.js4
-rw-r--r--toolkit/components/certviewer/tests/browser/head.js2
-rw-r--r--toolkit/components/cleardata/ClearDataService.sys.mjs100
-rw-r--r--toolkit/components/cleardata/PrincipalsCollector.sys.mjs8
-rw-r--r--toolkit/components/cleardata/SiteDataTestUtils.sys.mjs8
-rw-r--r--toolkit/components/cleardata/nsIClearDataService.idl28
-rw-r--r--toolkit/components/cleardata/tests/browser/browser.toml3
-rw-r--r--toolkit/components/cleardata/tests/browser/browser_css_cache.js1
-rw-r--r--toolkit/components/cleardata/tests/browser/browser_image_cache.js2
-rw-r--r--toolkit/components/cleardata/tests/browser/browser_preflight_cache.js8
-rw-r--r--toolkit/components/cleardata/tests/marionette/test_moved_origin_directory_cleanup.py9
-rw-r--r--toolkit/components/cleardata/tests/unit/test_permissions.js4
-rw-r--r--toolkit/components/cleardata/tests/unit/test_storage_permission.js8
-rw-r--r--toolkit/components/cleardata/tests/unit/test_storage_permission_clearing.js271
-rw-r--r--toolkit/components/cleardata/tests/unit/xpcshell.toml2
-rw-r--r--toolkit/components/contentanalysis/ContentAnalysis.cpp466
-rw-r--r--toolkit/components/contentanalysis/ContentAnalysis.h93
-rw-r--r--toolkit/components/contentanalysis/ContentAnalysisIPCTypes.h29
-rw-r--r--toolkit/components/contentanalysis/components.conf1
-rw-r--r--toolkit/components/contentanalysis/content_analysis/sdk/analysis.pb.cc187
-rw-r--r--toolkit/components/contentanalysis/content_analysis/sdk/analysis.pb.h200
-rw-r--r--toolkit/components/contentanalysis/nsIContentAnalysis.idl54
-rw-r--r--toolkit/components/contentanalysis/tests/browser/browser.toml17
-rw-r--r--toolkit/components/contentanalysis/tests/browser/browser_content_analysis_policies.js34
-rw-r--r--toolkit/components/contentanalysis/tests/browser/browser_print_changing_page_content_analysis.js339
-rw-r--r--toolkit/components/contentanalysis/tests/browser/browser_print_content_analysis.js390
-rw-r--r--toolkit/components/contentanalysis/tests/browser/changing_page_for_print.html12
-rw-r--r--toolkit/components/contentanalysis/tests/browser/head.js114
-rw-r--r--toolkit/components/contentprefs/ContentPrefService2.sys.mjs7
-rw-r--r--toolkit/components/contentprefs/ContentPrefServiceParent.sys.mjs2
-rw-r--r--toolkit/components/contentprefs/tests/browser/browser_remoteContentPrefs.js4
-rw-r--r--toolkit/components/contentprefs/tests/unit_cps2/head.js4
-rw-r--r--toolkit/components/contentprefs/tests/unit_cps2/test_migrationToSchema4.js2
-rw-r--r--toolkit/components/contentrelevancy/ContentRelevancyManager.sys.mjs326
-rw-r--r--toolkit/components/contentrelevancy/docs/index.md3
-rw-r--r--toolkit/components/contentrelevancy/moz.build27
-rw-r--r--toolkit/components/contentrelevancy/private/InputUtils.sys.mjs118
-rw-r--r--toolkit/components/contentrelevancy/tests/browser/browser.toml6
-rw-r--r--toolkit/components/contentrelevancy/tests/browser/browser_contentrelevancy_nimbus.js91
-rw-r--r--toolkit/components/contentrelevancy/tests/xpcshell/head.js6
-rw-r--r--toolkit/components/contentrelevancy/tests/xpcshell/test_ContentRelevancyManager.js149
-rw-r--r--toolkit/components/contentrelevancy/tests/xpcshell/test_InputUtils.js98
-rw-r--r--toolkit/components/contentrelevancy/tests/xpcshell/xpcshell.toml9
-rw-r--r--toolkit/components/contextualidentity/ContextualIdentityService.sys.mjs2
-rw-r--r--toolkit/components/cookiebanners/CookieBannerChild.sys.mjs16
-rw-r--r--toolkit/components/cookiebanners/components.conf13
-rw-r--r--toolkit/components/cookiebanners/cookieBanner.pb.cc983
-rw-r--r--toolkit/components/cookiebanners/cookieBanner.pb.h1058
-rw-r--r--toolkit/components/cookiebanners/cookieBanner.proto25
-rw-r--r--toolkit/components/cookiebanners/metrics.yaml66
-rw-r--r--toolkit/components/cookiebanners/moz.build7
-rw-r--r--toolkit/components/cookiebanners/nsCookieBannerTelemetryService.cpp392
-rw-r--r--toolkit/components/cookiebanners/nsCookieBannerTelemetryService.h47
-rw-r--r--toolkit/components/cookiebanners/nsICookieBannerRule.idl2
-rw-r--r--toolkit/components/cookiebanners/nsICookieBannerService.idl16
-rw-r--r--toolkit/components/cookiebanners/nsICookieBannerTelemetryService.idl13
-rw-r--r--toolkit/components/cookiebanners/test/browser/browser.toml2
-rw-r--r--toolkit/components/cookiebanners/test/browser/browser_cookiebanner_gdpr_telemetry.js189
-rw-r--r--toolkit/components/cookiebanners/test/browser/browser_cookiebanner_telemetry.js2
-rw-r--r--toolkit/components/corroborator/Corroborate.sys.mjs53
-rw-r--r--toolkit/components/corroborator/moz.build17
-rw-r--r--toolkit/components/corroborator/test/xpcshell/data/privileged.xpibin4659 -> 0 bytes
-rw-r--r--toolkit/components/corroborator/test/xpcshell/data/signed-amo.xpibin4702 -> 0 bytes
-rw-r--r--toolkit/components/corroborator/test/xpcshell/data/signed-components.xpibin6995 -> 0 bytes
-rw-r--r--toolkit/components/corroborator/test/xpcshell/data/signed-privileged.xpibin4659 -> 0 bytes
-rw-r--r--toolkit/components/corroborator/test/xpcshell/data/unsigned.xpibin528 -> 0 bytes
-rw-r--r--toolkit/components/corroborator/test/xpcshell/test_verify_jar.js31
-rw-r--r--toolkit/components/corroborator/test/xpcshell/xpcshell.toml5
-rw-r--r--toolkit/components/crashes/CrashService.sys.mjs4
-rw-r--r--toolkit/components/crashes/tests/xpcshell/test_crash_manager.js2
-rw-r--r--toolkit/components/crashes/tests/xpcshell/test_crash_service.js2
-rw-r--r--toolkit/components/crashes/tests/xpcshell/test_crash_store.js4
-rw-r--r--toolkit/components/crashmonitor/CrashMonitor.sys.mjs2
-rw-r--r--toolkit/components/crashmonitor/nsCrashMonitor.sys.mjs2
-rw-r--r--toolkit/components/crashmonitor/test/unit/test_register.js2
-rw-r--r--toolkit/components/credentialmanagement/tests/browser/browser.toml2
-rw-r--r--toolkit/components/ctypes/tests/chrome/xpcshellTestHarnessAdaptor.js2
-rw-r--r--toolkit/components/ctypes/tests/unit/test_errno.js5
-rw-r--r--toolkit/components/ctypes/tests/unit/test_jsctypes.js2
-rw-r--r--toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs2
-rw-r--r--toolkit/components/enterprisepolicies/nsIEnterprisePolicies.idl8
-rw-r--r--toolkit/components/enterprisepolicies/tests/EnterprisePolicyTesting.sys.mjs2
-rw-r--r--toolkit/components/enterprisepolicies/tests/browser/browser_policies_enterprise_only.js4
-rw-r--r--toolkit/components/extensions/.eslintrc.js4
-rw-r--r--toolkit/components/extensions/ConduitsChild.sys.mjs27
-rw-r--r--toolkit/components/extensions/ConduitsParent.sys.mjs4
-rw-r--r--toolkit/components/extensions/Extension.sys.mjs63
-rw-r--r--toolkit/components/extensions/ExtensionActions.sys.mjs46
-rw-r--r--toolkit/components/extensions/ExtensionChild.sys.mjs33
-rw-r--r--toolkit/components/extensions/ExtensionCommon.sys.mjs38
-rw-r--r--toolkit/components/extensions/ExtensionContent.sys.mjs52
-rw-r--r--toolkit/components/extensions/ExtensionDNR.sys.mjs14
-rw-r--r--toolkit/components/extensions/ExtensionPageChild.sys.mjs13
-rw-r--r--toolkit/components/extensions/ExtensionParent.sys.mjs125
-rw-r--r--toolkit/components/extensions/ExtensionPermissions.sys.mjs52
-rw-r--r--toolkit/components/extensions/ExtensionProcessScript.sys.mjs5
-rw-r--r--toolkit/components/extensions/ExtensionShortcuts.sys.mjs2
-rw-r--r--toolkit/components/extensions/ExtensionStorage.sys.mjs5
-rw-r--r--toolkit/components/extensions/ExtensionStorageIDB.sys.mjs3
-rw-r--r--toolkit/components/extensions/ExtensionStorageSync.sys.mjs2
-rw-r--r--toolkit/components/extensions/ExtensionStorageSyncKinto.sys.mjs21
-rw-r--r--toolkit/components/extensions/ExtensionTelemetry.sys.mjs12
-rw-r--r--toolkit/components/extensions/ExtensionUtils.sys.mjs9
-rw-r--r--toolkit/components/extensions/ExtensionWorkerChild.sys.mjs42
-rw-r--r--toolkit/components/extensions/ExtensionXPCShellUtils.sys.mjs5
-rw-r--r--toolkit/components/extensions/Schemas.sys.mjs93
-rw-r--r--toolkit/components/extensions/WebNavigation.sys.mjs10
-rw-r--r--toolkit/components/extensions/WebNavigationFrames.sys.mjs2
-rw-r--r--toolkit/components/extensions/extIWebNavigation.idl4
-rw-r--r--toolkit/components/extensions/mozIExtensionAPIRequestHandling.idl2
-rw-r--r--toolkit/components/extensions/mozIExtensionProcessScript.idl2
-rw-r--r--toolkit/components/extensions/parent/ext-identity.js2
-rw-r--r--toolkit/components/extensions/parent/ext-runtime.js2
-rw-r--r--toolkit/components/extensions/parent/ext-webRequest.js6
-rw-r--r--toolkit/components/extensions/schemas/manifest.json8
-rw-r--r--toolkit/components/extensions/schemas/web_request.json3
-rw-r--r--toolkit/components/extensions/storage/webext_storage_bridge/src/area.rs2
-rw-r--r--toolkit/components/extensions/test/browser/browser.toml1
-rw-r--r--toolkit/components/extensions/test/browser/browser_ext_extension_page_tab_navigated.js37
-rw-r--r--toolkit/components/extensions/test/browser/browser_ext_themes_ntp_colors.js95
-rw-r--r--toolkit/components/extensions/test/browser/browser_ext_themes_ntp_colors_perwindow.js8
-rw-r--r--toolkit/components/extensions/test/browser/browser_ext_themes_pbm.js2
-rw-r--r--toolkit/components/extensions/test/browser/browser_ext_themes_sanitization.js5
-rw-r--r--toolkit/components/extensions/test/browser/browser_ext_themes_separators.js7
-rw-r--r--toolkit/components/extensions/test/mochitest/test_check_startupcache.html2
-rw-r--r--toolkit/components/extensions/test/mochitest/test_ext_async_clipboard.html4
-rw-r--r--toolkit/components/extensions/test/mochitest/test_ext_contentscript_securecontext.html2
-rw-r--r--toolkit/components/extensions/test/mochitest/test_ext_runtime_connect.html1
-rw-r--r--toolkit/components/extensions/test/mochitest/test_ext_runtime_connect2.html1
-rw-r--r--toolkit/components/extensions/test/mochitest/test_ext_runtime_connect_iframe.html6
-rw-r--r--toolkit/components/extensions/test/mochitest/test_ext_tabs_captureTab.html135
-rw-r--r--toolkit/components/extensions/test/mochitest/test_ext_tabs_sendMessage.html180
-rw-r--r--toolkit/components/extensions/test/mochitest/test_ext_test.html2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_csp_validator.js4
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_adoption_with_private_field_xrays.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_adoption_with_xrays.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_clear_cached_resources.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_contentscript_dynamic_registration.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_contentscript_triggeringPrincipal.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_contentscript_xorigin_frame.js6
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_contentscript_xrays.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_contexts_gc.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_dnr_modifyHeaders.js1
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_file_access.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_i18n.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_ipcBlob.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_permissions.js49
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_permissions_api.js1
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_proxy_onauthrequired.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_schemas_manifest_permissions.js8
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_schemas_privileged.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts_css.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts_file.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_scripting_updateContentScripts.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_shadowdom.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_userScripts.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_webRequest_auth.js18
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_webRequest_permission.js21
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_load_all_api_modules.js5
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_native_manifests.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_webRequest_ancestors.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_webRequest_cookies.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_webRequest_filtering.js2
-rw-r--r--toolkit/components/extensions/test/xpcshell/xpcshell-common.toml10
-rw-r--r--toolkit/components/extensions/tsconfig.json13
-rw-r--r--toolkit/components/extensions/types/README.md11
-rw-r--r--toolkit/components/extensions/types/ext-tabs-base.d.ts1447
-rw-r--r--toolkit/components/extensions/types/extensions.ts52
-rw-r--r--toolkit/components/extensions/types/gecko.ts231
-rw-r--r--toolkit/components/extensions/types/glean.d.ts79
-rw-r--r--toolkit/components/extensions/types/globals.ts37
-rw-r--r--toolkit/components/extensions/webrequest/StreamFilterParent.cpp2
-rw-r--r--toolkit/components/forgetaboutsite/test/unit/head_forgetaboutsite.js2
-rw-r--r--toolkit/components/formautofill/AutofillProfileAutoComplete.sys.mjs95
-rw-r--r--toolkit/components/formautofill/FormAutofillChild.sys.mjs94
-rw-r--r--toolkit/components/formautofill/FormAutofillParent.sys.mjs103
-rw-r--r--toolkit/components/formautofill/Helpers.ios.mjs8
-rw-r--r--toolkit/components/formautofill/ProfileAutoCompleteResult.sys.mjs205
-rw-r--r--toolkit/components/formautofill/android/FormAutofillStorage.sys.mjs4
-rw-r--r--toolkit/components/formautofill/default/FormAutofillPrompter.sys.mjs15
-rw-r--r--toolkit/components/formautofill/shared/AddressComponent.sys.mjs3
-rw-r--r--toolkit/components/formautofill/shared/FormAutofillSection.sys.mjs6
-rw-r--r--toolkit/components/formautofill/shared/FormAutofillUtils.sys.mjs42
-rw-r--r--toolkit/components/gfx/SanityTest.sys.mjs2
-rw-r--r--toolkit/components/gfx/content/gfxFrameScript.js2
-rw-r--r--toolkit/components/glean/Cargo.toml2
-rw-r--r--toolkit/components/glean/api/Cargo.toml2
-rw-r--r--toolkit/components/glean/api/src/common_test.rs1
-rw-r--r--toolkit/components/glean/api/src/ffi/custom_distribution.rs20
-rw-r--r--toolkit/components/glean/api/src/ffi/event.rs5
-rw-r--r--toolkit/components/glean/api/src/private/custom_distribution.rs17
-rw-r--r--toolkit/components/glean/api/src/private/timing_distribution.rs12
-rw-r--r--toolkit/components/glean/bindings/private/Boolean.cpp2
-rw-r--r--toolkit/components/glean/bindings/private/Counter.cpp2
-rw-r--r--toolkit/components/glean/bindings/private/CustomDistribution.cpp23
-rw-r--r--toolkit/components/glean/bindings/private/CustomDistribution.h17
-rw-r--r--toolkit/components/glean/bindings/private/DistributionData.h22
-rw-r--r--toolkit/components/glean/bindings/private/Labeled.cpp2
-rw-r--r--toolkit/components/glean/bindings/private/Labeled.h2
-rw-r--r--toolkit/components/glean/bindings/private/Ping.cpp4
-rw-r--r--toolkit/components/glean/bindings/private/Timespan.cpp6
-rw-r--r--toolkit/components/glean/bindings/private/TimingDistribution.cpp20
-rw-r--r--toolkit/components/glean/build_scripts/mach_commands.py59
-rw-r--r--toolkit/components/glean/build_scripts/translate_events.py177
-rw-r--r--toolkit/components/glean/metrics_index.py3
-rw-r--r--toolkit/components/glean/src/init/mod.rs15
-rw-r--r--toolkit/components/glean/tests/gtest/TestFog.cpp11
-rw-r--r--toolkit/components/glean/xpcom/FOG.cpp15
-rw-r--r--toolkit/components/glean/xpcom/nsIFOG.idl4
-rw-r--r--toolkit/components/httpsonlyerror/tests/browser/browser_exception.js2
-rw-r--r--toolkit/components/kvstore/nsIKeyValue.idl2
-rw-r--r--toolkit/components/mediasniffer/test/unit/test_mediasniffer.js4
-rw-r--r--toolkit/components/mediasniffer/test/unit/test_mediasniffer_ext.js4
-rw-r--r--toolkit/components/messaging-system/lib/SpecialMessageActions.sys.mjs4
-rw-r--r--toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/test/browser/browser_sma_open_protection_panel.js2
-rw-r--r--toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/test/browser/browser_sma_pin_current_tab.js2
-rw-r--r--toolkit/components/messaging-system/schemas/TriggerActionSchemas/TriggerActionSchemas.json12
-rw-r--r--toolkit/components/messaging-system/schemas/TriggerActionSchemas/index.md7
-rw-r--r--toolkit/components/ml/actors/MLEngineParent.sys.mjs2
-rw-r--r--toolkit/components/ml/content/ModelHub.sys.mjs690
-rw-r--r--toolkit/components/ml/jar.mn1
-rw-r--r--toolkit/components/ml/tests/browser/browser.toml4
-rw-r--r--toolkit/components/ml/tests/browser/browser_ml_cache.js361
-rw-r--r--toolkit/components/ml/tests/browser/data/README.md5
-rw-r--r--toolkit/components/ml/tests/browser/data/acme/bert/resolve/main/config.json21
-rw-r--r--toolkit/components/ml/tests/browser/data/acme/bert/resolve/main/onnx/config.json21
-rw-r--r--toolkit/components/ml/tests/browser/head.js4
-rw-r--r--toolkit/components/moz.build5
-rw-r--r--toolkit/components/narrate/NarrateControls.sys.mjs6
-rw-r--r--toolkit/components/nimbus/FeatureManifest.yaml119
-rw-r--r--toolkit/components/nimbus/generate/generate_feature_manifest.py2
-rw-r--r--toolkit/components/nimbus/lib/ExperimentManager.sys.mjs21
-rw-r--r--toolkit/components/nimbus/test/unit/test_ExperimentManager_prefs.js204
-rw-r--r--toolkit/components/normandy/lib/RecipeRunner.sys.mjs8
-rw-r--r--toolkit/components/normandy/test/unit/test_NormandyApi.js4
-rw-r--r--toolkit/components/normandy/test/unit/test_RecipeRunner.js3
-rw-r--r--toolkit/components/passwordmgr/LoginAutoComplete.sys.mjs48
-rw-r--r--toolkit/components/passwordmgr/LoginManagerAuthPrompter.sys.mjs9
-rw-r--r--toolkit/components/passwordmgr/LoginManagerChild.sys.mjs10
-rw-r--r--toolkit/components/passwordmgr/nsILoginAutoCompleteSearch.idl4
-rw-r--r--toolkit/components/passwordmgr/test/LoginTestUtils.sys.mjs7
-rw-r--r--toolkit/components/passwordmgr/test/browser/browser_basicAuth_multiTab.js7
-rw-r--r--toolkit/components/passwordmgr/test/browser/browser_basicAuth_rateLimit.js4
-rw-r--r--toolkit/components/passwordmgr/test/browser/browser_basicAuth_switchTab.js6
-rw-r--r--toolkit/components/passwordmgr/test/browser/browser_doorhanger_form_password_edit.js20
-rw-r--r--toolkit/components/passwordmgr/test/browser/browser_doorhanger_generated_password.js28
-rw-r--r--toolkit/components/passwordmgr/test/browser/browser_entry_point_telemetry.js2
-rw-r--r--toolkit/components/passwordmgr/test/browser/browser_private_window.js4
-rw-r--r--toolkit/components/passwordmgr/test/browser/browser_proxyAuth_prompt.js4
-rw-r--r--toolkit/components/passwordmgr/test/mochitest/pwmgr_common.js24
-rw-r--r--toolkit/components/passwordmgr/test/mochitest/test_formless_submit_form_removal.html2
-rw-r--r--toolkit/components/passwordmgr/test/mochitest/test_prompt_async.html4
-rw-r--r--toolkit/components/passwordmgr/test/mochitest/test_prompt_http.html4
-rw-r--r--toolkit/components/passwordmgr/test/mochitest/test_prompt_noWindow.html4
-rw-r--r--toolkit/components/passwordmgr/test/mochitest/test_prompt_promptAuth.html4
-rw-r--r--toolkit/components/passwordmgr/test/mochitest/test_prompt_promptAuth_proxy.html4
-rw-r--r--toolkit/components/passwordmgr/test/mochitest/test_xhr.html15
-rw-r--r--toolkit/components/passwordmgr/test/unit/test_PasswordRulesManager_generatePassword.js2
-rw-r--r--toolkit/components/pdfjs/content/PdfJs.sys.mjs2
-rw-r--r--toolkit/components/pdfjs/content/PdfJsNetwork.sys.mjs6
-rw-r--r--toolkit/components/pdfjs/content/PdfJsTelemetry.sys.mjs14
-rw-r--r--toolkit/components/pdfjs/content/PdfStreamConverter.sys.mjs10
-rw-r--r--toolkit/components/pdfjs/content/PdfjsParent.sys.mjs2
-rw-r--r--toolkit/components/pdfjs/content/build/pdf.mjs219
-rw-r--r--toolkit/components/pdfjs/content/build/pdf.scripting.mjs4
-rw-r--r--toolkit/components/pdfjs/content/build/pdf.worker.mjs274
-rw-r--r--toolkit/components/pdfjs/content/web/viewer-geckoview.mjs114
-rw-r--r--toolkit/components/pdfjs/content/web/viewer.css4
-rw-r--r--toolkit/components/pdfjs/content/web/viewer.mjs169
-rw-r--r--toolkit/components/pdfjs/moz.yaml4
-rw-r--r--toolkit/components/pdfjs/test/browser_pdfjs_editing_contextmenu.js2
-rw-r--r--toolkit/components/pdfjs/test/browser_pdfjs_find.js2
-rw-r--r--toolkit/components/pdfjs/test/browser_pdfjs_force_opening_files.js4
-rw-r--r--toolkit/components/pdfjs/test/browser_pdfjs_fullscreen.js2
-rw-r--r--toolkit/components/pdfjs/test/browser_pdfjs_navigation.js2
-rw-r--r--toolkit/components/pdfjs/test/browser_pdfjs_octet_stream.js2
-rw-r--r--toolkit/components/pdfjs/test/browser_pdfjs_savedialog.js2
-rw-r--r--toolkit/components/places/PlacesFrecencyRecalculator.sys.mjs83
-rw-r--r--toolkit/components/places/PlacesQuery.sys.mjs8
-rw-r--r--toolkit/components/places/PlacesTransactions.sys.mjs11
-rw-r--r--toolkit/components/places/SQLFunctions.cpp19
-rw-r--r--toolkit/components/places/SyncedBookmarksMirror.sys.mjs6
-rw-r--r--toolkit/components/places/bookmark_sync/src/store.rs19
-rw-r--r--toolkit/components/places/mozIAsyncHistory.idl4
-rw-r--r--toolkit/components/places/mozISyncedBookmarksMirror.idl2
-rw-r--r--toolkit/components/places/tests/bookmarks/test_sync_fields.js2
-rw-r--r--toolkit/components/places/tests/browser/browser_visituri.js2
-rw-r--r--toolkit/components/places/tests/browser/previews/browser_thumbnails.js2
-rw-r--r--toolkit/components/places/tests/history/test_hasVisits.js2
-rw-r--r--toolkit/components/places/tests/history/test_insert.js2
-rw-r--r--toolkit/components/places/tests/history/test_insertMany.js2
-rw-r--r--toolkit/components/places/tests/history/test_remove.js2
-rw-r--r--toolkit/components/places/tests/history/test_removeMany.js2
-rw-r--r--toolkit/components/places/tests/history/test_removeVisitsByFilter.js2
-rw-r--r--toolkit/components/places/tests/history/test_update.js2
-rw-r--r--toolkit/components/places/tests/sync/test_bookmark_mirror_migration.js2
-rw-r--r--toolkit/components/places/tests/unit/test_PlacesObservers_counts.js58
-rw-r--r--toolkit/components/places/tests/unit/test_frecency_observers.js4
-rw-r--r--toolkit/components/places/tests/unit/test_frecency_recalculator.js16
-rw-r--r--toolkit/components/places/tests/unit/test_origins_parsing.js2
-rw-r--r--toolkit/components/places/tests/unit/xpcshell.toml2
-rw-r--r--toolkit/components/printing/content/print.css354
-rw-r--r--toolkit/components/printing/content/print.html44
-rw-r--r--toolkit/components/printing/content/print.js18
-rw-r--r--toolkit/components/printing/content/toggle-group.css14
-rw-r--r--toolkit/components/printing/tests/browser_preview_navigation.js2
-rw-r--r--toolkit/components/printing/tests/file_window_print_reentrant.html2
-rw-r--r--toolkit/components/printing/tests/head.js12
-rw-r--r--toolkit/components/processsingleton/MainProcessSingleton.sys.mjs2
-rw-r--r--toolkit/components/processtools/tests/browser/browser_test_powerMetrics.js14
-rw-r--r--toolkit/components/processtools/tests/browser/browser_test_procinfo.js2
-rw-r--r--toolkit/components/promiseworker/tests/xpcshell/data/worker.js2
-rw-r--r--toolkit/components/promiseworker/tests/xpcshell/data/worker.mjs2
-rw-r--r--toolkit/components/prompts/content/tabprompts.css119
-rw-r--r--toolkit/components/prompts/content/tabprompts.sys.mjs298
-rw-r--r--toolkit/components/prompts/jar.mn2
-rw-r--r--toolkit/components/prompts/src/CommonDialog.sys.mjs62
-rw-r--r--toolkit/components/prompts/src/Prompter.sys.mjs31
-rw-r--r--toolkit/components/prompts/test/chromeScript.js49
-rw-r--r--toolkit/components/prompts/test/prompt_common.js10
-rw-r--r--toolkit/components/prompts/test/test_bug619644.html2
-rw-r--r--toolkit/components/prompts/test/test_subresources_prompts.html7
-rwxr-xr-xtoolkit/components/protobuf/regenerate_cpp_files.sh1
-rw-r--r--toolkit/components/reader/.eslintrc.js2
-rw-r--r--toolkit/components/reader/AboutReader.sys.mjs241
-rw-r--r--toolkit/components/reader/ReaderMode.sys.mjs4
-rw-r--r--toolkit/components/reader/color-input.css47
-rw-r--r--toolkit/components/reader/color-input.mjs69
-rw-r--r--toolkit/components/reader/color-input.stories.mjs32
-rw-r--r--toolkit/components/reader/content/aboutReader.html71
-rw-r--r--toolkit/components/reader/jar.mn2
-rw-r--r--toolkit/components/reader/moz.build4
-rw-r--r--toolkit/components/reader/test/browser_readerMode_colorSchemePref.js67
-rw-r--r--toolkit/components/reader/test/browser_readerMode_menu.js69
-rw-r--r--toolkit/components/reader/tests/browser/browser.toml (renamed from toolkit/components/reader/test/browser.toml)0
-rw-r--r--toolkit/components/reader/tests/browser/browser_bug1124271_readerModePinnedTab.js (renamed from toolkit/components/reader/test/browser_bug1124271_readerModePinnedTab.js)0
-rw-r--r--toolkit/components/reader/tests/browser/browser_bug1453818_samesite_cookie.js (renamed from toolkit/components/reader/test/browser_bug1453818_samesite_cookie.js)0
-rw-r--r--toolkit/components/reader/tests/browser/browser_bug1780350_readerModeSaveScroll.js (renamed from toolkit/components/reader/test/browser_bug1780350_readerModeSaveScroll.js)0
-rw-r--r--toolkit/components/reader/tests/browser/browser_drag_url_readerMode.js (renamed from toolkit/components/reader/test/browser_drag_url_readerMode.js)0
-rw-r--r--toolkit/components/reader/tests/browser/browser_localfile_readerMode.js (renamed from toolkit/components/reader/test/browser_localfile_readerMode.js)0
-rw-r--r--toolkit/components/reader/tests/browser/browser_readerMode.js (renamed from toolkit/components/reader/test/browser_readerMode.js)2
-rw-r--r--toolkit/components/reader/tests/browser/browser_readerMode_bc_reuse.js (renamed from toolkit/components/reader/test/browser_readerMode_bc_reuse.js)0
-rw-r--r--toolkit/components/reader/tests/browser/browser_readerMode_cached.js (renamed from toolkit/components/reader/test/browser_readerMode_cached.js)2
-rw-r--r--toolkit/components/reader/tests/browser/browser_readerMode_colorSchemePref.js121
-rw-r--r--toolkit/components/reader/tests/browser/browser_readerMode_hidden_nodes.js (renamed from toolkit/components/reader/test/browser_readerMode_hidden_nodes.js)2
-rw-r--r--toolkit/components/reader/tests/browser/browser_readerMode_menu.js75
-rw-r--r--toolkit/components/reader/tests/browser/browser_readerMode_pocket.js (renamed from toolkit/components/reader/test/browser_readerMode_pocket.js)0
-rw-r--r--toolkit/components/reader/tests/browser/browser_readerMode_readingTime.js (renamed from toolkit/components/reader/test/browser_readerMode_readingTime.js)0
-rw-r--r--toolkit/components/reader/tests/browser/browser_readerMode_refresh.js (renamed from toolkit/components/reader/test/browser_readerMode_refresh.js)0
-rw-r--r--toolkit/components/reader/tests/browser/browser_readerMode_remoteType.js (renamed from toolkit/components/reader/test/browser_readerMode_remoteType.js)0
-rw-r--r--toolkit/components/reader/tests/browser/browser_readerMode_samesite_cookie_redirect.js (renamed from toolkit/components/reader/test/browser_readerMode_samesite_cookie_redirect.js)0
-rw-r--r--toolkit/components/reader/tests/browser/browser_readerMode_with_anchor.js (renamed from toolkit/components/reader/test/browser_readerMode_with_anchor.js)0
-rw-r--r--toolkit/components/reader/tests/browser/getCookies.sjs (renamed from toolkit/components/reader/test/getCookies.sjs)0
-rw-r--r--toolkit/components/reader/tests/browser/head.js (renamed from toolkit/components/reader/test/head.js)0
-rw-r--r--toolkit/components/reader/tests/browser/linkToGetCookies.html (renamed from toolkit/components/reader/test/linkToGetCookies.html)2
-rw-r--r--toolkit/components/reader/tests/browser/readerModeArticle.html (renamed from toolkit/components/reader/test/readerModeArticle.html)0
-rw-r--r--toolkit/components/reader/tests/browser/readerModeArticleContainsLink.html (renamed from toolkit/components/reader/test/readerModeArticleContainsLink.html)2
-rw-r--r--toolkit/components/reader/tests/browser/readerModeArticleHiddenNodes.html (renamed from toolkit/components/reader/test/readerModeArticleHiddenNodes.html)0
-rw-r--r--toolkit/components/reader/tests/browser/readerModeArticleMedium.html (renamed from toolkit/components/reader/test/readerModeArticleMedium.html)0
-rw-r--r--toolkit/components/reader/tests/browser/readerModeArticleShort.html (renamed from toolkit/components/reader/test/readerModeArticleShort.html)0
-rw-r--r--toolkit/components/reader/tests/browser/readerModeArticleTextPlain.txt (renamed from toolkit/components/reader/test/readerModeArticleTextPlain.txt)0
-rw-r--r--toolkit/components/reader/tests/browser/readerModeNonArticle.html (renamed from toolkit/components/reader/test/readerModeNonArticle.html)0
-rw-r--r--toolkit/components/reader/tests/browser/readerModeRandom.sjs (renamed from toolkit/components/reader/test/readerModeRandom.sjs)0
-rw-r--r--toolkit/components/reader/tests/browser/setSameSiteCookie.html (renamed from toolkit/components/reader/test/setSameSiteCookie.html)0
-rw-r--r--toolkit/components/reader/tests/browser/setSameSiteCookie.html^headers^ (renamed from toolkit/components/reader/test/setSameSiteCookie.html^headers^)0
-rw-r--r--toolkit/components/reader/tests/chrome/chrome.toml3
-rw-r--r--toolkit/components/reader/tests/chrome/test_color_input.html39
-rw-r--r--toolkit/components/remotebrowserutils/tests/browser/browser_documentChannel.js4
-rw-r--r--toolkit/components/remotebrowserutils/tests/browser/browser_httpCrossOriginOpenerPolicy.js2
-rw-r--r--toolkit/components/remotebrowserutils/tests/browser/browser_oopProcessSwap.js25
-rw-r--r--toolkit/components/reportbrokensite/ReportBrokenSiteChild.sys.mjs2
-rw-r--r--toolkit/components/reputationservice/nsIApplicationReputation.idl6
-rw-r--r--toolkit/components/reputationservice/test/unit/test_app_rep.js2
-rw-r--r--toolkit/components/reputationservice/test/unit/test_app_rep_maclinux.js2
-rw-r--r--toolkit/components/reputationservice/test/unit/test_app_rep_windows.js2
-rw-r--r--toolkit/components/resistfingerprinting/FingerprintingWebCompatService.sys.mjs2
-rw-r--r--toolkit/components/resistfingerprinting/KeyCodeConsensus_En_US.h2
-rw-r--r--toolkit/components/resistfingerprinting/RFPHelper.sys.mjs42
-rw-r--r--toolkit/components/resistfingerprinting/RFPTargetIPCUtils.h2
-rw-r--r--toolkit/components/resistfingerprinting/RFPTargets.inc2
-rw-r--r--toolkit/components/resistfingerprinting/RelativeTimeline.cpp2
-rw-r--r--toolkit/components/resistfingerprinting/RelativeTimeline.h2
-rw-r--r--toolkit/components/resistfingerprinting/UserCharacteristicsPageService.sys.mjs209
-rw-r--r--toolkit/components/resistfingerprinting/components.conf9
-rw-r--r--toolkit/components/resistfingerprinting/content/usercharacteristics.html18
-rw-r--r--toolkit/components/resistfingerprinting/content/usercharacteristics.js41
-rw-r--r--toolkit/components/resistfingerprinting/jar.mn7
-rw-r--r--toolkit/components/resistfingerprinting/metrics.yaml121
-rw-r--r--toolkit/components/resistfingerprinting/moz.build6
-rw-r--r--toolkit/components/resistfingerprinting/nsIFingerprintingWebCompatService.idl2
-rw-r--r--toolkit/components/resistfingerprinting/nsIRFPService.idl2
-rw-r--r--toolkit/components/resistfingerprinting/nsIUserCharacteristicsPageService.idl23
-rw-r--r--toolkit/components/resistfingerprinting/nsRFPService.cpp126
-rw-r--r--toolkit/components/resistfingerprinting/nsRFPService.h2
-rw-r--r--toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp319
-rw-r--r--toolkit/components/resistfingerprinting/nsUserCharacteristics.h14
-rw-r--r--toolkit/components/resistfingerprinting/pings.yaml2
-rw-r--r--toolkit/components/resistfingerprinting/tests/browser/browser.toml2
-rw-r--r--toolkit/components/resistfingerprinting/tests/browser/browser_canvas_fingerprinter_telemetry.js2
-rw-r--r--toolkit/components/resistfingerprinting/tests/browser/browser_canvas_randomization.js2
-rw-r--r--toolkit/components/resistfingerprinting/tests/browser/browser_canvas_randomization_worker.js2
-rw-r--r--toolkit/components/resistfingerprinting/tests/browser/browser_fingerprintingRemoteOverrides.js60
-rw-r--r--toolkit/components/resistfingerprinting/tests/browser/browser_fingerprintingWebCompat.js2
-rw-r--r--toolkit/components/resistfingerprinting/tests/browser/browser_font_fingerprinter_telemetry.js2
-rw-r--r--toolkit/components/resistfingerprinting/tests/browser/browser_fpiServiceWorkers_fingerprinting.js2
-rw-r--r--toolkit/components/resistfingerprinting/tests/browser/browser_serviceWorker_fingerprinting_webcompat.js4
-rw-r--r--toolkit/components/resistfingerprinting/tests/browser/browser_usercharacteristics.js51
-rw-r--r--toolkit/components/resistfingerprinting/tests/browser/head.js2
-rw-r--r--toolkit/components/resistfingerprinting/tests/gtest/test_reduceprecision.cpp2
-rw-r--r--toolkit/components/resistfingerprinting/tests/gtest/test_usercharping.cpp11
-rw-r--r--toolkit/components/satchel/FillHelpers.sys.mjs48
-rw-r--r--toolkit/components/satchel/FormAutoComplete.sys.mjs693
-rw-r--r--toolkit/components/satchel/FormHandlerChild.sys.mjs6
-rw-r--r--toolkit/components/satchel/FormHistoryAutoComplete.sys.mjs660
-rw-r--r--toolkit/components/satchel/components.conf8
-rw-r--r--toolkit/components/satchel/jar.mn10
-rw-r--r--toolkit/components/satchel/megalist/MegalistChild.sys.mjs17
-rw-r--r--toolkit/components/satchel/megalist/MegalistParent.sys.mjs27
-rw-r--r--toolkit/components/satchel/megalist/MegalistViewModel.sys.mjs291
-rw-r--r--toolkit/components/satchel/megalist/aggregator/Aggregator.sys.mjs78
-rw-r--r--toolkit/components/satchel/megalist/aggregator/DefaultAggregator.sys.mjs17
-rw-r--r--toolkit/components/satchel/megalist/aggregator/datasources/AddressesDataSource.sys.mjs258
-rw-r--r--toolkit/components/satchel/megalist/aggregator/datasources/BankCardDataSource.sys.mjs339
-rw-r--r--toolkit/components/satchel/megalist/aggregator/datasources/DataSourceBase.sys.mjs291
-rw-r--r--toolkit/components/satchel/megalist/aggregator/datasources/LoginDataSource.sys.mjs472
-rw-r--r--toolkit/components/satchel/megalist/aggregator/moz.build17
-rw-r--r--toolkit/components/satchel/megalist/content/MegalistView.mjs477
-rw-r--r--toolkit/components/satchel/megalist/content/VirtualizedList.mjs136
-rw-r--r--toolkit/components/satchel/megalist/content/megalist.css208
-rw-r--r--toolkit/components/satchel/megalist/content/megalist.ftl126
-rw-r--r--toolkit/components/satchel/megalist/content/megalist.html78
-rw-r--r--toolkit/components/satchel/megalist/content/search-input.mjs36
-rw-r--r--toolkit/components/satchel/megalist/content/tests/chrome/chrome.toml3
-rw-r--r--toolkit/components/satchel/megalist/content/tests/chrome/test_virtualized_list.html125
-rw-r--r--toolkit/components/satchel/megalist/moz.build20
-rw-r--r--toolkit/components/satchel/moz.build10
-rw-r--r--toolkit/components/satchel/nsFormFillController.cpp35
-rw-r--r--toolkit/components/satchel/nsFormFillController.h8
-rw-r--r--toolkit/components/satchel/nsIFormAutoComplete.idl44
-rw-r--r--toolkit/components/satchel/nsIFormFillController.idl14
-rw-r--r--toolkit/components/satchel/nsIFormHistoryAutoComplete.idl31
-rw-r--r--toolkit/components/satchel/test/test_capture_limit.html2
-rw-r--r--toolkit/components/satchel/test/unit/test_autocomplete.js4
-rw-r--r--toolkit/components/search/.eslintrc.js9
-rw-r--r--toolkit/components/search/AppProvidedSearchEngine.sys.mjs253
-rw-r--r--toolkit/components/search/SearchEngineSelector.sys.mjs46
-rw-r--r--toolkit/components/search/SearchService.sys.mjs32
-rw-r--r--toolkit/components/search/SearchSettings.sys.mjs13
-rw-r--r--toolkit/components/search/SearchSuggestions.sys.mjs9
-rw-r--r--toolkit/components/search/SearchUtils.sys.mjs12
-rw-r--r--toolkit/components/search/docs/SearchEngineConfiguration.rst66
-rw-r--r--toolkit/components/search/docs/SearchEngineConfigurationArchive.rst72
-rw-r--r--toolkit/components/search/docs/index.rst8
-rw-r--r--toolkit/components/search/nsISearchService.idl14
-rw-r--r--toolkit/components/search/schema/search-config-v2-schema.json58
-rw-r--r--toolkit/components/search/tests/xpcshell/data/search-config-v2.json2
-rw-r--r--toolkit/components/search/tests/xpcshell/data1/search-config-v2.json8
-rw-r--r--toolkit/components/search/tests/xpcshell/head_search.js95
-rw-r--r--toolkit/components/search/tests/xpcshell/searchconfigs/head_searchconfig.js2
-rw-r--r--toolkit/components/search/tests/xpcshell/searchconfigs/test_qwant.js26
-rw-r--r--toolkit/components/search/tests/xpcshell/searchconfigs/test_searchconfig_validates.js26
-rw-r--r--toolkit/components/search/tests/xpcshell/test_appProvided_engine.js182
-rw-r--r--toolkit/components/search/tests/xpcshell/test_appProvided_icons.js69
-rw-r--r--toolkit/components/search/tests/xpcshell/test_appProvided_icons_updates.js324
-rw-r--r--toolkit/components/search/tests/xpcshell/test_defaultEngine_fallback.js42
-rw-r--r--toolkit/components/search/tests/xpcshell/test_engine_selector_environment.js61
-rw-r--r--toolkit/components/search/tests/xpcshell/test_engine_selector_subvariants.js142
-rw-r--r--toolkit/components/search/tests/xpcshell/test_engine_selector_variants.js5
-rw-r--r--toolkit/components/search/tests/xpcshell/test_searchSuggest.js4
-rw-r--r--toolkit/components/search/tests/xpcshell/test_searchSuggest_cookies.js4
-rw-r--r--toolkit/components/search/tests/xpcshell/test_searchSuggest_private.js2
-rw-r--r--toolkit/components/search/tests/xpcshell/test_search_config_v2_nimbus.js85
-rw-r--r--toolkit/components/search/tests/xpcshell/test_settings_persist.js2
-rw-r--r--toolkit/components/search/tests/xpcshell/xpcshell.toml15
-rw-r--r--toolkit/components/shopping/content/ShoppingProduct.mjs2
-rw-r--r--toolkit/components/shopping/test/browser/browser_shopping_ads_test.js2
-rw-r--r--toolkit/components/shopping/test/browser/browser_shopping_sidebar_messages.js8
-rw-r--r--toolkit/components/shopping/test/browser/head.js4
-rw-r--r--toolkit/components/startup/public/nsIAppStartup.idl8
-rw-r--r--toolkit/components/startup/tests/browser/browser_bug511456.js2
-rw-r--r--toolkit/components/startup/tests/browser/browser_bug537449.js2
-rw-r--r--toolkit/components/taskscheduler/TaskSchedulerMacOSImpl.sys.mjs4
-rw-r--r--toolkit/components/telemetry/Events.yaml37
-rw-r--r--toolkit/components/telemetry/Histograms.json115
-rw-r--r--toolkit/components/telemetry/Scalars.yaml257
-rw-r--r--toolkit/components/telemetry/app/TelemetryEnvironment.sys.mjs5
-rw-r--r--toolkit/components/telemetry/app/TelemetryUtils.sys.mjs3
-rw-r--r--toolkit/components/telemetry/core/ipc/TelemetryComms.h10
-rw-r--r--toolkit/components/telemetry/docs/data/environment.rst5
-rw-r--r--toolkit/components/telemetry/tests/addons/signed-webext/.web-extension-id3
-rw-r--r--toolkit/components/telemetry/tests/addons/signed-webext/META-INF/manifest.mf7
-rw-r--r--toolkit/components/telemetry/tests/addons/signed-webext/META-INF/mozilla.rsabin4193 -> 0 bytes
-rw-r--r--toolkit/components/telemetry/tests/addons/signed-webext/META-INF/mozilla.sf4
-rw-r--r--toolkit/components/telemetry/tests/addons/signed-webext/manifest.json12
-rw-r--r--toolkit/components/telemetry/tests/marionette/harness/telemetry_harness/fog_testcase.py2
-rw-r--r--toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js13
-rw-r--r--toolkit/components/telemetry/tests/unit/test_TelemetryReportingPolicy.js6
-rw-r--r--toolkit/components/telemetry/tests/unit/xpcshell.toml1
-rw-r--r--toolkit/components/terminator/TerminatorTelemetry.sys.mjs119
-rw-r--r--toolkit/components/terminator/components.conf15
-rw-r--r--toolkit/components/terminator/moz.build8
-rw-r--r--toolkit/components/terminator/nsITerminatorTest.idl17
-rw-r--r--toolkit/components/terminator/nsTerminator.cpp236
-rw-r--r--toolkit/components/terminator/nsTerminator.h4
-rw-r--r--toolkit/components/terminator/tests/xpcshell/test_terminator_advance_phases.js157
-rw-r--r--toolkit/components/terminator/tests/xpcshell/test_terminator_record.js171
-rw-r--r--toolkit/components/terminator/tests/xpcshell/test_terminator_reload.js88
-rw-r--r--toolkit/components/terminator/tests/xpcshell/xpcshell.toml7
-rw-r--r--toolkit/components/thumbnails/BackgroundPageThumbs.sys.mjs4
-rw-r--r--toolkit/components/thumbnails/PageThumbs.sys.mjs4
-rw-r--r--toolkit/components/thumbnails/test/browser_thumbnails_bg_captureIfMissing.js2
-rw-r--r--toolkit/components/thumbnails/test/browser_thumbnails_bug727765.js2
-rw-r--r--toolkit/components/thumbnails/test/browser_thumbnails_bug818225.js2
-rw-r--r--toolkit/components/thumbnails/test/browser_thumbnails_redirect.js4
-rw-r--r--toolkit/components/thumbnails/test/browser_thumbnails_storage.js2
-rw-r--r--toolkit/components/thumbnails/test/browser_thumbnails_update.js8
-rw-r--r--toolkit/components/timermanager/UpdateTimerManager.sys.mjs2
-rw-r--r--toolkit/components/timermanager/tests/unit/consumerNotifications.js20
-rw-r--r--toolkit/components/tooltiptext/TooltipTextProvider.sys.mjs6
-rw-r--r--toolkit/components/tooltiptext/tests/browser.toml1
-rw-r--r--toolkit/components/tooltiptext/tests/browser_bug581947.js2
-rw-r--r--toolkit/components/translation/moz.build2
-rw-r--r--toolkit/components/translations/actors/AboutTranslationsChild.sys.mjs3
-rw-r--r--toolkit/components/translations/actors/TranslationsParent.sys.mjs281
-rw-r--r--toolkit/components/translations/content/Translator.mjs227
-rw-r--r--toolkit/components/translations/content/translations.mjs201
-rw-r--r--toolkit/components/translations/jar.mn1
-rw-r--r--toolkit/components/translations/moz.build2
-rw-r--r--toolkit/components/translations/tests/browser/browser.toml1
-rw-r--r--toolkit/components/translations/tests/browser/browser_about_translations_dropdowns.js2
-rw-r--r--toolkit/components/translations/tests/browser/shared-head.js154
-rw-r--r--toolkit/components/translations/tests/browser/translations-test.mjs29
-rw-r--r--toolkit/components/translations/tests/browser/translations-tester-es.html10
-rw-r--r--toolkit/components/translations/tests/browser/translations-tester-select.html76
-rw-r--r--toolkit/components/translations/translations.d.ts7
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/Cargo.toml2
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustRelevancy.sys.mjs1249
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustRemoteSettings.sys.mjs32
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustSuggest.sys.mjs192
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustSync15.sys.mjs4
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustTabs.sys.mjs66
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/components/moz.build1
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/config.toml35
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustArithmetic.sys.mjs14
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustCustomTypes.sys.mjs4
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustExternalTypes.sys.mjs4
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustFixtureCallbacks.sys.mjs4
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustGeometry.sys.mjs8
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustRefcounts.sys.mjs388
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustRondpoint.sys.mjs174
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustSprites.sys.mjs26
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustTodolist.sys.mjs48
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/moz.build1
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_external_types.js8
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_geometry.js32
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_refcounts.js57
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_rondpoint.js27
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_sprites.js26
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_todolist.js10
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_type_checking.js18
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/xpcshell.toml15
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/src/render/cpp.rs40
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/src/render/js.rs4
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/src/templates/UniFFIScaffolding.cpp3
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/src/templates/js/Enum.sys.mjs10
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/src/templates/js/Error.sys.mjs6
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/src/templates/js/Object.sys.mjs6
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/src/templates/js/Record.sys.mjs2
-rw-r--r--toolkit/components/uniffi-fixture-external-types/Cargo.toml2
-rw-r--r--toolkit/components/uniffi-fixture-refcounts/Cargo.toml12
-rw-r--r--toolkit/components/uniffi-fixture-refcounts/build.rs7
-rw-r--r--toolkit/components/uniffi-fixture-refcounts/src/lib.rs28
-rw-r--r--toolkit/components/uniffi-fixture-refcounts/src/refcounts.udl8
-rw-r--r--toolkit/components/uniffi-js/OwnedRustBuffer.cpp4
-rw-r--r--toolkit/components/uniffi-js/ScaffoldingConverter.h2
-rw-r--r--toolkit/components/uniffi-js/UniFFICallbacks.h2
-rw-r--r--toolkit/components/uniffi-js/UniFFIFixtureScaffolding.cpp527
-rw-r--r--toolkit/components/uniffi-js/UniFFIGeneratedScaffolding.cpp309
-rw-r--r--toolkit/components/uniffi-js/UniFFIPointer.cpp19
-rw-r--r--toolkit/components/uniffi-js/UniFFIPointer.h14
-rw-r--r--toolkit/components/uniffi-js/UniFFIPointerType.h4
-rw-r--r--toolkit/components/uniffi-js/UniFFIRust.h6
-rw-r--r--toolkit/components/url-classifier/nsUrlClassifierUtils.cpp26
-rw-r--r--toolkit/components/url-classifier/nsUrlClassifierUtils.h2
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/classifierCommon.js2
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/classifierHelper.js2
-rw-r--r--toolkit/components/url-classifier/tests/unit/test_canonicalization.js10
-rw-r--r--toolkit/components/utils/ClientEnvironment.sys.mjs2
-rw-r--r--toolkit/components/utils/SimpleServices.sys.mjs2
-rw-r--r--toolkit/components/viewsource/content/viewSourceUtils.js4
-rw-r--r--toolkit/components/viewsource/test/browser/browser_contextmenu.js2
-rw-r--r--toolkit/components/viewsource/test/browser/browser_viewsource_newwindow.js6
-rw-r--r--toolkit/components/viewsource/test/browser/head.js2
-rw-r--r--toolkit/components/windowcreator/test/320x240.ogvbin28942 -> 0 bytes
-rw-r--r--toolkit/components/windowcreator/test/320x240.webmbin0 -> 50163 bytes
-rw-r--r--toolkit/components/windowcreator/test/bug449141_page.html2
-rw-r--r--toolkit/components/windowcreator/test/chrome.toml2
-rw-r--r--toolkit/components/windowcreator/test/test_bug449141.html4
-rw-r--r--toolkit/components/windowwatcher/nsWindowWatcher.cpp2
-rw-r--r--toolkit/components/windowwatcher/test/browser_new_content_window_chromeflags.js8
-rw-r--r--toolkit/components/windowwatcher/test/browser_new_remote_window_flags.js2
-rw-r--r--toolkit/components/windowwatcher/test/browser_new_sized_window.js2
-rw-r--r--toolkit/components/windowwatcher/test/browser_non_popup_from_popup.js2
-rw-r--r--toolkit/components/windowwatcher/test/head.js4
-rw-r--r--toolkit/components/workerloader/tests/worker_test_loading.js2
-rw-r--r--toolkit/components/xulstore/XULStore.sys.mjs4
-rw-r--r--toolkit/components/xulstore/nsIXULStore.idl2
699 files changed, 24132 insertions, 6525 deletions
diff --git a/toolkit/components/aboutconfig/content/aboutconfig.js b/toolkit/components/aboutconfig/content/aboutconfig.js
index 5e67b764b5..ef4238975b 100644
--- a/toolkit/components/aboutconfig/content/aboutconfig.js
+++ b/toolkit/components/aboutconfig/content/aboutconfig.js
@@ -509,7 +509,7 @@ function loadPrefs() {
});
});
- showAll.addEventListener("click", event => {
+ showAll.addEventListener("click", () => {
search.focus();
search.value = "";
gFilterPrefsTask.disarm();
diff --git a/toolkit/components/aboutconfig/test/browser/browser.toml b/toolkit/components/aboutconfig/test/browser/browser.toml
index a0cce686b8..373d51cf06 100644
--- a/toolkit/components/aboutconfig/test/browser/browser.toml
+++ b/toolkit/components/aboutconfig/test/browser/browser.toml
@@ -13,7 +13,6 @@ support-files = ["head.js"]
["browser_clipboard.js"]
["browser_edit.js"]
-fail-if = ["a11y_checks"] # Bugs 1854447 and 1882380 span may not be focusable
skip-if = ["os == 'linux' && ccov"] # Bug 1613515, the test consistently times out on Linux coverage builds.
["browser_locked.js"]
diff --git a/toolkit/components/aboutmemory/content/aboutMemory.js b/toolkit/components/aboutmemory/content/aboutMemory.js
index 049818263f..6b6f2e637c 100644
--- a/toolkit/components/aboutmemory/content/aboutMemory.js
+++ b/toolkit/components/aboutmemory/content/aboutMemory.js
@@ -608,7 +608,7 @@ function dumpGCLogAndCCLog(aVerbose) {
);
let section = appendElement(gMain, "div", "section");
- function displayInfo(aGCLog, aCCLog, aIsParent) {
+ function displayInfo(aGCLog, aCCLog) {
appendElementWithText(section, "div", "", "Saved GC log to " + aGCLog.path);
let ccLogType = aVerbose ? "verbose" : "concise";
@@ -824,7 +824,7 @@ function loadMemoryReportsFromFile(aFilename, aTitleNote, aFn) {
"uncompressed",
{
data: [],
- onStartRequest(aR, aC) {},
+ onStartRequest() {},
onDataAvailable(aR, aStream, aO, aCount) {
let bi = new nsBinaryStream(aStream);
this.data.push(bi.readBytes(aCount));
diff --git a/toolkit/components/aboutmemory/tests/test_aboutmemory.xhtml b/toolkit/components/aboutmemory/tests/test_aboutmemory.xhtml
index 4617a019ad..a1a712dd0a 100644
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory.xhtml
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory.xhtml
@@ -40,7 +40,7 @@
const PERCENTAGE = Ci.nsIMemoryReporter.UNITS_PERCENTAGE;
let fakeReporters = [
- { collectReports(aCbObj, aClosure, aAnonymize) {
+ { collectReports(aCbObj, aClosure) {
function f(aP, aK, aU, aA) {
aCbObj.callback("Main Process", aP, aK, aU, aA, "Desc.", aClosure);
}
@@ -77,7 +77,7 @@
f("compartments/system/foo", OTHER, COUNT, 1);
}
},
- { collectReports(aCbObj, aClosure, aAnonymize) {
+ { collectReports(aCbObj, aClosure) {
function f(aP, aK, aU, aA) {
aCbObj.callback("Main Process", aP, aK, aU, aA, "Desc.", aClosure);
}
@@ -92,7 +92,7 @@
f("explicit/f/g/h/j", HEAP, BYTES, 10 * MB);
}
},
- { collectReports(aCbObj, aClosure, aAnonymize) {
+ { collectReports(aCbObj, aClosure) {
function f(aP, aK, aU, aA) {
aCbObj.callback("Main Process", aP, aK, aU, aA, "Desc.", aClosure);
}
@@ -103,7 +103,7 @@
f("compartments/user/https:\\\\very-long-url.com\\very-long\\oh-so-long\\really-quite-long.html?a=2&b=3&c=4&d=5&e=abcdefghijklmnopqrstuvwxyz&f=123456789123456789123456789", OTHER, COUNT, 1);
}
},
- { collectReports(aCbObj, aClosure, aAnonymize) {
+ { collectReports(aCbObj, aClosure) {
function f(aP) {
aCbObj.callback("Main Process", aP, OTHER, COUNT, 1, "Desc.", aClosure);
}
@@ -121,7 +121,7 @@
// the largest). Processes without a |resident| memory reporter are saved
// for the end.
let fakeReporters2 = [
- { collectReports(aCbObj, aClosure, aAnonymize) {
+ { collectReports(aCbObj, aClosure) {
function f(aP1, aP2, aK, aU, aA) {
aCbObj.callback(aP1, aP2, aK, aU, aA, "Desc.", aClosure);
}
diff --git a/toolkit/components/aboutmemory/tests/test_aboutmemory2.xhtml b/toolkit/components/aboutmemory/tests/test_aboutmemory2.xhtml
index 28c8f7fa4f..f5c3885959 100644
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory2.xhtml
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory2.xhtml
@@ -36,7 +36,7 @@
let jk2Path = "explicit/j/k2";
let fakeReporters = [
- { collectReports(aCbObj, aClosure, aAnonymize) {
+ { collectReports(aCbObj, aClosure) {
function f(aP, aK, aA) {
aCbObj.callback("Main Process ABC", aP, aK, BYTES, aA, "Desc.", aClosure);
}
diff --git a/toolkit/components/aboutmemory/tests/test_aboutmemory3.xhtml b/toolkit/components/aboutmemory/tests/test_aboutmemory3.xhtml
index 1391af38b7..7292cbf252 100644
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory3.xhtml
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory3.xhtml
@@ -31,7 +31,7 @@
const BYTES = Ci.nsIMemoryReporter.UNITS_BYTES;
let fakeReporters = [
- { collectReports(aCbObj, aClosure, aAnonymize) {
+ { collectReports(aCbObj, aClosure) {
function f(aP, aK, aA, aD) {
aCbObj.callback("", aP, aK, BYTES, aA, aD, aClosure);
}
diff --git a/toolkit/components/aboutmemory/tests/test_aboutmemory4.xhtml b/toolkit/components/aboutmemory/tests/test_aboutmemory4.xhtml
index 9d9db694f2..1fcdb86406 100644
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory4.xhtml
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory4.xhtml
@@ -35,7 +35,7 @@
frame.height = 300;
frame.src = "about:memory?file=" + makePathname(aFilename);
document.documentElement.appendChild(frame);
- frame.addEventListener("load", function onFrameLoad(e) {
+ frame.addEventListener("load", function onFrameLoad() {
frame.focus();
// Initialize the clipboard contents.
diff --git a/toolkit/components/aboutmemory/tests/test_aboutmemory7.xhtml b/toolkit/components/aboutmemory/tests/test_aboutmemory7.xhtml
index 28872cd516..a31eb6e6b4 100644
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory7.xhtml
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory7.xhtml
@@ -30,7 +30,7 @@
const BYTES = Ci.nsIMemoryReporter.UNITS_BYTES;
let fakeReporters = [
- { collectReports(aCbObj, aClosure, aAnonymize) {
+ { collectReports(aCbObj, aClosure) {
function f(aP, aK, aA) {
aCbObj.callback("Main Process", aP, aK, BYTES, aA, "Desc.", aClosure);
}
diff --git a/toolkit/components/aboutmemory/tests/test_memoryReporters.xhtml b/toolkit/components/aboutmemory/tests/test_memoryReporters.xhtml
index c97981258e..bc715b6778 100644
--- a/toolkit/components/aboutmemory/tests/test_memoryReporters.xhtml
+++ b/toolkit/components/aboutmemory/tests/test_memoryReporters.xhtml
@@ -77,8 +77,7 @@
let mySandbox = Cu.Sandbox(document.nodePrincipal,
{ sandboxName: "this-is-a-sandbox-name" });
- function handleReportNormal(aProcess, aPath, aKind, aUnits, aAmount,
- aDescription)
+ function handleReportNormal(aProcess, aPath, aKind, aUnits, aAmount)
{
if (aProcess.startsWith(`Utility `)) {
// The Utility process that runs the ORB JavaScript validator starts on first
@@ -128,8 +127,7 @@
}
}
- function handleReportAnonymized(aProcess, aPath, aKind, aUnits, aAmount,
- aDescription)
+ function handleReportAnonymized(aProcess, aPath)
{
// Path might include an xmlns using http, which is safe to ignore.
let reducedPath = aPath.replace(XUL_NS, "");
@@ -331,7 +329,7 @@
}
},
// nsIMemoryReporter
- collectReports(callback, data, anonymize) {
+ collectReports(callback, data) {
for (let path of Object.keys(this.tests)) {
try {
let test = this.tests[path];
@@ -350,7 +348,7 @@
}
},
// nsIHandleReportCallback
- callback(process, path, kind, units, amount, data) {
+ callback(process, path, kind, units, amount) {
if (path in this.tests) {
this.seen++;
let test = this.tests[path];
diff --git a/toolkit/components/aboutmemory/tests/test_memoryReporters2.xhtml b/toolkit/components/aboutmemory/tests/test_memoryReporters2.xhtml
index b7251c4e62..8dc7567109 100644
--- a/toolkit/components/aboutmemory/tests/test_memoryReporters2.xhtml
+++ b/toolkit/components/aboutmemory/tests/test_memoryReporters2.xhtml
@@ -55,7 +55,7 @@
{
let residents = {};
- let handleReport = function(aProcess, aPath, aKind, aUnits, aAmount, aDesc) {
+ let handleReport = function(aProcess, aPath, aKind, aUnits, aAmount) {
if (aProcess.startsWith(`Utility `)) {
// The Utility process that runs the ORB JavaScript validator starts on first
// idle in the parent process. This makes it notoriously hard to know _if_ it
diff --git a/toolkit/components/aboutmemory/tests/xpcshell/test_gpuprocess.js b/toolkit/components/aboutmemory/tests/xpcshell/test_gpuprocess.js
index 4a77752b17..764f5a1302 100644
--- a/toolkit/components/aboutmemory/tests/xpcshell/test_gpuprocess.js
+++ b/toolkit/components/aboutmemory/tests/xpcshell/test_gpuprocess.js
@@ -17,14 +17,7 @@ function run_test() {
};
let foundGPUProcess = false;
- let onHandleReport = function (
- aProcess,
- aPath,
- aKind,
- aUnits,
- aAmount,
- aDescription
- ) {
+ let onHandleReport = function (aProcess) {
if (/GPU \(pid \d+\)/.test(aProcess)) {
foundGPUProcess = true;
}
diff --git a/toolkit/components/aboutprocesses/content/aboutProcesses.js b/toolkit/components/aboutprocesses/content/aboutProcesses.js
index 4ad00b2840..3d80512407 100644
--- a/toolkit/components/aboutprocesses/content/aboutProcesses.js
+++ b/toolkit/components/aboutprocesses/content/aboutProcesses.js
@@ -787,7 +787,7 @@ var View = {
return isOpen;
},
- displayDOMWindowRow(data, parent) {
+ displayDOMWindowRow(data) {
const cellCount = 2;
let rowId = "w:" + data.outerWindowId;
let row = this._getOrCreateRow(rowId, cellCount);
@@ -1124,7 +1124,7 @@ var Control = {
// Visibility change:
// - stop updating while the user isn't looking;
// - resume updating when the user returns.
- window.addEventListener("visibilitychange", event => {
+ window.addEventListener("visibilitychange", () => {
if (!document.hidden) {
this._updateDisplay(true);
}
diff --git a/toolkit/components/aboutthirdparty/nsIAboutThirdParty.idl b/toolkit/components/aboutthirdparty/nsIAboutThirdParty.idl
index ef5590465a..68e41843d0 100644
--- a/toolkit/components/aboutthirdparty/nsIAboutThirdParty.idl
+++ b/toolkit/components/aboutthirdparty/nsIAboutThirdParty.idl
@@ -39,12 +39,12 @@ interface nsIAboutThirdParty : nsISupports
/**
* Returns true if DynamicBlocklist is available.
*/
- readonly attribute bool isDynamicBlocklistAvailable;
+ readonly attribute boolean isDynamicBlocklistAvailable;
/**
* Returns true if DynamicBlocklist is available but disabled.
*/
- readonly attribute bool isDynamicBlocklistDisabled;
+ readonly attribute boolean isDynamicBlocklistDisabled;
/**
* Add or remove an entry from the dynamic blocklist and save
diff --git a/toolkit/components/aboutthirdparty/tests/browser/browser_aboutthirdparty.js b/toolkit/components/aboutthirdparty/tests/browser/browser_aboutthirdparty.js
index 0a53823a35..865e7b949d 100644
--- a/toolkit/components/aboutthirdparty/tests/browser/browser_aboutthirdparty.js
+++ b/toolkit/components/aboutthirdparty/tests/browser/browser_aboutthirdparty.js
@@ -135,7 +135,7 @@ add_task(async () => {
"after system info was collected."
);
- await BrowserTestUtils.withNewTab("about:third-party", async browser => {
+ await BrowserTestUtils.withNewTab("about:third-party", async () => {
if (!content.fetchDataDone) {
const mainDiv = content.document.getElementById("main");
await BrowserTestUtils.waitForMutationCondition(
diff --git a/toolkit/components/aboutwebauthn/content/aboutWebauthn.js b/toolkit/components/aboutwebauthn/content/aboutWebauthn.js
index 9142ed198a..6232a30936 100644
--- a/toolkit/components/aboutwebauthn/content/aboutWebauthn.js
+++ b/toolkit/components/aboutwebauthn/content/aboutWebauthn.js
@@ -546,7 +546,7 @@ function sidebar_set_disabled(disabled) {
});
}
-function check_pin_repeat_is_correct(button) {
+function check_pin_repeat_is_correct() {
let pin = document.getElementById("new-pin");
let pin_repeat = document.getElementById("new-pin-repeat");
let has_current_pin = !document.getElementById("current-pin-div").hidden;
@@ -853,7 +853,7 @@ try {
Ci.nsIWebAuthnService
);
document.addEventListener("DOMContentLoaded", onLoad);
- window.addEventListener("beforeunload", event => {
+ window.addEventListener("beforeunload", () => {
AboutWebauthnManagerJS.uninit();
if (AboutWebauthnService) {
AboutWebauthnService.cancel(0);
diff --git a/toolkit/components/aboutwebauthn/tests/browser/browser_aboutwebauthn_credentials.js b/toolkit/components/aboutwebauthn/tests/browser/browser_aboutwebauthn_credentials.js
index ff45ea2962..e2b01d67ef 100644
--- a/toolkit/components/aboutwebauthn/tests/browser/browser_aboutwebauthn_credentials.js
+++ b/toolkit/components/aboutwebauthn/tests/browser/browser_aboutwebauthn_credentials.js
@@ -24,7 +24,7 @@ registerCleanupFunction(async function () {
function send_credential_list(credlist) {
let num_of_creds = 0;
credlist.forEach(domain => {
- domain.credentials.forEach(c => {
+ domain.credentials.forEach(() => {
num_of_creds += 1;
});
});
diff --git a/toolkit/components/aboutwindowsmessages/tests/browser/browser_aboutwindowsmessages.js b/toolkit/components/aboutwindowsmessages/tests/browser/browser_aboutwindowsmessages.js
index 7c667891d6..18fbff351a 100644
--- a/toolkit/components/aboutwindowsmessages/tests/browser/browser_aboutwindowsmessages.js
+++ b/toolkit/components/aboutwindowsmessages/tests/browser/browser_aboutwindowsmessages.js
@@ -29,7 +29,7 @@ add_task(async () => {
});
await BrowserTestUtils.withNewTab(
{ gBrowser: originalBrowser, url: "about:windows-messages" },
- async browser => {
+ async () => {
let messagesList = content.document.getElementById("windows-div");
// This is tricky because the test framework has its own windows
Assert.greaterOrEqual(
diff --git a/toolkit/components/alerts/nsIAlertsService.idl b/toolkit/components/alerts/nsIAlertsService.idl
index 2e75205ef5..412875a37b 100644
--- a/toolkit/components/alerts/nsIAlertsService.idl
+++ b/toolkit/components/alerts/nsIAlertsService.idl
@@ -312,14 +312,14 @@ interface nsIAlertsDoNotDisturb : nsISupports
* code to show an alert. e.g. on OS X, the system will take care not
* disrupting a user if we simply create a notification like usual.
*/
- attribute bool manualDoNotDisturb;
+ attribute boolean manualDoNotDisturb;
/**
* Toggles a mode for the service to suppress all notifications from
* being dispatched when sharing the screen via the getMediaDisplay
* API.
*/
- attribute bool suppressForScreenSharing;
+ attribute boolean suppressForScreenSharing;
};
[scriptable, uuid(fc6d7f0a-0cf6-4268-8c71-ab640842b9b1)]
diff --git a/toolkit/components/alerts/test/test_alerts_requireinteraction.html b/toolkit/components/alerts/test/test_alerts_requireinteraction.html
index ca8fa1c9e3..dc189920ad 100644
--- a/toolkit/components/alerts/test/test_alerts_requireinteraction.html
+++ b/toolkit/components/alerts/test/test_alerts_requireinteraction.html
@@ -23,7 +23,7 @@ const chromeScript = SpecialPowers.loadChromeScript(_ => {
sendAsyncMessage("waitForXULAlert", false);
}, 2000);
- var windowObserver = function(win, aTopic, aData) {
+ var windowObserver = function(win, aTopic) {
if (aTopic != "domwindowopened") {
return;
}
@@ -95,7 +95,7 @@ add_task(async function test_require_interaction() {
var actualSequence = [];
function createAlertListener(name, showCallback, finishCallback) {
- return (subject, topic, data) => {
+ return (subject, topic) => {
if (topic == "alertshow") {
actualSequence.push(name + " show");
if (showCallback) {
diff --git a/toolkit/components/alerts/test/test_invalid_utf16.html b/toolkit/components/alerts/test/test_invalid_utf16.html
index a4f862238c..5d7fa9c079 100644
--- a/toolkit/components/alerts/test/test_invalid_utf16.html
+++ b/toolkit/components/alerts/test/test_invalid_utf16.html
@@ -18,7 +18,7 @@
let promise = new Promise((res, rej) => {resolve = res; reject = rej});
let success = false;
- function observe(aSubject, aTopic, aData) {
+ function observe(aSubject, aTopic) {
if (aTopic == "alertshow") {
success = true;
notifier.closeAlert(alertName);
diff --git a/toolkit/components/alerts/test/test_multiple_alerts.html b/toolkit/components/alerts/test/test_multiple_alerts.html
index 95843b2e89..830dfdc1bb 100644
--- a/toolkit/components/alerts/test/test_multiple_alerts.html
+++ b/toolkit/components/alerts/test/test_multiple_alerts.html
@@ -26,7 +26,7 @@ const chromeScript = SpecialPowers.loadChromeScript(_ => {
sendAsyncMessage("waitedForPosition", null);
}, 2000);
- var windowObserver = function(win, aTopic, aData) {
+ var windowObserver = function(win, aTopic) {
if (aTopic != "domwindowopened") {
return;
}
diff --git a/toolkit/components/alerts/test/test_principal.html b/toolkit/components/alerts/test/test_principal.html
index 5464d92977..7b0795d3f8 100644
--- a/toolkit/components/alerts/test/test_principal.html
+++ b/toolkit/components/alerts/test/test_principal.html
@@ -38,7 +38,7 @@ const chromeScript = SpecialPowers.loadChromeScript(_ => {
function notify(alertName, principal) {
return new Promise((resolve, reject) => {
var source;
- async function observe(subject, topic, data) {
+ async function observe(subject, topic) {
if (topic == "alertclickcallback") {
reject(new Error("Alerts should not be clicked during test"));
} else if (topic == "alertshow") {
diff --git a/toolkit/components/antitracking/AntiTrackingUtils.cpp b/toolkit/components/antitracking/AntiTrackingUtils.cpp
index 2c530dc3da..d9624237de 100644
--- a/toolkit/components/antitracking/AntiTrackingUtils.cpp
+++ b/toolkit/components/antitracking/AntiTrackingUtils.cpp
@@ -514,6 +514,10 @@ AntiTrackingUtils::GetStoragePermissionStateInParent(nsIChannel* aChannel) {
return nsILoadInfo::NoStoragePermission;
}
+ if (targetPrincipal->IsSystemPrincipal()) {
+ return nsILoadInfo::HasStoragePermission;
+ }
+
nsCOMPtr<nsIURI> trackingURI;
rv = aChannel->GetURI(getter_AddRefs(trackingURI));
if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -821,7 +825,8 @@ void AntiTrackingUtils::ComputeIsThirdPartyToTopWindow(nsIChannel* aChannel) {
// whether the page is third-party, so we use channel result principal
// instead. By doing this, an the resource inherits the principal from
// its parent is considered not a third-party.
- if (NS_IsAboutBlank(uri) || NS_IsAboutSrcdoc(uri)) {
+ if (NS_IsAboutBlank(uri) || NS_IsAboutSrcdoc(uri) ||
+ uri->SchemeIs("blob")) {
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
if (NS_WARN_IF(!ssm)) {
return;
@@ -847,10 +852,36 @@ void AntiTrackingUtils::ComputeIsThirdPartyToTopWindow(nsIChannel* aChannel) {
bool AntiTrackingUtils::IsThirdPartyChannel(nsIChannel* aChannel) {
MOZ_ASSERT(aChannel);
- // We only care whether the channel is 3rd-party with respect to
- // the top-level.
- nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
- return loadInfo->GetIsThirdPartyContextToTopWindow();
+ // We have to handle blob URLs here because they always fail
+ // IsThirdPartyChannel because of how blob URLs are constructed. We just
+ // recompare to their ancestor chain from the loadInfo, bailing if any is
+ // third party.
+ nsAutoCString scheme;
+ nsCOMPtr<nsIURI> channelURI;
+ nsresult rv = aChannel->GetURI(getter_AddRefs(channelURI));
+ if (NS_SUCCEEDED(rv) && channelURI->SchemeIs("blob")) {
+ nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
+ for (const nsCOMPtr<nsIPrincipal>& principal :
+ loadInfo->AncestorPrincipals()) {
+ bool thirdParty = true;
+ rv = loadInfo->PrincipalToInherit()->IsThirdPartyPrincipal(principal,
+ &thirdParty);
+ if (NS_SUCCEEDED(rv) && thirdParty) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ nsCOMPtr<mozIThirdPartyUtil> tpuService =
+ mozilla::components::ThirdPartyUtil::Service();
+ if (!tpuService) {
+ return true;
+ }
+ bool thirdParty = true;
+ rv = tpuService->IsThirdPartyChannel(aChannel, nullptr, &thirdParty);
+ NS_ENSURE_SUCCESS(rv, true);
+ return thirdParty;
}
/* static */
@@ -903,19 +934,29 @@ bool AntiTrackingUtils::IsThirdPartyWindow(nsPIDOMWindowInner* aWindow,
/* static */
bool AntiTrackingUtils::IsThirdPartyDocument(Document* aDocument) {
MOZ_ASSERT(aDocument);
- if (!aDocument->GetChannel()) {
+ nsCOMPtr<mozIThirdPartyUtil> tpuService =
+ mozilla::components::ThirdPartyUtil::Service();
+ if (!tpuService) {
+ return true;
+ }
+ bool thirdParty = true;
+ if (!aDocument->GetChannel() ||
+ aDocument->GetDocumentURI()->SchemeIs("blob")) {
// If we can't get the channel from the document, i.e. initial about:blank
// page, we use the browsingContext of the document to check if it's in the
// third-party context. If the browsing context is still not available, we
// will treat the window as third-party.
+ // We also rely on IsThirdPartyContext for blob documents because the
+ // IsThirdPartyChannel check relies on getting the BaseDomain,
+ // which correctly fails for blobs URIs.
RefPtr<BrowsingContext> bc = aDocument->GetBrowsingContext();
return bc ? IsThirdPartyContext(bc) : true;
}
- // We only care whether the channel is 3rd-party with respect to
- // the top-level.
- nsCOMPtr<nsILoadInfo> loadInfo = aDocument->GetChannel()->LoadInfo();
- return loadInfo->GetIsThirdPartyContextToTopWindow();
+ nsresult rv = tpuService->IsThirdPartyChannel(aDocument->GetChannel(),
+ nullptr, &thirdParty);
+ NS_ENSURE_SUCCESS(rv, true);
+ return thirdParty;
}
/* static */
@@ -923,41 +964,47 @@ bool AntiTrackingUtils::IsThirdPartyContext(BrowsingContext* aBrowsingContext) {
MOZ_ASSERT(aBrowsingContext);
MOZ_ASSERT(aBrowsingContext->IsInProcess());
- if (aBrowsingContext->IsTopContent()) {
- return false;
- }
-
- // If the top browsing context is not in the same process, it's cross-origin.
- if (!aBrowsingContext->Top()->IsInProcess()) {
- return true;
- }
-
+ // iframes with SANDBOX_ORIGIN are always third-party contexts
+ // because they are a unique origin
nsIDocShell* docShell = aBrowsingContext->GetDocShell();
if (!docShell) {
return true;
}
Document* doc = docShell->GetExtantDocument();
- if (!doc) {
+ if (!doc || doc->GetSandboxFlags() & SANDBOXED_ORIGIN) {
return true;
}
nsIPrincipal* principal = doc->NodePrincipal();
- nsIDocShell* topDocShell = aBrowsingContext->Top()->GetDocShell();
- if (!topDocShell) {
- return true;
- }
- Document* topDoc = topDocShell->GetDocument();
- if (!topDoc) {
- return true;
- }
- nsIPrincipal* topPrincipal = topDoc->NodePrincipal();
+ BrowsingContext* traversingParent = aBrowsingContext->GetParent();
+ while (traversingParent) {
+ // If the parent browsing context is not in the same process, it's
+ // cross-origin.
+ if (!traversingParent->IsInProcess()) {
+ return true;
+ }
- auto* topBasePrin = BasePrincipal::Cast(topPrincipal);
- bool isThirdParty = true;
+ nsIDocShell* parentDocShell = traversingParent->GetDocShell();
+ if (!parentDocShell) {
+ return true;
+ }
+ Document* parentDoc = parentDocShell->GetDocument();
+ if (!parentDoc || parentDoc->GetSandboxFlags() & SANDBOXED_ORIGIN) {
+ return true;
+ }
+ nsIPrincipal* parentPrincipal = parentDoc->NodePrincipal();
+
+ auto* parentBasePrin = BasePrincipal::Cast(parentPrincipal);
+ bool isThirdParty = true;
- topBasePrin->IsThirdPartyPrincipal(principal, &isThirdParty);
+ parentBasePrin->IsThirdPartyPrincipal(principal, &isThirdParty);
+ if (isThirdParty) {
+ return true;
+ }
- return isThirdParty;
+ traversingParent = traversingParent->GetParent();
+ }
+ return false;
}
/* static */
@@ -1005,6 +1052,18 @@ void AntiTrackingUtils::UpdateAntiTrackingInfoForChannel(nsIChannel* aChannel) {
->MarkOverriddenFingerprintingSettingsAsSet();
#endif
+ ExtContentPolicyType contentType = loadInfo->GetExternalContentPolicyType();
+ if (contentType == ExtContentPolicy::TYPE_DOCUMENT ||
+ contentType == ExtContentPolicy::TYPE_SUBDOCUMENT) {
+ nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
+ Unused << loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));
+ // For subdocuments, the channel's partition key is that of the parent
+ // document. This document may have a different partition key, particularly
+ // one without the same-site bit.
+ net::CookieJarSettings::Cast(cookieJarSettings)
+ ->UpdatePartitionKeyForDocumentLoadedByChannel(aChannel);
+ }
+
// We only update the IsOnContentBlockingAllowList flag and the partition key
// for the top-level http channel.
//
@@ -1015,17 +1074,15 @@ void AntiTrackingUtils::UpdateAntiTrackingInfoForChannel(nsIChannel* aChannel) {
// The partition key is computed based on the site, so it's no point to set it
// for channels other than http channels.
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
- if (!httpChannel || loadInfo->GetExternalContentPolicyType() !=
- ExtContentPolicy::TYPE_DOCUMENT) {
+ if (!httpChannel || contentType != ExtContentPolicy::TYPE_DOCUMENT) {
return;
}
- nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
- Unused << loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));
-
// Update the IsOnContentBlockingAllowList flag in the CookieJarSettings
// if this is a top level loading. For sub-document loading, this flag
// would inherit from the parent.
+ nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
+ Unused << loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));
net::CookieJarSettings::Cast(cookieJarSettings)
->UpdateIsOnContentBlockingAllowList(aChannel);
@@ -1033,7 +1090,7 @@ void AntiTrackingUtils::UpdateAntiTrackingInfoForChannel(nsIChannel* aChannel) {
// propagated to non-top level loads via CookieJarSetting.
nsCOMPtr<nsIURI> uri;
Unused << aChannel->GetURI(getter_AddRefs(uri));
- net::CookieJarSettings::Cast(cookieJarSettings)->SetPartitionKey(uri);
+ net::CookieJarSettings::Cast(cookieJarSettings)->SetPartitionKey(uri, false);
// Generate the fingerprinting randomization key for top-level loads. The key
// will automatically be propagated to sub loads.
diff --git a/toolkit/components/antitracking/StorageAccess.h b/toolkit/components/antitracking/StorageAccess.h
index 4dbd5355c0..bca26057f3 100644
--- a/toolkit/components/antitracking/StorageAccess.h
+++ b/toolkit/components/antitracking/StorageAccess.h
@@ -40,8 +40,6 @@ enum class StorageAccess {
// Allow access to the storage, but only if it is secure to do so in a
// private browsing context.
ePrivateBrowsing = 1,
- // Allow access to the storage, but only persist it for the current session
- eSessionScoped = 2,
// Allow access to the storage
eAllow = 3,
// Keep this at the end. Used for serialization, but not a valid value.
diff --git a/toolkit/components/antitracking/StorageAccessAPIHelper.cpp b/toolkit/components/antitracking/StorageAccessAPIHelper.cpp
index 5baa2c2557..b4fcf2f6e9 100644
--- a/toolkit/components/antitracking/StorageAccessAPIHelper.cpp
+++ b/toolkit/components/antitracking/StorageAccessAPIHelper.cpp
@@ -475,7 +475,6 @@ StorageAccessAPIHelper::CompleteAllowAccessForOnParentProcess(
[aParentContext, aTopLevelWindowId, trackingOrigin, trackingPrincipal,
aCookieBehavior,
aReason](int aAllowMode) -> RefPtr<StorageAccessPermissionGrantPromise> {
- MOZ_ASSERT(!aParentContext->IsInProcess());
// We don't have the window, send an IPC to the content process that
// owns the parent window. But there is a special case, for window.open,
// we'll return to the content process we need to inform when this
@@ -1060,12 +1059,7 @@ StorageAccessAPIHelper::CheckSameSiteCallingContextDecidesStorageAccessAPI(
}
}
- nsIChannel* chan = aDocument->GetChannel();
- if (!chan) {
- return Some(false);
- }
- nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo();
- if (loadInfo->GetIsThirdPartyContextToTopWindow()) {
+ if (AntiTrackingUtils::IsThirdPartyDocument(aDocument)) {
return Some(false);
}
diff --git a/toolkit/components/antitracking/StoragePrincipalHelper.cpp b/toolkit/components/antitracking/StoragePrincipalHelper.cpp
index 10be1112ca..79bafead2c 100644
--- a/toolkit/components/antitracking/StoragePrincipalHelper.cpp
+++ b/toolkit/components/antitracking/StoragePrincipalHelper.cpp
@@ -86,8 +86,10 @@ bool ChooseOriginAttributes(nsIChannel* aChannel, OriginAttributes& aAttrs,
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
-
- aAttrs.SetPartitionKey(principalURI);
+ bool foreignByAncestorContext =
+ AntiTrackingUtils::IsThirdPartyChannel(aChannel) &&
+ !loadInfo->GetIsThirdPartyContextToTopWindow();
+ aAttrs.SetPartitionKey(principalURI, foreignByAncestorContext);
return true;
}
@@ -313,7 +315,7 @@ nsresult StoragePrincipalHelper::GetPrincipal(nsIChannel* aChannel,
// We only support foreign partitioned principal when dFPI is enabled.
if (cjs->GetCookieBehavior() ==
nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN &&
- loadInfo->GetIsThirdPartyContextToTopWindow()) {
+ AntiTrackingUtils::IsThirdPartyChannel(aChannel)) {
outPrincipal = partitionedPrincipal;
}
break;
@@ -435,7 +437,7 @@ bool StoragePrincipalHelper::ShouldUsePartitionPrincipalForServiceWorker(
return false;
}
- return aWorkerPrivate->IsThirdPartyContextToTopWindow();
+ return aWorkerPrivate->IsThirdPartyContext();
}
// static
@@ -479,7 +481,7 @@ bool StoragePrincipalHelper::GetOriginAttributes(
// Otherwise, we will use the regular principal.
if (cjs->GetCookieBehavior() ==
nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN &&
- loadInfo->GetIsThirdPartyContextToTopWindow()) {
+ AntiTrackingUtils::IsThirdPartyChannel(aChannel)) {
ChooseOriginAttributes(aChannel, aAttributes, true);
}
break;
@@ -560,7 +562,7 @@ void StoragePrincipalHelper::UpdateOriginAttributesForNetworkState(
return;
}
- aAttributes.SetPartitionKey(aFirstPartyURI);
+ aAttributes.SetPartitionKey(aFirstPartyURI, false);
}
enum SupportedScheme { HTTP, HTTPS };
@@ -664,8 +666,9 @@ bool StoragePrincipalHelper::PartitionKeyHasBaseDomain(
nsString scheme;
nsString pkBaseDomain;
int32_t port;
- bool success = OriginAttributes::ParsePartitionKey(aPartitionKey, scheme,
- pkBaseDomain, port);
+ bool foreign;
+ bool success = OriginAttributes::ParsePartitionKey(
+ aPartitionKey, scheme, pkBaseDomain, port, foreign);
if (!success) {
return false;
@@ -674,4 +677,26 @@ bool StoragePrincipalHelper::PartitionKeyHasBaseDomain(
return aBaseDomain.Equals(pkBaseDomain);
}
+// static
+void StoragePrincipalHelper::UpdatePartitionKeyWithForeignAncestorBit(
+ nsAString& aKey, bool aForeignByAncestorContext) {
+ bool site = 0 == aKey.Find(u"(");
+ if (!site) {
+ return;
+ }
+ if (aForeignByAncestorContext) {
+ int32_t index = aKey.Find(u",f)");
+ if (index == -1) {
+ uint32_t cutStart = aKey.Length() - 1;
+ aKey.ReplaceLiteral(cutStart, 1, u",f)");
+ }
+ } else {
+ int32_t index = aKey.Find(u",f)");
+ if (index != -1) {
+ uint32_t cutLength = aKey.Length() - index;
+ aKey.ReplaceLiteral(index, cutLength, u")");
+ }
+ }
+}
+
} // namespace mozilla
diff --git a/toolkit/components/antitracking/StoragePrincipalHelper.h b/toolkit/components/antitracking/StoragePrincipalHelper.h
index f813417eb6..0fe362d8c1 100644
--- a/toolkit/components/antitracking/StoragePrincipalHelper.h
+++ b/toolkit/components/antitracking/StoragePrincipalHelper.h
@@ -351,6 +351,14 @@ class StoragePrincipalHelper final {
static bool PartitionKeyHasBaseDomain(const nsAString& aPartitionKey,
const nsAString& aBaseDomain);
+
+ // Partition keys can have the same-site bit added or removed from them.
+ // "(https,foo.com)", false -> "(https,foo.com)"
+ // "(https,foo.com,f)", false -> "(https,foo.com)"
+ // "(https,foo.com,f)", true -> "(https,foo.com,f)"
+ // "(https,foo.com)", true -> "(https,foo.com,f)"
+ static void UpdatePartitionKeyWithForeignAncestorBit(
+ nsAString& aKey, bool aForeignByAncestorContext);
};
} // namespace mozilla
diff --git a/toolkit/components/antitracking/StripOnShareLists/LGPL/LICENSE b/toolkit/components/antitracking/StripOnShareLists/LGPL/LICENSE
new file mode 100644
index 0000000000..6600f1c98d
--- /dev/null
+++ b/toolkit/components/antitracking/StripOnShareLists/LGPL/LICENSE
@@ -0,0 +1,165 @@
+GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/toolkit/components/antitracking/StripOnShareLists/LGPL/StripOnShareLGPL.json b/toolkit/components/antitracking/StripOnShareLists/LGPL/StripOnShareLGPL.json
new file mode 100644
index 0000000000..b34fbfff6b
--- /dev/null
+++ b/toolkit/components/antitracking/StripOnShareLists/LGPL/StripOnShareLGPL.json
@@ -0,0 +1,556 @@
+{
+ "global": {
+ "queryParams": [
+ "at_campaign",
+ "at_send_date",
+ "at_campaign_type",
+ "at_recipient_list",
+ "at_creation",
+ "at_recipient_id",
+ "at_emailtype",
+ "at_ptr_name",
+ "at_link",
+ "at_link_id",
+ "at_medium",
+ "at_link_type",
+ "at_link_origin",
+ "utm_email",
+ "utm_email_id",
+ "utm_campaign_id",
+ "utm_newsletter_id"
+ ],
+ "topLevelSites": ["*"]
+ },
+ "twitter": {
+ "queryParams": ["refsrc", "cxt", "s"],
+ "topLevelSites": ["www.twitter.com", "twitter.com", "x.com"]
+ },
+ "amazon": {
+ "queryParams": [
+ "pd_rd_i",
+ "pf_rd_i",
+ "pf_rd_m",
+ "pf_rd_s",
+ "pf_rd_t",
+ "pf_rd_w",
+ "adgrpid",
+ "ascsubtag",
+ "creative",
+ "creativeASIN",
+ "dchild",
+ "field-lbr_brands_browse-bin",
+ "hvadid",
+ "hvbmt",
+ "hvdev",
+ "hvlocphy",
+ "hvnetw",
+ "hvrand",
+ "hvtargid",
+ "hydadcr",
+ "initialIssue",
+ "ingress",
+ "linkCode",
+ "linkId",
+ "plattr",
+ "qid",
+ "rdc",
+ "refRID",
+ "th",
+ "ts_id",
+ "visitId",
+ "vtr",
+ "spIA",
+ "_encoding",
+ "qualifier"
+ ],
+ "topLevelSites": [
+ "www.amazon.com",
+ "www.amazon.de",
+ "www.amazon.nl",
+ "www.amazon.fr",
+ "www.amazon.co.jp",
+ "www.amazon.in",
+ "www.amazon.es",
+ "www.amazon.ac",
+ "www.amazon.cn",
+ "www.amazon.eg",
+ "www.amazon.in",
+ "www.amazon.co.uk",
+ "www.amazon.it",
+ "www.amazon.pl",
+ "www.amazon.sg",
+ "www.amazon.ca"
+ ]
+ },
+ "youtube": {
+ "queryParams": ["si", "feature", "kw"],
+ "topLevelSites": ["www.youtube.com", "www.youtu.be"]
+ },
+ "carousell": {
+ "queryParams": [
+ "referrer_search_query",
+ "referrer_search_query_source",
+ "referrer_request_id",
+ "referrer_sort_by",
+ "referrer_source",
+ "referrer_page_type",
+ "referrer_category_id",
+ "t-source",
+ "tap_index",
+ "t-referrer_browse_type",
+ "t-id"
+ ],
+ "topLevelSites": [
+ "ca.carousell.com",
+ "www.carousell.sg",
+ "www.carousell.com.hk",
+ "us.carousell.com",
+ "au.carousell.com"
+ ]
+ },
+ "etsy": {
+ "queryParams": ["click_sum", "ref", "click_key", "organic_search_click"],
+ "topLevelSites": ["www.etsy.com"]
+ },
+ "wikipedia": {
+ "queryParams": ["wprov"],
+ "topLevelSites": ["www.wikipedia.org"]
+ },
+ "wallstreetjournal": {
+ "queryParams": ["reflink"],
+ "topLevelSites": ["www.wsj.com"]
+ },
+ "theathletic": {
+ "queryParams": ["source"],
+ "topLevelSites": ["theathletic.com"]
+ },
+ "forbes": {
+ "queryParams": ["sh"],
+ "topLevelSites": ["www.forbes.com"]
+ },
+ "bloomberg": {
+ "queryParams": ["leadSource", "sref", "srnd"],
+ "topLevelSites": ["www.bloomberg.com"]
+ },
+ "tiktok": {
+ "queryParams": [
+ "share_app_id",
+ "share_author_id",
+ "tt_from",
+ "embed_source",
+ "referer_url",
+ "refer",
+ "is_from_webapp",
+ "sender_device",
+ "sec_uid",
+ "web_id",
+ "enter_method",
+ "t",
+ "q"
+ ],
+ "topLevelSites": ["www.tiktok.com"]
+ },
+ "bbc": {
+ "queryParams": [
+ "facebook_page",
+ "at_bbc_team",
+ "ocid",
+ "ns_mchannel",
+ "ns_source",
+ "ns_campaign",
+ "ns_linkname",
+ "ns_fee",
+ "xtor"
+ ],
+ "topLevelSites": ["www.bbc.com"]
+ },
+ "cnn": {
+ "queryParams": ["ref"],
+ "topLevelSites": ["www.cnn.co.jp", "www.cnn.com"]
+ },
+ "imdb": {
+ "queryParams": [
+ "pf_rd_p",
+ "pf_rd_i",
+ "pf_rd_m",
+ "pf_rd_s",
+ "pf_rd_t",
+ "pf_rd_r"
+ ],
+ "topLevelSites": ["www.imdb.com"]
+ },
+ "mirror": {
+ "queryParams": ["int_source"],
+ "topLevelSites": ["www.mirror.co.uk"]
+ },
+ "wise": {
+ "queryParams": ["partnerizecampaignID", "clickref", "adref", "partnerID"],
+ "topLevelSites": ["wise.com"]
+ },
+ "facebook": {
+ "queryParams": ["tracking", "extid", "mibextid"],
+ "topLevelSites": ["www.facebook.com"]
+ },
+ "lazada": {
+ "queryParams": [
+ "mkttid",
+ "laz_trackid",
+ "spm",
+ "clickTrackInfo",
+ "trafficFrom",
+ "scm",
+ "acm",
+ "ad_src",
+ "did",
+ "mp",
+ "cid",
+ "pos"
+ ],
+ "topLevelSites": [
+ "www.lazada.com.ph",
+ "www.lazada.vn",
+ "www.lazada.sg",
+ "www.lazada.com.my"
+ ]
+ },
+ "msn": {
+ "queryParams": ["cvid", "pc", "ei"],
+ "topLevelSites": ["www.msn.com"]
+ },
+ "aliexpress": {
+ "queryParams": [
+ "sk",
+ "dp",
+ "spm",
+ "scm",
+ "pvid",
+ "scm_id",
+ "scm-url",
+ "utparam",
+ "aff_fsk",
+ "aff_fcid",
+ "terminal_id",
+ "aff_trace_key",
+ "pdp_npi",
+ "aff_short_key",
+ "aff_platform",
+ "algo_pvid",
+ "curPageLogUid",
+ "mall_affr",
+ "algo_expid",
+ "gps-id"
+ ],
+ "topLevelSites": ["www.tiktok.com"]
+ },
+ "reddit": {
+ "queryParams": [
+ "ref",
+ "ref_source",
+ "ref_campaign",
+ "correlation_id",
+ "share_id"
+ ],
+ "topLevelSites": ["www.reddit.com"]
+ },
+ "wired": {
+ "queryParams": ["mbid"],
+ "topLevelSites": ["www.wired.co.uk", "www.wired.com"]
+ },
+ "yandex": {
+ "queryParams": [
+ "utm-term",
+ "did",
+ "from",
+ "msid",
+ "stid",
+ "mlid",
+ "persistent_id",
+ "source-serpid",
+ "suggest_reqid",
+ "clid"
+ ],
+ "topLevelSites": ["yandex.com"]
+ },
+ "ebay": {
+ "queryParams": [
+ "_trkparms",
+ "mkcid",
+ "_trksid",
+ "mkevt",
+ "amdata",
+ "ssuid",
+ "mkrid",
+ "campid",
+ "sssrc",
+ "ssspo",
+ "_from",
+ "hash"
+ ],
+ "topLevelSites": ["www.ebay.com"]
+ },
+ "flipkart": {
+ "queryParams": [
+ "_refId",
+ "store",
+ "ssid",
+ "affid",
+ "cid",
+ "iid",
+ "pwsvid",
+ "pageUID",
+ "lid"
+ ],
+ "topLevelSites": ["www.flipkart.com"]
+ },
+ "google": {
+ "queryParams": [
+ "sca_esv",
+ "ved",
+ "pcampaignid",
+ "rlz",
+ "ei",
+ "sxsrf",
+ "sourceid",
+ "aqs",
+ "cad",
+ "usg",
+ "dpr",
+ "dcr",
+ "sei",
+ "cd",
+ "vet",
+ "esrc",
+ "site",
+ "gs_l",
+ "gs_lp",
+ "sclient",
+ "oe",
+ "visit_id",
+ "biw",
+ "bih",
+ "gs_lcp",
+ "gs_lcrp"
+ ],
+ "topLevelSites": [
+ "www.google.com",
+ "www.google.ad",
+ "www.google.ae",
+ "www.google.com.af",
+ "www.google.com.ag",
+ "www.google.al",
+ "www.google.am",
+ "www.google.co.ao",
+ "www.google.com.ar",
+ "www.google.as",
+ "www.google.at",
+ "www.google.com.au",
+ "www.google.az",
+ "www.google.ba",
+ "www.google.com.bd",
+ "www.google.be",
+ "www.google.bf",
+ "www.google.bg",
+ "www.google.com.bh",
+ "www.google.bi",
+ "www.google.bj",
+ "www.google.com.bn",
+ "www.google.com.bo",
+ "www.google.com.br",
+ "www.google.bs",
+ "www.google.bt",
+ "www.google.co.bw",
+ "www.google.by",
+ "www.google.com.bz",
+ "www.google.ca",
+ "www.google.cd",
+ "www.google.cf",
+ "www.google.cg",
+ "www.google.ch",
+ "www.google.ci",
+ "www.google.co.ck",
+ "www.google.cl",
+ "www.google.cm",
+ "www.google.cn",
+ "www.google.com.co",
+ "www.google.co.cr",
+ "www.google.com.cu",
+ "www.google.cv",
+ "www.google.com.cy",
+ "www.google.cz",
+ "www.google.de",
+ "www.google.dj",
+ "www.google.dk",
+ "www.google.dm",
+ "www.google.com.do",
+ "www.google.dz",
+ "www.google.com.ec",
+ "www.google.ee",
+ "www.google.com.eg",
+ "www.google.es",
+ "www.google.com.et",
+ "www.google.fi",
+ "www.google.com.fj",
+ "www.google.fm",
+ "www.google.fr",
+ "www.google.ga",
+ "www.google.ge",
+ "www.google.gg",
+ "www.google.com.gh",
+ "www.google.com.gi",
+ "www.google.gl",
+ "www.google.gm",
+ "www.google.gr",
+ "www.google.com.gt",
+ "www.google.gy",
+ "www.google.com.hk",
+ "www.google.hn",
+ "www.google.hr",
+ "www.google.ht",
+ "www.google.hu",
+ "www.google.co.id",
+ "www.google.ie",
+ "www.google.co.il",
+ "www.google.im",
+ "www.google.co.in",
+ "www.google.iq",
+ "www.google.is",
+ "www.google.it",
+ "www.google.je",
+ "www.google.com.jm",
+ "www.google.jo",
+ "www.google.co.jp",
+ "www.google.co.ke",
+ "www.google.com.kh",
+ "www.google.ki",
+ "www.google.kg",
+ "www.google.co.kr",
+ "www.google.com.kw",
+ "www.google.kz",
+ "www.google.la",
+ "www.google.com.lb",
+ "www.google.li",
+ "www.google.lk",
+ "www.google.co.ls",
+ "www.google.lt",
+ "www.google.lu",
+ "www.google.lv",
+ "www.google.com.ly",
+ "www.google.co.ma",
+ "www.google.md",
+ "www.google.me",
+ "www.google.mg",
+ "www.google.mk",
+ "www.google.ml",
+ "www.google.com.mm",
+ "www.google.mn",
+ "www.google.com.mt",
+ "www.google.mu",
+ "www.google.mv",
+ "www.google.mw",
+ "www.google.com.mx",
+ "www.google.com.my",
+ "www.google.co.mz",
+ "www.google.com.na",
+ "www.google.com.ng",
+ "www.google.com.ni",
+ "www.google.ne",
+ "www.google.nl",
+ "www.google.no",
+ "www.google.com.np",
+ "www.google.nr",
+ "www.google.nu",
+ "www.google.co.nz",
+ "www.google.com.om",
+ "www.google.com.pa",
+ "www.google.com.pe",
+ "www.google.com.pg",
+ "www.google.com.ph",
+ "www.google.com.pk",
+ "www.google.pl",
+ "www.google.pn",
+ "www.google.com.pr",
+ "www.google.ps",
+ "www.google.pt",
+ "www.google.com.py",
+ "www.google.com.qa",
+ "www.google.ro",
+ "www.google.ru",
+ "www.google.rw",
+ "www.google.com.sa",
+ "www.google.com.sb",
+ "www.google.sc",
+ "www.google.se",
+ "www.google.com.sg",
+ "www.google.sh",
+ "www.google.si",
+ "www.google.sk",
+ "www.google.com.sl",
+ "www.google.sn",
+ "www.google.so",
+ "www.google.sm",
+ "www.google.sr",
+ "www.google.st",
+ "www.google.com.sv",
+ "www.google.td",
+ "www.google.tg",
+ "www.google.co.th",
+ "www.google.com.tj",
+ "www.google.tl",
+ "www.google.tm",
+ "www.google.tn",
+ "www.google.to",
+ "www.google.com.tr",
+ "www.google.tt",
+ "www.google.com.tw",
+ "www.google.co.tz",
+ "www.google.com.ua",
+ "www.google.co.ug",
+ "www.google.co.uk",
+ "www.google.com.uy",
+ "www.google.co.uz",
+ "www.google.com.vc",
+ "www.google.co.ve",
+ "www.google.co.vi",
+ "www.google.com.vn",
+ "www.google.vu",
+ "www.google.ws",
+ "www.google.rs",
+ "www.google.co.za",
+ "www.google.co.zm",
+ "www.google.co.zw",
+ "www.google.cat"
+ ]
+ },
+ "bing": {
+ "queryParams": ["qp", "cvid", "qs", "form", "sk", "sc", "sp"],
+ "topLevelSites": ["www.bing.com"]
+ },
+ "twitch": {
+ "queryParams": ["tt_content", "tt_medium"],
+ "topLevelSites": ["www.twitch.tv"]
+ },
+ "cnet": {
+ "queryParams": ["ftag"],
+ "topLevelSites": ["www.cnet.com"]
+ },
+ "nytimes": {
+ "queryParams": ["smid"],
+ "topLevelSites": ["www.nytimes.com"]
+ },
+ "github": {
+ "queryParams": ["email_token", "email_source"],
+ "topLevelSites": ["github.com"]
+ },
+ "linkedin": {
+ "queryParams": ["refId", "trk", "trackingId"],
+ "topLevelSites": ["ca.linkedin.com"]
+ },
+ "newyorker": {
+ "queryParams": ["esrc", "bxid", "cndid", "source", "mbid"],
+ "topLevelSites": ["www.newyorker.com"]
+ },
+ "bestbuy": {
+ "queryParams": ["acampID", "irclickid", "irgwc", "loc", "mpid", "intl"],
+ "topLevelSites": ["www.bestbuy.com"]
+ }
+}
diff --git a/toolkit/components/antitracking/data/StripOnShare.json b/toolkit/components/antitracking/StripOnShareLists/MPL2/StripOnShare.json
index 394723fa00..00cc973af6 100644
--- a/toolkit/components/antitracking/data/StripOnShare.json
+++ b/toolkit/components/antitracking/StripOnShareLists/MPL2/StripOnShare.json
@@ -55,7 +55,7 @@
},
"twitter": {
"queryParams": ["ref_src", "ref_url"],
- "topLevelSites": ["www.twitter.com", "twitter.com"]
+ "topLevelSites": ["www.twitter.com", "twitter.com", "x.com"]
},
"instagram": {
"queryParams": ["igshid", "ig_rid"],
diff --git a/toolkit/components/antitracking/URLDecorationStripper.cpp b/toolkit/components/antitracking/URLDecorationStripper.cpp
index 38af391945..fc94fe8ed5 100644
--- a/toolkit/components/antitracking/URLDecorationStripper.cpp
+++ b/toolkit/components/antitracking/URLDecorationStripper.cpp
@@ -22,8 +22,8 @@ namespace mozilla {
nsresult URLDecorationStripper::StripTrackingIdentifiers(nsIURI* aURI,
nsACString& aOutSpec) {
- nsAutoString tokenList;
- nsresult rv = Preferences::GetString(kPrefName, tokenList);
+ nsAutoCString tokenList;
+ nsresult rv = Preferences::GetCString(kPrefName, tokenList);
ToLowerCase(tokenList);
nsAutoCString path;
@@ -34,12 +34,12 @@ nsresult URLDecorationStripper::StripTrackingIdentifiers(nsIURI* aURI,
int32_t queryBegins = path.FindChar('?');
// Only positive values are valid since the path must begin with a '/'.
if (queryBegins > 0) {
- for (const nsAString& token : tokenList.Split(' ')) {
+ for (const nsACString& token : tokenList.Split(' ')) {
if (token.IsEmpty()) {
continue;
}
- nsAutoString value;
+ nsAutoCString value;
if (URLParams::Extract(Substring(path, queryBegins + 1), token, value) &&
!value.IsVoid()) {
// Tracking identifier found in the URL!
diff --git a/toolkit/components/antitracking/URLQueryStringStripper.cpp b/toolkit/components/antitracking/URLQueryStringStripper.cpp
index 2e154b9103..5eb513472b 100644
--- a/toolkit/components/antitracking/URLQueryStringStripper.cpp
+++ b/toolkit/components/antitracking/URLQueryStringStripper.cpp
@@ -17,6 +17,7 @@
#include "nsIURIMutator.h"
#include "nsUnicharUtils.h"
#include "nsURLHelper.h"
+#include "nsNetUtil.h"
#include "mozilla/dom/StripOnShareRuleBinding.h"
namespace {
@@ -84,64 +85,16 @@ URLQueryStringStripper::StripForCopyOrShare(nsIURI* aURI,
NS_ENSURE_ARG_POINTER(strippedURI);
int aStripCount = 0;
- nsAutoCString query;
- nsresult rv = aURI->GetQuery(query);
- NS_ENSURE_SUCCESS(rv, rv);
- // We don't need to do anything if there is no query string.
- if (query.IsEmpty()) {
- Telemetry::Accumulate(Telemetry::STRIP_ON_SHARE_PARAMS_REMOVED, 0);
- return NS_OK;
- }
- nsAutoCString host;
- rv = aURI->GetHost(host);
+ nsresult rv =
+ StripForCopyOrShareInternal(aURI, strippedURI, aStripCount, false);
NS_ENSURE_SUCCESS(rv, rv);
- URLParams params;
-
- URLParams::Parse(query, true, [&](nsString&& name, nsString&& value) {
- nsAutoString lowerCaseName;
- ToLowerCase(name, lowerCaseName);
- // Look through the global rules.
- dom::StripRule globalRule;
- bool keyExists = mStripOnShareMap.Get("*"_ns, &globalRule);
- // There should always be a global rule.
- MOZ_ASSERT(keyExists);
- for (const auto& param : globalRule.mQueryParams) {
- if (param == lowerCaseName) {
- aStripCount++;
- return true;
- }
- }
-
- // Check for site specific rules.
- dom::StripRule siteSpecificRule;
- keyExists = mStripOnShareMap.Get(host, &siteSpecificRule);
- if (keyExists) {
- for (const auto& param : siteSpecificRule.mQueryParams) {
- if (param == lowerCaseName) {
- aStripCount++;
- return true;
- }
- }
- }
-
- params.Append(name, value);
- return true;
- });
-
Telemetry::Accumulate(Telemetry::STRIP_ON_SHARE_PARAMS_REMOVED, aStripCount);
if (!aStripCount) {
return NS_OK;
}
- nsAutoString newQuery;
- params.Serialize(newQuery, false);
-
- Unused << NS_MutateURI(aURI)
- .SetQuery(NS_ConvertUTF16toUTF8(newQuery))
- .Finalize(strippedURI);
-
// To calculate difference in length of the URL
// after stripping occurs for Telemetry
nsAutoCString specOriginalURI;
@@ -308,9 +261,8 @@ nsresult URLQueryStringStripper::StripQueryString(nsIURI* aURI,
URLParams params;
- URLParams::Parse(query, false, [&](nsString&& name, nsString&& value) {
- nsAutoString lowerCaseName;
-
+ URLParams::Parse(query, false, [&](nsCString&& name, nsCString&& value) {
+ nsAutoCString lowerCaseName;
ToLowerCase(name, lowerCaseName);
if (mList.Contains(lowerCaseName)) {
@@ -320,7 +272,7 @@ nsresult URLQueryStringStripper::StripQueryString(nsIURI* aURI,
// this will only count query params listed in the Histogram definition.
// Calls for any other query params will be discarded.
nsAutoCString telemetryLabel("param_");
- AppendUTF16toUTF8(lowerCaseName, telemetryLabel);
+ telemetryLabel.Append(lowerCaseName);
Telemetry::AccumulateCategorical(
Telemetry::QUERY_STRIPPING_COUNT_BY_PARAM, telemetryLabel);
@@ -336,13 +288,10 @@ nsresult URLQueryStringStripper::StripQueryString(nsIURI* aURI,
return NS_OK;
}
- nsAutoString newQuery;
+ nsAutoCString newQuery;
params.Serialize(newQuery, false);
- Unused << NS_MutateURI(uri)
- .SetQuery(NS_ConvertUTF16toUTF8(newQuery))
- .Finalize(aOutput);
-
+ Unused << NS_MutateURI(uri).SetQuery(newQuery).Finalize(aOutput);
return NS_OK;
}
@@ -362,10 +311,10 @@ bool URLQueryStringStripper::CheckAllowList(nsIURI* aURI) {
return mAllowList.Contains(baseDomain);
}
-void URLQueryStringStripper::PopulateStripList(const nsAString& aList) {
+void URLQueryStringStripper::PopulateStripList(const nsACString& aList) {
mList.Clear();
- for (const nsAString& item : aList.Split(' ')) {
+ for (const nsACString& item : aList.Split(' ')) {
mList.Insert(item);
}
}
@@ -380,7 +329,7 @@ void URLQueryStringStripper::PopulateAllowList(const nsACString& aList) {
NS_IMETHODIMP
URLQueryStringStripper::OnQueryStrippingListUpdate(
- const nsAString& aStripList, const nsACString& aAllowList) {
+ const nsACString& aStripList, const nsACString& aAllowList) {
PopulateStripList(aStripList);
PopulateAllowList(aAllowList);
return NS_OK;
@@ -396,8 +345,7 @@ URLQueryStringStripper::OnStripOnShareUpdate(const nsTArray<nsString>& aArgs,
continue;
}
for (const auto& topLevelSite : rule.mTopLevelSites) {
- mStripOnShareMap.InsertOrUpdate(NS_ConvertUTF16toUTF8(topLevelSite),
- rule);
+ mStripOnShareMap.InsertOrUpdate(topLevelSite, rule);
}
}
return NS_OK;
@@ -407,10 +355,9 @@ NS_IMETHODIMP
URLQueryStringStripper::TestGetStripList(nsACString& aStripList) {
aStripList.Truncate();
- StringJoinAppend(aStripList, " "_ns, mList,
- [](auto& aResult, const auto& aValue) {
- aResult.Append(NS_ConvertUTF16toUTF8(aValue));
- });
+ StringJoinAppend(
+ aStripList, " "_ns, mList,
+ [](auto& aResult, const auto& aValue) { aResult.Append(aValue); });
return NS_OK;
}
@@ -426,4 +373,110 @@ URLQueryStringStripper::Observe(nsISupports*, const char* aTopic,
return NS_OK;
}
+nsresult URLQueryStringStripper::StripForCopyOrShareInternal(
+ nsIURI* aURI, nsIURI** strippedURI, int& aStripCount,
+ bool aStripNestedURIs) {
+ nsAutoCString query;
+ nsresult rv = aURI->GetQuery(query);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // We don't need to do anything if there is no query string.
+ if (query.IsEmpty()) {
+ Telemetry::Accumulate(Telemetry::STRIP_ON_SHARE_PARAMS_REMOVED, 0);
+ return NS_OK;
+ }
+
+ nsAutoCString host;
+ rv = aURI->GetHost(host);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ URLParams params;
+
+ URLParams::Parse(query, false, [&](nsCString&& name, nsCString&& value) {
+ nsAutoCString lowerCaseName;
+ ToLowerCase(name, lowerCaseName);
+
+ // Look through the global rules.
+ dom::StripRule globalRule;
+ bool keyExists = mStripOnShareMap.Get("*"_ns, &globalRule);
+ // There should always be a global rule.
+ MOZ_ASSERT(keyExists);
+
+ // Look through the global rules.
+ for (const auto& param : globalRule.mQueryParams) {
+ if (param == lowerCaseName) {
+ aStripCount++;
+ return true;
+ }
+ }
+
+ // Check for site specific rules.
+ dom::StripRule siteSpecificRule;
+ keyExists = mStripOnShareMap.Get(host, &siteSpecificRule);
+ if (keyExists) {
+ for (const auto& param : siteSpecificRule.mQueryParams) {
+ if (param == lowerCaseName) {
+ aStripCount++;
+ return true;
+ }
+ }
+ }
+
+ // Only if it is top layer of the recursion then it
+ // checks if the value of the query parameter is a valid URI
+ // if not then it gets added back to the query, if it is then
+ // it gets passed back into this method but with the recursive
+ // stripping flag set to true
+ if (!aStripNestedURIs) {
+ nsAutoCString decodeValue;
+ URLParams::DecodeString(value, decodeValue);
+
+ nsCOMPtr<nsIURI> nestedURI;
+ rv = NS_NewURI(getter_AddRefs(nestedURI), decodeValue);
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ params.Append(name, value);
+ return true;
+ }
+
+ nsCOMPtr<nsIURI> strippedNestedURI;
+ rv = StripForCopyOrShareInternal(
+ nestedURI, getter_AddRefs(strippedNestedURI), aStripCount, true);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+
+ if (!strippedNestedURI) {
+ params.Append(name, value);
+ return true;
+ }
+
+ nsAutoCString nestedURIString;
+ rv = strippedNestedURI->GetSpec(nestedURIString);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+
+ // Encodes URI
+ nsAutoCString encodedURI;
+ URLParams::SerializeString(nestedURIString, encodedURI);
+
+ params.Append(name, encodedURI);
+ return true;
+ }
+
+ params.Append(name, value);
+ return true;
+ });
+
+ // Returns null for strippedURI if no query params have been stripped.
+ if (!aStripCount) {
+ return NS_OK;
+ }
+
+ nsAutoCString newQuery;
+ params.Serialize(newQuery, false);
+ return NS_MutateURI(aURI).SetQuery(newQuery).Finalize(strippedURI);
+}
+
} // namespace mozilla
diff --git a/toolkit/components/antitracking/URLQueryStringStripper.h b/toolkit/components/antitracking/URLQueryStringStripper.h
index 89466cfd12..3e8e8203a9 100644
--- a/toolkit/components/antitracking/URLQueryStringStripper.h
+++ b/toolkit/components/antitracking/URLQueryStringStripper.h
@@ -46,10 +46,16 @@ class URLQueryStringStripper final : public nsIObserver,
bool CheckAllowList(nsIURI* aURI);
- void PopulateStripList(const nsAString& aList);
+ void PopulateStripList(const nsACString& aList);
void PopulateAllowList(const nsACString& aList);
- nsTHashSet<nsString> mList;
+ // Recursive helper function that helps strip URIs of tracking parameters
+ // and enables the stripping of tracking paramerters that are in a URI which
+ // is nested in a query parameter
+ nsresult StripForCopyOrShareInternal(nsIURI* aURI, nsIURI** strippedURI,
+ int& aStripCount, bool aStripNestedURIs);
+
+ nsTHashSet<nsCString> mList;
nsTHashSet<nsCString> mAllowList;
nsCOMPtr<nsIURLQueryStrippingListService> mListService;
nsTHashMap<nsCString, dom::StripRule> mStripOnShareMap;
diff --git a/toolkit/components/antitracking/URLQueryStrippingListService.sys.mjs b/toolkit/components/antitracking/URLQueryStrippingListService.sys.mjs
index baaa9f3824..d9c1995821 100644
--- a/toolkit/components/antitracking/URLQueryStrippingListService.sys.mjs
+++ b/toolkit/components/antitracking/URLQueryStrippingListService.sys.mjs
@@ -31,10 +31,9 @@ XPCOMUtils.defineLazyPreferenceGetter(
PREF_STRIP_IS_TEST
);
-// Lazy getter for the strip-on-share strip list.
-ChromeUtils.defineLazyGetter(lazy, "StripOnShareList", async () => {
+async function fetchAndParseList(fileName) {
let response = await fetch(
- "chrome://global/content/antitracking/StripOnShare.json"
+ "chrome://global/content/antitracking/" + fileName
);
if (!response.ok) {
lazy.logger.error(
@@ -44,7 +43,48 @@ ChromeUtils.defineLazyGetter(lazy, "StripOnShareList", async () => {
"Error fetching strip-on-share strip list" + response.status
);
}
- return response.json();
+ return await response.json();
+}
+
+// Lazy getter for the strip-on-share strip list.
+ChromeUtils.defineLazyGetter(lazy, "StripOnShareList", async () => {
+ let [stripOnShareList, stripOnShareLGPLParams] = await Promise.all([
+ fetchAndParseList("StripOnShare.json"),
+ fetchAndParseList("StripOnShareLGPL.json"),
+ ]);
+
+ if (!stripOnShareList || !stripOnShareLGPLParams) {
+ lazy.logger.error("Error strip-on-share strip list were not loaded");
+ throw new Error("Error fetching strip-on-share strip list were not loaded");
+ }
+
+ // Combines the mozilla licensed strip on share param
+ // list and the LGPL licensed strip on share param list
+ for (const key in stripOnShareLGPLParams) {
+ if (Object.hasOwn(stripOnShareList, key)) {
+ stripOnShareList[key].queryParams.push(
+ ...stripOnShareLGPLParams[key].queryParams
+ );
+
+ stripOnShareList[key].topLevelSites.push(
+ ...stripOnShareLGPLParams[key].topLevelSites
+ );
+
+ // Removes duplicates topLevelSitres
+ stripOnShareList[key].topLevelSites = [
+ ...new Set(stripOnShareList[key].topLevelSites),
+ ];
+
+ // Removes duplicates queryParams
+ stripOnShareList[key].queryParams = [
+ ...new Set(stripOnShareList[key].queryParams),
+ ];
+ } else {
+ stripOnShareList[key] = stripOnShareLGPLParams[key];
+ }
+ }
+
+ return stripOnShareList;
});
export class URLQueryStrippingListService {
@@ -142,11 +182,11 @@ export class URLQueryStrippingListService {
}
// Get the list from pref.
- this._onPrefUpdate(
+ await this._onPrefUpdate(
PREF_STRIP_LIST_NAME,
Services.prefs.getStringPref(PREF_STRIP_LIST_NAME, "")
);
- this._onPrefUpdate(
+ await this._onPrefUpdate(
PREF_ALLOW_LIST_NAME,
Services.prefs.getStringPref(PREF_ALLOW_LIST_NAME, "")
);
@@ -219,7 +259,7 @@ export class URLQueryStrippingListService {
this._notifyObservers();
}
- _onPrefUpdate(pref, value) {
+ async _onPrefUpdate(pref, value) {
switch (pref) {
case PREF_STRIP_LIST_NAME:
this.prefStripList = new Set(value ? value.split(" ") : []);
@@ -235,7 +275,7 @@ export class URLQueryStrippingListService {
}
this._notifyObservers();
- this._notifyStripOnShareObservers();
+ await this._notifyStripOnShareObservers();
}
_getListFromSharedData() {
diff --git a/toolkit/components/antitracking/jar.mn b/toolkit/components/antitracking/jar.mn
index e45b37c5b6..2f8c4d7ee4 100644
--- a/toolkit/components/antitracking/jar.mn
+++ b/toolkit/components/antitracking/jar.mn
@@ -9,4 +9,7 @@ toolkit.jar:
# domain1: {queryParams: [param1, param2, ..], topLevelSites: [www.site.de, www.site.com,...]}, domain2: {...}
# This list will be consumed from the nsIQueryStrippingListService and
# later be dispatched to the nsIURLQueryStringStripper in a further processed form.
- content/global/antitracking/StripOnShare.json (data/StripOnShare.json)
+ content/global/antitracking/StripOnShare.json (StripOnShareLists/MPL2/StripOnShare.json)
+
+ # Separate StripOnShare list for parameters that are covered in the LGPL License
+ content/global/antitracking/StripOnShareLGPL.json (StripOnShareLists/LGPL/StripOnShareLGPL.json)
diff --git a/toolkit/components/antitracking/nsIURLQueryStringStripper.idl b/toolkit/components/antitracking/nsIURLQueryStringStripper.idl
index 372cc0f94a..b3e939094b 100644
--- a/toolkit/components/antitracking/nsIURLQueryStringStripper.idl
+++ b/toolkit/components/antitracking/nsIURLQueryStringStripper.idl
@@ -23,7 +23,7 @@ interface nsIURLQueryStringStripper : nsISupports {
// Strip the query parameters that are in the strip list. Return the amount of
// query parameters that have been stripped. Returns 0 if no query parameters
// have been stripped or the feature is disabled.
- uint32_t strip(in nsIURI aURI, in bool aIsPBM, out nsIURI aOutput);
+ uint32_t strip(in nsIURI aURI, in boolean aIsPBM, out nsIURI aOutput);
// Strip the query parameters that are in the stripForCopy/Share strip list.
// Returns ether the stripped URI or null if no query parameters have been stripped
diff --git a/toolkit/components/antitracking/nsIURLQueryStrippingListService.idl b/toolkit/components/antitracking/nsIURLQueryStrippingListService.idl
index f746126c13..1af2c4727f 100644
--- a/toolkit/components/antitracking/nsIURLQueryStrippingListService.idl
+++ b/toolkit/components/antitracking/nsIURLQueryStrippingListService.idl
@@ -22,7 +22,7 @@ interface nsIURLQueryStrippingListObserver : nsISupports
* A comma-separated list of hosts (eTLD+1) that are exempt from query
* stripping.
*/
- void onQueryStrippingListUpdate(in AString aStripList, in ACString aAllowList);
+ void onQueryStrippingListUpdate(in ACString aStripList, in ACString aAllowList);
/**
* Called by nsIQueryStrippingListService when the list of query stripping
diff --git a/toolkit/components/antitracking/test/browser/browser.toml b/toolkit/components/antitracking/test/browser/browser.toml
index 9d0874ed3a..edb429b49e 100644
--- a/toolkit/components/antitracking/test/browser/browser.toml
+++ b/toolkit/components/antitracking/test/browser/browser.toml
@@ -1,5 +1,9 @@
[DEFAULT]
-skip-if = ["os == 'linux' && (asan || tsan)"] # bug 1662229 - task exception
+skip-if = [
+ "os == 'linux' && os_version == '18.04' && asan", # bug 1662229 - task exception
+ "os == 'linux' && os_version == '18.04' && tsan", # bug 1662229 - task exception
+ "debug", # bug 1884982 - takes 20+ minutes to run on debug
+]
prefs = [
"dom.storage_access.prompt.testing=true", # Disable the Storage Access API prompts for all of the tests in this directory
"dom.storage_access.prompt.testing.allow=true",
@@ -108,6 +112,8 @@ skip-if = ["os == 'mac' && !debug"] # Bug 1503778, 1577362
["browser_onModifyRequestNotificationForTrackingResources.js"]
+["browser_partitionedABA.js"]
+
["browser_partitionedClearSiteDataHeader.js"]
support-files = ["clearSiteData.sjs"]
@@ -194,7 +200,7 @@ support-files = [
"!/browser/components/originattributes/test/browser/file_thirdPartyChild.request.html",
"!/browser/components/originattributes/test/browser/file_thirdPartyChild.script.js",
"!/browser/components/originattributes/test/browser/file_thirdPartyChild.sharedworker.js",
- "!/browser/components/originattributes/test/browser/file_thirdPartyChild.video.ogv",
+ "!/browser/components/originattributes/test/browser/file_thirdPartyChild.video.webm",
"!/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.fetch.html",
"!/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.js",
"!/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.request.html",
@@ -210,7 +216,7 @@ support-files = [
"file_saveAsImage.sjs",
"file_saveAsVideo.sjs",
"file_saveAsPageInfo.html",
- "file_video.ogv",
+ "file_video.webm",
]
["browser_staticPartition_tls_session.js"]
diff --git a/toolkit/components/antitracking/test/browser/browser_doublyNestedTracker.js b/toolkit/components/antitracking/test/browser/browser_doublyNestedTracker.js
index 4f38b29a7d..c3b6342ce1 100644
--- a/toolkit/components/antitracking/test/browser/browser_doublyNestedTracker.js
+++ b/toolkit/components/antitracking/test/browser/browser_doublyNestedTracker.js
@@ -36,7 +36,7 @@ add_task(async function () {
async function runChecks() {
is(document.cookie, "", "No cookies for me");
document.cookie = "name=value";
- is(document.cookie, "name=value", "I have the cookies!");
+ is(document.cookie, "", "I don't have the cookies!");
}
await new Promise(resolve => {
diff --git a/toolkit/components/antitracking/test/browser/browser_partitionedABA.js b/toolkit/components/antitracking/test/browser/browser_partitionedABA.js
new file mode 100644
index 0000000000..b6acc82d48
--- /dev/null
+++ b/toolkit/components/antitracking/test/browser/browser_partitionedABA.js
@@ -0,0 +1,86 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+/*
+ * A test to verify that ABA iframes partition at least localStorage and document.cookie
+ */
+
+"use strict";
+
+add_setup(async function () {
+ await setCookieBehaviorPref(
+ BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN,
+ false
+ );
+});
+
+add_task(async function runTest() {
+ info("Creating the tab");
+ let tab = BrowserTestUtils.addTab(gBrowser, TEST_TOP_PAGE);
+ gBrowser.selectedTab = tab;
+
+ let browser = tab.linkedBrowser;
+ await BrowserTestUtils.browserLoaded(browser);
+
+ info("Creating the third-party iframe");
+ let ifrBC = await SpecialPowers.spawn(
+ browser,
+ [TEST_TOP_PAGE_7],
+ async page => {
+ let ifr = content.document.createElement("iframe");
+
+ let loading = ContentTaskUtils.waitForEvent(ifr, "load");
+ content.document.body.appendChild(ifr);
+ ifr.src = page;
+ await loading;
+
+ return ifr.browsingContext;
+ }
+ );
+
+ info("Creating the ABA iframe");
+ let ifrABABC = await SpecialPowers.spawn(
+ ifrBC,
+ [TEST_TOP_PAGE],
+ async page => {
+ let ifr = content.document.createElement("iframe");
+
+ let loading = ContentTaskUtils.waitForEvent(ifr, "load");
+ content.document.body.appendChild(ifr);
+ ifr.src = page;
+ await loading;
+
+ return ifr.browsingContext;
+ }
+ );
+
+ info("Write cookie to the ABA third-party iframe");
+ await SpecialPowers.spawn(ifrABABC, [], async _ => {
+ content.document.cookie = "foo; SameSite=None; Secure; Partitioned";
+ });
+
+ let cookie = await SpecialPowers.spawn(browser, [], async () => {
+ return content.document.cookie;
+ });
+ is(cookie, "", "Cookie is not in the top level");
+
+ info("Write localstorage to the ABA third-party iframe");
+ await SpecialPowers.spawn(ifrABABC, [], async _ => {
+ content.localStorage.setItem("foo", "bar");
+ });
+
+ let storage = await SpecialPowers.spawn(browser, [], async () => {
+ return content.localStorage.getItem("foo");
+ });
+ is(storage, null, "LocalStorage update is not in the top level");
+
+ info("Clean up");
+ BrowserTestUtils.removeTab(tab);
+ await new Promise(resolve => {
+ Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, () =>
+ resolve()
+ );
+ });
+});
diff --git a/toolkit/components/antitracking/test/browser/browser_staticPartition_cache.js b/toolkit/components/antitracking/test/browser/browser_staticPartition_cache.js
index 9afe3180fb..9383e5a6f9 100644
--- a/toolkit/components/antitracking/test/browser/browser_staticPartition_cache.js
+++ b/toolkit/components/antitracking/test/browser/browser_staticPartition_cache.js
@@ -105,7 +105,7 @@ add_task(async function () {
// The CSS cache needs to be cleared in-process.
content.windowUtils.clearSharedStyleSheetCache();
- let videoURL = arg.urlPrefix + "file_thirdPartyChild.video.ogv";
+ let videoURL = arg.urlPrefix + "file_thirdPartyChild.video.webm";
let audioURL = arg.urlPrefix + "file_thirdPartyChild.audio.ogg";
let URLSuffix = "?r=" + arg.randomSuffix;
@@ -176,7 +176,7 @@ add_task(async function () {
"xhr.html",
"worker.xhr.html",
"audio.ogg",
- "video.ogv",
+ "video.webm",
"fetch.html",
"worker.fetch.html",
"request.html",
diff --git a/toolkit/components/antitracking/test/browser/browser_staticPartition_saveAs.js b/toolkit/components/antitracking/test/browser/browser_staticPartition_saveAs.js
index b9984829eb..9da751cadb 100644
--- a/toolkit/components/antitracking/test/browser/browser_staticPartition_saveAs.js
+++ b/toolkit/components/antitracking/test/browser/browser_staticPartition_saveAs.js
@@ -401,7 +401,7 @@ add_task(async function testPageInfoMediaSaveAs() {
);
info("Open the media panel of the pageinfo.");
- let pageInfo = BrowserPageInfo(
+ let pageInfo = BrowserCommands.pageInfo(
gBrowser.selectedBrowser.currentURI.spec,
"mediaTab"
);
@@ -478,7 +478,7 @@ add_task(async function testPageInfoMediaMultipleSelectedSaveAs() {
);
info("Open the media panel of the pageinfo.");
- let pageInfo = BrowserPageInfo(
+ let pageInfo = BrowserCommands.pageInfo(
gBrowser.selectedBrowser.currentURI.spec,
"mediaTab"
);
diff --git a/toolkit/components/antitracking/test/browser/browser_storageAccessThirdPartyChecks_alwaysPartition.js b/toolkit/components/antitracking/test/browser/browser_storageAccessThirdPartyChecks_alwaysPartition.js
index f31c7893d0..8ea97dac68 100644
--- a/toolkit/components/antitracking/test/browser/browser_storageAccessThirdPartyChecks_alwaysPartition.js
+++ b/toolkit/components/antitracking/test/browser/browser_storageAccessThirdPartyChecks_alwaysPartition.js
@@ -53,6 +53,7 @@ AntiTracking._createTask({
"https://tracking.example.org",
"https://tracking.example.org",
"https://tracking.example.org",
+ "https://another-tracking.example.net",
"https://itisatracker.org",
],
});
diff --git a/toolkit/components/antitracking/test/browser/file_saveAsPageInfo.html b/toolkit/components/antitracking/test/browser/file_saveAsPageInfo.html
index aa3de2a555..c8c409d130 100644
--- a/toolkit/components/antitracking/test/browser/file_saveAsPageInfo.html
+++ b/toolkit/components/antitracking/test/browser/file_saveAsPageInfo.html
@@ -1,6 +1,6 @@
<html>
<body>
<img src="http://example.net/browser/toolkit/components/antitracking/test/browser/raptor.jpg" id="image1">
- <video src="http://example.net/browser/toolkit/components/antitracking/test/browser/file_video.ogv" id="video1"> </video>
+ <video src="http://example.net/browser/toolkit/components/antitracking/test/browser/file_video.webm" id="video1"> </video>
</body>
</html>
diff --git a/toolkit/components/antitracking/test/browser/file_video.ogv b/toolkit/components/antitracking/test/browser/file_video.ogv
deleted file mode 100644
index 68dee3cf2b..0000000000
--- a/toolkit/components/antitracking/test/browser/file_video.ogv
+++ /dev/null
Binary files differ
diff --git a/toolkit/components/antitracking/test/browser/file_video.webm b/toolkit/components/antitracking/test/browser/file_video.webm
new file mode 100644
index 0000000000..2cee5a9e51
--- /dev/null
+++ b/toolkit/components/antitracking/test/browser/file_video.webm
Binary files differ
diff --git a/toolkit/components/antitracking/test/gtest/TestStoragePrincipalHelper.cpp b/toolkit/components/antitracking/test/gtest/TestStoragePrincipalHelper.cpp
index 44959e4dc8..666757c382 100644
--- a/toolkit/components/antitracking/test/gtest/TestStoragePrincipalHelper.cpp
+++ b/toolkit/components/antitracking/test/gtest/TestStoragePrincipalHelper.cpp
@@ -45,7 +45,7 @@ nsresult CreateMockChannel(nsIPrincipal* aPrincipal, bool isThirdParty,
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsILoadInfo> mockLoadInfo = mockChannel->LoadInfo();
- rv = mockLoadInfo->SetIsThirdPartyContextToTopWindow(isThirdParty);
+ rv = mockLoadInfo->SetIsInThirdPartyContext(isThirdParty);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsICookieJarSettings> cjs;
diff --git a/toolkit/components/antitracking/test/xpcshell/test_validate_strip_on_share_list.js b/toolkit/components/antitracking/test/xpcshell/test_validate_strip_on_share_list.js
index 48a0bd4682..f8be5fccad 100644
--- a/toolkit/components/antitracking/test/xpcshell/test_validate_strip_on_share_list.js
+++ b/toolkit/components/antitracking/test/xpcshell/test_validate_strip_on_share_list.js
@@ -7,67 +7,19 @@ const { JsonSchema } = ChromeUtils.importESModule(
"resource://gre/modules/JsonSchema.sys.mjs"
);
-let stripOnShareList;
+let stripOnShareMergedList, stripOnShareLGPLParams, stripOnShareList;
-// Fetching strip on share list
-add_setup(async function () {
- /* globals fetch */
- let response = await fetch(
- "chrome://global/content/antitracking/StripOnShare.json"
- );
+async function fetchAndParseList(fileName) {
+ let response = await fetch("chrome://global/content/" + fileName);
if (!response.ok) {
throw new Error(
"Error fetching strip-on-share strip list" + response.status
);
}
- stripOnShareList = await response.json();
-});
-
-// Check if the Strip on Share list contains any duplicate params
-add_task(async function test_check_duplicates() {
- let stripOnShareParams = stripOnShareList;
-
- const allQueryParams = [];
-
- for (const domain in stripOnShareParams) {
- for (let param in stripOnShareParams[domain].queryParams) {
- allQueryParams.push(stripOnShareParams[domain].queryParams[param]);
- }
- }
-
- let setOfParams = new Set(allQueryParams);
-
- if (setOfParams.size != allQueryParams.length) {
- let setToCheckDupes = new Set();
- let dupeList = new Set();
- for (const domain in stripOnShareParams) {
- for (let param in stripOnShareParams[domain].queryParams) {
- let tempParam = stripOnShareParams[domain].queryParams[param];
-
- if (setToCheckDupes.has(tempParam)) {
- dupeList.add(tempParam);
- } else {
- setToCheckDupes.add(tempParam);
- }
- }
- }
-
- Assert.equal(
- setOfParams.size,
- allQueryParams.length,
- "There are duplicates rules. The duplicate rules are " + [...dupeList]
- );
- }
+ return await response.json();
+}
- Assert.equal(
- setOfParams.size,
- allQueryParams.length,
- "There are no duplicates rules."
- );
-});
-
-// Validate the format of Strip on Share list with Schema
-add_task(async function test_check_schema() {
+async function validateSchema(paramList, nameOfList) {
let schema = {
$schema: "http://json-schema.org/draft-07/schema#",
type: "object",
@@ -88,13 +40,86 @@ add_task(async function test_check_schema() {
required: ["global"],
};
- let stripOnShareParams = stripOnShareList;
let validator = new JsonSchema.Validator(schema);
- let { valid, errors } = validator.validate(stripOnShareParams);
+ let { valid, errors } = validator.validate(paramList);
if (!valid) {
- info("validation errors: " + JSON.stringify(errors, null, 2));
+ info(
+ nameOfList +
+ " JSON contains these validation errors: " +
+ JSON.stringify(errors, null, 2)
+ );
+ }
+
+ Assert.ok(valid, nameOfList + " JSON is valid");
+}
+
+// Fetching strip on share list
+add_setup(async function () {
+ /* globals fetch */
+
+ [stripOnShareList, stripOnShareLGPLParams] = await Promise.all([
+ fetchAndParseList("antitracking/StripOnShare.json"),
+ fetchAndParseList("antitracking/StripOnShareLGPL.json"),
+ ]);
+
+ stripOnShareMergedList = stripOnShareList;
+
+ // Combines the mozilla licensed strip on share param
+ // list and the LGPL licensed strip on share param list
+ for (const key in stripOnShareLGPLParams) {
+ if (Object.hasOwn(stripOnShareMergedList, key)) {
+ stripOnShareMergedList[key].queryParams.push(
+ ...stripOnShareLGPLParams[key].queryParams
+ );
+ } else {
+ stripOnShareMergedList[key] = stripOnShareLGPLParams[key];
+ }
+ }
+});
+
+// Check if the Strip on Share list contains any duplicate params
+add_task(async function test_check_duplicates() {
+ for (const domain in stripOnShareMergedList) {
+ // Creates a set of query parameters for a given domain to check
+ // for duplicates
+ let setOfParams = new Set(stripOnShareMergedList[domain].queryParams);
+
+ // If there are duplicates which is known because the sizes of the set
+ // and array don't match, then we check which parameters are duplciates
+ if (setOfParams.size != stripOnShareMergedList[domain].queryParams.length) {
+ let setToCheckDupes = new Set();
+ let dupeList = new Set();
+ for (let param in stripOnShareMergedList[domain].queryParams) {
+ let tempParam = stripOnShareMergedList[domain].queryParams[param];
+
+ if (setToCheckDupes.has(tempParam)) {
+ dupeList.add(tempParam);
+ } else {
+ setToCheckDupes.add(tempParam);
+ }
+ }
+
+ Assert.equal(
+ setOfParams.size,
+ stripOnShareMergedList[domain].queryParams.length,
+ "There are duplicates rules in " +
+ domain +
+ ". The duplicate rules are " +
+ [...dupeList]
+ );
+ }
+
+ Assert.equal(
+ setOfParams.size,
+ stripOnShareMergedList[domain].queryParams.length,
+ "There are no duplicates rules."
+ );
}
+});
- Assert.ok(valid, "Strip on share JSON is valid");
+// Validate the format of Strip on Share list with Schema
+add_task(async function test_check_schema() {
+ validateSchema(stripOnShareLGPLParams, "Strip On Share LGPL");
+ validateSchema(stripOnShareList, "Strip On Share");
});
diff --git a/toolkit/components/autocomplete/nsAutoCompleteController.cpp b/toolkit/components/autocomplete/nsAutoCompleteController.cpp
index 7c3035271b..126199e3cf 100644
--- a/toolkit/components/autocomplete/nsAutoCompleteController.cpp
+++ b/toolkit/components/autocomplete/nsAutoCompleteController.cpp
@@ -49,13 +49,11 @@ nsAutoCompleteController::nsAutoCompleteController()
mPopupClosedByCompositionStart(false),
mProhibitAutoFill(false),
mUserClearedAutoFill(false),
- mClearingAutoFillSearchesAgain(false),
mCompositionState(eCompositionState_None),
mSearchStatus(nsAutoCompleteController::STATUS_NONE),
mMatchCount(0),
mSearchesOngoing(0),
mSearchesFailed(0),
- mImmediateSearchesCount(0),
mCompletedSelectionIndex(-1) {}
nsAutoCompleteController::~nsAutoCompleteController() { SetInput(nullptr); }
@@ -131,9 +129,6 @@ nsAutoCompleteController::SetInput(nsIAutoCompleteInput* aInput) {
input->GetTextValue(value);
SetSearchStringInternal(value);
- // Since the controller can be used as a service it's important to reset this.
- mClearingAutoFillSearchesAgain = false;
-
return NS_OK;
}
@@ -243,20 +238,16 @@ nsAutoCompleteController::HandleText(bool* _retval) {
newValue.Length() < mPlaceholderCompletionString.Length() &&
Substring(mPlaceholderCompletionString, 0, newValue.Length())
.Equals(newValue);
- bool searchAgainOnAutoFillClear =
- mUserClearedAutoFill && mClearingAutoFillSearchesAgain;
- if (!handlingCompositionCommit && !searchAgainOnAutoFillClear &&
- newValue.Length() > 0 && repeatingPreviousSearch) {
+ if (!handlingCompositionCommit && newValue.Length() > 0 &&
+ repeatingPreviousSearch) {
return NS_OK;
}
- if (userRemovedText || searchAgainOnAutoFillClear) {
- if (userRemovedText) {
- // We need to throw away previous results so we don't try to search
- // through them again.
- ClearResults();
- }
+ if (userRemovedText) {
+ // We need to throw away previous results so we don't try to search
+ // through them again.
+ ClearResults();
mProhibitAutoFill = true;
mPlaceholderCompletionString.Truncate();
} else {
@@ -848,16 +839,7 @@ MOZ_CAN_RUN_SCRIPT_BOUNDARY
NS_IMETHODIMP
nsAutoCompleteController::Notify(nsITimer* timer) {
mTimer = nullptr;
-
- if (mImmediateSearchesCount == 0) {
- // If there were no immediate searches, BeforeSearches has not yet been
- // called, so do it now.
- nsresult rv = BeforeSearches();
- if (NS_FAILED(rv)) return rv;
- }
- StartSearch(nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_DELAYED);
- AfterSearches();
- return NS_OK;
+ return DoSearches();
}
////////////////////////////////////////////////////////////////////////
@@ -928,7 +910,7 @@ nsresult nsAutoCompleteController::BeforeSearches() {
return NS_OK;
}
-nsresult nsAutoCompleteController::StartSearch(uint16_t aSearchType) {
+nsresult nsAutoCompleteController::StartSearch() {
NS_ENSURE_STATE(mInput);
nsCOMPtr<nsIAutoCompleteInput> input = mInput;
@@ -940,14 +922,6 @@ nsresult nsAutoCompleteController::StartSearch(uint16_t aSearchType) {
for (uint32_t i = 0; i < searchesCopy.Length(); ++i) {
nsCOMPtr<nsIAutoCompleteSearch> search = searchesCopy[i];
- // Filter on search type. Not all the searches implement this interface,
- // in such a case just consider them delayed.
- uint16_t searchType = nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_DELAYED;
- nsCOMPtr<nsIAutoCompleteSearchDescriptor> searchDesc =
- do_QueryInterface(search);
- if (searchDesc) searchDesc->GetSearchType(&searchType);
- if (searchType != aSearchType) continue;
-
nsIAutoCompleteResult* result = mResultCache.SafeObjectAt(i);
if (result) {
@@ -963,13 +937,6 @@ nsresult nsAutoCompleteController::StartSearch(uint16_t aSearchType) {
nsresult rv = input->GetSearchParam(searchParam);
if (NS_FAILED(rv)) return rv;
- // FormFill expects the searchParam to only contain the input element id,
- // other consumers may have other expectations, so this modifies it only
- // for new consumers handling autoFill by themselves.
- if (mProhibitAutoFill && mClearingAutoFillSearchesAgain) {
- searchParam.AppendLiteral(" prohibit-autofill");
- }
-
uint32_t userContextId;
rv = input->GetUserContextId(&userContextId);
if (NS_SUCCEEDED(rv) &&
@@ -1078,7 +1045,6 @@ nsresult nsAutoCompleteController::StartSearches() {
input->GetSearchCount(&searchCount);
mResults.SetCapacity(searchCount);
mSearches.SetCapacity(searchCount);
- mImmediateSearchesCount = 0;
const char* searchCID = kAutoCompleteSearchCID;
@@ -1095,24 +1061,6 @@ nsresult nsAutoCompleteController::StartSearches() {
nsCOMPtr<nsIAutoCompleteSearch> search = do_GetService(cid.get());
if (search) {
mSearches.AppendObject(search);
-
- // Count immediate searches.
- nsCOMPtr<nsIAutoCompleteSearchDescriptor> searchDesc =
- do_QueryInterface(search);
- if (searchDesc) {
- uint16_t searchType =
- nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_DELAYED;
- if (NS_SUCCEEDED(searchDesc->GetSearchType(&searchType)) &&
- searchType ==
- nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_IMMEDIATE) {
- mImmediateSearchesCount++;
- }
-
- if (!mClearingAutoFillSearchesAgain) {
- searchDesc->GetClearingAutoFillSearchesAgain(
- &mClearingAutoFillSearchesAgain);
- }
- }
}
}
}
@@ -1125,36 +1073,28 @@ nsresult nsAutoCompleteController::StartSearches() {
uint32_t timeout;
input->GetTimeout(&timeout);
- uint32_t immediateSearchesCount = mImmediateSearchesCount;
if (timeout == 0) {
- // All the searches should be executed immediately.
- immediateSearchesCount = mSearches.Length();
+ // If the timeout is 0, we still have to execute the delayed searches,
+ // otherwise this will be a no-op.
+ return DoSearches();
}
- if (immediateSearchesCount > 0) {
- nsresult rv = BeforeSearches();
- if (NS_FAILED(rv)) return rv;
- StartSearch(nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_IMMEDIATE);
-
- if (mSearches.Length() == immediateSearchesCount) {
- // Either all searches are immediate, or the timeout is 0. In the
- // latter case we still have to execute the delayed searches, otherwise
- // this will be a no-op.
- StartSearch(nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_DELAYED);
-
- // All the searches have been started, just finish.
- AfterSearches();
- return NS_OK;
- }
- }
-
- MOZ_ASSERT(timeout > 0, "Trying to delay searches with a 0 timeout!");
-
// Now start the delayed searches.
return NS_NewTimerWithCallback(getter_AddRefs(mTimer), this, timeout,
nsITimer::TYPE_ONE_SHOT);
}
+nsresult nsAutoCompleteController::DoSearches() {
+ nsresult rv = BeforeSearches();
+ if (NS_FAILED(rv)) return rv;
+
+ StartSearch();
+
+ // All the searches have been started, just finish.
+ AfterSearches();
+ return NS_OK;
+}
+
nsresult nsAutoCompleteController::ClearSearchTimer() {
if (mTimer) {
mTimer->Cancel();
diff --git a/toolkit/components/autocomplete/nsAutoCompleteController.h b/toolkit/components/autocomplete/nsAutoCompleteController.h
index 7e6d8a3b06..fdaf245f56 100644
--- a/toolkit/components/autocomplete/nsAutoCompleteController.h
+++ b/toolkit/components/autocomplete/nsAutoCompleteController.h
@@ -54,8 +54,9 @@ class nsAutoCompleteController final : public nsIAutoCompleteController,
MOZ_CAN_RUN_SCRIPT nsresult OpenPopup();
MOZ_CAN_RUN_SCRIPT nsresult ClosePopup();
- nsresult StartSearch(uint16_t aSearchType);
+ nsresult StartSearch();
+ MOZ_CAN_RUN_SCRIPT nsresult DoSearches();
nsresult BeforeSearches();
MOZ_CAN_RUN_SCRIPT nsresult StartSearches();
MOZ_CAN_RUN_SCRIPT void AfterSearches();
@@ -182,17 +183,13 @@ class nsAutoCompleteController final : public nsIAutoCompleteController,
bool mDefaultIndexCompleted;
bool mPopupClosedByCompositionStart;
- // Whether autofill is allowed for the next search. May be retrieved by the
- // search through the "prohibit-autofill" searchParam.
+ // Whether autofill is allowed for the next search.
bool mProhibitAutoFill;
// Indicates whether the user cleared the autofilled part, returning to the
// originally entered search string.
bool mUserClearedAutoFill;
- // Indicates whether clearing the autofilled string should issue a new search.
- bool mClearingAutoFillSearchesAgain;
-
enum CompositionState {
eCompositionState_None,
eCompositionState_Composing,
@@ -203,7 +200,6 @@ class nsAutoCompleteController final : public nsIAutoCompleteController,
uint32_t mMatchCount;
uint32_t mSearchesOngoing;
uint32_t mSearchesFailed;
- uint32_t mImmediateSearchesCount;
// The index of the match on the popup that was selected using the keyboard,
// if the completeselectedindex attribute is set.
// This is used to distinguish that selection (which would have been put in
diff --git a/toolkit/components/autocomplete/nsIAutoCompleteResult.idl b/toolkit/components/autocomplete/nsIAutoCompleteResult.idl
index 8e8b05ea46..7f85b49b82 100644
--- a/toolkit/components/autocomplete/nsIAutoCompleteResult.idl
+++ b/toolkit/components/autocomplete/nsIAutoCompleteResult.idl
@@ -83,7 +83,7 @@ interface nsIAutoCompleteResult : nsISupports
/**
* True if the value at the given index is removable.
*/
- bool isRemovableAt(in long rowIndex);
+ boolean isRemovableAt(in long rowIndex);
/**
* Remove the value at the given index from the autocomplete results.
diff --git a/toolkit/components/autocomplete/nsIAutoCompleteSearch.idl b/toolkit/components/autocomplete/nsIAutoCompleteSearch.idl
index 2545340bf4..ca41be4a36 100644
--- a/toolkit/components/autocomplete/nsIAutoCompleteSearch.idl
+++ b/toolkit/components/autocomplete/nsIAutoCompleteSearch.idl
@@ -43,26 +43,3 @@ interface nsIAutoCompleteObserver : nsISupports
[can_run_script] void onSearchResult(in nsIAutoCompleteSearch search,
in nsIAutoCompleteResult result);
};
-
-[scriptable, uuid(4c3e7462-fbfb-4310-8f4b-239238392b75)]
-interface nsIAutoCompleteSearchDescriptor : nsISupports
-{
- // The search is started after the timeout specified by the corresponding
- // nsIAutoCompleteInput implementation.
- const unsigned short SEARCH_TYPE_DELAYED = 0;
- // The search is started synchronously, before any delayed searches.
- const unsigned short SEARCH_TYPE_IMMEDIATE = 1;
-
- /**
- * Identifies the search behavior.
- * Should be one of the SEARCH_TYPE_* constants above.
- * Defaults to SEARCH_TYPE_DELAYED.
- */
- readonly attribute unsigned short searchType;
-
- /*
- * Whether a new search should be triggered when the user deletes the
- * autofilled part.
- */
- readonly attribute boolean clearingAutoFillSearchesAgain;
-};
diff --git a/toolkit/components/autocomplete/tests/unit/head_autocomplete.js b/toolkit/components/autocomplete/tests/unit/head_autocomplete.js
index 6aa0382d82..b11d02a855 100644
--- a/toolkit/components/autocomplete/tests/unit/head_autocomplete.js
+++ b/toolkit/components/autocomplete/tests/unit/head_autocomplete.js
@@ -101,7 +101,7 @@ AutoCompleteResultBase.prototype = {
return this._styles[aIndex];
},
- getImageAt(aIndex) {
+ getImageAt() {
return "";
},
@@ -109,11 +109,11 @@ AutoCompleteResultBase.prototype = {
return this._finalCompleteValues[aIndex] || this._values[aIndex];
},
- isRemovableAt(aRowIndex) {
+ isRemovableAt() {
return true;
},
- removeValueAt(aRowIndex) {},
+ removeValueAt() {},
// nsISupports implementation
QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteResult"]),
@@ -161,7 +161,7 @@ function AutocompletePopupBase(input) {
AutocompletePopupBase.prototype = {
selectedIndex: 0,
invalidate() {},
- selectBy(reverse, page) {
+ selectBy(reverse) {
let numRows = this.input.controller.matchCount;
if (numRows > 0) {
let delta = reverse ? -1 : 1;
diff --git a/toolkit/components/autocomplete/tests/unit/test_378079.js b/toolkit/components/autocomplete/tests/unit/test_378079.js
index 06819df40b..756783d17f 100644
--- a/toolkit/components/autocomplete/tests/unit/test_378079.js
+++ b/toolkit/components/autocomplete/tests/unit/test_378079.js
@@ -45,7 +45,7 @@ AutoCompleteInput.prototype = {
popupOpen: false,
popup: {
- setSelectedIndex(aIndex) {},
+ setSelectedIndex() {},
invalidate() {},
// nsISupports implementation
@@ -100,7 +100,7 @@ AutoCompleteResult.prototype = {
return this._styles[aIndex];
},
- getImageAt(aIndex) {
+ getImageAt() {
return "";
},
@@ -108,11 +108,11 @@ AutoCompleteResult.prototype = {
return this.getValueAt(aIndex);
},
- isRemovableAt(aRowIndex) {
+ isRemovableAt() {
return true;
},
- removeValueAt(aRowIndex) {},
+ removeValueAt() {},
// nsISupports implementation
QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteResult"]),
diff --git a/toolkit/components/autocomplete/tests/unit/test_393191.js b/toolkit/components/autocomplete/tests/unit/test_393191.js
index 9a0481718d..c39a7586e9 100644
--- a/toolkit/components/autocomplete/tests/unit/test_393191.js
+++ b/toolkit/components/autocomplete/tests/unit/test_393191.js
@@ -44,7 +44,7 @@ AutoCompleteInput.prototype = {
popupOpen: false,
popup: {
- setSelectedIndex(aIndex) {},
+ setSelectedIndex() {},
invalidate() {},
// nsISupports implementation
@@ -99,7 +99,7 @@ AutoCompleteResult.prototype = {
return this._styles[aIndex];
},
- getImageAt(aIndex) {
+ getImageAt() {
return "";
},
@@ -107,11 +107,11 @@ AutoCompleteResult.prototype = {
return this.getValueAt(aIndex);
},
- isRemovableAt(aRowIndex) {
+ isRemovableAt() {
return true;
},
- removeValueAt(aRowIndex) {},
+ removeValueAt() {},
// nsISupports implementation
QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteResult"]),
diff --git a/toolkit/components/autocomplete/tests/unit/test_440866.js b/toolkit/components/autocomplete/tests/unit/test_440866.js
index ef538cd931..fdc7850b28 100644
--- a/toolkit/components/autocomplete/tests/unit/test_440866.js
+++ b/toolkit/components/autocomplete/tests/unit/test_440866.js
@@ -43,7 +43,7 @@ AutoCompleteInput.prototype = {
popupOpen: false,
popup: {
- setSelectedIndex(aIndex) {},
+ setSelectedIndex() {},
invalidate() {},
// nsISupports implementation
@@ -98,7 +98,7 @@ AutoCompleteResult.prototype = {
return this._styles[aIndex];
},
- getImageAt(aIndex) {
+ getImageAt() {
return "";
},
@@ -106,11 +106,11 @@ AutoCompleteResult.prototype = {
return this.getValueAt(aIndex);
},
- isRemovableAt(aRowIndex) {
+ isRemovableAt() {
return true;
},
- removeValueAt(aRowIndex) {},
+ removeValueAt() {},
// nsISupports implementation
QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteResult"]),
diff --git a/toolkit/components/autocomplete/tests/unit/test_autocomplete_multiple.js b/toolkit/components/autocomplete/tests/unit/test_autocomplete_multiple.js
index 68885dc4ab..d94da5ae59 100644
--- a/toolkit/components/autocomplete/tests/unit/test_autocomplete_multiple.js
+++ b/toolkit/components/autocomplete/tests/unit/test_autocomplete_multiple.js
@@ -40,7 +40,7 @@ AutoCompleteInput.prototype = {
popupOpen: false,
popup: {
- setSelectedIndex(aIndex) {},
+ setSelectedIndex() {},
invalidate() {},
// nsISupports implementation
@@ -92,7 +92,7 @@ AutoCompleteResult.prototype = {
return this._styles[aIndex];
},
- getImageAt(aIndex) {
+ getImageAt() {
return "";
},
@@ -100,11 +100,11 @@ AutoCompleteResult.prototype = {
return this.getValueAt(aIndex);
},
- isRemovableAt(aRowIndex) {
+ isRemovableAt() {
return true;
},
- removeValueAt(aRowIndex) {},
+ removeValueAt() {},
// nsISupports implementation
QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteResult"]),
diff --git a/toolkit/components/autocomplete/tests/unit/test_autocomplete_userContextId.js b/toolkit/components/autocomplete/tests/unit/test_autocomplete_userContextId.js
index 4fbec408dc..6862e63ede 100644
--- a/toolkit/components/autocomplete/tests/unit/test_autocomplete_userContextId.js
+++ b/toolkit/components/autocomplete/tests/unit/test_autocomplete_userContextId.js
@@ -21,12 +21,7 @@ function doSearch(aString, aUserContextId) {
return new Promise(resolve => {
let search = new AutoCompleteSearch("test");
- search.startSearch = function (
- aSearchString,
- aSearchParam,
- aPreviousResult,
- aListener
- ) {
+ search.startSearch = function (aSearchString, aSearchParam) {
unregisterAutoCompleteSearch(search);
resolve(aSearchParam);
};
diff --git a/toolkit/components/autocomplete/tests/unit/test_immediate_search.js b/toolkit/components/autocomplete/tests/unit/test_immediate_search.js
deleted file mode 100644
index a4524ca5c9..0000000000
--- a/toolkit/components/autocomplete/tests/unit/test_immediate_search.js
+++ /dev/null
@@ -1,174 +0,0 @@
-/* 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/. */
-
-function AutoCompleteImmediateSearch(aName, aResult) {
- this.name = aName;
- this._result = aResult;
-}
-AutoCompleteImmediateSearch.prototype = Object.create(
- AutoCompleteSearchBase.prototype
-);
-AutoCompleteImmediateSearch.prototype.searchType =
- Ci.nsIAutoCompleteSearchDescriptor.SEARCH_TYPE_IMMEDIATE;
-AutoCompleteImmediateSearch.prototype.QueryInterface = ChromeUtils.generateQI([
- "nsIFactory",
- "nsIAutoCompleteSearch",
- "nsIAutoCompleteSearchDescriptor",
-]);
-
-function AutoCompleteDelayedSearch(aName, aResult) {
- this.name = aName;
- this._result = aResult;
-}
-AutoCompleteDelayedSearch.prototype = Object.create(
- AutoCompleteSearchBase.prototype
-);
-
-function AutoCompleteResult(aValues, aDefaultIndex) {
- this._values = aValues;
- this.defaultIndex = aDefaultIndex;
-}
-AutoCompleteResult.prototype = Object.create(AutoCompleteResultBase.prototype);
-
-/**
- * An immediate search should be executed synchronously.
- */
-add_test(function test_immediate_search() {
- let inputStr = "moz";
-
- let immediateSearch = new AutoCompleteImmediateSearch(
- "immediate",
- new AutoCompleteResult(["moz-immediate"], 0)
- );
- registerAutoCompleteSearch(immediateSearch);
- let delayedSearch = new AutoCompleteDelayedSearch(
- "delayed",
- new AutoCompleteResult(["moz-delayed"], 0)
- );
- registerAutoCompleteSearch(delayedSearch);
-
- let controller = Cc["@mozilla.org/autocomplete/controller;1"].getService(
- Ci.nsIAutoCompleteController
- );
-
- let input = new AutoCompleteInputBase([
- delayedSearch.name,
- immediateSearch.name,
- ]);
- input.completeDefaultIndex = true;
- input.textValue = inputStr;
-
- // Caret must be at the end. Autofill doesn't happen unless you're typing
- // characters at the end.
- let strLen = inputStr.length;
- input.selectTextRange(strLen, strLen);
-
- controller.input = input;
- controller.startSearch(inputStr);
-
- // Immediately check the result, the immediate search should have finished.
- Assert.equal(input.textValue, "moz-immediate");
-
- // Wait for both queries to finish.
- input.onSearchComplete = function () {
- // Sanity check.
- Assert.equal(input.textValue, "moz-immediate");
-
- unregisterAutoCompleteSearch(immediateSearch);
- unregisterAutoCompleteSearch(delayedSearch);
- run_next_test();
- };
-});
-
-/**
- * An immediate search should be executed before any delayed search.
- */
-add_test(function test_immediate_search_notimeout() {
- let inputStr = "moz";
-
- let immediateSearch = new AutoCompleteImmediateSearch(
- "immediate",
- new AutoCompleteResult(["moz-immediate"], 0)
- );
- registerAutoCompleteSearch(immediateSearch);
-
- let delayedSearch = new AutoCompleteDelayedSearch(
- "delayed",
- new AutoCompleteResult(["moz-delayed"], 0)
- );
- registerAutoCompleteSearch(delayedSearch);
-
- let controller = Cc["@mozilla.org/autocomplete/controller;1"].getService(
- Ci.nsIAutoCompleteController
- );
-
- let input = new AutoCompleteInputBase([
- delayedSearch.name,
- immediateSearch.name,
- ]);
- input.completeDefaultIndex = true;
- input.textValue = inputStr;
- input.timeout = 0;
-
- // Caret must be at the end. Autofill doesn't happen unless you're typing
- // characters at the end.
- let strLen = inputStr.length;
- input.selectTextRange(strLen, strLen);
-
- controller.input = input;
- let complete = false;
- input.onSearchComplete = function () {
- complete = true;
- };
- controller.startSearch(inputStr);
- Assert.ok(complete);
-
- // Immediately check the result, the immediate search should have finished.
- Assert.equal(input.textValue, "moz-immediate");
-
- unregisterAutoCompleteSearch(immediateSearch);
- unregisterAutoCompleteSearch(delayedSearch);
- run_next_test();
-});
-
-/**
- * A delayed search should be executed synchronously with a zero timeout.
- */
-add_test(function test_delayed_search_notimeout() {
- let inputStr = "moz";
-
- let delayedSearch = new AutoCompleteDelayedSearch(
- "delayed",
- new AutoCompleteResult(["moz-delayed"], 0)
- );
- registerAutoCompleteSearch(delayedSearch);
-
- let controller = Cc["@mozilla.org/autocomplete/controller;1"].getService(
- Ci.nsIAutoCompleteController
- );
-
- let input = new AutoCompleteInputBase([delayedSearch.name]);
- input.completeDefaultIndex = true;
- input.textValue = inputStr;
- input.timeout = 0;
-
- // Caret must be at the end. Autofill doesn't happen unless you're typing
- // characters at the end.
- let strLen = inputStr.length;
- input.selectTextRange(strLen, strLen);
-
- controller.input = input;
- let complete = false;
- input.onSearchComplete = function () {
- complete = true;
- };
- controller.startSearch(inputStr);
- Assert.ok(complete);
-
- // Immediately check the result, the delayed search should have finished.
- Assert.equal(input.textValue, "moz-delayed");
-
- unregisterAutoCompleteSearch(delayedSearch);
- run_next_test();
-});
diff --git a/toolkit/components/autocomplete/tests/unit/test_previousResult.js b/toolkit/components/autocomplete/tests/unit/test_previousResult.js
index 85db3f08c5..e015ff481e 100644
--- a/toolkit/components/autocomplete/tests/unit/test_previousResult.js
+++ b/toolkit/components/autocomplete/tests/unit/test_previousResult.js
@@ -45,7 +45,7 @@ AutoCompleteInput.prototype = {
popupOpen: false,
popup: {
- setSelectedIndex(aIndex) {},
+ setSelectedIndex() {},
invalidate() {},
// nsISupports implementation
@@ -100,7 +100,7 @@ AutoCompleteResult.prototype = {
return this._styles[aIndex];
},
- getImageAt(aIndex) {
+ getImageAt() {
return "";
},
@@ -108,11 +108,11 @@ AutoCompleteResult.prototype = {
return this.getValueAt(aIndex);
},
- isRemovableAt(aRowIndex) {
+ isRemovableAt() {
return true;
},
- removeValueAt(aRowIndex) {},
+ removeValueAt() {},
// nsISupports implementation
QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteResult"]),
diff --git a/toolkit/components/autocomplete/tests/unit/test_search_zerotimeout.js b/toolkit/components/autocomplete/tests/unit/test_search_zerotimeout.js
new file mode 100644
index 0000000000..a5a0f9aa0f
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_search_zerotimeout.js
@@ -0,0 +1,58 @@
+/* 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/. */
+
+function AutoCompleteDelayedSearch(aName, aResult) {
+ this.name = aName;
+ this._result = aResult;
+}
+AutoCompleteDelayedSearch.prototype = Object.create(
+ AutoCompleteSearchBase.prototype
+);
+
+function AutoCompleteResult(aValues, aDefaultIndex) {
+ this._values = aValues;
+ this.defaultIndex = aDefaultIndex;
+}
+AutoCompleteResult.prototype = Object.create(AutoCompleteResultBase.prototype);
+
+/**
+ * A delayed search should be executed synchronously with a zero timeout.
+ */
+add_test(function test_delayed_search_notimeout() {
+ let inputStr = "moz";
+
+ let delayedSearch = new AutoCompleteDelayedSearch(
+ "delayed",
+ new AutoCompleteResult(["moz-delayed"], 0)
+ );
+ registerAutoCompleteSearch(delayedSearch);
+
+ let controller = Cc["@mozilla.org/autocomplete/controller;1"].getService(
+ Ci.nsIAutoCompleteController
+ );
+
+ let input = new AutoCompleteInputBase([delayedSearch.name]);
+ input.completeDefaultIndex = true;
+ input.textValue = inputStr;
+ input.timeout = 0;
+
+ // Caret must be at the end. Autofill doesn't happen unless you're typing
+ // characters at the end.
+ let strLen = inputStr.length;
+ input.selectTextRange(strLen, strLen);
+
+ controller.input = input;
+ let complete = false;
+ input.onSearchComplete = function () {
+ complete = true;
+ };
+ controller.startSearch(inputStr);
+ Assert.ok(complete);
+
+ // Immediately check the result, the delayed search should have finished.
+ Assert.equal(input.textValue, "moz-delayed");
+
+ unregisterAutoCompleteSearch(delayedSearch);
+ run_next_test();
+});
diff --git a/toolkit/components/autocomplete/tests/unit/test_stopSearch.js b/toolkit/components/autocomplete/tests/unit/test_stopSearch.js
index aecf572df0..01bd67d63b 100644
--- a/toolkit/components/autocomplete/tests/unit/test_stopSearch.js
+++ b/toolkit/components/autocomplete/tests/unit/test_stopSearch.js
@@ -60,7 +60,7 @@ function AutoCompleteSearch(aName) {
AutoCompleteSearch.prototype = {
constructor: AutoCompleteSearch,
stopSearchInvoked: true,
- startSearch(aSearchString, aSearchParam, aPreviousResult, aListener) {
+ startSearch() {
info("Check stop search has been called");
Assert.ok(this.stopSearchInvoked);
this.stopSearchInvoked = false;
diff --git a/toolkit/components/autocomplete/tests/unit/xpcshell.toml b/toolkit/components/autocomplete/tests/unit/xpcshell.toml
index 00d238cdf7..0c2e12495e 100644
--- a/toolkit/components/autocomplete/tests/unit/xpcshell.toml
+++ b/toolkit/components/autocomplete/tests/unit/xpcshell.toml
@@ -33,12 +33,12 @@ head = "head_autocomplete.js"
["test_finalDefaultCompleteValue.js"]
-["test_immediate_search.js"]
-
["test_insertMatchAt.js"]
["test_previousResult.js"]
["test_removeMatchAt.js"]
+["test_search_zerotimeout.js"]
+
["test_stopSearch.js"]
diff --git a/toolkit/components/backgroundhangmonitor/BHRTelemetryService.sys.mjs b/toolkit/components/backgroundhangmonitor/BHRTelemetryService.sys.mjs
index 98c1274b5c..c675526559 100644
--- a/toolkit/components/backgroundhangmonitor/BHRTelemetryService.sys.mjs
+++ b/toolkit/components/backgroundhangmonitor/BHRTelemetryService.sys.mjs
@@ -142,7 +142,7 @@ BHRTelemetryService.prototype = Object.freeze({
this.submit();
},
- observe(aSubject, aTopic, aData) {
+ observe(aSubject, aTopic) {
switch (aTopic) {
case "profile-after-change":
this.resetPayload();
diff --git a/toolkit/components/backgroundhangmonitor/nsIHangDetails.idl b/toolkit/components/backgroundhangmonitor/nsIHangDetails.idl
index f9c6ba2de0..df5fcebea8 100644
--- a/toolkit/components/backgroundhangmonitor/nsIHangDetails.idl
+++ b/toolkit/components/backgroundhangmonitor/nsIHangDetails.idl
@@ -24,7 +24,7 @@ interface nsIHangDetails : nsISupports
* The hang was persisted to disk as a permahang, so we can clear the
* permahang file once we submit this.
*/
- readonly attribute bool wasPersisted;
+ readonly attribute boolean wasPersisted;
/**
* The detected duration of the hang in milliseconds.
diff --git a/toolkit/components/backgroundtasks/BackgroundTask_message.sys.mjs b/toolkit/components/backgroundtasks/BackgroundTask_message.sys.mjs
index 6fc1d789b9..aafe4a6dcf 100644
--- a/toolkit/components/backgroundtasks/BackgroundTask_message.sys.mjs
+++ b/toolkit/components/backgroundtasks/BackgroundTask_message.sys.mjs
@@ -82,10 +82,7 @@ outputInfo = (sentinel, info) => {
dump(`${sentinel}${JSON.stringify(info)}${sentinel}\n`);
};
-function monkeyPatchRemoteSettingsClient({
- last_modified = new Date().getTime(),
- data = [],
-}) {
+function monkeyPatchRemoteSettingsClient({ data = [] }) {
lazy.RemoteSettingsClient.prototype.get = async (options = {}) => {
outputInfo({ "RemoteSettingsClient.get": { options, response: { data } } });
return data;
diff --git a/toolkit/components/backgroundtasks/BackgroundTasksManager.sys.mjs b/toolkit/components/backgroundtasks/BackgroundTasksManager.sys.mjs
index dcf17625f7..97b2f4d93d 100644
--- a/toolkit/components/backgroundtasks/BackgroundTasksManager.sys.mjs
+++ b/toolkit/components/backgroundtasks/BackgroundTasksManager.sys.mjs
@@ -172,7 +172,7 @@ export class BackgroundTasksManager {
const waitFlag =
commandLine.findFlag("wait-for-jsdebugger", CASE_INSENSITIVE) != -1;
if (waitFlag) {
- function onDevtoolsThreadReady(subject, topic, data) {
+ function onDevtoolsThreadReady(subject, topic) {
lazy.log.info(
`${Services.appinfo.processID}: Setting breakpoints for background task named '${name}'` +
` (with ${commandLine.length} arguments)`
diff --git a/toolkit/components/backgroundtasks/BackgroundTasksUtils.sys.mjs b/toolkit/components/backgroundtasks/BackgroundTasksUtils.sys.mjs
index 9521af5a56..2ee2f085ae 100644
--- a/toolkit/components/backgroundtasks/BackgroundTasksUtils.sys.mjs
+++ b/toolkit/components/backgroundtasks/BackgroundTasksUtils.sys.mjs
@@ -193,7 +193,7 @@ export var BackgroundTasksUtils = {
lazy.log.info(`readPreferences: profile is locked`);
let prefs = {};
- let addPref = (kind, name, value, sticky, locked) => {
+ let addPref = (kind, name, value) => {
if (predicate && !predicate(name)) {
return;
}
diff --git a/toolkit/components/backgroundtasks/tests/BackgroundTask_crash.sys.mjs b/toolkit/components/backgroundtasks/tests/BackgroundTask_crash.sys.mjs
index 10764bc1f7..7afc94ed47 100644
--- a/toolkit/components/backgroundtasks/tests/BackgroundTask_crash.sys.mjs
+++ b/toolkit/components/backgroundtasks/tests/BackgroundTask_crash.sys.mjs
@@ -4,7 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
export async function runBackgroundTask(commandLine) {
- // This task depends on `CrashTestUtils.jsm` and requires the
+ // This task depends on `CrashTestUtils.sys.mjs` and requires the
// sibling `testcrasher` library to be in the current working
// directory. Fail right away if we can't find the module or the
// native library.
diff --git a/toolkit/components/backgroundtasks/tests/BackgroundTask_jsdebugger.sys.mjs b/toolkit/components/backgroundtasks/tests/BackgroundTask_jsdebugger.sys.mjs
index 45cf00a449..9479a404ff 100644
--- a/toolkit/components/backgroundtasks/tests/BackgroundTask_jsdebugger.sys.mjs
+++ b/toolkit/components/backgroundtasks/tests/BackgroundTask_jsdebugger.sys.mjs
@@ -11,7 +11,7 @@
* code to exit code 0.
*/
-export function runBackgroundTask(commandLine) {
+export function runBackgroundTask() {
// In the future, will be modifed by the JS debugger (to 0, success).
var exposedExitCode = 0;
diff --git a/toolkit/components/backgroundtasks/tests/BackgroundTask_shouldprocessupdates.sys.mjs b/toolkit/components/backgroundtasks/tests/BackgroundTask_shouldprocessupdates.sys.mjs
index 4030f42e5d..0b47970dee 100644
--- a/toolkit/components/backgroundtasks/tests/BackgroundTask_shouldprocessupdates.sys.mjs
+++ b/toolkit/components/backgroundtasks/tests/BackgroundTask_shouldprocessupdates.sys.mjs
@@ -3,7 +3,7 @@
* 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/. */
-export async function runBackgroundTask(commandLine) {
+export async function runBackgroundTask() {
const get = Services.env.get("MOZ_TEST_PROCESS_UPDATES");
let exitCode = 81;
if (get == "ShouldNotProcessUpdates(): OtherInstanceRunning") {
diff --git a/toolkit/components/backgroundtasks/tests/BackgroundTask_targeting.sys.mjs b/toolkit/components/backgroundtasks/tests/BackgroundTask_targeting.sys.mjs
index fd47d18470..b7a9d08896 100644
--- a/toolkit/components/backgroundtasks/tests/BackgroundTask_targeting.sys.mjs
+++ b/toolkit/components/backgroundtasks/tests/BackgroundTask_targeting.sys.mjs
@@ -20,7 +20,7 @@ const EXCLUDED_NAMES = [
* Return 0 (success) if all targeting getters succeed, 11 (failure)
* otherwise.
*/
-export async function runBackgroundTask(commandLine) {
+export async function runBackgroundTask() {
let exitCode = EXIT_CODE.SUCCESS;
// Can't use `ASRouterTargeting.getEnvironmentSnapshot`, since that
diff --git a/toolkit/components/backgroundtasks/tests/xpcshell/test_backgroundtask_experiments.js b/toolkit/components/backgroundtasks/tests/xpcshell/test_backgroundtask_experiments.js
index 9d834bfd5b..cb4f3bb957 100644
--- a/toolkit/components/backgroundtasks/tests/xpcshell/test_backgroundtask_experiments.js
+++ b/toolkit/components/backgroundtasks/tests/xpcshell/test_backgroundtask_experiments.js
@@ -136,8 +136,8 @@ async function doMessage({ extraArgs = [], extraEnv = {} } = {}) {
// i.e., persisted. Verify that messages are shown until we hit the lifetime
// frequency caps.
//
-// It's awkward to inspect the `ASRouter.jsm` internal state directly in this
-// manner, but this is the pattern for testing such things at the time of
+// It's awkward to inspect the `ASRouter.sys.mjs` internal state directly in
+// this manner, but this is the pattern for testing such things at the time of
// writing.
add_task(async function test_backgroundtask_caps() {
let experimentFile = do_get_file("experiment.json");
diff --git a/toolkit/components/bitsdownload/bits_client/bits/src/wide.rs b/toolkit/components/bitsdownload/bits_client/bits/src/wide.rs
index c108f8d629..b4f3157801 100644
--- a/toolkit/components/bitsdownload/bits_client/bits/src/wide.rs
+++ b/toolkit/components/bitsdownload/bits_client/bits/src/wide.rs
@@ -6,9 +6,8 @@
// Minimal null-terminated wide string support from wio.
-use std::ffi::{OsStr, OsString};
-use std::os::windows::ffi::{OsStrExt, OsStringExt};
-use std::slice;
+use std::ffi::OsStr;
+use std::os::windows::ffi::OsStrExt;
pub trait ToWideNull {
fn to_wide_null(&self) -> Vec<u16>;
@@ -19,20 +18,3 @@ impl<T: AsRef<OsStr>> ToWideNull for T {
self.as_ref().encode_wide().chain(Some(0)).collect()
}
}
-
-pub trait FromWidePtrNull {
- unsafe fn from_wide_ptr_null(wide: *const u16) -> Self;
-}
-
-impl FromWidePtrNull for OsString {
- unsafe fn from_wide_ptr_null(wide: *const u16) -> Self {
- assert!(!wide.is_null());
-
- for i in 0.. {
- if *wide.offset(i) == 0 {
- return Self::from_wide(&slice::from_raw_parts(wide, i as usize));
- }
- }
- unreachable!()
- }
-}
diff --git a/toolkit/components/bitsdownload/src/bits_interface/action.rs b/toolkit/components/bitsdownload/src/bits_interface/action.rs
index 1019e29827..c73d916a07 100644
--- a/toolkit/components/bitsdownload/src/bits_interface/action.rs
+++ b/toolkit/components/bitsdownload/src/bits_interface/action.rs
@@ -11,7 +11,6 @@
//! A value of type `ServiceAction` or `RequestAction` can easily be converted
//! to an `Action` using the `into()` method.
-use std::convert::From;
use xpcom::interfaces::nsIBits;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
diff --git a/toolkit/components/bitsdownload/src/bits_interface/error.rs b/toolkit/components/bitsdownload/src/bits_interface/error.rs
index c046ae5e70..bed6975f9d 100644
--- a/toolkit/components/bitsdownload/src/bits_interface/error.rs
+++ b/toolkit/components/bitsdownload/src/bits_interface/error.rs
@@ -13,7 +13,6 @@ use bits_client::{
use comedy::error::HResult as ComedyError;
use nserror::{nsresult, NS_ERROR_FAILURE};
use nsstring::nsCString;
-use std::convert::From;
use xpcom::interfaces::nsIBits;
#[derive(Debug, PartialEq, Clone, Copy)]
diff --git a/toolkit/components/browser/nsIWebBrowserChromeFocus.idl b/toolkit/components/browser/nsIWebBrowserChromeFocus.idl
index ee24155b51..077ce39201 100644
--- a/toolkit/components/browser/nsIWebBrowserChromeFocus.idl
+++ b/toolkit/components/browser/nsIWebBrowserChromeFocus.idl
@@ -21,12 +21,12 @@ interface nsIWebBrowserChromeFocus : nsISupports
* focus the parent window.
*/
- void focusNextElement(in bool aForDocumentNavigation);
+ void focusNextElement(in boolean aForDocumentNavigation);
/**
* Set the focus at the previous focusable element in the chrome.
*/
- void focusPrevElement(in bool aForDocumentNavigation);
+ void focusPrevElement(in boolean aForDocumentNavigation);
};
diff --git a/toolkit/components/browser/nsIWebBrowserPrint.idl b/toolkit/components/browser/nsIWebBrowserPrint.idl
index 5a900b7b65..2b5d7669bc 100644
--- a/toolkit/components/browser/nsIWebBrowserPrint.idl
+++ b/toolkit/components/browser/nsIWebBrowserPrint.idl
@@ -30,7 +30,7 @@ native PrintPreviewResolver(std::function<void(const mozilla::dom::PrintPreviewR
* nsIWebBrowserPrint corresponds to the main interface
* for printing an embedded Gecko web browser window/document
*/
-[scriptable, uuid(c9a934ed-fff1-4971-bfba-6c25ad70e1e6)]
+[scriptable, builtinclass, uuid(c9a934ed-fff1-4971-bfba-6c25ad70e1e6)]
interface nsIWebBrowserPrint : nsISupports
{
/**
@@ -85,6 +85,13 @@ interface nsIWebBrowserPrint : nsISupports
*/
readonly attribute long printPreviewCurrentPageNumber;
+ /*
+ * Whether the document to print needs to have its window closed after printing
+ * is done - see OPEN_PRINT_BROWSER and specifically handleStaticCloneCreatedForPrint().
+ * This is set if the document is not a static clone.
+ */
+ [infallible] attribute boolean closeWindowAfterPrint;
+
/**
* Print the specified DOM window
*
diff --git a/toolkit/components/captivedetect/CaptiveDetect.sys.mjs b/toolkit/components/captivedetect/CaptiveDetect.sys.mjs
index 9008fe8a08..5b191eccd8 100644
--- a/toolkit/components/captivedetect/CaptiveDetect.sys.mjs
+++ b/toolkit/components/captivedetect/CaptiveDetect.sys.mjs
@@ -56,7 +56,7 @@ function URLFetcher(url, timeout) {
xhr.onerror = function () {
self.onerror();
};
- xhr.onreadystatechange = function (oEvent) {
+ xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (self._isAborted) {
return;
@@ -181,10 +181,7 @@ function LoginObserver(captivePortalDetector) {
observeActivity: function observeActivity(
aHttpChannel,
aActivityType,
- aActivitySubtype,
- aTimestamp,
- aExtraSizeData,
- aExtraStringData
+ aActivitySubtype
) {
if (
aActivityType ===
@@ -541,5 +538,5 @@ if (DEBUG) {
};
} else {
// eslint-disable-next-line no-global-assign
- debug = function (s) {};
+ debug = function () {};
}
diff --git a/toolkit/components/captivedetect/nsICaptivePortalDetector.idl b/toolkit/components/captivedetect/nsICaptivePortalDetector.idl
index ba8fff416b..774c2dd284 100644
--- a/toolkit/components/captivedetect/nsICaptivePortalDetector.idl
+++ b/toolkit/components/captivedetect/nsICaptivePortalDetector.idl
@@ -16,7 +16,7 @@ interface nsICaptivePortalCallback : nsISupports
/**
* Invoke callbacks after captive portal detection finished.
*/
- void complete(in bool success);
+ void complete(in boolean success);
};
[scriptable, uuid(2f827c5a-f551-477f-af09-71adbfbd854a)]
diff --git a/toolkit/components/captivedetect/test/unit/test_abort.js b/toolkit/components/captivedetect/test/unit/test_abort.js
index 0e0a944f9b..5cb649b4c0 100644
--- a/toolkit/components/captivedetect/test/unit/test_abort.js
+++ b/toolkit/components/captivedetect/test/unit/test_abort.js
@@ -21,7 +21,7 @@ function xhr_handler(metadata, response) {
}
function fakeUIResponse() {
- Services.obs.addObserver(function observe(subject, topic, data) {
+ Services.obs.addObserver(function observe(subject, topic) {
if (topic === "captive-portal-login") {
do_throw("should not receive captive-portal-login event");
}
@@ -37,7 +37,7 @@ function test_abort() {
Assert.equal(++step, 1);
gCaptivePortalDetector.finishPreparation(kInterfaceName);
},
- complete: function complete(success) {
+ complete: function complete() {
do_throw("should not execute |complete| callback");
},
};
diff --git a/toolkit/components/captivedetect/test/unit/test_abort_during_user_login.js b/toolkit/components/captivedetect/test/unit/test_abort_during_user_login.js
index bd0011817d..a933caee55 100644
--- a/toolkit/components/captivedetect/test/unit/test_abort_during_user_login.js
+++ b/toolkit/components/captivedetect/test/unit/test_abort_during_user_login.js
@@ -51,7 +51,7 @@ function test_abort() {
Assert.equal(++step, 1);
gCaptivePortalDetector.finishPreparation(kInterfaceName);
},
- complete: function complete(success) {
+ complete: function complete() {
do_throw("should not execute |complete| callback");
},
};
diff --git a/toolkit/components/captivedetect/test/unit/test_abort_ongoing_request.js b/toolkit/components/captivedetect/test/unit/test_abort_ongoing_request.js
index b209e02796..187e27bc5d 100644
--- a/toolkit/components/captivedetect/test/unit/test_abort_ongoing_request.js
+++ b/toolkit/components/captivedetect/test/unit/test_abort_ongoing_request.js
@@ -22,7 +22,7 @@ function xhr_handler(metadata, response) {
}
function fakeUIResponse() {
- Services.obs.addObserver(function observe(subject, topic, data) {
+ Services.obs.addObserver(function observe(subject, topic) {
if (topic === "captive-portal-login") {
let xhr = new XMLHttpRequest();
xhr.open("GET", gServerURL + kCanonicalSitePath, true);
@@ -42,7 +42,7 @@ function test_multiple_requests_abort() {
Assert.equal(++step, 1);
gCaptivePortalDetector.finishPreparation(kInterfaceName);
},
- complete: function complete(success) {
+ complete: function complete() {
do_throw("should not execute |complete| callback for " + kInterfaceName);
},
};
diff --git a/toolkit/components/captivedetect/test/unit/test_abort_pending_request.js b/toolkit/components/captivedetect/test/unit/test_abort_pending_request.js
index 16d621a06b..db6d37d904 100644
--- a/toolkit/components/captivedetect/test/unit/test_abort_pending_request.js
+++ b/toolkit/components/captivedetect/test/unit/test_abort_pending_request.js
@@ -22,7 +22,7 @@ function xhr_handler(metadata, response) {
}
function fakeUIResponse() {
- Services.obs.addObserver(function observe(subject, topic, data) {
+ Services.obs.addObserver(function observe(subject, topic) {
if (topic === "captive-portal-login") {
let xhr = new XMLHttpRequest();
xhr.open("GET", gServerURL + kCanonicalSitePath, true);
@@ -56,7 +56,7 @@ function test_abort() {
"should not execute |prepare| callback for " + kOtherInterfaceName
);
},
- complete: function complete(success) {
+ complete: function complete() {
do_throw("should not execute |complete| callback for " + kInterfaceName);
},
};
diff --git a/toolkit/components/captivedetect/test/unit/test_captive_portal_found.js b/toolkit/components/captivedetect/test/unit/test_captive_portal_found.js
index 2a9acb8ab0..056bf072ee 100644
--- a/toolkit/components/captivedetect/test/unit/test_captive_portal_found.js
+++ b/toolkit/components/captivedetect/test/unit/test_captive_portal_found.js
@@ -21,7 +21,7 @@ function xhr_handler(metadata, response) {
}
function fakeUIResponse() {
- Services.obs.addObserver(function observe(subject, topic, data) {
+ Services.obs.addObserver(function observe(subject, topic) {
if (topic === "captive-portal-login") {
let xhr = new XMLHttpRequest();
xhr.open("GET", gServerURL + kCanonicalSitePath, true);
@@ -31,7 +31,7 @@ function fakeUIResponse() {
}
}, "captive-portal-login");
- Services.obs.addObserver(function observe(subject, topic, data) {
+ Services.obs.addObserver(function observe(subject, topic) {
if (topic === "captive-portal-login-success") {
Assert.equal(++step, 4);
gServer.stop(do_test_finished);
diff --git a/toolkit/components/captivedetect/test/unit/test_captive_portal_found_303.js b/toolkit/components/captivedetect/test/unit/test_captive_portal_found_303.js
index 1e4ec1f97d..deb80bc67c 100644
--- a/toolkit/components/captivedetect/test/unit/test_captive_portal_found_303.js
+++ b/toolkit/components/captivedetect/test/unit/test_captive_portal_found_303.js
@@ -26,7 +26,7 @@ function xhr_handler(metadata, response) {
}
function fakeUIResponse() {
- Services.obs.addObserver(function observe(subject, topic, data) {
+ Services.obs.addObserver(function observe(subject, topic) {
if (topic === "captive-portal-login") {
let xhr = new XMLHttpRequest();
xhr.open("GET", gServerURL + kCanonicalSitePath, true);
@@ -36,7 +36,7 @@ function fakeUIResponse() {
}
}, "captive-portal-login");
- Services.obs.addObserver(function observe(subject, topic, data) {
+ Services.obs.addObserver(function observe(subject, topic) {
if (topic === "captive-portal-login-success") {
Assert.equal(++step, 4);
gServer.stop(function () {
diff --git a/toolkit/components/captivedetect/test/unit/test_captive_portal_not_found.js b/toolkit/components/captivedetect/test/unit/test_captive_portal_not_found.js
index 25dd5f7c99..4f8c9615dc 100644
--- a/toolkit/components/captivedetect/test/unit/test_captive_portal_not_found.js
+++ b/toolkit/components/captivedetect/test/unit/test_captive_portal_not_found.js
@@ -19,7 +19,7 @@ function xhr_handler(metadata, response) {
}
function fakeUIResponse() {
- Services.obs.addObserver(function observe(subject, topic, data) {
+ Services.obs.addObserver(function observe(subject, topic) {
if (topic == "captive-portal-login") {
do_throw("should not receive captive-portal-login event");
}
diff --git a/toolkit/components/captivedetect/test/unit/test_captive_portal_not_found_404.js b/toolkit/components/captivedetect/test/unit/test_captive_portal_not_found_404.js
index 3f9d212a1f..c6608f9548 100644
--- a/toolkit/components/captivedetect/test/unit/test_captive_portal_not_found_404.js
+++ b/toolkit/components/captivedetect/test/unit/test_captive_portal_not_found_404.js
@@ -15,7 +15,7 @@ function xhr_handler(metadata, response) {
}
function fakeUIResponse() {
- Services.obs.addObserver(function observe(subject, topic, data) {
+ Services.obs.addObserver(function observe(subject, topic) {
if (topic === "captive-portal-login") {
do_throw("should not receive captive-portal-login event");
}
diff --git a/toolkit/components/captivedetect/test/unit/test_multiple_requests.js b/toolkit/components/captivedetect/test/unit/test_multiple_requests.js
index 7778a595b0..4b6d6511b2 100644
--- a/toolkit/components/captivedetect/test/unit/test_multiple_requests.js
+++ b/toolkit/components/captivedetect/test/unit/test_multiple_requests.js
@@ -23,7 +23,7 @@ function xhr_handler(metadata, response) {
}
function fakeUIResponse() {
- Services.obs.addObserver(function observe(subject, topic, data) {
+ Services.obs.addObserver(function observe(subject, topic) {
if (topic === "captive-portal-login") {
let xhr = new XMLHttpRequest();
xhr.open("GET", gServerURL + kCanonicalSitePath, true);
@@ -33,7 +33,7 @@ function fakeUIResponse() {
}
}, "captive-portal-login");
- Services.obs.addObserver(function observe(subject, topic, data) {
+ Services.obs.addObserver(function observe(subject, topic) {
if (topic === "captive-portal-login-success") {
loginSuccessCount++;
if (loginSuccessCount > 1) {
diff --git a/toolkit/components/certviewer/content/certviewer.mjs b/toolkit/components/certviewer/content/certviewer.mjs
index 3e48a069f5..ba1c6c9144 100644
--- a/toolkit/components/certviewer/content/certviewer.mjs
+++ b/toolkit/components/certviewer/content/certviewer.mjs
@@ -10,7 +10,7 @@ import {
pemToDER,
} from "chrome://global/content/certviewer/certDecoder.mjs";
-document.addEventListener("DOMContentLoaded", async e => {
+document.addEventListener("DOMContentLoaded", async () => {
let url = new URL(document.URL);
let certInfo = url.searchParams.getAll("cert");
if (certInfo.length === 0) {
@@ -469,7 +469,7 @@ const buildChain = async chain => {
let adjustedCerts = certs.map(cert => adjustCertInformation(cert));
return render(adjustedCerts, false);
})
- .catch(err => {
+ .catch(() => {
render(null, true);
});
};
diff --git a/toolkit/components/certviewer/tests/browser/browser_checkNonRepeatedCertTabs.js b/toolkit/components/certviewer/tests/browser/browser_checkNonRepeatedCertTabs.js
index 1e11cc4786..d0e2ad5644 100644
--- a/toolkit/components/certviewer/tests/browser/browser_checkNonRepeatedCertTabs.js
+++ b/toolkit/components/certviewer/tests/browser/browser_checkNonRepeatedCertTabs.js
@@ -56,7 +56,7 @@ add_task(async function testGoodCert() {
info(`Loading ${url}`);
await BrowserTestUtils.withNewTab({ gBrowser, url }, async function () {
info("Opening pageinfo");
- let pageInfo = BrowserPageInfo(url, "securityTab", {});
+ let pageInfo = BrowserCommands.pageInfo(url, "securityTab", {});
await BrowserTestUtils.waitForEvent(pageInfo, "load");
let securityTab = pageInfo.document.getElementById("securityTab");
diff --git a/toolkit/components/certviewer/tests/browser/browser_openTabAndSendCertInfo.js b/toolkit/components/certviewer/tests/browser/browser_openTabAndSendCertInfo.js
index c6307f529a..24e9ed9767 100644
--- a/toolkit/components/certviewer/tests/browser/browser_openTabAndSendCertInfo.js
+++ b/toolkit/components/certviewer/tests/browser/browser_openTabAndSendCertInfo.js
@@ -63,7 +63,7 @@ function openCertDownloadDialog(cert) {
cert,
returnVals
);
- return new Promise((resolve, reject) => {
+ return new Promise(resolve => {
win.addEventListener(
"load",
function () {
@@ -194,7 +194,7 @@ add_task(async function testGoodCert() {
info(`Loading ${url}`);
await BrowserTestUtils.withNewTab({ gBrowser, url }, async function () {
info("Opening pageinfo");
- let pageInfo = BrowserPageInfo(url, "securityTab", {});
+ let pageInfo = BrowserCommands.pageInfo(url, "securityTab", {});
await BrowserTestUtils.waitForEvent(pageInfo, "load");
let securityTab = pageInfo.document.getElementById("securityTab");
diff --git a/toolkit/components/certviewer/tests/browser/head.js b/toolkit/components/certviewer/tests/browser/head.js
index 2a28444f9b..90e3b8168f 100644
--- a/toolkit/components/certviewer/tests/browser/head.js
+++ b/toolkit/components/certviewer/tests/browser/head.js
@@ -21,7 +21,7 @@ function is_element_visible(aElement, aMsg) {
// Extracted from https://searchfox.org/mozilla-central/rev/40ef22080910c2e2c27d9e2120642376b1d8b8b2/browser/components/preferences/in-content/tests/head.js#41
function promiseLoadSubDialog(aURL) {
- return new Promise((resolve, reject) => {
+ return new Promise(resolve => {
content.gSubDialog._dialogStack.addEventListener(
"dialogopen",
function dialogopen(aEvent) {
diff --git a/toolkit/components/cleardata/ClearDataService.sys.mjs b/toolkit/components/cleardata/ClearDataService.sys.mjs
index ba34558f7e..5df5c03f58 100644
--- a/toolkit/components/cleardata/ClearDataService.sys.mjs
+++ b/toolkit/components/cleardata/ClearDataService.sys.mjs
@@ -193,7 +193,7 @@ const CookieCleaner = {
});
},
- deleteByRange(aFrom, aTo) {
+ deleteByRange(aFrom) {
return Services.cookies.removeAllSince(aFrom);
},
@@ -584,7 +584,7 @@ const DownloadsCleaner = {
};
const PasswordsCleaner = {
- deleteByHost(aHost, aOriginAttributes) {
+ deleteByHost(aHost) {
// Clearing by host also clears associated subdomains.
return this._deleteInternal(aLogin =>
Services.eTLD.hasRootDomain(aLogin.hostname, aHost)
@@ -630,7 +630,7 @@ const PasswordsCleaner = {
};
const MediaDevicesCleaner = {
- async deleteByRange(aFrom, aTo) {
+ async deleteByRange(aFrom) {
let mediaMgr = Cc["@mozilla.org/mediaManagerService;1"].getService(
Ci.nsIMediaManagerService
);
@@ -799,7 +799,7 @@ const QuotaCleaner = {
}
},
- async deleteByHost(aHost, aOriginAttributes) {
+ async deleteByHost(aHost) {
// XXX: The aOriginAttributes is expected to always be empty({}). Maybe have
// a debug assertion here to ensure that?
@@ -872,7 +872,7 @@ const QuotaCleaner = {
_ => /* exceptionThrown = */ false,
_ => /* exceptionThrown = */ true
)
- .then(exceptionThrown => {
+ .then(() => {
// QuotaManager: In the event of a failure, we call reject to propagate
// the error upwards.
return new Promise((aResolve, aReject) => {
@@ -1002,7 +1002,7 @@ const PushNotificationsCleaner = {
});
},
- deleteByHost(aHost, aOriginAttributes) {
+ deleteByHost(aHost) {
// Will also clear entries for subdomains of aHost. Data is cleared across
// all origin attributes.
return this._deleteByRootDomain(aHost);
@@ -1078,7 +1078,7 @@ const StorageAccessCleaner = {
});
},
- async deleteByHost(aHost, aOriginAttributes) {
+ async deleteByHost(aHost) {
// Clearing by host also clears associated subdomains.
this._deleteInternal(({ principal }) => {
let toBeRemoved = false;
@@ -1095,7 +1095,7 @@ const StorageAccessCleaner = {
);
},
- async deleteByRange(aFrom, aTo) {
+ async deleteByRange(aFrom) {
Services.perms.removeByTypeSince("storageAccessAPI", aFrom / 1000);
},
@@ -1105,7 +1105,7 @@ const StorageAccessCleaner = {
};
const HistoryCleaner = {
- deleteByHost(aHost, aOriginAttributes) {
+ deleteByHost(aHost) {
if (!AppConstants.MOZ_PLACES) {
return Promise.resolve();
}
@@ -1142,7 +1142,7 @@ const HistoryCleaner = {
};
const SessionHistoryCleaner = {
- async deleteByHost(aHost, aOriginAttributes) {
+ async deleteByHost(aHost) {
// Session storage and history also clear subdomains of aHost.
Services.obs.notifyObservers(null, "browser:purge-sessionStorage", aHost);
Services.obs.notifyObservers(
@@ -1160,7 +1160,7 @@ const SessionHistoryCleaner = {
return this.deleteByHost(aBaseDomain, {});
},
- async deleteByRange(aFrom, aTo) {
+ async deleteByRange(aFrom) {
Services.obs.notifyObservers(
null,
"browser:purge-session-history",
@@ -1275,7 +1275,7 @@ const PermissionsCleaner = {
}
},
- deleteByHost(aHost, aOriginAttributes) {
+ deleteByHost(aHost) {
return this._deleteInternal({ host: aHost });
},
@@ -1287,7 +1287,7 @@ const PermissionsCleaner = {
return this._deleteInternal({ baseDomain: aBaseDomain });
},
- async deleteByRange(aFrom, aTo) {
+ async deleteByRange(aFrom) {
Services.perms.removeAllSince(aFrom / 1000);
},
@@ -1301,7 +1301,7 @@ const PermissionsCleaner = {
};
const PreferencesCleaner = {
- deleteByHost(aHost, aOriginAttributes) {
+ deleteByHost(aHost) {
// Also clears subdomains of aHost.
return new Promise((aResolve, aReject) => {
let cps2 = Cc["@mozilla.org/content-pref/service;1"].getService(
@@ -1328,7 +1328,7 @@ const PreferencesCleaner = {
return this.deleteByHost(aBaseDomain, {});
},
- async deleteByRange(aFrom, aTo) {
+ async deleteByRange(aFrom) {
let cps2 = Cc["@mozilla.org/content-pref/service;1"].getService(
Ci.nsIContentPrefService2
);
@@ -1477,7 +1477,7 @@ const EMECleaner = {
};
const ReportsCleaner = {
- deleteByHost(aHost, aOriginAttributes) {
+ deleteByHost(aHost) {
// Also clears subdomains of aHost.
return new Promise(aResolve => {
Services.obs.notifyObservers(null, "reporting:purge-host", aHost);
@@ -1520,7 +1520,7 @@ const ContentBlockingCleaner = {
await this.deleteAll();
},
- deleteByRange(aFrom, aTo) {
+ deleteByRange(aFrom) {
return lazy.TrackingDBService.clearSince(aFrom);
},
};
@@ -1616,7 +1616,7 @@ const IdentityCredentialStorageCleaner = {
}
},
- async deleteByPrincipal(aPrincipal, aIsUserRequest) {
+ async deleteByPrincipal(aPrincipal) {
if (
Services.prefs.getBoolPref(
"dom.security.credentialmanagement.identity.enabled",
@@ -1698,7 +1698,7 @@ const BounceTrackingProtectionStateCleaner = {
await lazy.bounceTrackingProtection.clearAll();
},
- async deleteByPrincipal(aPrincipal, aIsUserRequest) {
+ async deleteByPrincipal(aPrincipal) {
if (!lazy.isBounceTrackingProtectionEnabled) {
return;
}
@@ -1709,7 +1709,7 @@ const BounceTrackingProtectionStateCleaner = {
);
},
- async deleteByBaseDomain(aBaseDomain, aIsUserRequest) {
+ async deleteByBaseDomain(aBaseDomain) {
if (!lazy.isBounceTrackingProtectionEnabled) {
return;
}
@@ -1723,7 +1723,7 @@ const BounceTrackingProtectionStateCleaner = {
await lazy.bounceTrackingProtection.clearByTimeRange(aFrom, aTo);
},
- async deleteByHost(aHost, aOriginAttributes) {
+ async deleteByHost(aHost) {
if (!lazy.isBounceTrackingProtectionEnabled) {
return;
}
@@ -1741,6 +1741,59 @@ const BounceTrackingProtectionStateCleaner = {
},
};
+const StoragePermissionsCleaner = {
+ async deleteByRange(aFrom) {
+ // We lack the ability to clear by range, but can clear from a certain time to now
+ // We have to divice aFrom by 1000 to convert the time from ms to microseconds
+ Services.perms.removeByTypeSince("storage-access", aFrom / 1000);
+ Services.perms.removeByTypeSince("persistent-storage", aFrom / 1000);
+ },
+
+ async deleteByPrincipal(aPrincipal) {
+ Services.perms.removeFromPrincipal(aPrincipal, "storage-access");
+ Services.perms.removeFromPrincipal(aPrincipal, "persistent-storage");
+ },
+
+ async deleteByHost(aHost) {
+ let permissions = this._getStoragePermissions();
+ for (let perm of permissions) {
+ if (Services.eTLD.hasRootDomain(perm.principal.host, aHost)) {
+ Services.perms.removePermission(perm);
+ }
+ }
+ },
+
+ async deleteByBaseDomain(aBaseDomain) {
+ let permissions = this._getStoragePermissions();
+ for (let perm of permissions) {
+ if (perm.principal.baseDomain == aBaseDomain) {
+ Services.perms.removePermission(perm);
+ }
+ }
+ },
+
+ async deleteByLocalFiles() {
+ let permissions = this._getStoragePermissions();
+ for (let perm of permissions) {
+ if (perm.principal.schemeIs("file")) {
+ Services.perms.removePermission(perm);
+ }
+ }
+ },
+
+ async deleteAll() {
+ Services.perms.removeByType("storage-access");
+ Services.perms.removeByType("persistent-storage");
+ },
+
+ _getStoragePermissions() {
+ return Services.perms.getAllByTypes([
+ "storage-access",
+ "persistent-storage",
+ ]);
+ },
+};
+
// Here the map of Flags-Cleaners.
const FLAGS_MAP = [
{
@@ -1875,6 +1928,11 @@ const FLAGS_MAP = [
flag: Ci.nsIClearDataService.CLEAR_BOUNCE_TRACKING_PROTECTION_STATE,
cleaners: [BounceTrackingProtectionStateCleaner],
},
+
+ {
+ flag: Ci.nsIClearDataService.CLEAR_STORAGE_PERMISSIONS,
+ cleaners: [StoragePermissionsCleaner],
+ },
];
export function ClearDataService() {
diff --git a/toolkit/components/cleardata/PrincipalsCollector.sys.mjs b/toolkit/components/cleardata/PrincipalsCollector.sys.mjs
index e4f5e827f6..a79da86757 100644
--- a/toolkit/components/cleardata/PrincipalsCollector.sys.mjs
+++ b/toolkit/components/cleardata/PrincipalsCollector.sys.mjs
@@ -66,8 +66,8 @@ export class PrincipalsCollector {
/**
* Fetches and collects all principals with cookies and/or site data (see module
- * description). Originally for usage in Sanitizer.jsm to compute principals to be
- * cleared on shutdown based on user settings.
+ * description). Originally for usage in Sanitizer.sys.mjs to compute principals
+ * to be cleared on shutdown based on user settings.
*
* This operation might take a while to complete on big profiles.
* DO NOT call or await this in a way that makes it block user interaction, or you
@@ -76,8 +76,8 @@ export class PrincipalsCollector {
* This function will cache its result and return the same list on second call,
* even if the actual number of principals with cookies and site data changed.
*
- * @param {Object} [optional] progress A Sanitizer.jsm progress object that will be
- * updated to reflect the current step of fetching principals.
+ * @param {Object} [optional] progress A Sanitizer.sys.mjs progress object that
+ * will be updated to reflect the current step of fetching principals.
* @returns {Array<nsIPrincipal>} the list of principals
*/
async getAllPrincipals(progress = {}) {
diff --git a/toolkit/components/cleardata/SiteDataTestUtils.sys.mjs b/toolkit/components/cleardata/SiteDataTestUtils.sys.mjs
index a777a19b7d..99bd1f72b0 100644
--- a/toolkit/components/cleardata/SiteDataTestUtils.sys.mjs
+++ b/toolkit/components/cleardata/SiteDataTestUtils.sys.mjs
@@ -244,10 +244,10 @@ export var SiteDataTestUtils = {
return new Promise(resolve => {
let data = true;
let request = indexedDB.openForPrincipal(principal, "TestDatabase", 1);
- request.onupgradeneeded = function (e) {
+ request.onupgradeneeded = function () {
data = false;
};
- request.onsuccess = function (e) {
+ request.onsuccess = function () {
resolve(data);
};
});
@@ -276,11 +276,11 @@ export var SiteDataTestUtils = {
CacheListener.prototype = {
QueryInterface: ChromeUtils.generateQI(["nsICacheEntryOpenCallback"]),
- onCacheEntryCheck(entry) {
+ onCacheEntryCheck() {
return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED;
},
- onCacheEntryAvailable(entry, isnew, status) {
+ onCacheEntryAvailable() {
resolve();
},
};
diff --git a/toolkit/components/cleardata/nsIClearDataService.idl b/toolkit/components/cleardata/nsIClearDataService.idl
index ea17059bbe..babbc8f05b 100644
--- a/toolkit/components/cleardata/nsIClearDataService.idl
+++ b/toolkit/components/cleardata/nsIClearDataService.idl
@@ -31,7 +31,7 @@ interface nsIClearDataService : nsISupports
* @param aCallback this callback will be executed when the operation is
* completed.
*/
- void deleteDataFromLocalFiles(in bool aIsUserRequest,
+ void deleteDataFromLocalFiles(in boolean aIsUserRequest,
in uint32_t aFlags,
in nsIClearDataCallback aCallback);
@@ -50,7 +50,7 @@ interface nsIClearDataService : nsISupports
* @deprecated Use deleteDataFromBaseDomain instead.
*/
void deleteDataFromHost(in AUTF8String aHost,
- in bool aIsUserRequest,
+ in boolean aIsUserRequest,
in uint32_t aFlags,
in nsIClearDataCallback aCallback);
@@ -76,7 +76,7 @@ interface nsIClearDataService : nsISupports
* may fall back to clearing by principal or host.
*/
void deleteDataFromBaseDomain(in AUTF8String aDomainOrHost,
- in bool aIsUserRequest,
+ in boolean aIsUserRequest,
in uint32_t aFlags,
in nsIClearDataCallback aCallback);
@@ -93,7 +93,7 @@ interface nsIClearDataService : nsISupports
* completed.
*/
void deleteDataFromPrincipal(in nsIPrincipal aPrincipal,
- in bool aIsUserRequest,
+ in boolean aIsUserRequest,
in uint32_t aFlags,
in nsIClearDataCallback aCallback);
@@ -111,7 +111,7 @@ interface nsIClearDataService : nsISupports
* completed.
*/
void deleteDataInTimeRange(in PRTime aFrom, in PRTime aTo,
- in bool aIsUserRequest,
+ in boolean aIsUserRequest,
in uint32_t aFlags,
in nsIClearDataCallback aCallback);
@@ -308,6 +308,11 @@ interface nsIClearDataService : nsISupports
const uint32_t CLEAR_BOUNCE_TRACKING_PROTECTION_STATE = 1 << 28;
/**
+ * Clear permissions of type "persistent-storage" and "storage-access"
+ */
+ const uint32_t CLEAR_STORAGE_PERMISSIONS = 1 << 29;
+
+ /**
* Use this value to delete all the data.
*/
const uint32_t CLEAR_ALL = 0xFFFFFFFF;
@@ -340,6 +345,19 @@ interface nsIClearDataService : nsISupports
CLEAR_CREDENTIAL_MANAGER_STATE | CLEAR_COOKIE_BANNER_EXCEPTION |
CLEAR_COOKIE_BANNER_EXECUTED_RECORD | CLEAR_FINGERPRINTING_PROTECTION_STATE |
CLEAR_BOUNCE_TRACKING_PROTECTION_STATE;
+
+ /**
+ * Helper flag for clearing cookies and site data.
+ * This flag groups state that we consider site data
+ * from the user perspective. If you implement UI that
+ * offers site data clearing this is almost always what you want.
+ * If you need more granular control please use more specific
+ * flags like CLEAR_COOKIES and CLEAR_DOM_STORAGES.
+ */
+ const uint32_t CLEAR_COOKIES_AND_SITE_DATA =
+ CLEAR_COOKIES | CLEAR_COOKIE_BANNER_EXECUTED_RECORD | CLEAR_DOM_STORAGES | CLEAR_HSTS |
+ CLEAR_EME | CLEAR_AUTH_TOKENS | CLEAR_AUTH_CACHE | CLEAR_FINGERPRINTING_PROTECTION_STATE |
+ CLEAR_BOUNCE_TRACKING_PROTECTION_STATE | CLEAR_STORAGE_PERMISSIONS;
};
/**
diff --git a/toolkit/components/cleardata/tests/browser/browser.toml b/toolkit/components/cleardata/tests/browser/browser.toml
index 16ecffce02..d13f4f4f32 100644
--- a/toolkit/components/cleardata/tests/browser/browser.toml
+++ b/toolkit/components/cleardata/tests/browser/browser.toml
@@ -3,21 +3,18 @@
["browser_auth_tokens.js"]
["browser_css_cache.js"]
-https_first_disabled = true
support-files = [
"file_css_cache.css",
"file_css_cache.html"
]
["browser_image_cache.js"]
-https_first_disabled = true
support-files = [
"file_image_cache.html",
"file_image_cache.jpg"
]
["browser_preflight_cache.js"]
-https_first_disabled = true
support-files = ["file_cors_preflight.sjs"]
["browser_quota.js"]
diff --git a/toolkit/components/cleardata/tests/browser/browser_css_cache.js b/toolkit/components/cleardata/tests/browser/browser_css_cache.js
index 47088e5011..61cee8b8d9 100644
--- a/toolkit/components/cleardata/tests/browser/browser_css_cache.js
+++ b/toolkit/components/cleardata/tests/browser/browser_css_cache.js
@@ -74,6 +74,7 @@ async function cleanupTestTabs() {
}
add_task(async function test_deleteByPrincipal() {
+ await SpecialPowers.setBoolPref("dom.security.https_first", false);
await addTestTabs();
// Clear data for content principal of A
diff --git a/toolkit/components/cleardata/tests/browser/browser_image_cache.js b/toolkit/components/cleardata/tests/browser/browser_image_cache.js
index d7116d2502..947d4aba3e 100644
--- a/toolkit/components/cleardata/tests/browser/browser_image_cache.js
+++ b/toolkit/components/cleardata/tests/browser/browser_image_cache.js
@@ -114,6 +114,7 @@ add_setup(function () {
});
add_task(async function test_deleteByPrincipal() {
+ await SpecialPowers.setBoolPref("dom.security.https_first", false);
await addTestTabs();
// Clear data for content principal of A
@@ -152,6 +153,7 @@ add_task(async function test_deleteByPrincipal() {
});
add_task(async function test_deleteByBaseDomain() {
+ await SpecialPowers.setBoolPref("dom.security.https_first", false);
await addTestTabs();
// Clear data for base domain of A.
diff --git a/toolkit/components/cleardata/tests/browser/browser_preflight_cache.js b/toolkit/components/cleardata/tests/browser/browser_preflight_cache.js
index d3eabb9e38..8973cc981c 100644
--- a/toolkit/components/cleardata/tests/browser/browser_preflight_cache.js
+++ b/toolkit/components/cleardata/tests/browser/browser_preflight_cache.js
@@ -9,8 +9,8 @@ const { SiteDataTestUtils } = ChromeUtils.importESModule(
const uuidGenerator = Services.uuid;
-const ORIGIN_A = "http://example.net";
-const ORIGIN_B = "http://example.org";
+const ORIGIN_A = "https://example.net";
+const ORIGIN_B = "https://example.org";
const PREFLIGHT_URL_PATH =
"/browser/toolkit/components/cleardata/tests/browser/file_cors_preflight.sjs";
@@ -45,7 +45,7 @@ async function testDeleteAll(
clearDataFlag,
{ deleteBy = "all", hasUserInput = false } = {}
) {
- await BrowserTestUtils.withNewTab("http://example.com", async browser => {
+ await BrowserTestUtils.withNewTab("https://example.com", async browser => {
let token = uuidGenerator.generateUUID().toString();
// Populate the preflight cache.
@@ -132,7 +132,7 @@ add_task(async function test_deletePrivateBrowsingCache() {
const tab = (browser.gBrowser.selectedTab = BrowserTestUtils.addTab(
browser.gBrowser,
- "http://example.com"
+ "https://example.com"
));
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
diff --git a/toolkit/components/cleardata/tests/marionette/test_moved_origin_directory_cleanup.py b/toolkit/components/cleardata/tests/marionette/test_moved_origin_directory_cleanup.py
index 50f4c93f65..d30cf438e1 100644
--- a/toolkit/components/cleardata/tests/marionette/test_moved_origin_directory_cleanup.py
+++ b/toolkit/components/cleardata/tests/marionette/test_moved_origin_directory_cleanup.py
@@ -16,6 +16,7 @@ class MovedOriginDirectoryCleanupTestCase(MarionetteTestCase):
"privacy.sanitize.sanitizeOnShutdown": True,
"privacy.clearOnShutdown.offlineApps": True,
"dom.quotaManager.backgroundTask.enabled": False,
+ "browser.sanitizer.loglevel": "All",
}
)
self.moved_origin_directory = (
@@ -30,6 +31,13 @@ class MovedOriginDirectoryCleanupTestCase(MarionetteTestCase):
with self.marionette.using_context("chrome"):
self.marionette.execute_script(
"""
+ let promise = new Promise(resolve => {
+ function observer() {
+ Services.obs.removeObserver(observer, "cookie-saved-on-disk");
+ resolve();
+ }
+ Services.obs.addObserver(observer, "cookie-saved-on-disk");
+ });
Services.cookies.add(
"example.local",
"path",
@@ -43,6 +51,7 @@ class MovedOriginDirectoryCleanupTestCase(MarionetteTestCase):
Ci.nsICookie.SAMESITE_NONE,
Ci.nsICookie.SCHEME_UNSET
);
+ return promise;
"""
)
diff --git a/toolkit/components/cleardata/tests/unit/test_permissions.js b/toolkit/components/cleardata/tests/unit/test_permissions.js
index 1f46ab5015..450476183a 100644
--- a/toolkit/components/cleardata/tests/unit/test_permissions.js
+++ b/toolkit/components/cleardata/tests/unit/test_permissions.js
@@ -88,7 +88,7 @@ add_task(async function test_principal_permissions() {
await new Promise(aResolve => {
Services.clearData.deleteData(
Ci.nsIClearDataService.CLEAR_PERMISSIONS,
- value => aResolve()
+ () => aResolve()
);
});
});
@@ -465,7 +465,7 @@ add_task(async function test_3rdpartystorage_permissions() {
await new Promise(aResolve => {
Services.clearData.deleteData(
Ci.nsIClearDataService.CLEAR_PERMISSIONS,
- value => aResolve()
+ () => aResolve()
);
});
});
diff --git a/toolkit/components/cleardata/tests/unit/test_storage_permission.js b/toolkit/components/cleardata/tests/unit/test_storage_permission.js
index a44e9f2c6a..5ff3e6d15c 100644
--- a/toolkit/components/cleardata/tests/unit/test_storage_permission.js
+++ b/toolkit/components/cleardata/tests/unit/test_storage_permission.js
@@ -62,7 +62,7 @@ add_task(async function test_removing_storage_permission() {
await new Promise(aResolve => {
Services.clearData.deleteData(
Ci.nsIClearDataService.CLEAR_PERMISSIONS,
- value => aResolve()
+ () => aResolve()
);
});
});
@@ -138,7 +138,7 @@ add_task(async function test_removing_storage_permission_from_principal() {
await new Promise(aResolve => {
Services.clearData.deleteData(
Ci.nsIClearDataService.CLEAR_PERMISSIONS,
- value => aResolve()
+ () => aResolve()
);
});
});
@@ -240,7 +240,7 @@ add_task(async function test_removing_storage_permission_from_base_domainl() {
await new Promise(aResolve => {
Services.clearData.deleteData(
Ci.nsIClearDataService.CLEAR_PERMISSIONS,
- value => aResolve()
+ () => aResolve()
);
});
});
@@ -392,7 +392,7 @@ add_task(async function test_deleteUserInteractionForClearingHistory() {
await new Promise(aResolve => {
Services.clearData.deleteData(
Ci.nsIClearDataService.CLEAR_PERMISSIONS,
- value => aResolve()
+ () => aResolve()
);
});
});
diff --git a/toolkit/components/cleardata/tests/unit/test_storage_permission_clearing.js b/toolkit/components/cleardata/tests/unit/test_storage_permission_clearing.js
new file mode 100644
index 0000000000..9285236958
--- /dev/null
+++ b/toolkit/components/cleardata/tests/unit/test_storage_permission_clearing.js
@@ -0,0 +1,271 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/***
+ * Tests clearing storage access and persistent storage permissions
+ */
+
+"use strict";
+
+const baseDomain = "example.net";
+const baseDomain2 = "host.org";
+const uri = Services.io.newURI(`https://` + baseDomain);
+const uri2 = Services.io.newURI(`https://` + baseDomain2);
+const uri3 = Services.io.newURI(`https://www.` + baseDomain);
+const principal = Services.scriptSecurityManager.createContentPrincipal(
+ uri,
+ {}
+);
+const principal2 = Services.scriptSecurityManager.createContentPrincipal(
+ uri2,
+ {}
+);
+const principal3 = Services.scriptSecurityManager.createContentPrincipal(
+ uri3,
+ {}
+);
+
+add_task(async function test_clearing_by_principal() {
+ Services.perms.addFromPrincipal(
+ principal,
+ "storage-access",
+ Services.perms.ALLOW_ACTION
+ );
+ Assert.equal(
+ Services.perms.testExactPermissionFromPrincipal(
+ principal,
+ "storage-access"
+ ),
+ Services.perms.ALLOW_ACTION,
+ "There is a storage-access permission set for principal 1"
+ );
+
+ Services.perms.addFromPrincipal(
+ principal,
+ "persistent-storage",
+ Services.perms.ALLOW_ACTION
+ );
+ Assert.equal(
+ Services.perms.testExactPermissionFromPrincipal(
+ principal,
+ "persistent-storage"
+ ),
+ Services.perms.ALLOW_ACTION,
+ "There is a persistent-storage permission set for principal 1"
+ );
+
+ // Add a principal that shouldn't get cleared
+ Services.perms.addFromPrincipal(
+ principal2,
+ "storage-access",
+ Services.perms.ALLOW_ACTION
+ );
+ Assert.equal(
+ Services.perms.testExactPermissionFromPrincipal(
+ principal2,
+ "storage-access"
+ ),
+ Services.perms.ALLOW_ACTION,
+ "There is a storage-access permission set for principal 2"
+ );
+
+ // Add an unrelated permission which we don't expect to be cleared
+ Services.perms.addFromPrincipal(
+ principal,
+ "desktop-notification",
+ Services.perms.ALLOW_ACTION
+ );
+ Assert.equal(
+ Services.perms.testExactPermissionFromPrincipal(
+ principal,
+ "desktop-notification"
+ ),
+ Services.perms.ALLOW_ACTION,
+ "There is a desktop-notification permission set for principal 1"
+ );
+
+ await new Promise(aResolve => {
+ Services.clearData.deleteDataFromPrincipal(
+ principal,
+ true,
+ Ci.nsIClearDataService.CLEAR_STORAGE_PERMISSIONS,
+ value => {
+ Assert.equal(value, 0);
+ aResolve();
+ }
+ );
+ });
+
+ Assert.equal(
+ Services.perms.testExactPermissionFromPrincipal(
+ principal,
+ "storage-access"
+ ),
+ Services.perms.UNKNOWN_ACTION,
+ "storage-access permission for principal 1 has been removed"
+ );
+ Assert.equal(
+ Services.perms.testExactPermissionFromPrincipal(
+ principal,
+ "persistent-storage"
+ ),
+ Services.perms.UNKNOWN_ACTION,
+ "persistent-storage permission for principal 1 should be removed"
+ );
+ Assert.equal(
+ Services.perms.testExactPermissionFromPrincipal(
+ principal2,
+ "storage-access"
+ ),
+ Services.perms.ALLOW_ACTION,
+ "storage-access permission for principal 2 should not be removed"
+ );
+ Assert.equal(
+ Services.perms.testExactPermissionFromPrincipal(
+ principal,
+ "desktop-notification"
+ ),
+ Services.perms.ALLOW_ACTION,
+ "desktop-notification permission for principal should not be removed"
+ );
+});
+
+add_task(async function test_clearing_by_baseDomain() {
+ Services.perms.addFromPrincipal(
+ principal,
+ "storage-access",
+ Services.perms.ALLOW_ACTION
+ );
+ Services.perms.addFromPrincipal(
+ principal2,
+ "storage-access",
+ Services.perms.ALLOW_ACTION
+ );
+ Services.perms.addFromPrincipal(
+ principal3,
+ "storage-access",
+ Services.perms.ALLOW_ACTION
+ );
+ Assert.equal(
+ Services.perms.testExactPermissionFromPrincipal(
+ principal,
+ "storage-access"
+ ),
+ Services.perms.ALLOW_ACTION,
+ "There is a storage-access permission set for principal 1"
+ );
+ Assert.equal(
+ Services.perms.testExactPermissionFromPrincipal(
+ principal2,
+ "storage-access"
+ ),
+ Services.perms.ALLOW_ACTION,
+ "There is a storage-access permission set for principal 2"
+ );
+
+ await new Promise(aResolve => {
+ Services.clearData.deleteDataFromBaseDomain(
+ baseDomain,
+ true,
+ Ci.nsIClearDataService.CLEAR_STORAGE_PERMISSIONS,
+ value => {
+ Assert.equal(value, 0);
+ aResolve();
+ }
+ );
+ });
+
+ Assert.equal(
+ Services.perms.testExactPermissionFromPrincipal(
+ principal,
+ "storage-access"
+ ),
+ Services.perms.UNKNOWN_ACTION,
+ "storage-access permission for principal 1 has been removed"
+ );
+ Assert.equal(
+ Services.perms.testExactPermissionFromPrincipal(
+ principal2,
+ "storage-access"
+ ),
+ Services.perms.ALLOW_ACTION,
+ "storage-access permission for principal 2 should not be removed"
+ );
+ Assert.equal(
+ Services.perms.testExactPermissionFromPrincipal(
+ principal3,
+ "storage-access"
+ ),
+ Services.perms.UNKNOWN_ACTION,
+ "storage-access permission for principal 3 should be removed"
+ );
+});
+
+add_task(async function test_clearing_by_range() {
+ let currTime = Date.now();
+ let modificationTimeTwoHoursAgo = new Date(currTime - 2 * 60 * 60 * 1000);
+ let modificationTimeThirtyMinsAgo = new Date(currTime - 30 * 60 * 1000);
+
+ Services.perms.testAddFromPrincipalByTime(
+ principal,
+ "storage-access",
+ Services.perms.ALLOW_ACTION,
+ modificationTimeTwoHoursAgo
+ );
+ Assert.equal(
+ Services.perms.testExactPermissionFromPrincipal(
+ principal,
+ "storage-access"
+ ),
+ Services.perms.ALLOW_ACTION,
+ "There is a storage-access permission set for principal 1"
+ );
+
+ Services.perms.testAddFromPrincipalByTime(
+ principal2,
+ "persistent-storage",
+ Services.perms.ALLOW_ACTION,
+ modificationTimeThirtyMinsAgo
+ );
+ Assert.equal(
+ Services.perms.testExactPermissionFromPrincipal(
+ principal2,
+ "persistent-storage"
+ ),
+ Services.perms.ALLOW_ACTION,
+ "There is a persistent-storage permission set for principal 2"
+ );
+
+ let modificationTimeOneHourAgo = new Date(currTime - 60 * 60 * 1000);
+ // We need to pass in microseconds to the clear data service
+ // so we multiply the ranges by 1000
+ await new Promise(aResolve => {
+ Services.clearData.deleteDataInTimeRange(
+ modificationTimeOneHourAgo * 1000,
+ Date.now() * 1000,
+ true,
+ Ci.nsIClearDataService.CLEAR_STORAGE_PERMISSIONS,
+ value => {
+ Assert.equal(value, 0);
+ aResolve();
+ }
+ );
+ });
+
+ Assert.equal(
+ Services.perms.testExactPermissionFromPrincipal(
+ principal,
+ "storage-access"
+ ),
+ Services.perms.ALLOW_ACTION,
+ "storage-access permission for principal 1 should not be removed"
+ );
+ Assert.equal(
+ Services.perms.testExactPermissionFromPrincipal(
+ principal2,
+ "persistent-storage"
+ ),
+ Services.perms.UNKNOWN_ACTION,
+ "persistent-storage permission for principal 2 should be removed"
+ );
+});
diff --git a/toolkit/components/cleardata/tests/unit/xpcshell.toml b/toolkit/components/cleardata/tests/unit/xpcshell.toml
index 2df07abcea..bf2813a401 100644
--- a/toolkit/components/cleardata/tests/unit/xpcshell.toml
+++ b/toolkit/components/cleardata/tests/unit/xpcshell.toml
@@ -38,3 +38,5 @@ skip-if = ["condprof"] # Bug 1769154 - expected fail w/condprof
["test_security_settings.js"]
["test_storage_permission.js"]
+
+["test_storage_permission_clearing.js"]
diff --git a/toolkit/components/contentanalysis/ContentAnalysis.cpp b/toolkit/components/contentanalysis/ContentAnalysis.cpp
index 1c71f5d986..2977072984 100644
--- a/toolkit/components/contentanalysis/ContentAnalysis.cpp
+++ b/toolkit/components/contentanalysis/ContentAnalysis.cpp
@@ -11,6 +11,7 @@
#include "base/process_util.h"
#include "GMPUtils.h" // ToHexString
#include "mozilla/Components.h"
+#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/Logging.h"
@@ -24,6 +25,9 @@
#include "nsIFile.h"
#include "nsIGlobalObject.h"
#include "nsIObserverService.h"
+#include "nsIOutputStream.h"
+#include "nsIPrintSettings.h"
+#include "nsIStorageStream.h"
#include "ScopedNSSTypes.h"
#include "xpcpublic.h"
@@ -35,6 +39,7 @@
# include <windows.h>
# define SECURITY_WIN32 1
# include <security.h>
+# include "mozilla/NativeNt.h"
# include "mozilla/WinDllServices.h"
#endif // XP_WIN
@@ -114,6 +119,11 @@ nsIContentAnalysisAcknowledgement::FinalAction ConvertResult(
} // anonymous namespace
namespace mozilla::contentanalysis {
+ContentAnalysisRequest::~ContentAnalysisRequest() {
+#ifdef XP_WIN
+ CloseHandle(mPrintDataHandle);
+#endif
+}
NS_IMETHODIMP
ContentAnalysisRequest::GetAnalysisType(AnalysisType* aAnalysisType) {
@@ -134,6 +144,34 @@ ContentAnalysisRequest::GetFilePath(nsAString& aFilePath) {
}
NS_IMETHODIMP
+ContentAnalysisRequest::GetPrintDataHandle(uint64_t* aPrintDataHandle) {
+#ifdef XP_WIN
+ uintptr_t printDataHandle = reinterpret_cast<uintptr_t>(mPrintDataHandle);
+ uint64_t printDataValue = static_cast<uint64_t>(printDataHandle);
+ *aPrintDataHandle = printDataValue;
+ return NS_OK;
+#else
+ return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+NS_IMETHODIMP
+ContentAnalysisRequest::GetPrinterName(nsAString& aPrinterName) {
+ aPrinterName = mPrinterName;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ContentAnalysisRequest::GetPrintDataSize(uint64_t* aPrintDataSize) {
+#ifdef XP_WIN
+ *aPrintDataSize = mPrintDataSize;
+ return NS_OK;
+#else
+ return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+NS_IMETHODIMP
ContentAnalysisRequest::GetUrl(nsIURI** aUrl) {
NS_ENSURE_ARG_POINTER(aUrl);
NS_IF_ADDREF(*aUrl = mUrl);
@@ -234,6 +272,8 @@ ContentAnalysisRequest::ContentAnalysisRequest(
mUrl(std::move(aUrl)),
mSha256Digest(std::move(aSha256Digest)),
mWindowGlobalParent(aWindowGlobalParent) {
+ MOZ_ASSERT(aAnalysisType != AnalysisType::ePrint,
+ "Print should use other ContentAnalysisRequest constructor!");
if (aStringIsFilePath) {
mFilePath = std::move(aString);
} else {
@@ -251,6 +291,32 @@ ContentAnalysisRequest::ContentAnalysisRequest(
mRequestToken = GenerateRequestToken();
}
+ContentAnalysisRequest::ContentAnalysisRequest(
+ const nsTArray<uint8_t> aPrintData, nsCOMPtr<nsIURI> aUrl,
+ nsString aPrinterName, dom::WindowGlobalParent* aWindowGlobalParent)
+ : mAnalysisType(AnalysisType::ePrint),
+ mUrl(std::move(aUrl)),
+ mPrinterName(std::move(aPrinterName)),
+ mWindowGlobalParent(aWindowGlobalParent) {
+#ifdef XP_WIN
+ LARGE_INTEGER dataContentLength;
+ dataContentLength.QuadPart = static_cast<LONGLONG>(aPrintData.Length());
+ mPrintDataHandle = ::CreateFileMappingW(
+ INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, dataContentLength.HighPart,
+ dataContentLength.LowPart, nullptr);
+ if (mPrintDataHandle) {
+ mozilla::nt::AutoMappedView view(mPrintDataHandle, FILE_MAP_ALL_ACCESS);
+ memcpy(view.as<uint8_t>(), aPrintData.Elements(), aPrintData.Length());
+ mPrintDataSize = aPrintData.Length();
+ }
+#else
+ MOZ_ASSERT_UNREACHABLE(
+ "Content Analysis is not supported on non-Windows platforms");
+#endif
+ mOperationTypeForDisplay = OperationType::eOperationPrint;
+ mRequestToken = GenerateRequestToken();
+}
+
nsresult ContentAnalysisRequest::GetFileDigest(const nsAString& aFilePath,
nsCString& aDigestString) {
MOZ_DIAGNOSTIC_ASSERT(
@@ -366,22 +432,44 @@ static nsresult ConvertToProtobuf(
requestData->set_digest(sha256Digest.get());
}
- nsString filePath;
- rv = aIn->GetFilePath(filePath);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!filePath.IsEmpty()) {
- std::string filePathStr = NS_ConvertUTF16toUTF8(filePath).get();
- aOut->set_file_path(filePathStr);
- auto filename = filePathStr.substr(filePathStr.find_last_of("/\\") + 1);
- if (!filename.empty()) {
- requestData->set_filename(filename);
+ if (analysisType == nsIContentAnalysisRequest::AnalysisType::ePrint) {
+#if XP_WIN
+ uint64_t printDataHandle;
+ MOZ_TRY(aIn->GetPrintDataHandle(&printDataHandle));
+ if (!printDataHandle) {
+ return NS_ERROR_OUT_OF_MEMORY;
}
+ aOut->mutable_print_data()->set_handle(printDataHandle);
+
+ uint64_t printDataSize;
+ MOZ_TRY(aIn->GetPrintDataSize(&printDataSize));
+ aOut->mutable_print_data()->set_size(printDataSize);
+
+ nsString printerName;
+ MOZ_TRY(aIn->GetPrinterName(printerName));
+ requestData->mutable_print_metadata()->set_printer_name(
+ NS_ConvertUTF16toUTF8(printerName).get());
+#else
+ return NS_ERROR_NOT_IMPLEMENTED;
+#endif
} else {
- nsString textContent;
- rv = aIn->GetTextContent(textContent);
+ nsString filePath;
+ rv = aIn->GetFilePath(filePath);
NS_ENSURE_SUCCESS(rv, rv);
- MOZ_ASSERT(!textContent.IsEmpty());
- aOut->set_text_content(NS_ConvertUTF16toUTF8(textContent).get());
+ if (!filePath.IsEmpty()) {
+ std::string filePathStr = NS_ConvertUTF16toUTF8(filePath).get();
+ aOut->set_file_path(filePathStr);
+ auto filename = filePathStr.substr(filePathStr.find_last_of("/\\") + 1);
+ if (!filename.empty()) {
+ requestData->set_filename(filename);
+ }
+ } else {
+ nsString textContent;
+ rv = aIn->GetTextContent(textContent);
+ NS_ENSURE_SUCCESS(rv, rv);
+ MOZ_ASSERT(!textContent.IsEmpty());
+ aOut->set_text_content(NS_ConvertUTF16toUTF8(textContent).get());
+ }
}
#ifdef XP_WIN
@@ -412,8 +500,31 @@ static nsresult ConvertToProtobuf(
return NS_OK;
}
+namespace {
+// We don't want this overload to be called for string parameters, so
+// use std::enable_if
+template <typename T>
+typename std::enable_if_t<!std::is_same<std::string, std::decay_t<T>>::value,
+ void>
+LogWithMaxLength(std::stringstream& ss, T value, size_t maxLength) {
+ ss << value;
+}
+
+// 0 indicates no max length
+template <typename T>
+typename std::enable_if_t<std::is_same<std::string, std::decay_t<T>>::value,
+ void>
+LogWithMaxLength(std::stringstream& ss, T value, size_t maxLength) {
+ if (!maxLength || value.length() < maxLength) {
+ ss << value;
+ } else {
+ ss << value.substr(0, maxLength) << " (truncated)";
+ }
+}
+} // namespace
+
static void LogRequest(
- content_analysis::sdk::ContentAnalysisRequest* aPbRequest) {
+ const content_analysis::sdk::ContentAnalysisRequest* aPbRequest) {
// We cannot use Protocol Buffer's DebugString() because we optimize for
// lite runtime.
if (!static_cast<LogModule*>(gContentAnalysisLog)
@@ -425,12 +536,13 @@ static void LogRequest(
ss << "ContentAnalysisRequest:"
<< "\n";
-#define ADD_FIELD(PBUF, NAME, FUNC) \
- ss << " " << (NAME) << ": "; \
- if ((PBUF)->has_##FUNC()) \
- ss << (PBUF)->FUNC() << "\n"; \
- else \
- ss << "<none>" \
+#define ADD_FIELD(PBUF, NAME, FUNC) \
+ ss << " " << (NAME) << ": "; \
+ if ((PBUF)->has_##FUNC()) { \
+ LogWithMaxLength(ss, (PBUF)->FUNC(), 500); \
+ ss << "\n"; \
+ } else \
+ ss << "<none>" \
<< "\n";
#define ADD_EXISTS(PBUF, NAME, FUNC) \
@@ -612,6 +724,12 @@ ContentAnalysisResponse::GetAction(Action* aAction) {
return NS_OK;
}
+NS_IMETHODIMP
+ContentAnalysisResponse::GetCancelError(CancelError* aCancelError) {
+ *aCancelError = mCancelError;
+ return NS_OK;
+}
+
static void LogAcknowledgement(
content_analysis::sdk::ContentAnalysisAcknowledgement* aPbAck) {
if (!static_cast<LogModule*>(gContentAnalysisLog)
@@ -643,6 +761,10 @@ void ContentAnalysisResponse::SetOwner(RefPtr<ContentAnalysis> aOwner) {
mOwner = std::move(aOwner);
}
+void ContentAnalysisResponse::SetCancelError(CancelError aCancelError) {
+ mCancelError = aCancelError;
+}
+
void ContentAnalysisResponse::ResolveWarnAction(bool aAllowContent) {
MOZ_ASSERT(mAction == Action::eWarn);
mAction = aAllowContent ? Action::eAllow : Action::eBlock;
@@ -684,15 +806,17 @@ NS_IMETHODIMP ContentAnalysisResult::GetShouldAllowContent(
if (mValue.is<NoContentAnalysisResult>()) {
NoContentAnalysisResult result = mValue.as<NoContentAnalysisResult>();
if (Preferences::GetBool(kDefaultAllowPref)) {
- *aShouldAllowContent = result != NoContentAnalysisResult::CANCELED;
+ *aShouldAllowContent =
+ result != NoContentAnalysisResult::DENY_DUE_TO_CANCELED;
} else {
// Note that we allow content if we're unable to get it (for example, if
// there's clipboard content that is not text or file)
*aShouldAllowContent =
- result == NoContentAnalysisResult::CONTENT_ANALYSIS_NOT_ACTIVE ||
- result ==
- NoContentAnalysisResult::CONTEXT_EXEMPT_FROM_CONTENT_ANALYSIS ||
- result == NoContentAnalysisResult::ERROR_COULD_NOT_GET_DATA;
+ result == NoContentAnalysisResult::
+ ALLOW_DUE_TO_CONTENT_ANALYSIS_NOT_ACTIVE ||
+ result == NoContentAnalysisResult::
+ ALLOW_DUE_TO_CONTEXT_EXEMPT_FROM_CONTENT_ANALYSIS ||
+ result == NoContentAnalysisResult::ALLOW_DUE_TO_COULD_NOT_GET_DATA;
}
} else {
*aShouldAllowContent =
@@ -735,8 +859,8 @@ ContentAnalysis::UrlFilterResult ContentAnalysis::FilterByUrlLists(
nsIContentAnalysisRequest* aRequest) {
EnsureParsedUrlFilters();
- nsIURI* nsiUrl = nullptr;
- MOZ_ALWAYS_SUCCEEDS(aRequest->GetUrl(&nsiUrl));
+ nsCOMPtr<nsIURI> nsiUrl;
+ MOZ_ALWAYS_SUCCEEDS(aRequest->GetUrl(getter_AddRefs(nsiUrl)));
nsCString urlString;
nsresult rv = nsiUrl->GetSpec(urlString);
NS_ENSURE_SUCCESS(rv, UrlFilterResult::eDeny);
@@ -825,6 +949,8 @@ NS_IMPL_ISUPPORTS(ContentAnalysisAcknowledgement,
nsIContentAnalysisAcknowledgement);
NS_IMPL_ISUPPORTS(ContentAnalysisCallback, nsIContentAnalysisCallback);
NS_IMPL_ISUPPORTS(ContentAnalysisResult, nsIContentAnalysisResult);
+NS_IMPL_ISUPPORTS(ContentAnalysisDiagnosticInfo,
+ nsIContentAnalysisDiagnosticInfo);
NS_IMPL_ISUPPORTS(ContentAnalysis, nsIContentAnalysis, ContentAnalysis);
ContentAnalysis::ContentAnalysis()
@@ -942,6 +1068,7 @@ nsresult ContentAnalysis::CancelWithError(nsCString aRequestToken,
// May be shutting down
return;
}
+ owner->SetLastResult(aResult);
nsCOMPtr<nsIObserverService> obsServ =
mozilla::services::GetObserverService();
bool allow = Preferences::GetBool(kDefaultAllowPref);
@@ -951,6 +1078,20 @@ nsresult ContentAnalysis::CancelWithError(nsCString aRequestToken,
: nsIContentAnalysisResponse::Action::eCanceled,
aRequestToken);
response->SetOwner(owner);
+ nsIContentAnalysisResponse::CancelError cancelError;
+ switch (aResult) {
+ case NS_ERROR_NOT_AVAILABLE:
+ cancelError = nsIContentAnalysisResponse::CancelError::eNoAgent;
+ break;
+ case NS_ERROR_INVALID_SIGNATURE:
+ cancelError =
+ nsIContentAnalysisResponse::CancelError::eInvalidAgentSignature;
+ break;
+ default:
+ cancelError = nsIContentAnalysisResponse::CancelError::eErrorOther;
+ break;
+ }
+ response->SetCancelError(cancelError);
obsServ->NotifyObservers(response, "dlp-response", nullptr);
nsMainThreadPtrHandle<nsIContentAnalysisCallback> callbackHolder;
{
@@ -1156,6 +1297,8 @@ void ContentAnalysis::IssueResponse(RefPtr<ContentAnalysisResponse>& response) {
LOGE("Content analysis couldn't get request token from response!");
return;
}
+ // Successfully made a request to the agent, so mark that we succeeded
+ mLastResult = NS_OK;
Maybe<CallbackData> maybeCallbackData;
{
@@ -1187,7 +1330,9 @@ void ContentAnalysis::IssueResponse(RefPtr<ContentAnalysisResponse>& response) {
LOGD("Content analysis resolving response promise for token %s",
responseRequestToken.get());
- nsIContentAnalysisResponse::Action action = response->GetAction();
+ nsIContentAnalysisResponse::Action action;
+ DebugOnly<nsresult> rv = response->GetAction(&action);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
nsCOMPtr<nsIObserverService> obsServ =
mozilla::services::GetObserverService();
if (action == nsIContentAnalysisResponse::Action::eWarn) {
@@ -1232,8 +1377,28 @@ NS_IMETHODIMP
ContentAnalysis::AnalyzeContentRequestCallback(
nsIContentAnalysisRequest* aRequest, bool aAutoAcknowledge,
nsIContentAnalysisCallback* aCallback) {
+ MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_ARG(aRequest);
NS_ENSURE_ARG(aCallback);
+ nsresult rv = AnalyzeContentRequestCallbackPrivate(aRequest, aAutoAcknowledge,
+ aCallback);
+ if (NS_FAILED(rv)) {
+ nsCString requestToken;
+ nsresult requestTokenRv = aRequest->GetRequestToken(requestToken);
+ NS_ENSURE_SUCCESS(requestTokenRv, requestTokenRv);
+ CancelWithError(requestToken, rv);
+ }
+ return rv;
+}
+
+nsresult ContentAnalysis::AnalyzeContentRequestCallbackPrivate(
+ nsIContentAnalysisRequest* aRequest, bool aAutoAcknowledge,
+ nsIContentAnalysisCallback* aCallback) {
+ // Make sure we send the notification first, so if we later return
+ // an error the JS will handle it correctly.
+ nsCOMPtr<nsIObserverService> obsServ =
+ mozilla::services::GetObserverService();
+ obsServ->NotifyObservers(aRequest, "dlp-request-made", nullptr);
bool isActive;
nsresult rv = GetIsActive(&isActive);
@@ -1242,10 +1407,6 @@ ContentAnalysis::AnalyzeContentRequestCallback(
return NS_ERROR_NOT_AVAILABLE;
}
- nsCOMPtr<nsIObserverService> obsServ =
- mozilla::services::GetObserverService();
- obsServ->NotifyObservers(aRequest, "dlp-request-made", nullptr);
-
MOZ_ASSERT(NS_IsMainThread());
// since we're on the main thread, don't need to synchronize this
int64_t requestCount = ++mRequestCount;
@@ -1333,7 +1494,9 @@ ContentAnalysis::RespondToWarnDialog(const nsACString& aRequestToken,
return;
}
entry->mResponse->ResolveWarnAction(aAllowContent);
- auto action = entry->mResponse->GetAction();
+ nsIContentAnalysisResponse::Action action;
+ DebugOnly<nsresult> rv = entry->mResponse->GetAction(&action);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
if (entry->mCallbackData.AutoAcknowledge()) {
RefPtr<ContentAnalysisAcknowledgement> acknowledgement =
new ContentAnalysisAcknowledgement(
@@ -1358,6 +1521,181 @@ ContentAnalysis::RespondToWarnDialog(const nsACString& aRequestToken,
return NS_OK;
}
+#if defined(XP_WIN)
+RefPtr<ContentAnalysis::PrintAllowedPromise>
+ContentAnalysis::PrintToPDFToDetermineIfPrintAllowed(
+ dom::CanonicalBrowsingContext* aBrowsingContext,
+ nsIPrintSettings* aPrintSettings) {
+ // Note that the IsChrome() check here excludes a few
+ // common about pages like about:config, about:preferences,
+ // and about:support, but other about: pages may still
+ // go through content analysis.
+ if (aBrowsingContext->IsChrome()) {
+ return PrintAllowedPromise::CreateAndResolve(PrintAllowedResult(true),
+ __func__);
+ }
+ nsCOMPtr<nsIPrintSettings> contentAnalysisPrintSettings;
+ if (NS_WARN_IF(NS_FAILED(aPrintSettings->Clone(
+ getter_AddRefs(contentAnalysisPrintSettings)))) ||
+ NS_WARN_IF(!aBrowsingContext->GetCurrentWindowGlobal())) {
+ return PrintAllowedPromise::CreateAndReject(
+ PrintAllowedError(NS_ERROR_FAILURE), __func__);
+ }
+ contentAnalysisPrintSettings->SetOutputDestination(
+ nsIPrintSettings::OutputDestinationType::kOutputDestinationStream);
+ contentAnalysisPrintSettings->SetOutputFormat(
+ nsIPrintSettings::kOutputFormatPDF);
+ nsCOMPtr<nsIStorageStream> storageStream =
+ do_CreateInstance("@mozilla.org/storagestream;1");
+ if (!storageStream) {
+ return PrintAllowedPromise::CreateAndReject(
+ PrintAllowedError(NS_ERROR_FAILURE), __func__);
+ }
+ // Use segment size of 512K
+ nsresult rv = storageStream->Init(0x80000, UINT32_MAX);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return PrintAllowedPromise::CreateAndReject(PrintAllowedError(rv),
+ __func__);
+ }
+
+ nsCOMPtr<nsIOutputStream> outputStream;
+ storageStream->QueryInterface(NS_GET_IID(nsIOutputStream),
+ getter_AddRefs(outputStream));
+ MOZ_ASSERT(outputStream);
+
+ contentAnalysisPrintSettings->SetOutputStream(outputStream.get());
+ RefPtr<dom::CanonicalBrowsingContext> browsingContext = aBrowsingContext;
+ auto promise = MakeRefPtr<PrintAllowedPromise::Private>(__func__);
+ nsCOMPtr<nsIPrintSettings> finalPrintSettings(aPrintSettings);
+ aBrowsingContext
+ ->PrintWithNoContentAnalysis(contentAnalysisPrintSettings, true, nullptr)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [browsingContext, contentAnalysisPrintSettings, finalPrintSettings,
+ promise](
+ dom::MaybeDiscardedBrowsingContext cachedStaticBrowsingContext)
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA mutable {
+ nsCOMPtr<nsIOutputStream> outputStream;
+ contentAnalysisPrintSettings->GetOutputStream(
+ getter_AddRefs(outputStream));
+ nsCOMPtr<nsIStorageStream> storageStream =
+ do_QueryInterface(outputStream);
+ MOZ_ASSERT(storageStream);
+ nsTArray<uint8_t> printData;
+ uint32_t length = 0;
+ storageStream->GetLength(&length);
+ if (!printData.SetLength(length, fallible)) {
+ promise->Reject(
+ PrintAllowedError(NS_ERROR_OUT_OF_MEMORY,
+ cachedStaticBrowsingContext),
+ __func__);
+ return;
+ }
+ nsCOMPtr<nsIInputStream> inputStream;
+ nsresult rv = storageStream->NewInputStream(
+ 0, getter_AddRefs(inputStream));
+ if (NS_FAILED(rv)) {
+ promise->Reject(
+ PrintAllowedError(rv, cachedStaticBrowsingContext),
+ __func__);
+ return;
+ }
+ uint32_t currentPosition = 0;
+ while (currentPosition < length) {
+ uint32_t elementsRead = 0;
+ // Make sure the reinterpret_cast<> below is safe
+ static_assert(std::is_trivially_assignable_v<
+ decltype(*printData.Elements()), char>);
+ rv = inputStream->Read(
+ reinterpret_cast<char*>(printData.Elements()) +
+ currentPosition,
+ length - currentPosition, &elementsRead);
+ if (NS_WARN_IF(NS_FAILED(rv) || !elementsRead)) {
+ promise->Reject(
+ PrintAllowedError(NS_FAILED(rv) ? rv : NS_ERROR_FAILURE,
+ cachedStaticBrowsingContext),
+ __func__);
+ return;
+ }
+ currentPosition += elementsRead;
+ }
+
+ nsString printerName;
+ rv = contentAnalysisPrintSettings->GetPrinterName(printerName);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ promise->Reject(
+ PrintAllowedError(rv, cachedStaticBrowsingContext),
+ __func__);
+ return;
+ }
+
+ auto* windowParent = browsingContext->GetCurrentWindowGlobal();
+ if (!windowParent) {
+ // The print window may have been closed by the user by now.
+ // Cancel the print.
+ promise->Reject(
+ PrintAllowedError(NS_ERROR_ABORT,
+ cachedStaticBrowsingContext),
+ __func__);
+ return;
+ }
+ nsCOMPtr<nsIURI> uri = windowParent->GetDocumentURI();
+ nsCOMPtr<nsIContentAnalysisRequest> contentAnalysisRequest =
+ new contentanalysis::ContentAnalysisRequest(
+ std::move(printData), std::move(uri),
+ std::move(printerName), windowParent);
+ auto callback =
+ MakeRefPtr<contentanalysis::ContentAnalysisCallback>(
+ [browsingContext, cachedStaticBrowsingContext, promise,
+ finalPrintSettings = std::move(finalPrintSettings)](
+ nsIContentAnalysisResponse* aResponse)
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA mutable {
+ bool shouldAllow = false;
+ DebugOnly<nsresult> rv =
+ aResponse->GetShouldAllowContent(
+ &shouldAllow);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ promise->Resolve(
+ PrintAllowedResult(
+ shouldAllow, cachedStaticBrowsingContext),
+ __func__);
+ },
+ [promise,
+ cachedStaticBrowsingContext](nsresult aError) {
+ promise->Reject(
+ PrintAllowedError(aError,
+ cachedStaticBrowsingContext),
+ __func__);
+ });
+ nsCOMPtr<nsIContentAnalysis> contentAnalysis =
+ mozilla::components::nsIContentAnalysis::Service();
+ if (NS_WARN_IF(!contentAnalysis)) {
+ promise->Reject(
+ PrintAllowedError(rv, cachedStaticBrowsingContext),
+ __func__);
+ } else {
+ bool isActive = false;
+ nsresult rv = contentAnalysis->GetIsActive(&isActive);
+ // Should not be called if content analysis is not active
+ MOZ_ASSERT(isActive);
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+ rv = contentAnalysis->AnalyzeContentRequestCallback(
+ contentAnalysisRequest, /* aAutoAcknowledge */ true,
+ callback);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ promise->Reject(
+ PrintAllowedError(rv, cachedStaticBrowsingContext),
+ __func__);
+ }
+ }
+ },
+ [promise](nsresult aError) {
+ promise->Reject(PrintAllowedError(aError), __func__);
+ });
+ return promise;
+}
+#endif
+
NS_IMETHODIMP
ContentAnalysisResponse::Acknowledge(
nsIContentAnalysisAcknowledgement* aAcknowledgement) {
@@ -1425,6 +1763,46 @@ nsresult ContentAnalysis::RunAcknowledgeTask(
return rv;
}
+bool ContentAnalysis::LastRequestSucceeded() {
+ return mLastResult != NS_ERROR_NOT_AVAILABLE &&
+ mLastResult != NS_ERROR_INVALID_SIGNATURE &&
+ mLastResult != NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+ContentAnalysis::GetDiagnosticInfo(JSContext* aCx,
+ mozilla::dom::Promise** aPromise) {
+ RefPtr<mozilla::dom::Promise> promise;
+ nsresult rv = MakePromise(aCx, &promise);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mCaClientPromise->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [promise](std::shared_ptr<content_analysis::sdk::Client> client) mutable {
+ if (!client) {
+ auto info = MakeRefPtr<ContentAnalysisDiagnosticInfo>(
+ false, EmptyString(), false, 0);
+ promise->MaybeResolve(info);
+ return;
+ }
+ RefPtr<ContentAnalysis> self = GetContentAnalysisFromService();
+ std::string agentPath = client->GetAgentInfo().binary_path;
+ nsString agentWidePath = NS_ConvertUTF8toUTF16(agentPath);
+ auto info = MakeRefPtr<ContentAnalysisDiagnosticInfo>(
+ self->LastRequestSucceeded(), std::move(agentWidePath), false,
+ self ? self->mRequestCount : 0);
+ promise->MaybeResolve(info);
+ },
+ [promise](nsresult rv) {
+ RefPtr<ContentAnalysis> self = GetContentAnalysisFromService();
+ auto info = MakeRefPtr<ContentAnalysisDiagnosticInfo>(
+ false, EmptyString(), rv == NS_ERROR_INVALID_SIGNATURE,
+ self ? self->mRequestCount : 0);
+ promise->MaybeResolve(info);
+ });
+ promise.forget(aPromise);
+ return NS_OK;
+}
+
NS_IMETHODIMP ContentAnalysisCallback::ContentResult(
nsIContentAnalysisResponse* aResponse) {
if (mPromise.isSome()) {
@@ -1448,6 +1826,28 @@ ContentAnalysisCallback::ContentAnalysisCallback(RefPtr<dom::Promise> aPromise)
: mPromise(Some(new nsMainThreadPtrHolder<dom::Promise>(
"content analysis promise", aPromise))) {}
+NS_IMETHODIMP ContentAnalysisDiagnosticInfo::GetConnectedToAgent(
+ bool* aConnectedToAgent) {
+ *aConnectedToAgent = mConnectedToAgent;
+ return NS_OK;
+}
+NS_IMETHODIMP ContentAnalysisDiagnosticInfo::GetAgentPath(
+ nsAString& aAgentPath) {
+ aAgentPath = mAgentPath;
+ return NS_OK;
+}
+NS_IMETHODIMP ContentAnalysisDiagnosticInfo::GetFailedSignatureVerification(
+ bool* aFailedSignatureVerification) {
+ *aFailedSignatureVerification = mFailedSignatureVerification;
+ return NS_OK;
+}
+
+NS_IMETHODIMP ContentAnalysisDiagnosticInfo::GetRequestCount(
+ int64_t* aRequestCount) {
+ *aRequestCount = mRequestCount;
+ return NS_OK;
+}
+
#undef LOGD
#undef LOGE
} // namespace mozilla::contentanalysis
diff --git a/toolkit/components/contentanalysis/ContentAnalysis.h b/toolkit/components/contentanalysis/ContentAnalysis.h
index 17b6e3fc1b..f2545624fd 100644
--- a/toolkit/components/contentanalysis/ContentAnalysis.h
+++ b/toolkit/components/contentanalysis/ContentAnalysis.h
@@ -8,6 +8,8 @@
#include "mozilla/DataMutex.h"
#include "mozilla/MozPromise.h"
+#include "mozilla/dom/BrowsingContext.h"
+#include "mozilla/dom/MaybeDiscarded.h"
#include "mozilla/dom/Promise.h"
#include "nsIContentAnalysis.h"
#include "nsProxyRelease.h"
@@ -18,10 +20,16 @@
#include <regex>
#include <string>
+#ifdef XP_WIN
+# include <windows.h>
+#endif // XP_WIN
+
class nsIPrincipal;
+class nsIPrintSettings;
class ContentAnalysisTest;
namespace mozilla::dom {
+class CanonicalBrowsingContext;
class DataTransfer;
class WindowGlobalParent;
} // namespace mozilla::dom
@@ -34,6 +42,27 @@ class ContentAnalysisResponse;
namespace mozilla::contentanalysis {
+class ContentAnalysisDiagnosticInfo final
+ : public nsIContentAnalysisDiagnosticInfo {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSICONTENTANALYSISDIAGNOSTICINFO
+ ContentAnalysisDiagnosticInfo(bool aConnectedToAgent, nsString aAgentPath,
+ bool aFailedSignatureVerification,
+ int64_t aRequestCount)
+ : mConnectedToAgent(aConnectedToAgent),
+ mAgentPath(std::move(aAgentPath)),
+ mFailedSignatureVerification(aFailedSignatureVerification),
+ mRequestCount(aRequestCount) {}
+
+ private:
+ ~ContentAnalysisDiagnosticInfo() = default;
+ bool mConnectedToAgent;
+ nsString mAgentPath;
+ bool mFailedSignatureVerification;
+ int64_t mRequestCount;
+};
+
class ContentAnalysisRequest final : public nsIContentAnalysisRequest {
public:
NS_DECL_ISUPPORTS
@@ -43,11 +72,15 @@ class ContentAnalysisRequest final : public nsIContentAnalysisRequest {
bool aStringIsFilePath, nsCString aSha256Digest,
nsCOMPtr<nsIURI> aUrl, OperationType aOperationType,
dom::WindowGlobalParent* aWindowGlobalParent);
+ ContentAnalysisRequest(const nsTArray<uint8_t> aPrintData,
+ nsCOMPtr<nsIURI> aUrl, nsString aPrinterName,
+ dom::WindowGlobalParent* aWindowGlobalParent);
static nsresult GetFileDigest(const nsAString& aFilePath,
nsCString& aDigestString);
private:
- ~ContentAnalysisRequest() = default;
+ ~ContentAnalysisRequest();
+
// Remove unneeded copy constructor/assignment
ContentAnalysisRequest(const ContentAnalysisRequest&) = delete;
ContentAnalysisRequest& operator=(ContentAnalysisRequest&) = delete;
@@ -84,7 +117,16 @@ class ContentAnalysisRequest final : public nsIContentAnalysisRequest {
// OPERATION_CUSTOMDISPLAYSTRING
nsString mOperationDisplayString;
+ // The name of the printer being printed to
+ nsString mPrinterName;
+
RefPtr<dom::WindowGlobalParent> mWindowGlobalParent;
+#ifdef XP_WIN
+ // The printed data to analyze, in PDF format
+ HANDLE mPrintDataHandle = 0;
+ // The size of the printed data in mPrintDataHandle
+ uint64_t mPrintDataSize = 0;
+#endif
friend class ::ContentAnalysisTest;
};
@@ -105,6 +147,40 @@ class ContentAnalysis final : public nsIContentAnalysis {
ContentAnalysis();
nsCString GetUserActionId();
+ void SetLastResult(nsresult aLastResult) { mLastResult = aLastResult; }
+
+ struct PrintAllowedResult final {
+ bool mAllowed;
+ dom::MaybeDiscarded<dom::BrowsingContext>
+ mCachedStaticDocumentBrowsingContext;
+ PrintAllowedResult(bool aAllowed, dom::MaybeDiscarded<dom::BrowsingContext>
+ aCachedStaticDocumentBrowsingContext)
+ : mAllowed(aAllowed),
+ mCachedStaticDocumentBrowsingContext(
+ aCachedStaticDocumentBrowsingContext) {}
+ explicit PrintAllowedResult(bool aAllowed)
+ : PrintAllowedResult(aAllowed, dom::MaybeDiscardedBrowsingContext()) {}
+ };
+ struct PrintAllowedError final {
+ nsresult mError;
+ dom::MaybeDiscarded<dom::BrowsingContext>
+ mCachedStaticDocumentBrowsingContext;
+ PrintAllowedError(nsresult aError, dom::MaybeDiscarded<dom::BrowsingContext>
+ aCachedStaticDocumentBrowsingContext)
+ : mError(aError),
+ mCachedStaticDocumentBrowsingContext(
+ aCachedStaticDocumentBrowsingContext) {}
+ explicit PrintAllowedError(nsresult aError)
+ : PrintAllowedError(aError, dom::MaybeDiscardedBrowsingContext()) {}
+ };
+ using PrintAllowedPromise =
+ MozPromise<PrintAllowedResult, PrintAllowedError, true>;
+#if defined(XP_WIN)
+ MOZ_CAN_RUN_SCRIPT static RefPtr<PrintAllowedPromise>
+ PrintToPDFToDetermineIfPrintAllowed(
+ dom::CanonicalBrowsingContext* aBrowsingContext,
+ nsIPrintSettings* aPrintSettings);
+#endif // defined(XP_WIN)
private:
~ContentAnalysis();
@@ -114,6 +190,10 @@ class ContentAnalysis final : public nsIContentAnalysis {
nsresult CreateContentAnalysisClient(nsCString&& aPipePathName,
nsString&& aClientSignatureSetting,
bool aIsPerUser);
+ nsresult AnalyzeContentRequestCallbackPrivate(
+ nsIContentAnalysisRequest* aRequest, bool aAutoAcknowledge,
+ nsIContentAnalysisCallback* aCallback);
+
nsresult RunAnalyzeRequestTask(
const RefPtr<nsIContentAnalysisRequest>& aRequest, bool aAutoAcknowledge,
int64_t aRequestCount,
@@ -129,6 +209,7 @@ class ContentAnalysis final : public nsIContentAnalysis {
content_analysis::sdk::ContentAnalysisRequest&& aRequest,
const std::shared_ptr<content_analysis::sdk::Client>& aClient);
void IssueResponse(RefPtr<ContentAnalysisResponse>& response);
+ bool LastRequestSucceeded();
// Did the URL filter completely handle the request or do we need to check
// with the agent.
@@ -147,6 +228,7 @@ class ContentAnalysis final : public nsIContentAnalysis {
bool mClientCreationAttempted;
bool mSetByEnterprise;
+ nsresult mLastResult = NS_OK;
class CallbackData final {
public:
@@ -180,7 +262,7 @@ class ContentAnalysis final : public nsIContentAnalysis {
std::vector<std::regex> mAllowUrlList;
std::vector<std::regex> mDenyUrlList;
- bool mParsedUrlLists;
+ bool mParsedUrlLists = false;
friend class ContentAnalysisResponse;
friend class ::ContentAnalysisTest;
@@ -198,6 +280,7 @@ class ContentAnalysisResponse final : public nsIContentAnalysisResponse {
void SetOwner(RefPtr<ContentAnalysis> aOwner);
void DoNotAcknowledge() { mDoNotAcknowledge = true; }
+ void SetCancelError(CancelError aCancelError);
private:
~ContentAnalysisResponse() = default;
@@ -218,7 +301,11 @@ class ContentAnalysisResponse final : public nsIContentAnalysisResponse {
// Identifier for the corresponding nsIContentAnalysisRequest
nsCString mRequestToken;
- // ContentAnalysis (or, more precisely, it's Client object) must outlive
+ // If mAction is eCanceled, this is the error explaining why the request was
+ // canceled, or eUserInitiated if the user canceled it.
+ CancelError mCancelError = CancelError::eUserInitiated;
+
+ // ContentAnalysis (or, more precisely, its Client object) must outlive
// the transaction.
RefPtr<ContentAnalysis> mOwner;
diff --git a/toolkit/components/contentanalysis/ContentAnalysisIPCTypes.h b/toolkit/components/contentanalysis/ContentAnalysisIPCTypes.h
index a7cf812d0b..a554036257 100644
--- a/toolkit/components/contentanalysis/ContentAnalysisIPCTypes.h
+++ b/toolkit/components/contentanalysis/ContentAnalysisIPCTypes.h
@@ -17,12 +17,12 @@ namespace mozilla {
namespace contentanalysis {
enum class NoContentAnalysisResult : uint8_t {
- CONTENT_ANALYSIS_NOT_ACTIVE,
- CONTEXT_EXEMPT_FROM_CONTENT_ANALYSIS,
- CANCELED,
- ERROR_INVALID_JSON_RESPONSE,
- ERROR_COULD_NOT_GET_DATA,
- ERROR_OTHER,
+ ALLOW_DUE_TO_CONTENT_ANALYSIS_NOT_ACTIVE,
+ ALLOW_DUE_TO_CONTEXT_EXEMPT_FROM_CONTENT_ANALYSIS,
+ ALLOW_DUE_TO_COULD_NOT_GET_DATA,
+ DENY_DUE_TO_CANCELED,
+ DENY_DUE_TO_INVALID_JSON_RESPONSE,
+ DENY_DUE_TO_OTHER_ERROR,
LAST_VALUE
};
@@ -59,7 +59,8 @@ class ContentAnalysisResult : public nsIContentAnalysisResult {
}
}
}
- return FromNoResult(NoContentAnalysisResult::ERROR_INVALID_JSON_RESPONSE);
+ return FromNoResult(
+ NoContentAnalysisResult::DENY_DUE_TO_INVALID_JSON_RESPONSE);
}
static RefPtr<ContentAnalysisResult> FromJSONContentAnalysisResponse(
@@ -76,16 +77,21 @@ class ContentAnalysisResult : public nsIContentAnalysisResult {
} else if (shouldAllowValue.isFalse()) {
return FromAction(nsIContentAnalysisResponse::Action::eBlock);
} else {
- return FromNoResult(NoContentAnalysisResult::ERROR_OTHER);
+ return FromNoResult(NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR);
}
}
}
- return FromNoResult(NoContentAnalysisResult::ERROR_INVALID_JSON_RESPONSE);
+ return FromNoResult(
+ NoContentAnalysisResult::DENY_DUE_TO_INVALID_JSON_RESPONSE);
}
static RefPtr<ContentAnalysisResult> FromContentAnalysisResponse(
nsIContentAnalysisResponse* aResponse) {
- if (aResponse->GetShouldAllowContent()) {
+ bool shouldAllowContent = false;
+ DebugOnly<nsresult> rv =
+ aResponse->GetShouldAllowContent(&shouldAllowContent);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ if (shouldAllowContent) {
return FromAction(nsIContentAnalysisResponse::Action::eAllow);
} else {
return FromAction(nsIContentAnalysisResponse::Action::eBlock);
@@ -159,7 +165,8 @@ struct ParamTraits<mozilla::contentanalysis::ContentAnalysisResult*> {
return true;
}
*aResult = mozilla::contentanalysis::ContentAnalysisResult::FromNoResult(
- mozilla::contentanalysis::NoContentAnalysisResult::ERROR_OTHER);
+ mozilla::contentanalysis::NoContentAnalysisResult::
+ DENY_DUE_TO_OTHER_ERROR);
return ReadParam(aReader, &((*aResult)->mValue));
}
};
diff --git a/toolkit/components/contentanalysis/components.conf b/toolkit/components/contentanalysis/components.conf
index 82236cb1b9..1683ef99d7 100644
--- a/toolkit/components/contentanalysis/components.conf
+++ b/toolkit/components/contentanalysis/components.conf
@@ -11,5 +11,6 @@ Classes = [
'contract_ids': ['@mozilla.org/contentanalysis;1'],
'type': 'mozilla::contentanalysis::ContentAnalysis',
'headers': ['/toolkit/components/contentanalysis/ContentAnalysis.h'],
+ 'overridable': True,
},
]
diff --git a/toolkit/components/contentanalysis/content_analysis/sdk/analysis.pb.cc b/toolkit/components/contentanalysis/content_analysis/sdk/analysis.pb.cc
index 6a2e1ccf46..b7a7434685 100644
--- a/toolkit/components/contentanalysis/content_analysis/sdk/analysis.pb.cc
+++ b/toolkit/components/contentanalysis/content_analysis/sdk/analysis.pb.cc
@@ -1,7 +1,7 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
-// source: analysis.proto
+// source: content_analysis/sdk/analysis.proto
-#include "analysis.pb.h"
+#include "content_analysis/sdk/analysis.pb.h"
#include <algorithm>
@@ -134,9 +134,10 @@ PROTOBUF_CONSTEXPR ContentAnalysisRequest::ContentAnalysisRequest(
, /*decltype(_impl_.user_action_id_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
, /*decltype(_impl_.request_data_)*/nullptr
, /*decltype(_impl_.client_metadata_)*/nullptr
+ , /*decltype(_impl_.analysis_connector_)*/0
+ , /*decltype(_impl_.reason_)*/0
, /*decltype(_impl_.expires_at_)*/int64_t{0}
, /*decltype(_impl_.user_action_requests_count_)*/int64_t{0}
- , /*decltype(_impl_.analysis_connector_)*/0
, /*decltype(_impl_.content_data_)*/{}
, /*decltype(_impl_._oneof_case_)*/{}} {}
struct ContentAnalysisRequestDefaultTypeInternal {
@@ -400,6 +401,94 @@ constexpr ClientDownloadRequest_ResourceType ClientDownloadRequest::ResourceType
constexpr ClientDownloadRequest_ResourceType ClientDownloadRequest::ResourceType_MAX;
constexpr int ClientDownloadRequest::ResourceType_ARRAYSIZE;
#endif // (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
+bool ContentAnalysisRequest_Reason_IsValid(int value) {
+ switch (value) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<std::string> ContentAnalysisRequest_Reason_strings[8] = {};
+
+static const char ContentAnalysisRequest_Reason_names[] =
+ "CLIPBOARD_PASTE"
+ "DRAG_AND_DROP"
+ "FILE_PICKER_DIALOG"
+ "NORMAL_DOWNLOAD"
+ "PRINT_PREVIEW_PRINT"
+ "SAVE_AS_DOWNLOAD"
+ "SYSTEM_DIALOG_PRINT"
+ "UNKNOWN";
+
+static const ::PROTOBUF_NAMESPACE_ID::internal::EnumEntry ContentAnalysisRequest_Reason_entries[] = {
+ { {ContentAnalysisRequest_Reason_names + 0, 15}, 1 },
+ { {ContentAnalysisRequest_Reason_names + 15, 13}, 2 },
+ { {ContentAnalysisRequest_Reason_names + 28, 18}, 3 },
+ { {ContentAnalysisRequest_Reason_names + 46, 15}, 6 },
+ { {ContentAnalysisRequest_Reason_names + 61, 19}, 4 },
+ { {ContentAnalysisRequest_Reason_names + 80, 16}, 7 },
+ { {ContentAnalysisRequest_Reason_names + 96, 19}, 5 },
+ { {ContentAnalysisRequest_Reason_names + 115, 7}, 0 },
+};
+
+static const int ContentAnalysisRequest_Reason_entries_by_number[] = {
+ 7, // 0 -> UNKNOWN
+ 0, // 1 -> CLIPBOARD_PASTE
+ 1, // 2 -> DRAG_AND_DROP
+ 2, // 3 -> FILE_PICKER_DIALOG
+ 4, // 4 -> PRINT_PREVIEW_PRINT
+ 6, // 5 -> SYSTEM_DIALOG_PRINT
+ 3, // 6 -> NORMAL_DOWNLOAD
+ 5, // 7 -> SAVE_AS_DOWNLOAD
+};
+
+const std::string& ContentAnalysisRequest_Reason_Name(
+ ContentAnalysisRequest_Reason value) {
+ static const bool dummy =
+ ::PROTOBUF_NAMESPACE_ID::internal::InitializeEnumStrings(
+ ContentAnalysisRequest_Reason_entries,
+ ContentAnalysisRequest_Reason_entries_by_number,
+ 8, ContentAnalysisRequest_Reason_strings);
+ (void) dummy;
+ int idx = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumName(
+ ContentAnalysisRequest_Reason_entries,
+ ContentAnalysisRequest_Reason_entries_by_number,
+ 8, value);
+ return idx == -1 ? ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString() :
+ ContentAnalysisRequest_Reason_strings[idx].get();
+}
+bool ContentAnalysisRequest_Reason_Parse(
+ ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, ContentAnalysisRequest_Reason* value) {
+ int int_value;
+ bool success = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumValue(
+ ContentAnalysisRequest_Reason_entries, 8, name, &int_value);
+ if (success) {
+ *value = static_cast<ContentAnalysisRequest_Reason>(int_value);
+ }
+ return success;
+}
+#if (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
+constexpr ContentAnalysisRequest_Reason ContentAnalysisRequest::UNKNOWN;
+constexpr ContentAnalysisRequest_Reason ContentAnalysisRequest::CLIPBOARD_PASTE;
+constexpr ContentAnalysisRequest_Reason ContentAnalysisRequest::DRAG_AND_DROP;
+constexpr ContentAnalysisRequest_Reason ContentAnalysisRequest::FILE_PICKER_DIALOG;
+constexpr ContentAnalysisRequest_Reason ContentAnalysisRequest::PRINT_PREVIEW_PRINT;
+constexpr ContentAnalysisRequest_Reason ContentAnalysisRequest::SYSTEM_DIALOG_PRINT;
+constexpr ContentAnalysisRequest_Reason ContentAnalysisRequest::NORMAL_DOWNLOAD;
+constexpr ContentAnalysisRequest_Reason ContentAnalysisRequest::SAVE_AS_DOWNLOAD;
+constexpr ContentAnalysisRequest_Reason ContentAnalysisRequest::Reason_MIN;
+constexpr ContentAnalysisRequest_Reason ContentAnalysisRequest::Reason_MAX;
+constexpr int ContentAnalysisRequest::Reason_ARRAYSIZE;
+#endif // (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
bool ContentAnalysisResponse_Result_TriggeredRule_Action_IsValid(int value) {
switch (value) {
case 0:
@@ -2650,7 +2739,7 @@ class ContentAnalysisRequest::_Internal {
(*has_bits)[0] |= 1u;
}
static void set_has_analysis_connector(HasBits* has_bits) {
- (*has_bits)[0] |= 64u;
+ (*has_bits)[0] |= 16u;
}
static const ::content_analysis::sdk::ContentMetaData& request_data(const ContentAnalysisRequest* msg);
static void set_has_request_data(HasBits* has_bits) {
@@ -2662,12 +2751,15 @@ class ContentAnalysisRequest::_Internal {
}
static const ::content_analysis::sdk::ContentAnalysisRequest_PrintData& print_data(const ContentAnalysisRequest* msg);
static void set_has_expires_at(HasBits* has_bits) {
- (*has_bits)[0] |= 16u;
+ (*has_bits)[0] |= 64u;
}
static void set_has_user_action_id(HasBits* has_bits) {
(*has_bits)[0] |= 2u;
}
static void set_has_user_action_requests_count(HasBits* has_bits) {
+ (*has_bits)[0] |= 128u;
+ }
+ static void set_has_reason(HasBits* has_bits) {
(*has_bits)[0] |= 32u;
}
};
@@ -2716,9 +2808,10 @@ ContentAnalysisRequest::ContentAnalysisRequest(const ContentAnalysisRequest& fro
, decltype(_impl_.user_action_id_){}
, decltype(_impl_.request_data_){nullptr}
, decltype(_impl_.client_metadata_){nullptr}
+ , decltype(_impl_.analysis_connector_){}
+ , decltype(_impl_.reason_){}
, decltype(_impl_.expires_at_){}
, decltype(_impl_.user_action_requests_count_){}
- , decltype(_impl_.analysis_connector_){}
, decltype(_impl_.content_data_){}
, /*decltype(_impl_._oneof_case_)*/{}};
@@ -2745,9 +2838,9 @@ ContentAnalysisRequest::ContentAnalysisRequest(const ContentAnalysisRequest& fro
if (from._internal_has_client_metadata()) {
_this->_impl_.client_metadata_ = new ::content_analysis::sdk::ClientMetadata(*from._impl_.client_metadata_);
}
- ::memcpy(&_impl_.expires_at_, &from._impl_.expires_at_,
- static_cast<size_t>(reinterpret_cast<char*>(&_impl_.analysis_connector_) -
- reinterpret_cast<char*>(&_impl_.expires_at_)) + sizeof(_impl_.analysis_connector_));
+ ::memcpy(&_impl_.analysis_connector_, &from._impl_.analysis_connector_,
+ static_cast<size_t>(reinterpret_cast<char*>(&_impl_.user_action_requests_count_) -
+ reinterpret_cast<char*>(&_impl_.analysis_connector_)) + sizeof(_impl_.user_action_requests_count_));
clear_has_content_data();
switch (from.content_data_case()) {
case kTextContent: {
@@ -2782,9 +2875,10 @@ inline void ContentAnalysisRequest::SharedCtor(
, decltype(_impl_.user_action_id_){}
, decltype(_impl_.request_data_){nullptr}
, decltype(_impl_.client_metadata_){nullptr}
+ , decltype(_impl_.analysis_connector_){0}
+ , decltype(_impl_.reason_){0}
, decltype(_impl_.expires_at_){int64_t{0}}
, decltype(_impl_.user_action_requests_count_){int64_t{0}}
- , decltype(_impl_.analysis_connector_){0}
, decltype(_impl_.content_data_){}
, /*decltype(_impl_._oneof_case_)*/{}
};
@@ -2873,10 +2967,10 @@ void ContentAnalysisRequest::Clear() {
_impl_.client_metadata_->Clear();
}
}
- if (cached_has_bits & 0x00000070u) {
- ::memset(&_impl_.expires_at_, 0, static_cast<size_t>(
- reinterpret_cast<char*>(&_impl_.analysis_connector_) -
- reinterpret_cast<char*>(&_impl_.expires_at_)) + sizeof(_impl_.analysis_connector_));
+ if (cached_has_bits & 0x000000f0u) {
+ ::memset(&_impl_.analysis_connector_, 0, static_cast<size_t>(
+ reinterpret_cast<char*>(&_impl_.user_action_requests_count_) -
+ reinterpret_cast<char*>(&_impl_.analysis_connector_)) + sizeof(_impl_.user_action_requests_count_));
}
clear_content_data();
_impl_._has_bits_.Clear();
@@ -2995,6 +3089,19 @@ const char* ContentAnalysisRequest::_InternalParse(const char* ptr, ::_pbi::Pars
} else
goto handle_unusual;
continue;
+ // optional .content_analysis.sdk.ContentAnalysisRequest.Reason reason = 19;
+ case 19:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 152)) {
+ uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+ CHK_(ptr);
+ if (PROTOBUF_PREDICT_TRUE(::content_analysis::sdk::ContentAnalysisRequest_Reason_IsValid(val))) {
+ _internal_set_reason(static_cast<::content_analysis::sdk::ContentAnalysisRequest_Reason>(val));
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(19, val, mutable_unknown_fields());
+ }
+ } else
+ goto handle_unusual;
+ continue;
default:
goto handle_unusual;
} // switch
@@ -3033,7 +3140,7 @@ uint8_t* ContentAnalysisRequest::_InternalSerialize(
}
// optional .content_analysis.sdk.AnalysisConnector analysis_connector = 9;
- if (cached_has_bits & 0x00000040u) {
+ if (cached_has_bits & 0x00000010u) {
target = stream->EnsureSpace(target);
target = ::_pbi::WireFormatLite::WriteEnumToArray(
9, this->_internal_analysis_connector(), target);
@@ -3073,7 +3180,7 @@ uint8_t* ContentAnalysisRequest::_InternalSerialize(
default: ;
}
// optional int64 expires_at = 15;
- if (cached_has_bits & 0x00000010u) {
+ if (cached_has_bits & 0x00000040u) {
target = stream->EnsureSpace(target);
target = ::_pbi::WireFormatLite::WriteInt64ToArray(15, this->_internal_expires_at(), target);
}
@@ -3085,7 +3192,7 @@ uint8_t* ContentAnalysisRequest::_InternalSerialize(
}
// optional int64 user_action_requests_count = 17;
- if (cached_has_bits & 0x00000020u) {
+ if (cached_has_bits & 0x00000080u) {
target = stream->EnsureSpace(target);
target = ::_pbi::WireFormatLite::WriteInt64ToArray(17, this->_internal_user_action_requests_count(), target);
}
@@ -3097,6 +3204,13 @@ uint8_t* ContentAnalysisRequest::_InternalSerialize(
_Internal::print_data(this).GetCachedSize(), target, stream);
}
+ // optional .content_analysis.sdk.ContentAnalysisRequest.Reason reason = 19;
+ if (cached_has_bits & 0x00000020u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteEnumToArray(
+ 19, this->_internal_reason(), target);
+ }
+
if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
@@ -3122,7 +3236,7 @@ size_t ContentAnalysisRequest::ByteSizeLong() const {
}
cached_has_bits = _impl_._has_bits_[0];
- if (cached_has_bits & 0x0000007fu) {
+ if (cached_has_bits & 0x000000ffu) {
// optional string request_token = 5;
if (cached_has_bits & 0x00000001u) {
total_size += 1 +
@@ -3151,22 +3265,28 @@ size_t ContentAnalysisRequest::ByteSizeLong() const {
*_impl_.client_metadata_);
}
- // optional int64 expires_at = 15;
+ // optional .content_analysis.sdk.AnalysisConnector analysis_connector = 9;
if (cached_has_bits & 0x00000010u) {
- total_size += ::_pbi::WireFormatLite::Int64SizePlusOne(this->_internal_expires_at());
+ total_size += 1 +
+ ::_pbi::WireFormatLite::EnumSize(this->_internal_analysis_connector());
}
- // optional int64 user_action_requests_count = 17;
+ // optional .content_analysis.sdk.ContentAnalysisRequest.Reason reason = 19;
if (cached_has_bits & 0x00000020u) {
total_size += 2 +
- ::_pbi::WireFormatLite::Int64Size(
- this->_internal_user_action_requests_count());
+ ::_pbi::WireFormatLite::EnumSize(this->_internal_reason());
}
- // optional .content_analysis.sdk.AnalysisConnector analysis_connector = 9;
+ // optional int64 expires_at = 15;
if (cached_has_bits & 0x00000040u) {
- total_size += 1 +
- ::_pbi::WireFormatLite::EnumSize(this->_internal_analysis_connector());
+ total_size += ::_pbi::WireFormatLite::Int64SizePlusOne(this->_internal_expires_at());
+ }
+
+ // optional int64 user_action_requests_count = 17;
+ if (cached_has_bits & 0x00000080u) {
+ total_size += 2 +
+ ::_pbi::WireFormatLite::Int64Size(
+ this->_internal_user_action_requests_count());
}
}
@@ -3219,7 +3339,7 @@ void ContentAnalysisRequest::MergeFrom(const ContentAnalysisRequest& from) {
_this->_impl_.tags_.MergeFrom(from._impl_.tags_);
cached_has_bits = from._impl_._has_bits_[0];
- if (cached_has_bits & 0x0000007fu) {
+ if (cached_has_bits & 0x000000ffu) {
if (cached_has_bits & 0x00000001u) {
_this->_internal_set_request_token(from._internal_request_token());
}
@@ -3235,13 +3355,16 @@ void ContentAnalysisRequest::MergeFrom(const ContentAnalysisRequest& from) {
from._internal_client_metadata());
}
if (cached_has_bits & 0x00000010u) {
- _this->_impl_.expires_at_ = from._impl_.expires_at_;
+ _this->_impl_.analysis_connector_ = from._impl_.analysis_connector_;
}
if (cached_has_bits & 0x00000020u) {
- _this->_impl_.user_action_requests_count_ = from._impl_.user_action_requests_count_;
+ _this->_impl_.reason_ = from._impl_.reason_;
}
if (cached_has_bits & 0x00000040u) {
- _this->_impl_.analysis_connector_ = from._impl_.analysis_connector_;
+ _this->_impl_.expires_at_ = from._impl_.expires_at_;
+ }
+ if (cached_has_bits & 0x00000080u) {
+ _this->_impl_.user_action_requests_count_ = from._impl_.user_action_requests_count_;
}
_this->_impl_._has_bits_[0] |= cached_has_bits;
}
@@ -3296,8 +3419,8 @@ void ContentAnalysisRequest::InternalSwap(ContentAnalysisRequest* other) {
&other->_impl_.user_action_id_, rhs_arena
);
::PROTOBUF_NAMESPACE_ID::internal::memswap<
- PROTOBUF_FIELD_OFFSET(ContentAnalysisRequest, _impl_.analysis_connector_)
- + sizeof(ContentAnalysisRequest::_impl_.analysis_connector_)
+ PROTOBUF_FIELD_OFFSET(ContentAnalysisRequest, _impl_.user_action_requests_count_)
+ + sizeof(ContentAnalysisRequest::_impl_.user_action_requests_count_)
- PROTOBUF_FIELD_OFFSET(ContentAnalysisRequest, _impl_.request_data_)>(
reinterpret_cast<char*>(&_impl_.request_data_),
reinterpret_cast<char*>(&other->_impl_.request_data_));
diff --git a/toolkit/components/contentanalysis/content_analysis/sdk/analysis.pb.h b/toolkit/components/contentanalysis/content_analysis/sdk/analysis.pb.h
index 186cc97649..875c632ebf 100644
--- a/toolkit/components/contentanalysis/content_analysis/sdk/analysis.pb.h
+++ b/toolkit/components/contentanalysis/content_analysis/sdk/analysis.pb.h
@@ -1,8 +1,8 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
-// source: analysis.proto
+// source: content_analysis/sdk/analysis.proto
-#ifndef GOOGLE_PROTOBUF_INCLUDED_analysis_2eproto
-#define GOOGLE_PROTOBUF_INCLUDED_analysis_2eproto
+#ifndef GOOGLE_PROTOBUF_INCLUDED_content_5fanalysis_2fsdk_2fanalysis_2eproto
+#define GOOGLE_PROTOBUF_INCLUDED_content_5fanalysis_2fsdk_2fanalysis_2eproto
#include <limits>
#include <string>
@@ -13,7 +13,7 @@
#error incompatible with your Protocol Buffer headers. Please update
#error your headers.
#endif
-#if 3021006 < PROTOBUF_MIN_PROTOC_VERSION
+#if 3021012 < PROTOBUF_MIN_PROTOC_VERSION
#error This file was generated by an older version of protoc which is
#error incompatible with your Protocol Buffer headers. Please
#error regenerate this file with a newer version of protoc.
@@ -31,7 +31,7 @@
#include <google/protobuf/generated_enum_util.h>
// @@protoc_insertion_point(includes)
#include <google/protobuf/port_def.inc>
-#define PROTOBUF_INTERNAL_EXPORT_analysis_2eproto
+#define PROTOBUF_INTERNAL_EXPORT_content_5fanalysis_2fsdk_2fanalysis_2eproto
PROTOBUF_NAMESPACE_OPEN
namespace internal {
class AnyMetadata;
@@ -39,7 +39,7 @@ class AnyMetadata;
PROTOBUF_NAMESPACE_CLOSE
// Internal implementation detail -- do not use these members.
-struct TableStruct_analysis_2eproto {
+struct TableStruct_content_5fanalysis_2fsdk_2fanalysis_2eproto {
static const uint32_t offsets[];
};
namespace content_analysis {
@@ -154,6 +154,31 @@ inline const std::string& ClientDownloadRequest_ResourceType_Name(T enum_t_value
}
bool ClientDownloadRequest_ResourceType_Parse(
::PROTOBUF_NAMESPACE_ID::ConstStringParam name, ClientDownloadRequest_ResourceType* value);
+enum ContentAnalysisRequest_Reason : int {
+ ContentAnalysisRequest_Reason_UNKNOWN = 0,
+ ContentAnalysisRequest_Reason_CLIPBOARD_PASTE = 1,
+ ContentAnalysisRequest_Reason_DRAG_AND_DROP = 2,
+ ContentAnalysisRequest_Reason_FILE_PICKER_DIALOG = 3,
+ ContentAnalysisRequest_Reason_PRINT_PREVIEW_PRINT = 4,
+ ContentAnalysisRequest_Reason_SYSTEM_DIALOG_PRINT = 5,
+ ContentAnalysisRequest_Reason_NORMAL_DOWNLOAD = 6,
+ ContentAnalysisRequest_Reason_SAVE_AS_DOWNLOAD = 7
+};
+bool ContentAnalysisRequest_Reason_IsValid(int value);
+constexpr ContentAnalysisRequest_Reason ContentAnalysisRequest_Reason_Reason_MIN = ContentAnalysisRequest_Reason_UNKNOWN;
+constexpr ContentAnalysisRequest_Reason ContentAnalysisRequest_Reason_Reason_MAX = ContentAnalysisRequest_Reason_SAVE_AS_DOWNLOAD;
+constexpr int ContentAnalysisRequest_Reason_Reason_ARRAYSIZE = ContentAnalysisRequest_Reason_Reason_MAX + 1;
+
+const std::string& ContentAnalysisRequest_Reason_Name(ContentAnalysisRequest_Reason value);
+template<typename T>
+inline const std::string& ContentAnalysisRequest_Reason_Name(T enum_t_value) {
+ static_assert(::std::is_same<T, ContentAnalysisRequest_Reason>::value ||
+ ::std::is_integral<T>::value,
+ "Incorrect type passed to function ContentAnalysisRequest_Reason_Name.");
+ return ContentAnalysisRequest_Reason_Name(static_cast<ContentAnalysisRequest_Reason>(enum_t_value));
+}
+bool ContentAnalysisRequest_Reason_Parse(
+ ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, ContentAnalysisRequest_Reason* value);
enum ContentAnalysisResponse_Result_TriggeredRule_Action : int {
ContentAnalysisResponse_Result_TriggeredRule_Action_ACTION_UNSPECIFIED = 0,
ContentAnalysisResponse_Result_TriggeredRule_Action_REPORT_ONLY = 1,
@@ -448,7 +473,7 @@ class ContentMetaData_PrintMetadata final :
int printer_type_;
};
union { Impl_ _impl_; };
- friend struct ::TableStruct_analysis_2eproto;
+ friend struct ::TableStruct_content_5fanalysis_2fsdk_2fanalysis_2eproto;
};
// -------------------------------------------------------------------
@@ -717,7 +742,7 @@ class ContentMetaData final :
::content_analysis::sdk::ContentMetaData_PrintMetadata* print_metadata_;
};
union { Impl_ _impl_; };
- friend struct ::TableStruct_analysis_2eproto;
+ friend struct ::TableStruct_content_5fanalysis_2fsdk_2fanalysis_2eproto;
};
// -------------------------------------------------------------------
@@ -864,7 +889,7 @@ class ClientMetadata_Browser final :
::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr machine_user_;
};
union { Impl_ _impl_; };
- friend struct ::TableStruct_analysis_2eproto;
+ friend struct ::TableStruct_content_5fanalysis_2fsdk_2fanalysis_2eproto;
};
// -------------------------------------------------------------------
@@ -1013,7 +1038,7 @@ class ClientMetadata final :
::content_analysis::sdk::ClientMetadata_Browser* browser_;
};
union { Impl_ _impl_; };
- friend struct ::TableStruct_analysis_2eproto;
+ friend struct ::TableStruct_content_5fanalysis_2fsdk_2fanalysis_2eproto;
};
// -------------------------------------------------------------------
@@ -1178,7 +1203,7 @@ class ClientDownloadRequest_Resource final :
int type_;
};
union { Impl_ _impl_; };
- friend struct ::TableStruct_analysis_2eproto;
+ friend struct ::TableStruct_content_5fanalysis_2fsdk_2fanalysis_2eproto;
};
// -------------------------------------------------------------------
@@ -1360,7 +1385,7 @@ class ClientDownloadRequest final :
mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
};
union { Impl_ _impl_; };
- friend struct ::TableStruct_analysis_2eproto;
+ friend struct ::TableStruct_content_5fanalysis_2fsdk_2fanalysis_2eproto;
};
// -------------------------------------------------------------------
@@ -1517,7 +1542,7 @@ class ContentAnalysisRequest_PrintData final :
int64_t size_;
};
union { Impl_ _impl_; };
- friend struct ::TableStruct_analysis_2eproto;
+ friend struct ::TableStruct_content_5fanalysis_2fsdk_2fanalysis_2eproto;
};
// -------------------------------------------------------------------
@@ -1637,6 +1662,44 @@ class ContentAnalysisRequest final :
typedef ContentAnalysisRequest_PrintData PrintData;
+ typedef ContentAnalysisRequest_Reason Reason;
+ static constexpr Reason UNKNOWN =
+ ContentAnalysisRequest_Reason_UNKNOWN;
+ static constexpr Reason CLIPBOARD_PASTE =
+ ContentAnalysisRequest_Reason_CLIPBOARD_PASTE;
+ static constexpr Reason DRAG_AND_DROP =
+ ContentAnalysisRequest_Reason_DRAG_AND_DROP;
+ static constexpr Reason FILE_PICKER_DIALOG =
+ ContentAnalysisRequest_Reason_FILE_PICKER_DIALOG;
+ static constexpr Reason PRINT_PREVIEW_PRINT =
+ ContentAnalysisRequest_Reason_PRINT_PREVIEW_PRINT;
+ static constexpr Reason SYSTEM_DIALOG_PRINT =
+ ContentAnalysisRequest_Reason_SYSTEM_DIALOG_PRINT;
+ static constexpr Reason NORMAL_DOWNLOAD =
+ ContentAnalysisRequest_Reason_NORMAL_DOWNLOAD;
+ static constexpr Reason SAVE_AS_DOWNLOAD =
+ ContentAnalysisRequest_Reason_SAVE_AS_DOWNLOAD;
+ static inline bool Reason_IsValid(int value) {
+ return ContentAnalysisRequest_Reason_IsValid(value);
+ }
+ static constexpr Reason Reason_MIN =
+ ContentAnalysisRequest_Reason_Reason_MIN;
+ static constexpr Reason Reason_MAX =
+ ContentAnalysisRequest_Reason_Reason_MAX;
+ static constexpr int Reason_ARRAYSIZE =
+ ContentAnalysisRequest_Reason_Reason_ARRAYSIZE;
+ template<typename T>
+ static inline const std::string& Reason_Name(T enum_t_value) {
+ static_assert(::std::is_same<T, Reason>::value ||
+ ::std::is_integral<T>::value,
+ "Incorrect type passed to function Reason_Name.");
+ return ContentAnalysisRequest_Reason_Name(enum_t_value);
+ }
+ static inline bool Reason_Parse(::PROTOBUF_NAMESPACE_ID::ConstStringParam name,
+ Reason* value) {
+ return ContentAnalysisRequest_Reason_Parse(name, value);
+ }
+
// accessors -------------------------------------------------------
enum : int {
@@ -1645,9 +1708,10 @@ class ContentAnalysisRequest final :
kUserActionIdFieldNumber = 16,
kRequestDataFieldNumber = 10,
kClientMetadataFieldNumber = 12,
+ kAnalysisConnectorFieldNumber = 9,
+ kReasonFieldNumber = 19,
kExpiresAtFieldNumber = 15,
kUserActionRequestsCountFieldNumber = 17,
- kAnalysisConnectorFieldNumber = 9,
kTextContentFieldNumber = 13,
kFilePathFieldNumber = 14,
kPrintDataFieldNumber = 18,
@@ -1748,6 +1812,32 @@ class ContentAnalysisRequest final :
::content_analysis::sdk::ClientMetadata* client_metadata);
::content_analysis::sdk::ClientMetadata* unsafe_arena_release_client_metadata();
+ // optional .content_analysis.sdk.AnalysisConnector analysis_connector = 9;
+ bool has_analysis_connector() const;
+ private:
+ bool _internal_has_analysis_connector() const;
+ public:
+ void clear_analysis_connector();
+ ::content_analysis::sdk::AnalysisConnector analysis_connector() const;
+ void set_analysis_connector(::content_analysis::sdk::AnalysisConnector value);
+ private:
+ ::content_analysis::sdk::AnalysisConnector _internal_analysis_connector() const;
+ void _internal_set_analysis_connector(::content_analysis::sdk::AnalysisConnector value);
+ public:
+
+ // optional .content_analysis.sdk.ContentAnalysisRequest.Reason reason = 19;
+ bool has_reason() const;
+ private:
+ bool _internal_has_reason() const;
+ public:
+ void clear_reason();
+ ::content_analysis::sdk::ContentAnalysisRequest_Reason reason() const;
+ void set_reason(::content_analysis::sdk::ContentAnalysisRequest_Reason value);
+ private:
+ ::content_analysis::sdk::ContentAnalysisRequest_Reason _internal_reason() const;
+ void _internal_set_reason(::content_analysis::sdk::ContentAnalysisRequest_Reason value);
+ public:
+
// optional int64 expires_at = 15;
bool has_expires_at() const;
private:
@@ -1774,19 +1864,6 @@ class ContentAnalysisRequest final :
void _internal_set_user_action_requests_count(int64_t value);
public:
- // optional .content_analysis.sdk.AnalysisConnector analysis_connector = 9;
- bool has_analysis_connector() const;
- private:
- bool _internal_has_analysis_connector() const;
- public:
- void clear_analysis_connector();
- ::content_analysis::sdk::AnalysisConnector analysis_connector() const;
- void set_analysis_connector(::content_analysis::sdk::AnalysisConnector value);
- private:
- ::content_analysis::sdk::AnalysisConnector _internal_analysis_connector() const;
- void _internal_set_analysis_connector(::content_analysis::sdk::AnalysisConnector value);
- public:
-
// string text_content = 13;
bool has_text_content() const;
private:
@@ -1864,9 +1941,10 @@ class ContentAnalysisRequest final :
::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr user_action_id_;
::content_analysis::sdk::ContentMetaData* request_data_;
::content_analysis::sdk::ClientMetadata* client_metadata_;
+ int analysis_connector_;
+ int reason_;
int64_t expires_at_;
int64_t user_action_requests_count_;
- int analysis_connector_;
union ContentDataUnion {
constexpr ContentDataUnion() : _constinit_{} {}
::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized _constinit_;
@@ -1878,7 +1956,7 @@ class ContentAnalysisRequest final :
};
union { Impl_ _impl_; };
- friend struct ::TableStruct_analysis_2eproto;
+ friend struct ::TableStruct_content_5fanalysis_2fsdk_2fanalysis_2eproto;
};
// -------------------------------------------------------------------
@@ -2090,7 +2168,7 @@ class ContentAnalysisResponse_Result_TriggeredRule final :
int action_;
};
union { Impl_ _impl_; };
- friend struct ::TableStruct_analysis_2eproto;
+ friend struct ::TableStruct_content_5fanalysis_2fsdk_2fanalysis_2eproto;
};
// -------------------------------------------------------------------
@@ -2302,7 +2380,7 @@ class ContentAnalysisResponse_Result final :
int status_;
};
union { Impl_ _impl_; };
- friend struct ::TableStruct_analysis_2eproto;
+ friend struct ::TableStruct_content_5fanalysis_2fsdk_2fanalysis_2eproto;
};
// -------------------------------------------------------------------
@@ -2471,7 +2549,7 @@ class ContentAnalysisResponse final :
::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr request_token_;
};
union { Impl_ _impl_; };
- friend struct ::TableStruct_analysis_2eproto;
+ friend struct ::TableStruct_content_5fanalysis_2fsdk_2fanalysis_2eproto;
};
// -------------------------------------------------------------------
@@ -2708,7 +2786,7 @@ class ContentAnalysisAcknowledgement final :
int status_;
};
union { Impl_ _impl_; };
- friend struct ::TableStruct_analysis_2eproto;
+ friend struct ::TableStruct_content_5fanalysis_2fsdk_2fanalysis_2eproto;
};
// -------------------------------------------------------------------
@@ -2855,7 +2933,7 @@ class ContentAnalysisCancelRequests final :
::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr user_action_id_;
};
union { Impl_ _impl_; };
- friend struct ::TableStruct_analysis_2eproto;
+ friend struct ::TableStruct_content_5fanalysis_2fsdk_2fanalysis_2eproto;
};
// -------------------------------------------------------------------
@@ -3042,7 +3120,7 @@ class ChromeToAgent final :
::content_analysis::sdk::ContentAnalysisCancelRequests* cancel_;
};
union { Impl_ _impl_; };
- friend struct ::TableStruct_analysis_2eproto;
+ friend struct ::TableStruct_content_5fanalysis_2fsdk_2fanalysis_2eproto;
};
// -------------------------------------------------------------------
@@ -3189,7 +3267,7 @@ class AgentToChrome final :
::content_analysis::sdk::ContentAnalysisResponse* response_;
};
union { Impl_ _impl_; };
- friend struct ::TableStruct_analysis_2eproto;
+ friend struct ::TableStruct_content_5fanalysis_2fsdk_2fanalysis_2eproto;
};
// ===================================================================
@@ -4268,7 +4346,7 @@ inline void ContentAnalysisRequest::set_allocated_request_token(std::string* req
// optional .content_analysis.sdk.AnalysisConnector analysis_connector = 9;
inline bool ContentAnalysisRequest::_internal_has_analysis_connector() const {
- bool value = (_impl_._has_bits_[0] & 0x00000040u) != 0;
+ bool value = (_impl_._has_bits_[0] & 0x00000010u) != 0;
return value;
}
inline bool ContentAnalysisRequest::has_analysis_connector() const {
@@ -4276,7 +4354,7 @@ inline bool ContentAnalysisRequest::has_analysis_connector() const {
}
inline void ContentAnalysisRequest::clear_analysis_connector() {
_impl_.analysis_connector_ = 0;
- _impl_._has_bits_[0] &= ~0x00000040u;
+ _impl_._has_bits_[0] &= ~0x00000010u;
}
inline ::content_analysis::sdk::AnalysisConnector ContentAnalysisRequest::_internal_analysis_connector() const {
return static_cast< ::content_analysis::sdk::AnalysisConnector >(_impl_.analysis_connector_);
@@ -4287,7 +4365,7 @@ inline ::content_analysis::sdk::AnalysisConnector ContentAnalysisRequest::analys
}
inline void ContentAnalysisRequest::_internal_set_analysis_connector(::content_analysis::sdk::AnalysisConnector value) {
assert(::content_analysis::sdk::AnalysisConnector_IsValid(value));
- _impl_._has_bits_[0] |= 0x00000040u;
+ _impl_._has_bits_[0] |= 0x00000010u;
_impl_.analysis_connector_ = value;
}
inline void ContentAnalysisRequest::set_analysis_connector(::content_analysis::sdk::AnalysisConnector value) {
@@ -4780,7 +4858,7 @@ inline ::content_analysis::sdk::ContentAnalysisRequest_PrintData* ContentAnalysi
// optional int64 expires_at = 15;
inline bool ContentAnalysisRequest::_internal_has_expires_at() const {
- bool value = (_impl_._has_bits_[0] & 0x00000010u) != 0;
+ bool value = (_impl_._has_bits_[0] & 0x00000040u) != 0;
return value;
}
inline bool ContentAnalysisRequest::has_expires_at() const {
@@ -4788,7 +4866,7 @@ inline bool ContentAnalysisRequest::has_expires_at() const {
}
inline void ContentAnalysisRequest::clear_expires_at() {
_impl_.expires_at_ = int64_t{0};
- _impl_._has_bits_[0] &= ~0x00000010u;
+ _impl_._has_bits_[0] &= ~0x00000040u;
}
inline int64_t ContentAnalysisRequest::_internal_expires_at() const {
return _impl_.expires_at_;
@@ -4798,7 +4876,7 @@ inline int64_t ContentAnalysisRequest::expires_at() const {
return _internal_expires_at();
}
inline void ContentAnalysisRequest::_internal_set_expires_at(int64_t value) {
- _impl_._has_bits_[0] |= 0x00000010u;
+ _impl_._has_bits_[0] |= 0x00000040u;
_impl_.expires_at_ = value;
}
inline void ContentAnalysisRequest::set_expires_at(int64_t value) {
@@ -4876,7 +4954,7 @@ inline void ContentAnalysisRequest::set_allocated_user_action_id(std::string* us
// optional int64 user_action_requests_count = 17;
inline bool ContentAnalysisRequest::_internal_has_user_action_requests_count() const {
- bool value = (_impl_._has_bits_[0] & 0x00000020u) != 0;
+ bool value = (_impl_._has_bits_[0] & 0x00000080u) != 0;
return value;
}
inline bool ContentAnalysisRequest::has_user_action_requests_count() const {
@@ -4884,7 +4962,7 @@ inline bool ContentAnalysisRequest::has_user_action_requests_count() const {
}
inline void ContentAnalysisRequest::clear_user_action_requests_count() {
_impl_.user_action_requests_count_ = int64_t{0};
- _impl_._has_bits_[0] &= ~0x00000020u;
+ _impl_._has_bits_[0] &= ~0x00000080u;
}
inline int64_t ContentAnalysisRequest::_internal_user_action_requests_count() const {
return _impl_.user_action_requests_count_;
@@ -4894,7 +4972,7 @@ inline int64_t ContentAnalysisRequest::user_action_requests_count() const {
return _internal_user_action_requests_count();
}
inline void ContentAnalysisRequest::_internal_set_user_action_requests_count(int64_t value) {
- _impl_._has_bits_[0] |= 0x00000020u;
+ _impl_._has_bits_[0] |= 0x00000080u;
_impl_.user_action_requests_count_ = value;
}
inline void ContentAnalysisRequest::set_user_action_requests_count(int64_t value) {
@@ -4902,6 +4980,35 @@ inline void ContentAnalysisRequest::set_user_action_requests_count(int64_t value
// @@protoc_insertion_point(field_set:content_analysis.sdk.ContentAnalysisRequest.user_action_requests_count)
}
+// optional .content_analysis.sdk.ContentAnalysisRequest.Reason reason = 19;
+inline bool ContentAnalysisRequest::_internal_has_reason() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000020u) != 0;
+ return value;
+}
+inline bool ContentAnalysisRequest::has_reason() const {
+ return _internal_has_reason();
+}
+inline void ContentAnalysisRequest::clear_reason() {
+ _impl_.reason_ = 0;
+ _impl_._has_bits_[0] &= ~0x00000020u;
+}
+inline ::content_analysis::sdk::ContentAnalysisRequest_Reason ContentAnalysisRequest::_internal_reason() const {
+ return static_cast< ::content_analysis::sdk::ContentAnalysisRequest_Reason >(_impl_.reason_);
+}
+inline ::content_analysis::sdk::ContentAnalysisRequest_Reason ContentAnalysisRequest::reason() const {
+ // @@protoc_insertion_point(field_get:content_analysis.sdk.ContentAnalysisRequest.reason)
+ return _internal_reason();
+}
+inline void ContentAnalysisRequest::_internal_set_reason(::content_analysis::sdk::ContentAnalysisRequest_Reason value) {
+ assert(::content_analysis::sdk::ContentAnalysisRequest_Reason_IsValid(value));
+ _impl_._has_bits_[0] |= 0x00000020u;
+ _impl_.reason_ = value;
+}
+inline void ContentAnalysisRequest::set_reason(::content_analysis::sdk::ContentAnalysisRequest_Reason value) {
+ _internal_set_reason(value);
+ // @@protoc_insertion_point(field_set:content_analysis.sdk.ContentAnalysisRequest.reason)
+}
+
inline bool ContentAnalysisRequest::has_content_data() const {
return content_data_case() != CONTENT_DATA_NOT_SET;
}
@@ -5944,6 +6051,7 @@ PROTOBUF_NAMESPACE_OPEN
template <> struct is_proto_enum< ::content_analysis::sdk::ContentMetaData_PrintMetadata_PrinterType> : ::std::true_type {};
template <> struct is_proto_enum< ::content_analysis::sdk::ClientDownloadRequest_ResourceType> : ::std::true_type {};
+template <> struct is_proto_enum< ::content_analysis::sdk::ContentAnalysisRequest_Reason> : ::std::true_type {};
template <> struct is_proto_enum< ::content_analysis::sdk::ContentAnalysisResponse_Result_TriggeredRule_Action> : ::std::true_type {};
template <> struct is_proto_enum< ::content_analysis::sdk::ContentAnalysisResponse_Result_Status> : ::std::true_type {};
template <> struct is_proto_enum< ::content_analysis::sdk::ContentAnalysisAcknowledgement_Status> : ::std::true_type {};
@@ -5955,4 +6063,4 @@ PROTOBUF_NAMESPACE_CLOSE
// @@protoc_insertion_point(global_scope)
#include <google/protobuf/port_undef.inc>
-#endif // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_analysis_2eproto
+#endif // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_content_5fanalysis_2fsdk_2fanalysis_2eproto
diff --git a/toolkit/components/contentanalysis/nsIContentAnalysis.idl b/toolkit/components/contentanalysis/nsIContentAnalysis.idl
index 21f98b88e6..63ac8d12fb 100644
--- a/toolkit/components/contentanalysis/nsIContentAnalysis.idl
+++ b/toolkit/components/contentanalysis/nsIContentAnalysis.idl
@@ -36,7 +36,7 @@ interface nsIContentAnalysisAcknowledgement : nsISupports
readonly attribute nsIContentAnalysisAcknowledgement_FinalAction finalAction;
};
-[scriptable, builtinclass, uuid(89088c61-15f6-4ace-a880-a1b5ea47ca66)]
+[scriptable, uuid(89088c61-15f6-4ace-a880-a1b5ea47ca66)]
interface nsIContentAnalysisResponse : nsISupports
{
// These values must stay synchronized with ContentAnalysisResponse
@@ -52,8 +52,18 @@ interface nsIContentAnalysisResponse : nsISupports
eCanceled = 1001,
};
- [infallible] readonly attribute nsIContentAnalysisResponse_Action action;
- [infallible] readonly attribute boolean shouldAllowContent;
+ cenum CancelError : 32 {
+ eUserInitiated = 0,
+ eNoAgent = 1,
+ eInvalidAgentSignature = 2,
+ eErrorOther = 3,
+ };
+
+ readonly attribute nsIContentAnalysisResponse_Action action;
+ readonly attribute boolean shouldAllowContent;
+ // If action is eCanceled, this is the error explaining why the request was canceled,
+ // or eUserInitiated if the user canceled it.
+ readonly attribute nsIContentAnalysisResponse_CancelError cancelError;
// Identifier for the corresponding nsIContentAnalysisRequest
readonly attribute ACString requestToken;
@@ -121,6 +131,7 @@ interface nsIContentAnalysisRequest : nsISupports
eCustomDisplayString = 0,
eClipboard = 1,
eDroppedText = 2,
+ eOperationPrint = 3,
};
readonly attribute nsIContentAnalysisRequest_OperationType operationTypeForDisplay;
readonly attribute AString operationDisplayString;
@@ -131,6 +142,15 @@ interface nsIContentAnalysisRequest : nsISupports
// Name of file to analyze. Only one of textContent or filePath is defined.
readonly attribute AString filePath;
+ // HANDLE to the printed data in PDF format.
+ readonly attribute unsigned long long printDataHandle;
+
+ // Size of the data stored in printDataHandle.
+ readonly attribute unsigned long long printDataSize;
+
+ // Name of the printer being printed to.
+ readonly attribute AString printerName;
+
// The URL containing the file download/upload or to which web content is
// being uploaded.
readonly attribute nsIURI url;
@@ -166,7 +186,16 @@ interface nsIContentAnalysisCallback : nsISupports
void error(in nsresult aResult);
};
-[scriptable, builtinclass, uuid(61497587-2bba-4a88-acd3-3fbb2cedf163)]
+[scriptable, builtinclass, uuid(a430f6ef-a526-4055-8a82-7741ea757367)]
+interface nsIContentAnalysisDiagnosticInfo : nsISupports
+{
+ [infallible] readonly attribute boolean connectedToAgent;
+ readonly attribute AString agentPath;
+ [infallible] readonly attribute boolean failedSignatureVerification;
+ [infallible] readonly attribute long long requestCount;
+};
+
+[scriptable, uuid(61497587-2bba-4a88-acd3-3fbb2cedf163)]
interface nsIContentAnalysis : nsISupports
{
/**
@@ -182,14 +211,14 @@ interface nsIContentAnalysis : nsISupports
* into the parent process to determine whether content analysis is actually
* active.
*/
- readonly attribute bool mightBeActive;
+ readonly attribute boolean mightBeActive;
/**
* True if content-analysis activation was determined by enterprise policy,
* as opposed to enabled with the `allow-content-analysis` command-line
* parameter.
*/
- attribute bool isSetByEnterprisePolicy;
+ attribute boolean isSetByEnterprisePolicy;
/**
* Consults content analysis server, if any, to request a permission
@@ -210,7 +239,7 @@ interface nsIContentAnalysis : nsISupports
* calling nsIContentAnalysisResponse::acknowledge() if the Promise is resolved.
*/
[implicit_jscontext]
- Promise analyzeContentRequest(in nsIContentAnalysisRequest aCar, in bool aAutoAcknowledge);
+ Promise analyzeContentRequest(in nsIContentAnalysisRequest aCar, in boolean aAutoAcknowledge);
/**
* Same functionality as AnalyzeContentRequest(), but more convenient to call
@@ -226,7 +255,7 @@ interface nsIContentAnalysis : nsISupports
* @param callback
* Callbacks to be called when the agent sends a response message (or when there is an error).
*/
- void analyzeContentRequestCallback(in nsIContentAnalysisRequest aCar, in bool aAutoAcknowledge, in nsIContentAnalysisCallback callback);
+ void analyzeContentRequestCallback(in nsIContentAnalysisRequest aCar, in boolean aAutoAcknowledge, in nsIContentAnalysisCallback callback);
/**
* Cancels the request that is in progress. This may not actually cancel the request
@@ -242,7 +271,7 @@ interface nsIContentAnalysis : nsISupports
* Indicates that the user has responded to a WARN dialog. aAllowContent represents
* whether the user wants to allow the request to go through.
*/
- void respondToWarnDialog(in ACString aRequestToken, in bool aAllowContent);
+ void respondToWarnDialog(in ACString aRequestToken, in boolean aAllowContent);
/**
* Cancels all outstanding DLP requests. Used on shutdown.
@@ -254,4 +283,11 @@ interface nsIContentAnalysis : nsISupports
* given to Gecko on the command line.
*/
void testOnlySetCACmdLineArg(in boolean aVal);
+
+ /**
+ * Gets diagnostic information about content analysis. Returns a
+ * nsIContentAnalysisDiagnosticInfo via the returned promise.
+ */
+ [implicit_jscontext]
+ Promise getDiagnosticInfo();
};
diff --git a/toolkit/components/contentanalysis/tests/browser/browser.toml b/toolkit/components/contentanalysis/tests/browser/browser.toml
index 0e21090299..bdbf350593 100644
--- a/toolkit/components/contentanalysis/tests/browser/browser.toml
+++ b/toolkit/components/contentanalysis/tests/browser/browser.toml
@@ -1,3 +1,20 @@
[DEFAULT]
+run-if = ["os == 'win'"]
+support-files = [
+ "head.js",
+]
["browser_content_analysis_policies.js"]
+
+["browser_print_changing_page_content_analysis.js"]
+support-files = [
+ "!/toolkit/components/printing/tests/head.js",
+ "changing_page_for_print.html",
+]
+
+["browser_print_content_analysis.js"]
+support-files = [
+ "!/toolkit/components/printing/tests/head.js",
+ "!/toolkit/components/printing/tests/longerArticle.html",
+ "!/toolkit/components/printing/tests/simplifyArticleSample.html",
+]
diff --git a/toolkit/components/contentanalysis/tests/browser/browser_content_analysis_policies.js b/toolkit/components/contentanalysis/tests/browser/browser_content_analysis_policies.js
index e2e001e9d1..b226c0a37a 100644
--- a/toolkit/components/contentanalysis/tests/browser/browser_content_analysis_policies.js
+++ b/toolkit/components/contentanalysis/tests/browser/browser_content_analysis_policies.js
@@ -11,15 +11,18 @@
"use strict";
-const { EnterprisePolicyTesting } = ChromeUtils.importESModule(
- "resource://testing-common/EnterprisePolicyTesting.sys.mjs"
-);
+const { EnterprisePolicyTesting, PoliciesPrefTracker } =
+ ChromeUtils.importESModule(
+ "resource://testing-common/EnterprisePolicyTesting.sys.mjs"
+ );
const kEnabledPref = "enabled";
const kPipeNamePref = "pipe_path_name";
const kTimeoutPref = "agent_timeout";
const kAllowUrlPref = "allow_url_regex_list";
const kDenyUrlPref = "deny_url_regex_list";
+const kAgentNamePref = "agent_name";
+const kClientSignaturePref = "client_signature";
const kPerUserPref = "is_per_user";
const kShowBlockedPref = "show_blocked_result";
const kDefaultAllowPref = "default_allow";
@@ -29,6 +32,7 @@ const ca = Cc["@mozilla.org/contentanalysis;1"].getService(
);
add_task(async function test_ca_active() {
+ PoliciesPrefTracker.start();
ok(!ca.isActive, "CA is inactive when pref and cmd line arg are missing");
// Set the pref without enterprise policy. CA should not be active.
@@ -62,11 +66,15 @@ add_task(async function test_ca_active() {
},
});
ok(ca.isActive, "CA is active when enabled by enterprise policy pref");
+ PoliciesPrefTracker.stop();
});
add_task(async function test_ca_enterprise_config() {
+ PoliciesPrefTracker.start();
const string1 = "this is a string";
const string2 = "this is another string";
+ const string3 = "an agent name";
+ const string4 = "a client signature";
await EnterprisePolicyTesting.setupPolicyEngineWithJson({
policies: {
@@ -75,6 +83,8 @@ add_task(async function test_ca_enterprise_config() {
AgentTimeout: 99,
AllowUrlRegexList: string1,
DenyUrlRegexList: string2,
+ AgentName: string3,
+ ClientSignature: string4,
IsPerUser: true,
ShowBlockedResult: false,
DefaultAllow: true,
@@ -103,6 +113,18 @@ add_task(async function test_ca_enterprise_config() {
"deny urls match"
);
is(
+ Services.prefs.getStringPref("browser.contentanalysis." + kAgentNamePref),
+ string3,
+ "agent names match"
+ );
+ is(
+ Services.prefs.getStringPref(
+ "browser.contentanalysis." + kClientSignaturePref
+ ),
+ string4,
+ "client signatures match"
+ );
+ is(
Services.prefs.getBoolPref("browser.contentanalysis." + kPerUserPref),
true,
"per user match"
@@ -117,6 +139,7 @@ add_task(async function test_ca_enterprise_config() {
true,
"default allow match"
);
+ PoliciesPrefTracker.stop();
});
add_task(async function test_cleanup() {
@@ -124,4 +147,9 @@ add_task(async function test_cleanup() {
await EnterprisePolicyTesting.setupPolicyEngineWithJson({
policies: {},
});
+ // These may have gotten set when ContentAnalysis was enabled through
+ // the policy and do not get cleared if there is no ContentAnalysis
+ // element - reset them manually here.
+ ca.isSetByEnterprisePolicy = false;
+ Services.prefs.setBoolPref("browser.contentanalysis." + kEnabledPref, false);
});
diff --git a/toolkit/components/contentanalysis/tests/browser/browser_print_changing_page_content_analysis.js b/toolkit/components/contentanalysis/tests/browser/browser_print_changing_page_content_analysis.js
new file mode 100644
index 0000000000..72a7dcbb91
--- /dev/null
+++ b/toolkit/components/contentanalysis/tests/browser/browser_print_changing_page_content_analysis.js
@@ -0,0 +1,339 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/toolkit/components/printing/tests/head.js",
+ this
+);
+
+const PSSVC = Cc["@mozilla.org/gfx/printsettings-service;1"].getService(
+ Ci.nsIPrintSettingsService
+);
+
+let mockCA = {
+ isActive: true,
+ mightBeActive: true,
+ errorValue: undefined,
+
+ setupForTest(shouldAllowRequest) {
+ this.shouldAllowRequest = shouldAllowRequest;
+ this.errorValue = undefined;
+ this.calls = [];
+ },
+
+ setupForTestWithError(errorValue) {
+ this.errorValue = errorValue;
+ this.calls = [];
+ },
+
+ getAction() {
+ if (this.shouldAllowRequest === undefined) {
+ this.shouldAllowRequest = true;
+ }
+ return this.shouldAllowRequest
+ ? Ci.nsIContentAnalysisResponse.eAllow
+ : Ci.nsIContentAnalysisResponse.eBlock;
+ },
+
+ // nsIContentAnalysis methods
+ async analyzeContentRequest(request, _autoAcknowledge) {
+ info(
+ "Mock ContentAnalysis service: analyzeContentRequest, this.shouldAllowRequest=" +
+ this.shouldAllowRequest +
+ ", this.errorValue=" +
+ this.errorValue
+ );
+ this.calls.push(request);
+ if (this.errorValue) {
+ throw this.errorValue;
+ }
+ // Use setTimeout to simulate an async activity
+ await new Promise(res => setTimeout(res, 0));
+ return makeContentAnalysisResponse(this.getAction(), request.requestToken);
+ },
+
+ analyzeContentRequestCallback(request, autoAcknowledge, callback) {
+ info(
+ "Mock ContentAnalysis service: analyzeContentRequestCallback, this.shouldAllowRequest=" +
+ this.shouldAllowRequest +
+ ", this.errorValue=" +
+ this.errorValue
+ );
+ this.calls.push(request);
+ if (this.errorValue) {
+ throw this.errorValue;
+ }
+ let response = makeContentAnalysisResponse(
+ this.getAction(),
+ request.requestToken
+ );
+ // Use setTimeout to simulate an async activity
+ setTimeout(() => {
+ callback.contentResult(response);
+ }, 0);
+ },
+};
+
+add_setup(async function test_setup() {
+ mockCA = mockContentAnalysisService(mockCA);
+});
+
+const TEST_PAGE_URL = PrintHelper.getTestPageUrlHTTPS(
+ "changing_page_for_print.html"
+);
+
+function addUniqueSuffix(prefix) {
+ return `${prefix}-${Services.uuid
+ .generateUUID()
+ .toString()
+ .slice(1, -1)}.pdf`;
+}
+
+async function printToDestination(aBrowser, aDestination) {
+ let tmpDir = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ let fileName = addUniqueSuffix(`printDestinationTest-${aDestination}`);
+ let filePath = PathUtils.join(tmpDir.path, fileName);
+
+ info(`Printing to ${filePath}`);
+
+ let settings = PSSVC.createNewPrintSettings();
+ settings.outputFormat = Ci.nsIPrintSettings.kOutputFormatPDF;
+ settings.outputDestination = aDestination;
+
+ settings.headerStrCenter = "";
+ settings.headerStrLeft = "";
+ settings.headerStrRight = "";
+ settings.footerStrCenter = "";
+ settings.footerStrLeft = "";
+ settings.footerStrRight = "";
+
+ settings.unwriteableMarginTop = 1; /* Just to ensure settings are respected on both */
+ let outStream = null;
+ if (aDestination == Ci.nsIPrintSettings.kOutputDestinationFile) {
+ settings.toFileName = PathUtils.join(tmpDir.path, fileName);
+ } else {
+ is(aDestination, Ci.nsIPrintSettings.kOutputDestinationStream);
+ outStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(
+ Ci.nsIFileOutputStream
+ );
+ let tmpFile = tmpDir.clone();
+ tmpFile.append(fileName);
+ outStream.init(tmpFile, -1, 0o666, 0);
+ settings.outputStream = outStream;
+ }
+
+ await aBrowser.browsingContext.print(settings);
+
+ return filePath;
+}
+
+function assertContentAnalysisRequest(request) {
+ is(request.url.spec, TEST_PAGE_URL, "request has correct URL");
+ is(
+ request.analysisType,
+ Ci.nsIContentAnalysisRequest.ePrint,
+ "request has print analysisType"
+ );
+ is(
+ request.operationTypeForDisplay,
+ Ci.nsIContentAnalysisRequest.eOperationPrint,
+ "request has print operationTypeForDisplay"
+ );
+ is(request.textContent, "", "request textContent should be empty");
+ is(request.filePath, "", "request filePath should be empty");
+ isnot(request.printDataHandle, 0, "request printDataHandle should not be 0");
+ isnot(request.printDataSize, 0, "request printDataSize should not be 0");
+ ok(!!request.requestToken.length, "request requestToken should not be empty");
+}
+
+add_task(
+ async function testPrintToStreamWithContentAnalysisActiveAndAllowing() {
+ await PrintHelper.withTestPage(
+ async helper => {
+ mockCA.setupForTest(true);
+
+ let filePath = await printToDestination(
+ helper.sourceBrowser,
+ Ci.nsIPrintSettings.kOutputDestinationFile
+ );
+ is(
+ mockCA.calls.length,
+ 1,
+ "Correct number of calls to Content Analysis"
+ );
+ assertContentAnalysisRequest(mockCA.calls[0]);
+
+ // This effectively tests that the PDF content sent to Content Analysis
+ // and the content that is actually printed matches. This is necessary
+ // because a previous iteration of the Content Analysis code didn't use
+ // a static Document clone for this and so the content would differ. (since
+ // the .html file in question adds content to the page when print events
+ // happen)
+ await waitForFileToAlmostMatchSize(
+ filePath,
+ mockCA.calls[0].printDataSize
+ );
+
+ await IOUtils.remove(filePath);
+ },
+ TEST_PAGE_URL,
+ true
+ );
+ }
+);
+
+add_task(
+ async function testPrintToStreamWithContentAnalysisActiveAndBlocking() {
+ await PrintHelper.withTestPage(
+ async helper => {
+ mockCA.setupForTest(false);
+
+ try {
+ await printToDestination(
+ helper.sourceBrowser,
+ Ci.nsIPrintSettings.kOutputDestinationFile
+ );
+ ok(false, "Content analysis should make this fail to print");
+ } catch (e) {
+ ok(
+ /NS_ERROR_CONTENT_BLOCKED/.test(e.toString()),
+ "Got content blocked error"
+ );
+ }
+ is(
+ mockCA.calls.length,
+ 1,
+ "Correct number of calls to Content Analysis"
+ );
+ assertContentAnalysisRequest(mockCA.calls[0]);
+ },
+ TEST_PAGE_URL,
+ true
+ );
+ }
+);
+
+add_task(async function testPrintToStreamWithContentAnalysisReturningError() {
+ await PrintHelper.withTestPage(
+ async helper => {
+ expectUncaughtException();
+ mockCA.setupForTestWithError(Cr.NS_ERROR_NOT_AVAILABLE);
+
+ try {
+ await printToDestination(
+ helper.sourceBrowser,
+ Ci.nsIPrintSettings.kOutputDestinationFile
+ );
+ ok(false, "Content analysis should make this fail to print");
+ } catch (e) {
+ ok(
+ /NS_ERROR_NOT_AVAILABLE/.test(e.toString()),
+ "Error in mock CA was propagated out"
+ );
+ }
+ is(mockCA.calls.length, 1, "Correct number of calls to Content Analysis");
+ assertContentAnalysisRequest(mockCA.calls[0]);
+ },
+ TEST_PAGE_URL,
+ true
+ );
+});
+
+add_task(async function testPrintThroughDialogWithContentAnalysisActive() {
+ await PrintHelper.withTestPage(
+ async helper => {
+ mockCA.setupForTest(true);
+
+ let fileName = addUniqueSuffix(`printDialogTest`);
+ let file = helper.mockFilePicker(fileName);
+ info(`Printing to ${file.path}`);
+ await helper.startPrint();
+ await helper.assertPrintToFile(file, () => {
+ EventUtils.sendKey("return", helper.win);
+ });
+
+ is(mockCA.calls.length, 1, "Correct number of calls to Content Analysis");
+ assertContentAnalysisRequest(mockCA.calls[0]);
+
+ await waitForFileToAlmostMatchSize(
+ file.path,
+ mockCA.calls[0].printDataSize
+ );
+ },
+ TEST_PAGE_URL,
+ true
+ );
+});
+
+add_task(
+ async function testPrintThroughDialogWithContentAnalysisActiveAndBlocking() {
+ await PrintHelper.withTestPage(
+ async helper => {
+ mockCA.setupForTest(false);
+
+ await helper.startPrint();
+ let fileName = addUniqueSuffix(`printDialogTest`);
+ let file = helper.mockFilePicker(fileName);
+ info(`Printing to ${file.path}`);
+ try {
+ await helper.assertPrintToFile(file, () => {
+ EventUtils.sendKey("return", helper.win);
+ });
+ } catch (e) {
+ ok(
+ /Wait for target file to get created/.test(e.toString()),
+ "Target file should not get created"
+ );
+ }
+ ok(!file.exists(), "File should not exist");
+
+ is(
+ mockCA.calls.length,
+ 1,
+ "Correct number of calls to Content Analysis"
+ );
+ assertContentAnalysisRequest(mockCA.calls[0]);
+ },
+ TEST_PAGE_URL,
+ true
+ );
+ }
+);
+
+add_task(
+ async function testPrintThroughDialogWithContentAnalysisReturningError() {
+ await PrintHelper.withTestPage(
+ async helper => {
+ expectUncaughtException();
+ mockCA.setupForTestWithError(Cr.NS_ERROR_NOT_AVAILABLE);
+
+ await helper.startPrint();
+ let fileName = addUniqueSuffix(`printDialogTest`);
+ let file = helper.mockFilePicker(fileName);
+ info(`Printing to ${file.path}`);
+ try {
+ await helper.assertPrintToFile(file, () => {
+ EventUtils.sendKey("return", helper.win);
+ });
+ } catch (e) {
+ ok(
+ /Wait for target file to get created/.test(e.toString()),
+ "Target file should not get created"
+ );
+ }
+ ok(!file.exists(), "File should not exist");
+
+ is(
+ mockCA.calls.length,
+ 1,
+ "Correct number of calls to Content Analysis"
+ );
+ assertContentAnalysisRequest(mockCA.calls[0]);
+ },
+ TEST_PAGE_URL,
+ true
+ );
+ }
+);
diff --git a/toolkit/components/contentanalysis/tests/browser/browser_print_content_analysis.js b/toolkit/components/contentanalysis/tests/browser/browser_print_content_analysis.js
new file mode 100644
index 0000000000..9b4c0ffa60
--- /dev/null
+++ b/toolkit/components/contentanalysis/tests/browser/browser_print_content_analysis.js
@@ -0,0 +1,390 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/toolkit/components/printing/tests/head.js",
+ this
+);
+
+const PSSVC = Cc["@mozilla.org/gfx/printsettings-service;1"].getService(
+ Ci.nsIPrintSettingsService
+);
+
+let mockCA = {
+ isActive: true,
+ mightBeActive: true,
+ errorValue: undefined,
+
+ setupForTest(shouldAllowRequest) {
+ this.shouldAllowRequest = shouldAllowRequest;
+ this.errorValue = undefined;
+ this.calls = [];
+ },
+
+ setupForTestWithError(errorValue) {
+ this.errorValue = errorValue;
+ this.calls = [];
+ },
+
+ clearCalls() {
+ this.calls = [];
+ },
+
+ getAction() {
+ if (this.shouldAllowRequest === undefined) {
+ this.shouldAllowRequest = true;
+ }
+ return this.shouldAllowRequest
+ ? Ci.nsIContentAnalysisResponse.eAllow
+ : Ci.nsIContentAnalysisResponse.eBlock;
+ },
+
+ // nsIContentAnalysis methods
+ async analyzeContentRequest(request, _autoAcknowledge) {
+ info(
+ "Mock ContentAnalysis service: analyzeContentRequest, this.shouldAllowRequest=" +
+ this.shouldAllowRequest +
+ ", this.errorValue=" +
+ this.errorValue
+ );
+ this.calls.push(request);
+ if (this.errorValue) {
+ throw this.errorValue;
+ }
+ // Use setTimeout to simulate an async activity
+ await new Promise(res => setTimeout(res, 0));
+ return makeContentAnalysisResponse(this.getAction(), request.requestToken);
+ },
+
+ analyzeContentRequestCallback(request, autoAcknowledge, callback) {
+ info(
+ "Mock ContentAnalysis service: analyzeContentRequestCallback, this.shouldAllowRequest=" +
+ this.shouldAllowRequest +
+ ", this.errorValue=" +
+ this.errorValue
+ );
+ this.calls.push(request);
+ if (this.errorValue) {
+ throw this.errorValue;
+ }
+ let response = makeContentAnalysisResponse(
+ this.getAction(),
+ request.requestToken
+ );
+ // Use setTimeout to simulate an async activity
+ setTimeout(() => {
+ callback.contentResult(response);
+ }, 0);
+ },
+};
+
+add_setup(async function test_setup() {
+ mockCA = mockContentAnalysisService(mockCA);
+});
+
+const TEST_PAGE_URL =
+ "https://example.com/browser/toolkit/components/printing/tests/simplifyArticleSample.html";
+const TEST_PAGE_URL_2 =
+ "https://example.com/browser/toolkit/components/printing/tests/longerArticle.html";
+
+function addUniqueSuffix(prefix) {
+ return `${prefix}-${Services.uuid
+ .generateUUID()
+ .toString()
+ .slice(1, -1)}.pdf`;
+}
+
+async function printToDestination(aBrowser, aDestination) {
+ let tmpDir = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ let fileName = addUniqueSuffix(`printDestinationTest-${aDestination}`);
+ let filePath = PathUtils.join(tmpDir.path, fileName);
+
+ info(`Printing to ${filePath}`);
+
+ let settings = PSSVC.createNewPrintSettings();
+ settings.outputFormat = Ci.nsIPrintSettings.kOutputFormatPDF;
+ settings.outputDestination = aDestination;
+
+ settings.headerStrCenter = "";
+ settings.headerStrLeft = "";
+ settings.headerStrRight = "";
+ settings.footerStrCenter = "";
+ settings.footerStrLeft = "";
+ settings.footerStrRight = "";
+
+ settings.unwriteableMarginTop = 1; /* Just to ensure settings are respected on both */
+ let outStream = null;
+ if (aDestination == Ci.nsIPrintSettings.kOutputDestinationFile) {
+ settings.toFileName = PathUtils.join(tmpDir.path, fileName);
+ } else {
+ is(aDestination, Ci.nsIPrintSettings.kOutputDestinationStream);
+ outStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(
+ Ci.nsIFileOutputStream
+ );
+ let tmpFile = tmpDir.clone();
+ tmpFile.append(fileName);
+ outStream.init(tmpFile, -1, 0o666, 0);
+ settings.outputStream = outStream;
+ }
+
+ await aBrowser.browsingContext.print(settings);
+
+ return filePath;
+}
+
+function assertContentAnalysisRequest(request, expectedUrl) {
+ is(request.url.spec, expectedUrl ?? TEST_PAGE_URL, "request has correct URL");
+ is(
+ request.analysisType,
+ Ci.nsIContentAnalysisRequest.ePrint,
+ "request has print analysisType"
+ );
+ is(
+ request.operationTypeForDisplay,
+ Ci.nsIContentAnalysisRequest.eOperationPrint,
+ "request has print operationTypeForDisplay"
+ );
+ is(request.textContent, "", "request textContent should be empty");
+ is(request.filePath, "", "request filePath should be empty");
+ isnot(request.printDataHandle, 0, "request printDataHandle should not be 0");
+ isnot(request.printDataSize, 0, "request printDataSize should not be 0");
+ ok(!!request.requestToken.length, "request requestToken should not be empty");
+}
+
+// Printing to a stream is different than going through the print preview dialog because it
+// doesn't make a static clone of the document before the print, which causes the
+// Content Analysis code to go through a different code path. This is similar to what
+// happens when various preferences are set to skip the print preview dialog, for example
+// print.prefer_system_dialog.
+add_task(
+ async function testPrintToStreamWithContentAnalysisActiveAndAllowing() {
+ await PrintHelper.withTestPage(
+ async helper => {
+ mockCA.setupForTest(true);
+
+ let filePath = await printToDestination(
+ helper.sourceBrowser,
+ Ci.nsIPrintSettings.kOutputDestinationFile
+ );
+ is(
+ mockCA.calls.length,
+ 1,
+ "Correct number of calls to Content Analysis"
+ );
+ assertContentAnalysisRequest(mockCA.calls[0]);
+
+ await waitForFileToAlmostMatchSize(
+ filePath,
+ mockCA.calls[0].printDataSize
+ );
+
+ await IOUtils.remove(filePath);
+ },
+ TEST_PAGE_URL,
+ true
+ );
+ }
+);
+
+add_task(
+ async function testPrintToStreamAfterNavigationWithContentAnalysisActiveAndAllowing() {
+ await PrintHelper.withTestPage(
+ async helper => {
+ mockCA.setupForTest(true);
+
+ let filePath = await printToDestination(
+ helper.sourceBrowser,
+ Ci.nsIPrintSettings.kOutputDestinationFile
+ );
+ is(
+ mockCA.calls.length,
+ 1,
+ "Correct number of calls to Content Analysis"
+ );
+ assertContentAnalysisRequest(mockCA.calls[0]);
+ mockCA.clearCalls();
+
+ await IOUtils.remove(filePath);
+
+ BrowserTestUtils.startLoadingURIString(
+ helper.sourceBrowser,
+ TEST_PAGE_URL_2
+ );
+ await BrowserTestUtils.browserLoaded(helper.sourceBrowser);
+
+ filePath = await printToDestination(
+ helper.sourceBrowser,
+ Ci.nsIPrintSettings.kOutputDestinationFile
+ );
+ is(
+ mockCA.calls.length,
+ 1,
+ "Correct number of calls to Content Analysis"
+ );
+ assertContentAnalysisRequest(mockCA.calls[0], TEST_PAGE_URL_2);
+ await waitForFileToAlmostMatchSize(
+ filePath,
+ mockCA.calls[0].printDataSize
+ );
+ },
+ TEST_PAGE_URL,
+ true
+ );
+ }
+);
+
+add_task(
+ async function testPrintToStreamWithContentAnalysisActiveAndBlocking() {
+ await PrintHelper.withTestPage(
+ async helper => {
+ mockCA.setupForTest(false);
+
+ try {
+ await printToDestination(
+ helper.sourceBrowser,
+ Ci.nsIPrintSettings.kOutputDestinationFile
+ );
+ ok(false, "Content analysis should make this fail to print");
+ } catch (e) {
+ ok(
+ /NS_ERROR_CONTENT_BLOCKED/.test(e.toString()),
+ "Got content blocked error"
+ );
+ }
+ is(
+ mockCA.calls.length,
+ 1,
+ "Correct number of calls to Content Analysis"
+ );
+ assertContentAnalysisRequest(mockCA.calls[0]);
+ },
+ TEST_PAGE_URL,
+ true
+ );
+ }
+);
+
+add_task(async function testPrintToStreamWithContentAnalysisReturningError() {
+ await PrintHelper.withTestPage(
+ async helper => {
+ expectUncaughtException();
+ mockCA.setupForTestWithError(Cr.NS_ERROR_NOT_AVAILABLE);
+
+ try {
+ await printToDestination(
+ helper.sourceBrowser,
+ Ci.nsIPrintSettings.kOutputDestinationFile
+ );
+ ok(false, "Content analysis should make this fail to print");
+ } catch (e) {
+ ok(
+ /NS_ERROR_NOT_AVAILABLE/.test(e.toString()),
+ "Error in mock CA was propagated out"
+ );
+ }
+ is(mockCA.calls.length, 1, "Correct number of calls to Content Analysis");
+ assertContentAnalysisRequest(mockCA.calls[0]);
+ },
+ TEST_PAGE_URL,
+ true
+ );
+});
+
+add_task(async function testPrintThroughDialogWithContentAnalysisActive() {
+ await PrintHelper.withTestPage(
+ async helper => {
+ mockCA.setupForTest(true);
+
+ await helper.startPrint();
+ let fileName = addUniqueSuffix(`printDialogTest`);
+ let file = helper.mockFilePicker(fileName);
+ info(`Printing to ${file.path}`);
+ await helper.assertPrintToFile(file, () => {
+ EventUtils.sendKey("return", helper.win);
+ });
+
+ is(mockCA.calls.length, 1, "Correct number of calls to Content Analysis");
+ assertContentAnalysisRequest(mockCA.calls[0]);
+
+ await waitForFileToAlmostMatchSize(
+ file.path,
+ mockCA.calls[0].printDataSize
+ );
+ },
+ TEST_PAGE_URL,
+ true
+ );
+});
+
+add_task(
+ async function testPrintThroughDialogWithContentAnalysisActiveAndBlocking() {
+ await PrintHelper.withTestPage(
+ async helper => {
+ mockCA.setupForTest(false);
+
+ await helper.startPrint();
+ let fileName = addUniqueSuffix(`printDialogTest`);
+ let file = helper.mockFilePicker(fileName);
+ info(`Printing to ${file.path}`);
+ try {
+ await helper.assertPrintToFile(file, () => {
+ EventUtils.sendKey("return", helper.win);
+ });
+ } catch (e) {
+ ok(
+ /Wait for target file to get created/.test(e.toString()),
+ "Target file should not get created"
+ );
+ }
+ ok(!file.exists(), "File should not exist");
+
+ is(
+ mockCA.calls.length,
+ 1,
+ "Correct number of calls to Content Analysis"
+ );
+ assertContentAnalysisRequest(mockCA.calls[0]);
+ },
+ TEST_PAGE_URL,
+ true
+ );
+ }
+);
+
+add_task(
+ async function testPrintThroughDialogWithContentAnalysisReturningError() {
+ await PrintHelper.withTestPage(
+ async helper => {
+ expectUncaughtException();
+ mockCA.setupForTestWithError(Cr.NS_ERROR_NOT_AVAILABLE);
+
+ await helper.startPrint();
+ let fileName = addUniqueSuffix(`printDialogTest`);
+ let file = helper.mockFilePicker(fileName);
+ info(`Printing to ${file.path}`);
+ try {
+ await helper.assertPrintToFile(file, () => {
+ EventUtils.sendKey("return", helper.win);
+ });
+ } catch (e) {
+ ok(
+ /Wait for target file to get created/.test(e.toString()),
+ "Target file should not get created"
+ );
+ }
+ ok(!file.exists(), "File should not exist");
+
+ is(
+ mockCA.calls.length,
+ 1,
+ "Correct number of calls to Content Analysis"
+ );
+ assertContentAnalysisRequest(mockCA.calls[0]);
+ },
+ TEST_PAGE_URL,
+ true
+ );
+ }
+);
diff --git a/toolkit/components/contentanalysis/tests/browser/changing_page_for_print.html b/toolkit/components/contentanalysis/tests/browser/changing_page_for_print.html
new file mode 100644
index 0000000000..de6f9001aa
--- /dev/null
+++ b/toolkit/components/contentanalysis/tests/browser/changing_page_for_print.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<p>Some random text</p>
+<button onclick="print()">Print the page</button>
+<pre id="log"></pre>
+<script>
+let i = 0;
+for (let t of ["beforeprint", "afterprint"]) {
+ addEventListener(t, () => {
+ document.getElementById("log").appendChild(document.createTextNode(`[${i++}] ${t}\n`));
+ });
+}
+</script>
diff --git a/toolkit/components/contentanalysis/tests/browser/head.js b/toolkit/components/contentanalysis/tests/browser/head.js
new file mode 100644
index 0000000000..e645caa2d7
--- /dev/null
+++ b/toolkit/components/contentanalysis/tests/browser/head.js
@@ -0,0 +1,114 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { MockRegistrar } = ChromeUtils.importESModule(
+ "resource://testing-common/MockRegistrar.sys.mjs"
+);
+
+// Wraps the given object in an XPConnect wrapper and, if an interface
+// is passed, queries the result to that interface.
+function xpcWrap(obj, iface) {
+ let ifacePointer = Cc[
+ "@mozilla.org/supports-interface-pointer;1"
+ ].createInstance(Ci.nsISupportsInterfacePointer);
+
+ ifacePointer.data = obj;
+ if (iface) {
+ return ifacePointer.data.QueryInterface(iface);
+ }
+ return ifacePointer.data;
+}
+
+/**
+ * Mock a (set of) service(s) as the object mockService.
+ *
+ * @param {[string]} serviceNames
+ * array of services names that mockService will be
+ * allowed to QI to. Must include the name of the
+ * service referenced by contractId.
+ * @param {string} contractId
+ * the component ID that will reference the mock object
+ * instead of the original service
+ * @param {object} interfaceObj
+ * interface object for the component
+ * @param {object} mockService
+ * object that satisfies the contract well
+ * enough to use as a mock of it
+ * @returns {object} The newly-mocked service
+ */
+function mockService(serviceNames, contractId, interfaceObj, mockService) {
+ // xpcWrap allows us to mock [implicit_jscontext] methods.
+ let newService = {
+ ...mockService,
+ QueryInterface: ChromeUtils.generateQI(serviceNames),
+ };
+ let o = xpcWrap(newService, interfaceObj);
+ let cid = MockRegistrar.register(contractId, o);
+ registerCleanupFunction(() => {
+ MockRegistrar.unregister(cid);
+ });
+ return newService;
+}
+
+/**
+ * Mock the nsIContentAnalysis service with the object mockCAService.
+ *
+ * @param {object} mockCAService
+ * the service to mock for nsIContentAnalysis
+ * @returns {object} The newly-mocked service
+ */
+function mockContentAnalysisService(mockCAService) {
+ return mockService(
+ ["nsIContentAnalysis"],
+ "@mozilla.org/contentanalysis;1",
+ Ci.nsIContentAnalysis,
+ mockCAService
+ );
+}
+
+/**
+ * Make an nsIContentAnalysisResponse.
+ *
+ * @param {number} action The action to take, from the
+ * nsIContentAnalysisResponse.Action enum.
+ * @param {string} token The requestToken.
+ * @returns {object} An object that conforms to nsIContentAnalysisResponse.
+ */
+function makeContentAnalysisResponse(action, token) {
+ return {
+ action,
+ shouldAllowContent: action != Ci.nsIContentAnalysisResponse.eBlock,
+ requestToken: token,
+ acknowledge: _acknowledgement => {},
+ };
+}
+
+async function waitForFileToAlmostMatchSize(filePath, expectedSize) {
+ // In Cocoa the CGContext adds a hash, plus there are other minor
+ // non-user-visible differences, so we need to be a bit more sloppy there.
+ //
+ // We see one byte difference in Windows and Linux on automation sometimes,
+ // though files are consistently the same locally, that needs
+ // investigation, but it's probably harmless.
+ // Note that this is copied from browser_print_stream.js.
+ const maxSizeDifference = AppConstants.platform == "macosx" ? 100 : 3;
+
+ // Buffering shenanigans? Wait for sizes to match... There's no great
+ // IOUtils methods to force a flush without writing anything...
+ // Note that this means if this results in a timeout this is exactly
+ // the same as a test failure.
+ // This is taken from toolkit/components/printing/tests/browser_print_stream.js
+ await TestUtils.waitForCondition(async function () {
+ let fileStat = await IOUtils.stat(filePath);
+
+ info("got size: " + fileStat.size + " expected: " + expectedSize);
+ Assert.greater(
+ fileStat.size,
+ 0,
+ "File should not be empty: " + fileStat.size
+ );
+ return Math.abs(fileStat.size - expectedSize) <= maxSizeDifference;
+ }, "Sizes should (almost) match");
+}
diff --git a/toolkit/components/contentprefs/ContentPrefService2.sys.mjs b/toolkit/components/contentprefs/ContentPrefService2.sys.mjs
index 43fa0c4cb6..069229c9c5 100644
--- a/toolkit/components/contentprefs/ContentPrefService2.sys.mjs
+++ b/toolkit/components/contentprefs/ContentPrefService2.sys.mjs
@@ -39,7 +39,7 @@ export function ContentPrefService2() {
}
const cache = new ContentPrefStore();
-cache.set = function CPS_cache_set(group, name, val) {
+cache.set = function CPS_cache_set() {
Object.getPrototypeOf(this).set.apply(this, arguments);
let groupCount = this._groups.size;
if (groupCount >= CACHE_MAX_GROUP_ENTRIES) {
@@ -211,7 +211,7 @@ ContentPrefService2.prototype = {
cbHandleResult(callback, new ContentPref(grp, name, val));
}
},
- onDone: (reason, ok, gotRow) => {
+ onDone: (reason, ok) => {
if (ok) {
for (let [pbGroup, pbName, pbVal] of pbPrefs) {
cbHandleResult(callback, new ContentPref(pbGroup, pbName, pbVal));
@@ -1088,9 +1088,8 @@ ContentPrefService2.prototype = {
*
* @param subj This value depends on topic.
* @param topic The backchannel "method" name.
- * @param data This value depends on topic.
*/
- observe: function CPS2_observe(subj, topic, data) {
+ observe: function CPS2_observe(subj, topic) {
switch (topic) {
case "profile-before-change":
this._destroy();
diff --git a/toolkit/components/contentprefs/ContentPrefServiceParent.sys.mjs b/toolkit/components/contentprefs/ContentPrefServiceParent.sys.mjs
index 66b4a02741..13ef8e871c 100644
--- a/toolkit/components/contentprefs/ContentPrefServiceParent.sys.mjs
+++ b/toolkit/components/contentprefs/ContentPrefServiceParent.sys.mjs
@@ -92,7 +92,7 @@ export class ContentPrefsParent extends JSProcessActorParent {
let actor = this;
let args = data.args;
- return new Promise(resolve => {
+ return new Promise(() => {
let listener = {
handleResult(pref) {
actor.sendAsyncMessage("ContentPrefs:HandleResult", {
diff --git a/toolkit/components/contentprefs/tests/browser/browser_remoteContentPrefs.js b/toolkit/components/contentprefs/tests/browser/browser_remoteContentPrefs.js
index 00c845e488..0b9dd22f35 100644
--- a/toolkit/components/contentprefs/tests/browser/browser_remoteContentPrefs.js
+++ b/toolkit/components/contentprefs/tests/browser/browser_remoteContentPrefs.js
@@ -173,7 +173,7 @@ async function runTestsForFrame(browser, isPrivate) {
onContentPrefSet(group, name, value, isPrivate) {
resolve({ group, name, value, isPrivate });
},
- onContentPrefRemoved(group, name, isPrivate) {
+ onContentPrefRemoved() {
reject("got unexpected notification");
},
};
@@ -202,7 +202,7 @@ async function runTestsForFrame(browser, isPrivate) {
info("received handleResult");
results.push(pref);
},
- handleCompletion(reason) {
+ handleCompletion() {
resolve();
},
handleError(rv) {
diff --git a/toolkit/components/contentprefs/tests/unit_cps2/head.js b/toolkit/components/contentprefs/tests/unit_cps2/head.js
index c500d2bf01..bf5dcf2bc8 100644
--- a/toolkit/components/contentprefs/tests/unit_cps2/head.js
+++ b/toolkit/components/contentprefs/tests/unit_cps2/head.js
@@ -82,7 +82,7 @@ function setWithDate(group, name, val, timestamp, context) {
});
}
-async function getDate(group, name, context) {
+async function getDate(group, name) {
let conn = await sendMessage("db");
let [result] = await conn.execute(
`
@@ -158,7 +158,7 @@ async function getGlobalOK(args, expectedVal) {
await getOKEx("getGlobal", args, expectedPrefs);
}
-async function getOKEx(methodName, args, expectedPrefs, strict, context) {
+async function getOKEx(methodName, args, expectedPrefs, strict) {
let actualPrefs = [];
await new Promise(resolve => {
args.push(
diff --git a/toolkit/components/contentprefs/tests/unit_cps2/test_migrationToSchema4.js b/toolkit/components/contentprefs/tests/unit_cps2/test_migrationToSchema4.js
index 916fc55498..c5faf1a833 100644
--- a/toolkit/components/contentprefs/tests/unit_cps2/test_migrationToSchema4.js
+++ b/toolkit/components/contentprefs/tests/unit_cps2/test_migrationToSchema4.js
@@ -25,7 +25,7 @@ BEGIN TRANSACTION;
CREATE INDEX prefs_idx ON prefs(groupID, settingID);
COMMIT;`;
-function prepareVersion3Schema(callback) {
+function prepareVersion3Schema() {
var dbFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
dbFile.append("content-prefs.sqlite");
diff --git a/toolkit/components/contentrelevancy/ContentRelevancyManager.sys.mjs b/toolkit/components/contentrelevancy/ContentRelevancyManager.sys.mjs
new file mode 100644
index 0000000000..ea3f2a78a2
--- /dev/null
+++ b/toolkit/components/contentrelevancy/ContentRelevancyManager.sys.mjs
@@ -0,0 +1,326 @@
+/* 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/. */
+
+import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
+
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ getFrecentRecentCombinedUrls:
+ "resource://gre/modules/contentrelevancy/private/InputUtils.sys.mjs",
+ NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
+ RelevancyStore: "resource://gre/modules/RustRelevancy.sys.mjs",
+});
+
+XPCOMUtils.defineLazyServiceGetter(
+ lazy,
+ "timerManager",
+ "@mozilla.org/updates/timer-manager;1",
+ "nsIUpdateTimerManager"
+);
+
+// Constants used by `nsIUpdateTimerManager` for a cross-session timer.
+const TIMER_ID = "content-relevancy-timer";
+const PREF_TIMER_LAST_UPDATE = `app.update.lastUpdateTime.${TIMER_ID}`;
+const PREF_TIMER_INTERVAL = "toolkit.contentRelevancy.timerInterval";
+// Set the timer interval to 1 day for validation.
+const DEFAULT_TIMER_INTERVAL_SECONDS = 1 * 24 * 60 * 60;
+
+// Default maximum input URLs to fetch from Places.
+const DEFAULT_MAX_URLS = 100;
+// Default minimal input URLs for clasification.
+const DEFAULT_MIN_URLS = 0;
+
+// File name of the relevancy database
+const RELEVANCY_STORE_FILENAME = "content-relevancy.sqlite";
+
+// Nimbus variables
+const NIMBUS_VARIABLE_ENABLED = "enabled";
+const NIMBUS_VARIABLE_MAX_INPUT_URLS = "maxInputUrls";
+const NIMBUS_VARIABLE_MIN_INPUT_URLS = "minInputUrls";
+const NIMBUS_VARIABLE_TIMER_INTERVAL = "timerInterval";
+
+ChromeUtils.defineLazyGetter(lazy, "log", () => {
+ return console.createInstance({
+ prefix: "ContentRelevancyManager",
+ maxLogLevel: Services.prefs.getBoolPref(
+ "toolkit.contentRelevancy.log",
+ false
+ )
+ ? "Debug"
+ : "Error",
+ });
+});
+
+class RelevancyManager {
+ get initialized() {
+ return this.#initialized;
+ }
+
+ /**
+ * Init the manager. An update timer is registered if the feature is enabled.
+ * The pref observer is always set so we can toggle the feature without restarting
+ * the browser.
+ *
+ * Note that this should be called once only. `#enable` and `#disable` can be
+ * used to toggle the feature once the manager is initialized.
+ */
+ async init() {
+ if (this.initialized) {
+ return;
+ }
+
+ lazy.log.info("Initializing the manager");
+
+ if (this.shouldEnable) {
+ await this.#enable();
+ }
+
+ this._nimbusUpdateCallback = this.#onNimbusUpdate.bind(this);
+ // This will handle both Nimbus updates and pref changes.
+ lazy.NimbusFeatures.contentRelevancy.onUpdate(this._nimbusUpdateCallback);
+ this.#initialized = true;
+ }
+
+ uninit() {
+ if (!this.initialized) {
+ return;
+ }
+
+ lazy.log.info("Uninitializing the manager");
+
+ lazy.NimbusFeatures.contentRelevancy.offUpdate(this._nimbusUpdateCallback);
+ this.#disable();
+
+ this.#initialized = false;
+ }
+
+ /**
+ * Determine whether the feature should be enabled based on prefs and Nimbus.
+ */
+ get shouldEnable() {
+ return (
+ lazy.NimbusFeatures.contentRelevancy.getVariable(
+ NIMBUS_VARIABLE_ENABLED
+ ) ?? false
+ );
+ }
+
+ #startUpTimer() {
+ // Log the last timer tick for debugging.
+ const lastTick = Services.prefs.getIntPref(PREF_TIMER_LAST_UPDATE, 0);
+ if (lastTick) {
+ lazy.log.debug(
+ `Last timer tick: ${lastTick}s (${
+ Math.round(Date.now() / 1000) - lastTick
+ })s ago`
+ );
+ } else {
+ lazy.log.debug("Last timer tick: none");
+ }
+
+ const interval =
+ lazy.NimbusFeatures.contentRelevancy.getVariable(
+ NIMBUS_VARIABLE_TIMER_INTERVAL
+ ) ??
+ Services.prefs.getIntPref(
+ PREF_TIMER_INTERVAL,
+ DEFAULT_TIMER_INTERVAL_SECONDS
+ );
+ lazy.timerManager.registerTimer(
+ TIMER_ID,
+ this,
+ interval,
+ interval != 0 // Do not skip the first timer tick for a zero interval for testing
+ );
+ }
+
+ get #storePath() {
+ return PathUtils.join(
+ Services.dirsvc.get("ProfLD", Ci.nsIFile).path,
+ RELEVANCY_STORE_FILENAME
+ );
+ }
+
+ async #enable() {
+ if (!this.#_store) {
+ // Init the relevancy store.
+ const path = this.#storePath;
+ lazy.log.info(`Initializing RelevancyStore: ${path}`);
+
+ try {
+ this.#_store = await lazy.RelevancyStore.init(path);
+ } catch (error) {
+ lazy.log.error(`Error initializing RelevancyStore: ${error}`);
+ return;
+ }
+ }
+
+ this.#startUpTimer();
+ }
+
+ /**
+ * The reciprocal of `#enable()`, ensure this is safe to call when you add
+ * new disabling code here. It should be so even if `#enable()` hasn't been
+ * called.
+ */
+ #disable() {
+ this.#_store = null;
+ lazy.timerManager.unregisterTimer(TIMER_ID);
+ }
+
+ async #toggleFeature() {
+ if (this.shouldEnable) {
+ await this.#enable();
+ } else {
+ this.#disable();
+ }
+ }
+
+ /**
+ * nsITimerCallback
+ */
+ notify() {
+ lazy.log.info("Background job timer fired");
+ this.#doClassification();
+ }
+
+ get isInProgress() {
+ return this.#isInProgress;
+ }
+
+ /**
+ * Perform classification based on browsing history.
+ *
+ * It will fetch up to `DEFAULT_MAX_URLS` (or the corresponding Nimbus value)
+ * URLs from top frecent URLs and use most recent URLs as a fallback if the
+ * former is insufficient. The returned URLs might be fewer than requested.
+ *
+ * The classification will not be performed if the total number of input URLs
+ * is less than `DEFAULT_MIN_URLS` (or the corresponding Nimbus value).
+ */
+ async #doClassification() {
+ if (this.isInProgress) {
+ lazy.log.info(
+ "Another classification is in progress, aborting interest classification"
+ );
+ return;
+ }
+
+ // Set a flag indicating this classification. Ensure it's cleared upon early
+ // exit points & success.
+ this.#isInProgress = true;
+
+ try {
+ lazy.log.info("Fetching input data for interest classification");
+
+ const maxUrls =
+ lazy.NimbusFeatures.contentRelevancy.getVariable(
+ NIMBUS_VARIABLE_MAX_INPUT_URLS
+ ) ?? DEFAULT_MAX_URLS;
+ const minUrls =
+ lazy.NimbusFeatures.contentRelevancy.getVariable(
+ NIMBUS_VARIABLE_MIN_INPUT_URLS
+ ) ?? DEFAULT_MIN_URLS;
+ const urls = await lazy.getFrecentRecentCombinedUrls(maxUrls);
+ if (urls.length < minUrls) {
+ lazy.log.info("Aborting interest classification: insufficient input");
+ return;
+ }
+
+ lazy.log.info("Starting interest classification");
+ await this.#doClassificationHelper(urls);
+ } catch (error) {
+ if (error instanceof StoreNotAvailableError) {
+ lazy.log.error("#store became null, aborting interest classification");
+ } else {
+ lazy.log.error("Classification error: " + (error.reason ?? error));
+ }
+ } finally {
+ this.#isInProgress = false;
+ }
+
+ lazy.log.info("Finished interest classification");
+ }
+
+ /**
+ * Classification helper. Use the getter `this.#store` rather than `#_store`
+ * to access the store so that when it becomes null, a `StoreNotAvailableError`
+ * will be raised. Likewise, other store related errors should be propagated
+ * to the caller if you want to perform custom error handling in this helper.
+ *
+ * @param {Array} urls
+ * An array of URLs.
+ * @throws {StoreNotAvailableError}
+ * Thrown when the store became unavailable (i.e. set to null elsewhere).
+ * @throws {RelevancyAPIError}
+ * Thrown for other API errors on the store.
+ */
+ async #doClassificationHelper(urls) {
+ // The following logs are unnecessary, only used to suppress the linting error.
+ // TODO(nanj): delete me once the following TODO is done.
+ if (!this.#store) {
+ lazy.log.error("#store became null, aborting interest classification");
+ }
+ lazy.log.info("Classification input: " + urls);
+
+ // TODO(nanj): uncomment the following once `ingest()` is implemented.
+ // await this.#store.ingest(urls);
+ }
+
+ /**
+ * Exposed for testing.
+ */
+ async _test_doClassification(urls) {
+ await this.#doClassificationHelper(urls);
+ }
+
+ /**
+ * Internal getter for `#_store` used by for classification. It will throw
+ * a `StoreNotAvailableError` is the store is not ready.
+ */
+ get #store() {
+ if (!this._isStoreReady) {
+ throw new StoreNotAvailableError("Store is not available");
+ }
+
+ return this.#_store;
+ }
+
+ /**
+ * Whether or not the store is ready (i.e. not null).
+ */
+ get _isStoreReady() {
+ return !!this.#_store;
+ }
+
+ /**
+ * Nimbus update listener.
+ */
+ #onNimbusUpdate(_event, _reason) {
+ this.#toggleFeature();
+ }
+
+ // The `RustRelevancy` store.
+ #_store;
+
+ // Whether or not the module is initialized.
+ #initialized = false;
+
+ // Whether or not there is an in-progress classification. Used to prevent
+ // duplicate classification tasks.
+ #isInProgress = false;
+}
+
+/**
+ * Error raised when attempting to access a null store.
+ */
+class StoreNotAvailableError extends Error {
+ constructor(message, ...params) {
+ super(message, ...params);
+ this.name = "StoreNotAvailableError";
+ }
+}
+
+export var ContentRelevancyManager = new RelevancyManager();
diff --git a/toolkit/components/contentrelevancy/docs/index.md b/toolkit/components/contentrelevancy/docs/index.md
new file mode 100644
index 0000000000..bd377d68dc
--- /dev/null
+++ b/toolkit/components/contentrelevancy/docs/index.md
@@ -0,0 +1,3 @@
+# Content Relevancy
+
+This is the home for the project: Interest-based Content Relevance Ranking & Personalization for Firefox, a client-based privacy preserving approach to enhancing content experience of Firefox.
diff --git a/toolkit/components/contentrelevancy/moz.build b/toolkit/components/contentrelevancy/moz.build
new file mode 100644
index 0000000000..551af69924
--- /dev/null
+++ b/toolkit/components/contentrelevancy/moz.build
@@ -0,0 +1,27 @@
+# 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/.
+
+with Files("**"):
+ BUG_COMPONENT = ("Application Services", "Relevancy")
+
+EXTRA_JS_MODULES += [
+ "ContentRelevancyManager.sys.mjs",
+]
+
+EXTRA_JS_MODULES["contentrelevancy/private"] += [
+ "private/InputUtils.sys.mjs",
+]
+
+XPCSHELL_TESTS_MANIFESTS += [
+ "tests/xpcshell/xpcshell.toml",
+]
+
+BROWSER_CHROME_MANIFESTS += [
+ "tests/browser/browser.toml",
+]
+
+SPHINX_TREES["/toolkit/components/contentrelevancy"] = "docs"
+
+with Files("docs/**"):
+ SCHEDULES.exclusive = ["docs"]
diff --git a/toolkit/components/contentrelevancy/private/InputUtils.sys.mjs b/toolkit/components/contentrelevancy/private/InputUtils.sys.mjs
new file mode 100644
index 0000000000..eccc5a5768
--- /dev/null
+++ b/toolkit/components/contentrelevancy/private/InputUtils.sys.mjs
@@ -0,0 +1,118 @@
+/* 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, {
+ NewTabUtils: "resource://gre/modules/NewTabUtils.sys.mjs",
+});
+
+/**
+ * Get the URLs with the top frecency scores.
+ *
+ * Note:
+ * - Blocked URLs are excluded
+ * - Allow multiple URLs from the same domain (www vs non-www urls)
+ *
+ * @param {number} maxUrls
+ * The maximum number of URLs to fetch.
+ * @param {number} frecencyThreshold
+ * The minimal frecency score of the URL. Will use the default set by
+ * the upstream module if unspecified. For reference, "100" means one
+ * visit in the past 3 days. see more details at:
+ * `/browser/components/urlbar/docs/ranking.rst`
+ * @returns {Array}
+ * An array of URLs. Note that the actual number could be less than `maxUrls`.
+ */
+export async function getTopFrecentUrls(
+ maxUrls,
+ frecencyThreshold /* for test */
+) {
+ const options = {
+ ignoreBlocked: true,
+ onePerDomain: false,
+ includeFavicon: false,
+ topsiteFrecency: frecencyThreshold,
+ numItems: maxUrls,
+ };
+ const records = await lazy.NewTabUtils.activityStreamLinks.getTopSites(
+ options
+ );
+
+ return records.map(site => site.url);
+}
+
+/**
+ * Get the URLs of the most recent browsing history.
+ *
+ * Note:
+ * - Blocked URLs are excluded
+ * - Recent bookmarks are excluded
+ * - Recent "Save-to-Pocket" URLs are excluded
+ * - It would only return URLs if the page meta data is present. We can relax
+ * this in the future.
+ * - Multiple URLs may be returned for the same domain
+ *
+ * @param {number} maxUrls
+ * The maximum number of URLs to fetch.
+ * @returns {Array}
+ * An array of URLs. Note that the actual number could be less than `maxUrls`.
+ */
+export async function getMostRecentUrls(maxUrls) {
+ const options = {
+ ignoreBlocked: true,
+ excludeBookmarks: true,
+ excludeHistory: false,
+ excludePocket: true,
+ withFavicons: false,
+ numItems: maxUrls,
+ };
+ const records = await lazy.NewTabUtils.activityStreamLinks.getHighlights(
+ options
+ );
+
+ return records.map(site => site.url);
+}
+
+/**
+ * Get the URLs as a combination of the top frecent and the most recent
+ * browsing history.
+ *
+ * It will fetch `maxUrls` URLs from top frecent URLs and use most recent URLs
+ * as a fallback if the former is insufficient. Duplicates will be removed
+ * As a result, the returned URLs might be fewer than requested.
+ *
+ * @param {number} maxUrls
+ * The maximum number of URLs to fetch.
+ * @returns {Array}
+ * An array of URLs.
+ */
+export async function getFrecentRecentCombinedUrls(maxUrls) {
+ let urls = await getTopFrecentUrls(maxUrls);
+ if (urls.length < maxUrls) {
+ const n = Math.round((maxUrls - urls.length) * 1.2); // Over-fetch for deduping
+ const recentUrls = await getMostRecentUrls(n);
+ urls = dedupUrls(urls, recentUrls).slice(0, maxUrls);
+ }
+
+ return urls;
+}
+
+/**
+ * A helper to deduplicate items from any number of grouped URLs.
+ *
+ * Note:
+ * - Currently, all the elements (URLs) of the input arrays are treated as keys.
+ * - It doesn't assume the uniqueness within the group, therefore, in-group
+ * duplicates will be deduped as well.
+ *
+ * @param {Array} groups
+ * Contains an arbitrary number of arrays of URLs.
+ * @returns {Array}
+ * An array of unique URLs from the input groups.
+ */
+function dedupUrls(...groups) {
+ const uniques = new Set(groups.flat());
+ return [...uniques];
+}
diff --git a/toolkit/components/contentrelevancy/tests/browser/browser.toml b/toolkit/components/contentrelevancy/tests/browser/browser.toml
new file mode 100644
index 0000000000..ec1d3a3e66
--- /dev/null
+++ b/toolkit/components/contentrelevancy/tests/browser/browser.toml
@@ -0,0 +1,6 @@
+[DEFAULT]
+prefs = [
+ "toolkit.contentRelevancy.enabled=false",
+]
+
+["browser_contentrelevancy_nimbus.js"]
diff --git a/toolkit/components/contentrelevancy/tests/browser/browser_contentrelevancy_nimbus.js b/toolkit/components/contentrelevancy/tests/browser/browser_contentrelevancy_nimbus.js
new file mode 100644
index 0000000000..47d54c2a87
--- /dev/null
+++ b/toolkit/components/contentrelevancy/tests/browser/browser_contentrelevancy_nimbus.js
@@ -0,0 +1,91 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { ExperimentAPI } = ChromeUtils.importESModule(
+ "resource://nimbus/ExperimentAPI.sys.mjs"
+);
+const { ExperimentFakes } = ChromeUtils.importESModule(
+ "resource://testing-common/NimbusTestUtils.sys.mjs"
+);
+const { ContentRelevancyManager } = ChromeUtils.importESModule(
+ "resource://gre/modules/ContentRelevancyManager.sys.mjs"
+);
+const { sinon } = ChromeUtils.importESModule(
+ "resource://testing-common/Sinon.sys.mjs"
+);
+
+let gSandbox;
+
+add_setup(() => {
+ gSandbox = sinon.createSandbox();
+
+ registerCleanupFunction(() => {
+ gSandbox.restore();
+ });
+});
+
+/**
+ * Test Nimbus integration - enable.
+ */
+add_task(async function test_NimbusIntegration_enable() {
+ gSandbox.spy(ContentRelevancyManager, "notify");
+
+ await ExperimentAPI.ready();
+ const doExperimentCleanup = await ExperimentFakes.enrollWithFeatureConfig({
+ featureId: "contentRelevancy",
+ value: {
+ enabled: true,
+ minInputUrls: 1,
+ maxInputUrls: 3,
+ // Set the timer interval to 0 will trigger the timer right away.
+ timerInterval: 0,
+ },
+ });
+
+ await TestUtils.waitForCondition(
+ () => ContentRelevancyManager.shouldEnable,
+ "Should enable it via Nimbus"
+ );
+
+ await TestUtils.waitForCondition(
+ () => ContentRelevancyManager.notify.called,
+ "The timer callback should be called"
+ );
+
+ await doExperimentCleanup();
+ gSandbox.restore();
+});
+
+/**
+ * Test Nimbus integration - disable.
+ */
+add_task(async function test_NimbusIntegration_disable() {
+ gSandbox.spy(ContentRelevancyManager, "notify");
+
+ await ExperimentAPI.ready();
+ const doExperimentCleanup = await ExperimentFakes.enrollWithFeatureConfig({
+ featureId: "contentRelevancy",
+ value: {
+ enabled: false,
+ minInputUrls: 1,
+ maxInputUrls: 3,
+ // Set the timer interval to 0 will trigger the timer right away.
+ timerInterval: 0,
+ },
+ });
+
+ await TestUtils.waitForCondition(
+ () => !ContentRelevancyManager.shouldEnable,
+ "Should disable it via Nimbus"
+ );
+
+ await TestUtils.waitForCondition(
+ () => ContentRelevancyManager.notify.notCalled,
+ "The timer callback should not be called"
+ );
+
+ await doExperimentCleanup();
+ gSandbox.restore();
+});
diff --git a/toolkit/components/contentrelevancy/tests/xpcshell/head.js b/toolkit/components/contentrelevancy/tests/xpcshell/head.js
new file mode 100644
index 0000000000..e8c31f589c
--- /dev/null
+++ b/toolkit/components/contentrelevancy/tests/xpcshell/head.js
@@ -0,0 +1,6 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// This requires the profile directory for Places and the content relevancy
+// component needs a profile directory for storage.
+do_get_profile();
diff --git a/toolkit/components/contentrelevancy/tests/xpcshell/test_ContentRelevancyManager.js b/toolkit/components/contentrelevancy/tests/xpcshell/test_ContentRelevancyManager.js
new file mode 100644
index 0000000000..633f9fc49b
--- /dev/null
+++ b/toolkit/components/contentrelevancy/tests/xpcshell/test_ContentRelevancyManager.js
@@ -0,0 +1,149 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+ChromeUtils.defineESModuleGetters(this, {
+ ContentRelevancyManager:
+ "resource://gre/modules/ContentRelevancyManager.sys.mjs",
+ TestUtils: "resource://testing-common/TestUtils.sys.mjs",
+ setTimeout: "resource://gre/modules/Timer.sys.mjs",
+ sinon: "resource://testing-common/Sinon.sys.mjs",
+});
+
+const PREF_CONTENT_RELEVANCY_ENABLED = "toolkit.contentRelevancy.enabled";
+const PREF_TIMER_INTERVAL = "toolkit.contentRelevancy.timerInterval";
+
+// These consts are copied from the update timer manager test. See
+// `initUpdateTimerManager()`.
+const PREF_APP_UPDATE_TIMERMINIMUMDELAY = "app.update.timerMinimumDelay";
+const PREF_APP_UPDATE_TIMERFIRSTINTERVAL = "app.update.timerFirstInterval";
+const MAIN_TIMER_INTERVAL = 1000; // milliseconds
+const CATEGORY_UPDATE_TIMER = "update-timer";
+
+let gSandbox;
+
+add_setup(async () => {
+ gSandbox = sinon.createSandbox();
+ initUpdateTimerManager();
+ Services.prefs.setBoolPref(PREF_CONTENT_RELEVANCY_ENABLED, true);
+ await ContentRelevancyManager.init();
+
+ registerCleanupFunction(() => {
+ Services.prefs.clearUserPref(PREF_CONTENT_RELEVANCY_ENABLED);
+ gSandbox.restore();
+ });
+});
+
+add_task(async function test_init() {
+ Assert.ok(ContentRelevancyManager.initialized, "Init should succeed");
+});
+
+add_task(async function test_uninit() {
+ ContentRelevancyManager.uninit();
+
+ Assert.ok(!ContentRelevancyManager.initialized, "Uninit should succeed");
+});
+
+add_task(async function test_timer() {
+ // Set the timer interval to 0 will trigger the timer right away.
+ Services.prefs.setIntPref(PREF_TIMER_INTERVAL, 0);
+ gSandbox.spy(ContentRelevancyManager, "notify");
+
+ await ContentRelevancyManager.init();
+
+ await TestUtils.waitForCondition(
+ () => ContentRelevancyManager.notify.called,
+ "The timer callback should be called"
+ );
+
+ Services.prefs.clearUserPref(PREF_TIMER_INTERVAL);
+ gSandbox.restore();
+});
+
+add_task(async function test_feature_toggling() {
+ Services.prefs.setBoolPref(PREF_CONTENT_RELEVANCY_ENABLED, false);
+ // Set the timer interval to 0 will trigger the timer right away.
+ Services.prefs.setIntPref(PREF_TIMER_INTERVAL, 0);
+ gSandbox.spy(ContentRelevancyManager, "notify");
+
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(resolve => setTimeout(resolve, 1100));
+ Assert.ok(
+ ContentRelevancyManager.notify.notCalled,
+ "Timer should not be registered if disabled"
+ );
+
+ // Toggle the pref again should re-enable the feature.
+ Services.prefs.setBoolPref(PREF_CONTENT_RELEVANCY_ENABLED, true);
+ await TestUtils.waitForTick();
+
+ await TestUtils.waitForCondition(
+ () => ContentRelevancyManager.notify.called,
+ "The timer callback should be called"
+ );
+
+ Services.prefs.clearUserPref(PREF_CONTENT_RELEVANCY_ENABLED);
+ Services.prefs.clearUserPref(PREF_TIMER_INTERVAL);
+ gSandbox.restore();
+});
+
+add_task(async function test_call_disable_twice() {
+ Services.prefs.setBoolPref(PREF_CONTENT_RELEVANCY_ENABLED, false);
+ await TestUtils.waitForTick();
+
+ Services.prefs.setBoolPref(PREF_CONTENT_RELEVANCY_ENABLED, false);
+ await TestUtils.waitForTick();
+
+ Assert.ok(true, "`#disable` should be safe to call multiple times");
+
+ Services.prefs.clearUserPref(PREF_CONTENT_RELEVANCY_ENABLED);
+});
+
+add_task(async function test_doClassification() {
+ Services.prefs.setBoolPref(PREF_CONTENT_RELEVANCY_ENABLED, true);
+ await TestUtils.waitForCondition(() => ContentRelevancyManager._isStoreReady);
+ await ContentRelevancyManager._test_doClassification([]);
+
+ // Disable it to reset the store.
+ Services.prefs.setBoolPref(PREF_CONTENT_RELEVANCY_ENABLED, false);
+ await TestUtils.waitForTick();
+
+ await Assert.rejects(
+ ContentRelevancyManager._test_doClassification([]),
+ /Store is not available/,
+ "Should throw with an unset store"
+ );
+
+ Services.prefs.clearUserPref(PREF_CONTENT_RELEVANCY_ENABLED);
+});
+
+/**
+ * Sets up the update timer manager for testing: makes it fire more often,
+ * removes all existing timers, and initializes it for testing. The body of this
+ * function is copied from:
+ * https://searchfox.org/mozilla-central/source/toolkit/components/timermanager/tests/unit/consumerNotifications.js
+ */
+function initUpdateTimerManager() {
+ // Set the timer to fire every second
+ Services.prefs.setIntPref(
+ PREF_APP_UPDATE_TIMERMINIMUMDELAY,
+ MAIN_TIMER_INTERVAL / 1000
+ );
+ Services.prefs.setIntPref(
+ PREF_APP_UPDATE_TIMERFIRSTINTERVAL,
+ MAIN_TIMER_INTERVAL
+ );
+
+ // Remove existing update timers to prevent them from being notified
+ for (let { data: entry } of Services.catMan.enumerateCategory(
+ CATEGORY_UPDATE_TIMER
+ )) {
+ Services.catMan.deleteCategoryEntry(CATEGORY_UPDATE_TIMER, entry, false);
+ }
+
+ Cc["@mozilla.org/updates/timer-manager;1"]
+ .getService(Ci.nsIUpdateTimerManager)
+ .QueryInterface(Ci.nsIObserver)
+ .observe(null, "utm-test-init", "");
+}
diff --git a/toolkit/components/contentrelevancy/tests/xpcshell/test_InputUtils.js b/toolkit/components/contentrelevancy/tests/xpcshell/test_InputUtils.js
new file mode 100644
index 0000000000..2bb2b8e62e
--- /dev/null
+++ b/toolkit/components/contentrelevancy/tests/xpcshell/test_InputUtils.js
@@ -0,0 +1,98 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+ChromeUtils.defineESModuleGetters(this, {
+ getFrecentRecentCombinedUrls:
+ "resource://gre/modules/contentrelevancy/private/InputUtils.sys.mjs",
+ getMostRecentUrls:
+ "resource://gre/modules/contentrelevancy/private/InputUtils.sys.mjs",
+ getTopFrecentUrls:
+ "resource://gre/modules/contentrelevancy/private/InputUtils.sys.mjs",
+ PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs",
+ PlacesTestUtils: "resource://testing-common/PlacesTestUtils.sys.mjs",
+});
+
+const FRECENCY_SCORE_FOR_ONE_VISIT = 100;
+const TEST_VISITS = [
+ "http://test-1.com/",
+ "http://test-2.com/",
+ "http://test-3.com/",
+ "http://test-4.com/",
+];
+
+add_task(async function test_GetTopFrecentUrls() {
+ await PlacesUtils.history.clear();
+ let urls = new Set(await getTopFrecentUrls(3, FRECENCY_SCORE_FOR_ONE_VISIT));
+
+ Assert.strictEqual(urls.size, 0, "Should have no top frecent links.");
+
+ await PlacesTestUtils.addVisits(TEST_VISITS);
+ urls = new Set(await getTopFrecentUrls(3, FRECENCY_SCORE_FOR_ONE_VISIT));
+
+ Assert.strictEqual(urls.size, 3, "Should fetch the expected links");
+ urls.forEach(url => {
+ Assert.ok(TEST_VISITS.includes(url), "Should be a link of the test visits");
+ });
+});
+
+add_task(async function test_GetMostRecentUrls() {
+ await PlacesUtils.history.clear();
+ let urls = new Set(await getMostRecentUrls(3));
+
+ Assert.strictEqual(urls.size, 0, "Should have no recent links.");
+
+ // Add visits and page meta data.
+ await PlacesTestUtils.addVisits(TEST_VISITS);
+ for (let url of TEST_VISITS) {
+ await PlacesUtils.history.update({
+ description: "desc",
+ previewImageURL: "https://image/",
+ url,
+ });
+ }
+
+ urls = new Set(await getMostRecentUrls(3));
+
+ Assert.strictEqual(urls.size, 3, "Should fetch the expected links");
+ urls.forEach(url => {
+ Assert.ok(TEST_VISITS.includes(url), "Should be a link of the test visits");
+ });
+});
+
+add_task(async function test_GetFrecentRecentCombinedUrls() {
+ await PlacesUtils.history.clear();
+ let urls = new Set(await getFrecentRecentCombinedUrls(3));
+
+ Assert.strictEqual(urls.size, 0, "Should have no links.");
+
+ // Add visits and page meta data.
+ await PlacesTestUtils.addVisits(TEST_VISITS);
+ for (let url of TEST_VISITS) {
+ await PlacesUtils.history.update({
+ description: "desc",
+ previewImageURL: "https://image/",
+ url,
+ });
+ }
+
+ urls = new Set(await getFrecentRecentCombinedUrls(3));
+
+ Assert.strictEqual(urls.size, 3, "Should fetch the expected links");
+ urls.forEach(url => {
+ Assert.ok(TEST_VISITS.includes(url), "Should be a link of the test visits");
+ });
+
+ // Try getting twice as many URLs as the total in Places.
+ urls = new Set(await getFrecentRecentCombinedUrls(TEST_VISITS.length * 2));
+
+ Assert.strictEqual(
+ urls.size,
+ TEST_VISITS.length,
+ "Should not include duplicates"
+ );
+ urls.forEach(url => {
+ Assert.ok(TEST_VISITS.includes(url), "Should be a link of the test visits");
+ });
+});
diff --git a/toolkit/components/contentrelevancy/tests/xpcshell/xpcshell.toml b/toolkit/components/contentrelevancy/tests/xpcshell/xpcshell.toml
new file mode 100644
index 0000000000..70f6d45c2d
--- /dev/null
+++ b/toolkit/components/contentrelevancy/tests/xpcshell/xpcshell.toml
@@ -0,0 +1,9 @@
+[DEFAULT]
+head = "head.js"
+firefox-appdir = "browser"
+
+["test_ContentRelevancyManager.js"]
+skip-if = ["os == 'android'"] # bug 1886601
+
+["test_InputUtils.js"]
+skip-if = ["os == 'android'"] # bug 1886601
diff --git a/toolkit/components/contextualidentity/ContextualIdentityService.sys.mjs b/toolkit/components/contextualidentity/ContextualIdentityService.sys.mjs
index 738f8743ee..32f3577c93 100644
--- a/toolkit/components/contextualidentity/ContextualIdentityService.sys.mjs
+++ b/toolkit/components/contextualidentity/ContextualIdentityService.sys.mjs
@@ -41,7 +41,7 @@ _TabRemovalObserver.prototype = {
QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
- observe(subject, topic, data) {
+ observe(subject) {
let remoteTab = subject.QueryInterface(Ci.nsIRemoteTab);
if (this._remoteTabIds.has(remoteTab.tabId)) {
this._remoteTabIds.delete(remoteTab.tabId);
diff --git a/toolkit/components/cookiebanners/CookieBannerChild.sys.mjs b/toolkit/components/cookiebanners/CookieBannerChild.sys.mjs
index 4d8b481504..b56303d95b 100644
--- a/toolkit/components/cookiebanners/CookieBannerChild.sys.mjs
+++ b/toolkit/components/cookiebanners/CookieBannerChild.sys.mjs
@@ -488,18 +488,18 @@ export class CookieBannerChild extends JSWindowActorChild {
let querySelectorTimeUS = Math.round(querySelectorTimeMS * 1000);
if (this.#isTopLevel) {
- Glean.cookieBannersClick.querySelectorRunCountPerWindowTopLevel.accumulateSamples(
- [querySelectorCount]
+ Glean.cookieBannersClick.querySelectorRunCountPerWindowTopLevel.accumulateSingleSample(
+ querySelectorCount
);
- Glean.cookieBannersClick.querySelectorRunDurationPerWindowTopLevel.accumulateSamples(
- [querySelectorTimeUS]
+ Glean.cookieBannersClick.querySelectorRunDurationPerWindowTopLevel.accumulateSingleSample(
+ querySelectorTimeUS
);
} else {
- Glean.cookieBannersClick.querySelectorRunCountPerWindowFrame.accumulateSamples(
- [querySelectorCount]
+ Glean.cookieBannersClick.querySelectorRunCountPerWindowFrame.accumulateSingleSample(
+ querySelectorCount
);
- Glean.cookieBannersClick.querySelectorRunDurationPerWindowFrame.accumulateSamples(
- [querySelectorTimeUS]
+ Glean.cookieBannersClick.querySelectorRunDurationPerWindowFrame.accumulateSingleSample(
+ querySelectorTimeUS
);
}
diff --git a/toolkit/components/cookiebanners/components.conf b/toolkit/components/cookiebanners/components.conf
index adf85f8d2d..744fb75510 100644
--- a/toolkit/components/cookiebanners/components.conf
+++ b/toolkit/components/cookiebanners/components.conf
@@ -36,4 +36,17 @@ Classes = [
'constructor': 'CookieBannerListService',
'processes': ProcessSelector.MAIN_PROCESS_ONLY,
},
+ {
+ 'cid': '{56197e18-d144-45b5-9f77-84102f064462}',
+ 'interfaces': ['nsICookieBannerTelemetryService'],
+ 'headers': ['/toolkit/components/cookiebanners/nsCookieBannerTelemetryService.h'],
+ 'contract_ids': ['@mozilla.org/cookie-banner-telemetry-service;1'],
+ 'type': 'mozilla::nsCookieBannerTelemetryService',
+ 'singleton': True,
+ 'constructor': 'mozilla::nsCookieBannerTelemetryService::GetSingleton',
+ 'categories': {
+ 'profile-after-change': 'nsCookieBannerTelemetryService',
+ },
+ 'processes': ProcessSelector.MAIN_PROCESS_ONLY,
+ },
]
diff --git a/toolkit/components/cookiebanners/cookieBanner.pb.cc b/toolkit/components/cookiebanners/cookieBanner.pb.cc
new file mode 100644
index 0000000000..e415588ce0
--- /dev/null
+++ b/toolkit/components/cookiebanners/cookieBanner.pb.cc
@@ -0,0 +1,983 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: cookieBanner.proto
+
+#include "cookieBanner.pb.h"
+
+#include <algorithm>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+
+PROTOBUF_PRAGMA_INIT_SEG
+
+namespace _pb = ::PROTOBUF_NAMESPACE_ID;
+namespace _pbi = _pb::internal;
+
+namespace mozilla {
+namespace cookieBanner {
+PROTOBUF_CONSTEXPR GoogleSOCSCookie_extraData::GoogleSOCSCookie_extraData(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_._has_bits_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , /*decltype(_impl_.platform_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+ , /*decltype(_impl_.region_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+ , /*decltype(_impl_.unused1_)*/0u
+ , /*decltype(_impl_.unused2_)*/0u} {}
+struct GoogleSOCSCookie_extraDataDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR GoogleSOCSCookie_extraDataDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~GoogleSOCSCookie_extraDataDefaultTypeInternal() {}
+ union {
+ GoogleSOCSCookie_extraData _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 GoogleSOCSCookie_extraDataDefaultTypeInternal _GoogleSOCSCookie_extraData_default_instance_;
+PROTOBUF_CONSTEXPR GoogleSOCSCookie_timeData::GoogleSOCSCookie_timeData(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_._has_bits_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , /*decltype(_impl_.timestamp_)*/uint64_t{0u}} {}
+struct GoogleSOCSCookie_timeDataDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR GoogleSOCSCookie_timeDataDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~GoogleSOCSCookie_timeDataDefaultTypeInternal() {}
+ union {
+ GoogleSOCSCookie_timeData _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 GoogleSOCSCookie_timeDataDefaultTypeInternal _GoogleSOCSCookie_timeData_default_instance_;
+PROTOBUF_CONSTEXPR GoogleSOCSCookie::GoogleSOCSCookie(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_._has_bits_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , /*decltype(_impl_.data_)*/nullptr
+ , /*decltype(_impl_.time_)*/nullptr
+ , /*decltype(_impl_.gdpr_choice_)*/0u} {}
+struct GoogleSOCSCookieDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR GoogleSOCSCookieDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~GoogleSOCSCookieDefaultTypeInternal() {}
+ union {
+ GoogleSOCSCookie _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 GoogleSOCSCookieDefaultTypeInternal _GoogleSOCSCookie_default_instance_;
+} // namespace cookieBanner
+} // namespace mozilla
+namespace mozilla {
+namespace cookieBanner {
+
+// ===================================================================
+
+class GoogleSOCSCookie_extraData::_Internal {
+ public:
+ using HasBits = decltype(std::declval<GoogleSOCSCookie_extraData>()._impl_._has_bits_);
+ static void set_has_unused1(HasBits* has_bits) {
+ (*has_bits)[0] |= 4u;
+ }
+ static void set_has_platform(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+ static void set_has_region(HasBits* has_bits) {
+ (*has_bits)[0] |= 2u;
+ }
+ static void set_has_unused2(HasBits* has_bits) {
+ (*has_bits)[0] |= 8u;
+ }
+ static bool MissingRequiredFields(const HasBits& has_bits) {
+ return ((has_bits[0] & 0x0000000f) ^ 0x0000000f) != 0;
+ }
+};
+
+GoogleSOCSCookie_extraData::GoogleSOCSCookie_extraData(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.cookieBanner.GoogleSOCSCookie.extraData)
+}
+GoogleSOCSCookie_extraData::GoogleSOCSCookie_extraData(const GoogleSOCSCookie_extraData& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ GoogleSOCSCookie_extraData* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){from._impl_._has_bits_}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.platform_){}
+ , decltype(_impl_.region_){}
+ , decltype(_impl_.unused1_){}
+ , decltype(_impl_.unused2_){}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ _impl_.platform_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.platform_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (from._internal_has_platform()) {
+ _this->_impl_.platform_.Set(from._internal_platform(),
+ _this->GetArenaForAllocation());
+ }
+ _impl_.region_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.region_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (from._internal_has_region()) {
+ _this->_impl_.region_.Set(from._internal_region(),
+ _this->GetArenaForAllocation());
+ }
+ ::memcpy(&_impl_.unused1_, &from._impl_.unused1_,
+ static_cast<size_t>(reinterpret_cast<char*>(&_impl_.unused2_) -
+ reinterpret_cast<char*>(&_impl_.unused1_)) + sizeof(_impl_.unused2_));
+ // @@protoc_insertion_point(copy_constructor:mozilla.cookieBanner.GoogleSOCSCookie.extraData)
+}
+
+inline void GoogleSOCSCookie_extraData::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.platform_){}
+ , decltype(_impl_.region_){}
+ , decltype(_impl_.unused1_){0u}
+ , decltype(_impl_.unused2_){0u}
+ };
+ _impl_.platform_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.platform_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.region_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.region_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+GoogleSOCSCookie_extraData::~GoogleSOCSCookie_extraData() {
+ // @@protoc_insertion_point(destructor:mozilla.cookieBanner.GoogleSOCSCookie.extraData)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void GoogleSOCSCookie_extraData::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+ _impl_.platform_.Destroy();
+ _impl_.region_.Destroy();
+}
+
+void GoogleSOCSCookie_extraData::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void GoogleSOCSCookie_extraData::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.cookieBanner.GoogleSOCSCookie.extraData)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ if (cached_has_bits & 0x00000001u) {
+ _impl_.platform_.ClearNonDefaultToEmpty();
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _impl_.region_.ClearNonDefaultToEmpty();
+ }
+ }
+ if (cached_has_bits & 0x0000000cu) {
+ ::memset(&_impl_.unused1_, 0, static_cast<size_t>(
+ reinterpret_cast<char*>(&_impl_.unused2_) -
+ reinterpret_cast<char*>(&_impl_.unused1_)) + sizeof(_impl_.unused2_));
+ }
+ _impl_._has_bits_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* GoogleSOCSCookie_extraData::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // required uint32 unused1 = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+ _Internal::set_has_unused1(&has_bits);
+ _impl_.unused1_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // required string platform = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+ auto str = _internal_mutable_platform();
+ ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // required string region = 3;
+ case 3:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 26)) {
+ auto str = _internal_mutable_region();
+ ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // required uint32 unused2 = 4;
+ case 4:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 32)) {
+ _Internal::set_has_unused2(&has_bits);
+ _impl_.unused2_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ _impl_._has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* GoogleSOCSCookie_extraData::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.cookieBanner.GoogleSOCSCookie.extraData)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ // required uint32 unused1 = 1;
+ if (cached_has_bits & 0x00000004u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteUInt32ToArray(1, this->_internal_unused1(), target);
+ }
+
+ // required string platform = 2;
+ if (cached_has_bits & 0x00000001u) {
+ target = stream->WriteStringMaybeAliased(
+ 2, this->_internal_platform(), target);
+ }
+
+ // required string region = 3;
+ if (cached_has_bits & 0x00000002u) {
+ target = stream->WriteStringMaybeAliased(
+ 3, this->_internal_region(), target);
+ }
+
+ // required uint32 unused2 = 4;
+ if (cached_has_bits & 0x00000008u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteUInt32ToArray(4, this->_internal_unused2(), target);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.cookieBanner.GoogleSOCSCookie.extraData)
+ return target;
+}
+
+size_t GoogleSOCSCookie_extraData::RequiredFieldsByteSizeFallback() const {
+// @@protoc_insertion_point(required_fields_byte_size_fallback_start:mozilla.cookieBanner.GoogleSOCSCookie.extraData)
+ size_t total_size = 0;
+
+ if (_internal_has_platform()) {
+ // required string platform = 2;
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+ this->_internal_platform());
+ }
+
+ if (_internal_has_region()) {
+ // required string region = 3;
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+ this->_internal_region());
+ }
+
+ if (_internal_has_unused1()) {
+ // required uint32 unused1 = 1;
+ total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_unused1());
+ }
+
+ if (_internal_has_unused2()) {
+ // required uint32 unused2 = 4;
+ total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_unused2());
+ }
+
+ return total_size;
+}
+size_t GoogleSOCSCookie_extraData::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.cookieBanner.GoogleSOCSCookie.extraData)
+ size_t total_size = 0;
+
+ if (((_impl_._has_bits_[0] & 0x0000000f) ^ 0x0000000f) == 0) { // All required fields are present.
+ // required string platform = 2;
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+ this->_internal_platform());
+
+ // required string region = 3;
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+ this->_internal_region());
+
+ // required uint32 unused1 = 1;
+ total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_unused1());
+
+ // required uint32 unused2 = 4;
+ total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_unused2());
+
+ } else {
+ total_size += RequiredFieldsByteSizeFallback();
+ }
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void GoogleSOCSCookie_extraData::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const GoogleSOCSCookie_extraData*>(
+ &from));
+}
+
+void GoogleSOCSCookie_extraData::MergeFrom(const GoogleSOCSCookie_extraData& from) {
+ GoogleSOCSCookie_extraData* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.cookieBanner.GoogleSOCSCookie.extraData)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = from._impl_._has_bits_[0];
+ if (cached_has_bits & 0x0000000fu) {
+ if (cached_has_bits & 0x00000001u) {
+ _this->_internal_set_platform(from._internal_platform());
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _this->_internal_set_region(from._internal_region());
+ }
+ if (cached_has_bits & 0x00000004u) {
+ _this->_impl_.unused1_ = from._impl_.unused1_;
+ }
+ if (cached_has_bits & 0x00000008u) {
+ _this->_impl_.unused2_ = from._impl_.unused2_;
+ }
+ _this->_impl_._has_bits_[0] |= cached_has_bits;
+ }
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void GoogleSOCSCookie_extraData::CopyFrom(const GoogleSOCSCookie_extraData& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.cookieBanner.GoogleSOCSCookie.extraData)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool GoogleSOCSCookie_extraData::IsInitialized() const {
+ if (_Internal::MissingRequiredFields(_impl_._has_bits_)) return false;
+ return true;
+}
+
+void GoogleSOCSCookie_extraData::InternalSwap(GoogleSOCSCookie_extraData* other) {
+ using std::swap;
+ auto* lhs_arena = GetArenaForAllocation();
+ auto* rhs_arena = other->GetArenaForAllocation();
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+ &_impl_.platform_, lhs_arena,
+ &other->_impl_.platform_, rhs_arena
+ );
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+ &_impl_.region_, lhs_arena,
+ &other->_impl_.region_, rhs_arena
+ );
+ ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+ PROTOBUF_FIELD_OFFSET(GoogleSOCSCookie_extraData, _impl_.unused2_)
+ + sizeof(GoogleSOCSCookie_extraData::_impl_.unused2_)
+ - PROTOBUF_FIELD_OFFSET(GoogleSOCSCookie_extraData, _impl_.unused1_)>(
+ reinterpret_cast<char*>(&_impl_.unused1_),
+ reinterpret_cast<char*>(&other->_impl_.unused1_));
+}
+
+std::string GoogleSOCSCookie_extraData::GetTypeName() const {
+ return "mozilla.cookieBanner.GoogleSOCSCookie.extraData";
+}
+
+
+// ===================================================================
+
+class GoogleSOCSCookie_timeData::_Internal {
+ public:
+ using HasBits = decltype(std::declval<GoogleSOCSCookie_timeData>()._impl_._has_bits_);
+ static void set_has_timestamp(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+ static bool MissingRequiredFields(const HasBits& has_bits) {
+ return ((has_bits[0] & 0x00000001) ^ 0x00000001) != 0;
+ }
+};
+
+GoogleSOCSCookie_timeData::GoogleSOCSCookie_timeData(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.cookieBanner.GoogleSOCSCookie.timeData)
+}
+GoogleSOCSCookie_timeData::GoogleSOCSCookie_timeData(const GoogleSOCSCookie_timeData& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ GoogleSOCSCookie_timeData* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){from._impl_._has_bits_}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.timestamp_){}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ _this->_impl_.timestamp_ = from._impl_.timestamp_;
+ // @@protoc_insertion_point(copy_constructor:mozilla.cookieBanner.GoogleSOCSCookie.timeData)
+}
+
+inline void GoogleSOCSCookie_timeData::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.timestamp_){uint64_t{0u}}
+ };
+}
+
+GoogleSOCSCookie_timeData::~GoogleSOCSCookie_timeData() {
+ // @@protoc_insertion_point(destructor:mozilla.cookieBanner.GoogleSOCSCookie.timeData)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void GoogleSOCSCookie_timeData::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+}
+
+void GoogleSOCSCookie_timeData::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void GoogleSOCSCookie_timeData::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.cookieBanner.GoogleSOCSCookie.timeData)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ _impl_.timestamp_ = uint64_t{0u};
+ _impl_._has_bits_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* GoogleSOCSCookie_timeData::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // required uint64 timeStamp = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+ _Internal::set_has_timestamp(&has_bits);
+ _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ _impl_._has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* GoogleSOCSCookie_timeData::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.cookieBanner.GoogleSOCSCookie.timeData)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ // required uint64 timeStamp = 1;
+ if (cached_has_bits & 0x00000001u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.cookieBanner.GoogleSOCSCookie.timeData)
+ return target;
+}
+
+size_t GoogleSOCSCookie_timeData::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.cookieBanner.GoogleSOCSCookie.timeData)
+ size_t total_size = 0;
+
+ // required uint64 timeStamp = 1;
+ if (_internal_has_timestamp()) {
+ total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp());
+ }
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void GoogleSOCSCookie_timeData::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const GoogleSOCSCookie_timeData*>(
+ &from));
+}
+
+void GoogleSOCSCookie_timeData::MergeFrom(const GoogleSOCSCookie_timeData& from) {
+ GoogleSOCSCookie_timeData* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.cookieBanner.GoogleSOCSCookie.timeData)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ if (from._internal_has_timestamp()) {
+ _this->_internal_set_timestamp(from._internal_timestamp());
+ }
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void GoogleSOCSCookie_timeData::CopyFrom(const GoogleSOCSCookie_timeData& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.cookieBanner.GoogleSOCSCookie.timeData)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool GoogleSOCSCookie_timeData::IsInitialized() const {
+ if (_Internal::MissingRequiredFields(_impl_._has_bits_)) return false;
+ return true;
+}
+
+void GoogleSOCSCookie_timeData::InternalSwap(GoogleSOCSCookie_timeData* other) {
+ using std::swap;
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+ swap(_impl_.timestamp_, other->_impl_.timestamp_);
+}
+
+std::string GoogleSOCSCookie_timeData::GetTypeName() const {
+ return "mozilla.cookieBanner.GoogleSOCSCookie.timeData";
+}
+
+
+// ===================================================================
+
+class GoogleSOCSCookie::_Internal {
+ public:
+ using HasBits = decltype(std::declval<GoogleSOCSCookie>()._impl_._has_bits_);
+ static void set_has_gdpr_choice(HasBits* has_bits) {
+ (*has_bits)[0] |= 4u;
+ }
+ static const ::mozilla::cookieBanner::GoogleSOCSCookie_extraData& data(const GoogleSOCSCookie* msg);
+ static void set_has_data(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+ static const ::mozilla::cookieBanner::GoogleSOCSCookie_timeData& time(const GoogleSOCSCookie* msg);
+ static void set_has_time(HasBits* has_bits) {
+ (*has_bits)[0] |= 2u;
+ }
+ static bool MissingRequiredFields(const HasBits& has_bits) {
+ return ((has_bits[0] & 0x00000007) ^ 0x00000007) != 0;
+ }
+};
+
+const ::mozilla::cookieBanner::GoogleSOCSCookie_extraData&
+GoogleSOCSCookie::_Internal::data(const GoogleSOCSCookie* msg) {
+ return *msg->_impl_.data_;
+}
+const ::mozilla::cookieBanner::GoogleSOCSCookie_timeData&
+GoogleSOCSCookie::_Internal::time(const GoogleSOCSCookie* msg) {
+ return *msg->_impl_.time_;
+}
+GoogleSOCSCookie::GoogleSOCSCookie(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.cookieBanner.GoogleSOCSCookie)
+}
+GoogleSOCSCookie::GoogleSOCSCookie(const GoogleSOCSCookie& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ GoogleSOCSCookie* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){from._impl_._has_bits_}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.data_){nullptr}
+ , decltype(_impl_.time_){nullptr}
+ , decltype(_impl_.gdpr_choice_){}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ if (from._internal_has_data()) {
+ _this->_impl_.data_ = new ::mozilla::cookieBanner::GoogleSOCSCookie_extraData(*from._impl_.data_);
+ }
+ if (from._internal_has_time()) {
+ _this->_impl_.time_ = new ::mozilla::cookieBanner::GoogleSOCSCookie_timeData(*from._impl_.time_);
+ }
+ _this->_impl_.gdpr_choice_ = from._impl_.gdpr_choice_;
+ // @@protoc_insertion_point(copy_constructor:mozilla.cookieBanner.GoogleSOCSCookie)
+}
+
+inline void GoogleSOCSCookie::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.data_){nullptr}
+ , decltype(_impl_.time_){nullptr}
+ , decltype(_impl_.gdpr_choice_){0u}
+ };
+}
+
+GoogleSOCSCookie::~GoogleSOCSCookie() {
+ // @@protoc_insertion_point(destructor:mozilla.cookieBanner.GoogleSOCSCookie)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void GoogleSOCSCookie::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+ if (this != internal_default_instance()) delete _impl_.data_;
+ if (this != internal_default_instance()) delete _impl_.time_;
+}
+
+void GoogleSOCSCookie::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void GoogleSOCSCookie::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.cookieBanner.GoogleSOCSCookie)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ if (cached_has_bits & 0x00000001u) {
+ GOOGLE_DCHECK(_impl_.data_ != nullptr);
+ _impl_.data_->Clear();
+ }
+ if (cached_has_bits & 0x00000002u) {
+ GOOGLE_DCHECK(_impl_.time_ != nullptr);
+ _impl_.time_->Clear();
+ }
+ }
+ _impl_.gdpr_choice_ = 0u;
+ _impl_._has_bits_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* GoogleSOCSCookie::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // required uint32 gdpr_choice = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+ _Internal::set_has_gdpr_choice(&has_bits);
+ _impl_.gdpr_choice_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // required .mozilla.cookieBanner.GoogleSOCSCookie.extraData data = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+ ptr = ctx->ParseMessage(_internal_mutable_data(), ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // required .mozilla.cookieBanner.GoogleSOCSCookie.timeData time = 3;
+ case 3:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 26)) {
+ ptr = ctx->ParseMessage(_internal_mutable_time(), ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ _impl_._has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* GoogleSOCSCookie::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.cookieBanner.GoogleSOCSCookie)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ // required uint32 gdpr_choice = 1;
+ if (cached_has_bits & 0x00000004u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteUInt32ToArray(1, this->_internal_gdpr_choice(), target);
+ }
+
+ // required .mozilla.cookieBanner.GoogleSOCSCookie.extraData data = 2;
+ if (cached_has_bits & 0x00000001u) {
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(2, _Internal::data(this),
+ _Internal::data(this).GetCachedSize(), target, stream);
+ }
+
+ // required .mozilla.cookieBanner.GoogleSOCSCookie.timeData time = 3;
+ if (cached_has_bits & 0x00000002u) {
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(3, _Internal::time(this),
+ _Internal::time(this).GetCachedSize(), target, stream);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.cookieBanner.GoogleSOCSCookie)
+ return target;
+}
+
+size_t GoogleSOCSCookie::RequiredFieldsByteSizeFallback() const {
+// @@protoc_insertion_point(required_fields_byte_size_fallback_start:mozilla.cookieBanner.GoogleSOCSCookie)
+ size_t total_size = 0;
+
+ if (_internal_has_data()) {
+ // required .mozilla.cookieBanner.GoogleSOCSCookie.extraData data = 2;
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *_impl_.data_);
+ }
+
+ if (_internal_has_time()) {
+ // required .mozilla.cookieBanner.GoogleSOCSCookie.timeData time = 3;
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *_impl_.time_);
+ }
+
+ if (_internal_has_gdpr_choice()) {
+ // required uint32 gdpr_choice = 1;
+ total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_gdpr_choice());
+ }
+
+ return total_size;
+}
+size_t GoogleSOCSCookie::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.cookieBanner.GoogleSOCSCookie)
+ size_t total_size = 0;
+
+ if (((_impl_._has_bits_[0] & 0x00000007) ^ 0x00000007) == 0) { // All required fields are present.
+ // required .mozilla.cookieBanner.GoogleSOCSCookie.extraData data = 2;
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *_impl_.data_);
+
+ // required .mozilla.cookieBanner.GoogleSOCSCookie.timeData time = 3;
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *_impl_.time_);
+
+ // required uint32 gdpr_choice = 1;
+ total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_gdpr_choice());
+
+ } else {
+ total_size += RequiredFieldsByteSizeFallback();
+ }
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void GoogleSOCSCookie::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const GoogleSOCSCookie*>(
+ &from));
+}
+
+void GoogleSOCSCookie::MergeFrom(const GoogleSOCSCookie& from) {
+ GoogleSOCSCookie* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.cookieBanner.GoogleSOCSCookie)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = from._impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000007u) {
+ if (cached_has_bits & 0x00000001u) {
+ _this->_internal_mutable_data()->::mozilla::cookieBanner::GoogleSOCSCookie_extraData::MergeFrom(
+ from._internal_data());
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _this->_internal_mutable_time()->::mozilla::cookieBanner::GoogleSOCSCookie_timeData::MergeFrom(
+ from._internal_time());
+ }
+ if (cached_has_bits & 0x00000004u) {
+ _this->_impl_.gdpr_choice_ = from._impl_.gdpr_choice_;
+ }
+ _this->_impl_._has_bits_[0] |= cached_has_bits;
+ }
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void GoogleSOCSCookie::CopyFrom(const GoogleSOCSCookie& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.cookieBanner.GoogleSOCSCookie)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool GoogleSOCSCookie::IsInitialized() const {
+ if (_Internal::MissingRequiredFields(_impl_._has_bits_)) return false;
+ if (_internal_has_data()) {
+ if (!_impl_.data_->IsInitialized()) return false;
+ }
+ if (_internal_has_time()) {
+ if (!_impl_.time_->IsInitialized()) return false;
+ }
+ return true;
+}
+
+void GoogleSOCSCookie::InternalSwap(GoogleSOCSCookie* other) {
+ using std::swap;
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+ ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+ PROTOBUF_FIELD_OFFSET(GoogleSOCSCookie, _impl_.gdpr_choice_)
+ + sizeof(GoogleSOCSCookie::_impl_.gdpr_choice_)
+ - PROTOBUF_FIELD_OFFSET(GoogleSOCSCookie, _impl_.data_)>(
+ reinterpret_cast<char*>(&_impl_.data_),
+ reinterpret_cast<char*>(&other->_impl_.data_));
+}
+
+std::string GoogleSOCSCookie::GetTypeName() const {
+ return "mozilla.cookieBanner.GoogleSOCSCookie";
+}
+
+
+// @@protoc_insertion_point(namespace_scope)
+} // namespace cookieBanner
+} // namespace mozilla
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_NOINLINE ::mozilla::cookieBanner::GoogleSOCSCookie_extraData*
+Arena::CreateMaybeMessage< ::mozilla::cookieBanner::GoogleSOCSCookie_extraData >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::cookieBanner::GoogleSOCSCookie_extraData >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::cookieBanner::GoogleSOCSCookie_timeData*
+Arena::CreateMaybeMessage< ::mozilla::cookieBanner::GoogleSOCSCookie_timeData >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::cookieBanner::GoogleSOCSCookie_timeData >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::cookieBanner::GoogleSOCSCookie*
+Arena::CreateMaybeMessage< ::mozilla::cookieBanner::GoogleSOCSCookie >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::cookieBanner::GoogleSOCSCookie >(arena);
+}
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+#include <google/protobuf/port_undef.inc>
diff --git a/toolkit/components/cookiebanners/cookieBanner.pb.h b/toolkit/components/cookiebanners/cookieBanner.pb.h
new file mode 100644
index 0000000000..f7bb138489
--- /dev/null
+++ b/toolkit/components/cookiebanners/cookieBanner.pb.h
@@ -0,0 +1,1058 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: cookieBanner.proto
+
+#ifndef GOOGLE_PROTOBUF_INCLUDED_cookieBanner_2eproto
+#define GOOGLE_PROTOBUF_INCLUDED_cookieBanner_2eproto
+
+#include <limits>
+#include <string>
+
+#include <google/protobuf/port_def.inc>
+#if PROTOBUF_VERSION < 3021000
+#error This file was generated by a newer version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please update
+#error your headers.
+#endif
+#if 3021012 < PROTOBUF_MIN_PROTOC_VERSION
+#error This file was generated by an older version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please
+#error regenerate this file with a newer version of protoc.
+#endif
+
+#include <google/protobuf/port_undef.inc>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/metadata_lite.h>
+#include <google/protobuf/message_lite.h>
+#include <google/protobuf/repeated_field.h> // IWYU pragma: export
+#include <google/protobuf/extension_set.h> // IWYU pragma: export
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+#define PROTOBUF_INTERNAL_EXPORT_cookieBanner_2eproto
+PROTOBUF_NAMESPACE_OPEN
+namespace internal {
+class AnyMetadata;
+} // namespace internal
+PROTOBUF_NAMESPACE_CLOSE
+
+// Internal implementation detail -- do not use these members.
+struct TableStruct_cookieBanner_2eproto {
+ static const uint32_t offsets[];
+};
+namespace mozilla {
+namespace cookieBanner {
+class GoogleSOCSCookie;
+struct GoogleSOCSCookieDefaultTypeInternal;
+extern GoogleSOCSCookieDefaultTypeInternal _GoogleSOCSCookie_default_instance_;
+class GoogleSOCSCookie_extraData;
+struct GoogleSOCSCookie_extraDataDefaultTypeInternal;
+extern GoogleSOCSCookie_extraDataDefaultTypeInternal _GoogleSOCSCookie_extraData_default_instance_;
+class GoogleSOCSCookie_timeData;
+struct GoogleSOCSCookie_timeDataDefaultTypeInternal;
+extern GoogleSOCSCookie_timeDataDefaultTypeInternal _GoogleSOCSCookie_timeData_default_instance_;
+} // namespace cookieBanner
+} // namespace mozilla
+PROTOBUF_NAMESPACE_OPEN
+template<> ::mozilla::cookieBanner::GoogleSOCSCookie* Arena::CreateMaybeMessage<::mozilla::cookieBanner::GoogleSOCSCookie>(Arena*);
+template<> ::mozilla::cookieBanner::GoogleSOCSCookie_extraData* Arena::CreateMaybeMessage<::mozilla::cookieBanner::GoogleSOCSCookie_extraData>(Arena*);
+template<> ::mozilla::cookieBanner::GoogleSOCSCookie_timeData* Arena::CreateMaybeMessage<::mozilla::cookieBanner::GoogleSOCSCookie_timeData>(Arena*);
+PROTOBUF_NAMESPACE_CLOSE
+namespace mozilla {
+namespace cookieBanner {
+
+// ===================================================================
+
+class GoogleSOCSCookie_extraData final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.cookieBanner.GoogleSOCSCookie.extraData) */ {
+ public:
+ inline GoogleSOCSCookie_extraData() : GoogleSOCSCookie_extraData(nullptr) {}
+ ~GoogleSOCSCookie_extraData() override;
+ explicit PROTOBUF_CONSTEXPR GoogleSOCSCookie_extraData(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ GoogleSOCSCookie_extraData(const GoogleSOCSCookie_extraData& from);
+ GoogleSOCSCookie_extraData(GoogleSOCSCookie_extraData&& from) noexcept
+ : GoogleSOCSCookie_extraData() {
+ *this = ::std::move(from);
+ }
+
+ inline GoogleSOCSCookie_extraData& operator=(const GoogleSOCSCookie_extraData& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline GoogleSOCSCookie_extraData& operator=(GoogleSOCSCookie_extraData&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const GoogleSOCSCookie_extraData& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const GoogleSOCSCookie_extraData* internal_default_instance() {
+ return reinterpret_cast<const GoogleSOCSCookie_extraData*>(
+ &_GoogleSOCSCookie_extraData_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 0;
+
+ friend void swap(GoogleSOCSCookie_extraData& a, GoogleSOCSCookie_extraData& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(GoogleSOCSCookie_extraData* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(GoogleSOCSCookie_extraData* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ GoogleSOCSCookie_extraData* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<GoogleSOCSCookie_extraData>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const GoogleSOCSCookie_extraData& from);
+ void MergeFrom(const GoogleSOCSCookie_extraData& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(GoogleSOCSCookie_extraData* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.cookieBanner.GoogleSOCSCookie.extraData";
+ }
+ protected:
+ explicit GoogleSOCSCookie_extraData(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kPlatformFieldNumber = 2,
+ kRegionFieldNumber = 3,
+ kUnused1FieldNumber = 1,
+ kUnused2FieldNumber = 4,
+ };
+ // required string platform = 2;
+ bool has_platform() const;
+ private:
+ bool _internal_has_platform() const;
+ public:
+ void clear_platform();
+ const std::string& platform() const;
+ template <typename ArgT0 = const std::string&, typename... ArgT>
+ void set_platform(ArgT0&& arg0, ArgT... args);
+ std::string* mutable_platform();
+ PROTOBUF_NODISCARD std::string* release_platform();
+ void set_allocated_platform(std::string* platform);
+ private:
+ const std::string& _internal_platform() const;
+ inline PROTOBUF_ALWAYS_INLINE void _internal_set_platform(const std::string& value);
+ std::string* _internal_mutable_platform();
+ public:
+
+ // required string region = 3;
+ bool has_region() const;
+ private:
+ bool _internal_has_region() const;
+ public:
+ void clear_region();
+ const std::string& region() const;
+ template <typename ArgT0 = const std::string&, typename... ArgT>
+ void set_region(ArgT0&& arg0, ArgT... args);
+ std::string* mutable_region();
+ PROTOBUF_NODISCARD std::string* release_region();
+ void set_allocated_region(std::string* region);
+ private:
+ const std::string& _internal_region() const;
+ inline PROTOBUF_ALWAYS_INLINE void _internal_set_region(const std::string& value);
+ std::string* _internal_mutable_region();
+ public:
+
+ // required uint32 unused1 = 1;
+ bool has_unused1() const;
+ private:
+ bool _internal_has_unused1() const;
+ public:
+ void clear_unused1();
+ uint32_t unused1() const;
+ void set_unused1(uint32_t value);
+ private:
+ uint32_t _internal_unused1() const;
+ void _internal_set_unused1(uint32_t value);
+ public:
+
+ // required uint32 unused2 = 4;
+ bool has_unused2() const;
+ private:
+ bool _internal_has_unused2() const;
+ public:
+ void clear_unused2();
+ uint32_t unused2() const;
+ void set_unused2(uint32_t value);
+ private:
+ uint32_t _internal_unused2() const;
+ void _internal_set_unused2(uint32_t value);
+ public:
+
+ // @@protoc_insertion_point(class_scope:mozilla.cookieBanner.GoogleSOCSCookie.extraData)
+ private:
+ class _Internal;
+
+ // helper for ByteSizeLong()
+ size_t RequiredFieldsByteSizeFallback() const;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr platform_;
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr region_;
+ uint32_t unused1_;
+ uint32_t unused2_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_cookieBanner_2eproto;
+};
+// -------------------------------------------------------------------
+
+class GoogleSOCSCookie_timeData final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.cookieBanner.GoogleSOCSCookie.timeData) */ {
+ public:
+ inline GoogleSOCSCookie_timeData() : GoogleSOCSCookie_timeData(nullptr) {}
+ ~GoogleSOCSCookie_timeData() override;
+ explicit PROTOBUF_CONSTEXPR GoogleSOCSCookie_timeData(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ GoogleSOCSCookie_timeData(const GoogleSOCSCookie_timeData& from);
+ GoogleSOCSCookie_timeData(GoogleSOCSCookie_timeData&& from) noexcept
+ : GoogleSOCSCookie_timeData() {
+ *this = ::std::move(from);
+ }
+
+ inline GoogleSOCSCookie_timeData& operator=(const GoogleSOCSCookie_timeData& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline GoogleSOCSCookie_timeData& operator=(GoogleSOCSCookie_timeData&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const GoogleSOCSCookie_timeData& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const GoogleSOCSCookie_timeData* internal_default_instance() {
+ return reinterpret_cast<const GoogleSOCSCookie_timeData*>(
+ &_GoogleSOCSCookie_timeData_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 1;
+
+ friend void swap(GoogleSOCSCookie_timeData& a, GoogleSOCSCookie_timeData& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(GoogleSOCSCookie_timeData* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(GoogleSOCSCookie_timeData* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ GoogleSOCSCookie_timeData* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<GoogleSOCSCookie_timeData>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const GoogleSOCSCookie_timeData& from);
+ void MergeFrom(const GoogleSOCSCookie_timeData& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(GoogleSOCSCookie_timeData* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.cookieBanner.GoogleSOCSCookie.timeData";
+ }
+ protected:
+ explicit GoogleSOCSCookie_timeData(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kTimeStampFieldNumber = 1,
+ };
+ // required uint64 timeStamp = 1;
+ bool has_timestamp() const;
+ private:
+ bool _internal_has_timestamp() const;
+ public:
+ void clear_timestamp();
+ uint64_t timestamp() const;
+ void set_timestamp(uint64_t value);
+ private:
+ uint64_t _internal_timestamp() const;
+ void _internal_set_timestamp(uint64_t value);
+ public:
+
+ // @@protoc_insertion_point(class_scope:mozilla.cookieBanner.GoogleSOCSCookie.timeData)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ uint64_t timestamp_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_cookieBanner_2eproto;
+};
+// -------------------------------------------------------------------
+
+class GoogleSOCSCookie final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.cookieBanner.GoogleSOCSCookie) */ {
+ public:
+ inline GoogleSOCSCookie() : GoogleSOCSCookie(nullptr) {}
+ ~GoogleSOCSCookie() override;
+ explicit PROTOBUF_CONSTEXPR GoogleSOCSCookie(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ GoogleSOCSCookie(const GoogleSOCSCookie& from);
+ GoogleSOCSCookie(GoogleSOCSCookie&& from) noexcept
+ : GoogleSOCSCookie() {
+ *this = ::std::move(from);
+ }
+
+ inline GoogleSOCSCookie& operator=(const GoogleSOCSCookie& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline GoogleSOCSCookie& operator=(GoogleSOCSCookie&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const GoogleSOCSCookie& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const GoogleSOCSCookie* internal_default_instance() {
+ return reinterpret_cast<const GoogleSOCSCookie*>(
+ &_GoogleSOCSCookie_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 2;
+
+ friend void swap(GoogleSOCSCookie& a, GoogleSOCSCookie& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(GoogleSOCSCookie* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(GoogleSOCSCookie* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ GoogleSOCSCookie* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<GoogleSOCSCookie>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const GoogleSOCSCookie& from);
+ void MergeFrom(const GoogleSOCSCookie& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(GoogleSOCSCookie* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.cookieBanner.GoogleSOCSCookie";
+ }
+ protected:
+ explicit GoogleSOCSCookie(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ typedef GoogleSOCSCookie_extraData extraData;
+ typedef GoogleSOCSCookie_timeData timeData;
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kDataFieldNumber = 2,
+ kTimeFieldNumber = 3,
+ kGdprChoiceFieldNumber = 1,
+ };
+ // required .mozilla.cookieBanner.GoogleSOCSCookie.extraData data = 2;
+ bool has_data() const;
+ private:
+ bool _internal_has_data() const;
+ public:
+ void clear_data();
+ const ::mozilla::cookieBanner::GoogleSOCSCookie_extraData& data() const;
+ PROTOBUF_NODISCARD ::mozilla::cookieBanner::GoogleSOCSCookie_extraData* release_data();
+ ::mozilla::cookieBanner::GoogleSOCSCookie_extraData* mutable_data();
+ void set_allocated_data(::mozilla::cookieBanner::GoogleSOCSCookie_extraData* data);
+ private:
+ const ::mozilla::cookieBanner::GoogleSOCSCookie_extraData& _internal_data() const;
+ ::mozilla::cookieBanner::GoogleSOCSCookie_extraData* _internal_mutable_data();
+ public:
+ void unsafe_arena_set_allocated_data(
+ ::mozilla::cookieBanner::GoogleSOCSCookie_extraData* data);
+ ::mozilla::cookieBanner::GoogleSOCSCookie_extraData* unsafe_arena_release_data();
+
+ // required .mozilla.cookieBanner.GoogleSOCSCookie.timeData time = 3;
+ bool has_time() const;
+ private:
+ bool _internal_has_time() const;
+ public:
+ void clear_time();
+ const ::mozilla::cookieBanner::GoogleSOCSCookie_timeData& time() const;
+ PROTOBUF_NODISCARD ::mozilla::cookieBanner::GoogleSOCSCookie_timeData* release_time();
+ ::mozilla::cookieBanner::GoogleSOCSCookie_timeData* mutable_time();
+ void set_allocated_time(::mozilla::cookieBanner::GoogleSOCSCookie_timeData* time);
+ private:
+ const ::mozilla::cookieBanner::GoogleSOCSCookie_timeData& _internal_time() const;
+ ::mozilla::cookieBanner::GoogleSOCSCookie_timeData* _internal_mutable_time();
+ public:
+ void unsafe_arena_set_allocated_time(
+ ::mozilla::cookieBanner::GoogleSOCSCookie_timeData* time);
+ ::mozilla::cookieBanner::GoogleSOCSCookie_timeData* unsafe_arena_release_time();
+
+ // required uint32 gdpr_choice = 1;
+ bool has_gdpr_choice() const;
+ private:
+ bool _internal_has_gdpr_choice() const;
+ public:
+ void clear_gdpr_choice();
+ uint32_t gdpr_choice() const;
+ void set_gdpr_choice(uint32_t value);
+ private:
+ uint32_t _internal_gdpr_choice() const;
+ void _internal_set_gdpr_choice(uint32_t value);
+ public:
+
+ // @@protoc_insertion_point(class_scope:mozilla.cookieBanner.GoogleSOCSCookie)
+ private:
+ class _Internal;
+
+ // helper for ByteSizeLong()
+ size_t RequiredFieldsByteSizeFallback() const;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::mozilla::cookieBanner::GoogleSOCSCookie_extraData* data_;
+ ::mozilla::cookieBanner::GoogleSOCSCookie_timeData* time_;
+ uint32_t gdpr_choice_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_cookieBanner_2eproto;
+};
+// ===================================================================
+
+
+// ===================================================================
+
+#ifdef __GNUC__
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#endif // __GNUC__
+// GoogleSOCSCookie_extraData
+
+// required uint32 unused1 = 1;
+inline bool GoogleSOCSCookie_extraData::_internal_has_unused1() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000004u) != 0;
+ return value;
+}
+inline bool GoogleSOCSCookie_extraData::has_unused1() const {
+ return _internal_has_unused1();
+}
+inline void GoogleSOCSCookie_extraData::clear_unused1() {
+ _impl_.unused1_ = 0u;
+ _impl_._has_bits_[0] &= ~0x00000004u;
+}
+inline uint32_t GoogleSOCSCookie_extraData::_internal_unused1() const {
+ return _impl_.unused1_;
+}
+inline uint32_t GoogleSOCSCookie_extraData::unused1() const {
+ // @@protoc_insertion_point(field_get:mozilla.cookieBanner.GoogleSOCSCookie.extraData.unused1)
+ return _internal_unused1();
+}
+inline void GoogleSOCSCookie_extraData::_internal_set_unused1(uint32_t value) {
+ _impl_._has_bits_[0] |= 0x00000004u;
+ _impl_.unused1_ = value;
+}
+inline void GoogleSOCSCookie_extraData::set_unused1(uint32_t value) {
+ _internal_set_unused1(value);
+ // @@protoc_insertion_point(field_set:mozilla.cookieBanner.GoogleSOCSCookie.extraData.unused1)
+}
+
+// required string platform = 2;
+inline bool GoogleSOCSCookie_extraData::_internal_has_platform() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+ return value;
+}
+inline bool GoogleSOCSCookie_extraData::has_platform() const {
+ return _internal_has_platform();
+}
+inline void GoogleSOCSCookie_extraData::clear_platform() {
+ _impl_.platform_.ClearToEmpty();
+ _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& GoogleSOCSCookie_extraData::platform() const {
+ // @@protoc_insertion_point(field_get:mozilla.cookieBanner.GoogleSOCSCookie.extraData.platform)
+ return _internal_platform();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void GoogleSOCSCookie_extraData::set_platform(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.platform_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+ // @@protoc_insertion_point(field_set:mozilla.cookieBanner.GoogleSOCSCookie.extraData.platform)
+}
+inline std::string* GoogleSOCSCookie_extraData::mutable_platform() {
+ std::string* _s = _internal_mutable_platform();
+ // @@protoc_insertion_point(field_mutable:mozilla.cookieBanner.GoogleSOCSCookie.extraData.platform)
+ return _s;
+}
+inline const std::string& GoogleSOCSCookie_extraData::_internal_platform() const {
+ return _impl_.platform_.Get();
+}
+inline void GoogleSOCSCookie_extraData::_internal_set_platform(const std::string& value) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.platform_.Set(value, GetArenaForAllocation());
+}
+inline std::string* GoogleSOCSCookie_extraData::_internal_mutable_platform() {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ return _impl_.platform_.Mutable(GetArenaForAllocation());
+}
+inline std::string* GoogleSOCSCookie_extraData::release_platform() {
+ // @@protoc_insertion_point(field_release:mozilla.cookieBanner.GoogleSOCSCookie.extraData.platform)
+ if (!_internal_has_platform()) {
+ return nullptr;
+ }
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ auto* p = _impl_.platform_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.platform_.IsDefault()) {
+ _impl_.platform_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ return p;
+}
+inline void GoogleSOCSCookie_extraData::set_allocated_platform(std::string* platform) {
+ if (platform != nullptr) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ _impl_.platform_.SetAllocated(platform, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.platform_.IsDefault()) {
+ _impl_.platform_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ // @@protoc_insertion_point(field_set_allocated:mozilla.cookieBanner.GoogleSOCSCookie.extraData.platform)
+}
+
+// required string region = 3;
+inline bool GoogleSOCSCookie_extraData::_internal_has_region() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+ return value;
+}
+inline bool GoogleSOCSCookie_extraData::has_region() const {
+ return _internal_has_region();
+}
+inline void GoogleSOCSCookie_extraData::clear_region() {
+ _impl_.region_.ClearToEmpty();
+ _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline const std::string& GoogleSOCSCookie_extraData::region() const {
+ // @@protoc_insertion_point(field_get:mozilla.cookieBanner.GoogleSOCSCookie.extraData.region)
+ return _internal_region();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void GoogleSOCSCookie_extraData::set_region(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ _impl_.region_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+ // @@protoc_insertion_point(field_set:mozilla.cookieBanner.GoogleSOCSCookie.extraData.region)
+}
+inline std::string* GoogleSOCSCookie_extraData::mutable_region() {
+ std::string* _s = _internal_mutable_region();
+ // @@protoc_insertion_point(field_mutable:mozilla.cookieBanner.GoogleSOCSCookie.extraData.region)
+ return _s;
+}
+inline const std::string& GoogleSOCSCookie_extraData::_internal_region() const {
+ return _impl_.region_.Get();
+}
+inline void GoogleSOCSCookie_extraData::_internal_set_region(const std::string& value) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ _impl_.region_.Set(value, GetArenaForAllocation());
+}
+inline std::string* GoogleSOCSCookie_extraData::_internal_mutable_region() {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ return _impl_.region_.Mutable(GetArenaForAllocation());
+}
+inline std::string* GoogleSOCSCookie_extraData::release_region() {
+ // @@protoc_insertion_point(field_release:mozilla.cookieBanner.GoogleSOCSCookie.extraData.region)
+ if (!_internal_has_region()) {
+ return nullptr;
+ }
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ auto* p = _impl_.region_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.region_.IsDefault()) {
+ _impl_.region_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ return p;
+}
+inline void GoogleSOCSCookie_extraData::set_allocated_region(std::string* region) {
+ if (region != nullptr) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ }
+ _impl_.region_.SetAllocated(region, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.region_.IsDefault()) {
+ _impl_.region_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ // @@protoc_insertion_point(field_set_allocated:mozilla.cookieBanner.GoogleSOCSCookie.extraData.region)
+}
+
+// required uint32 unused2 = 4;
+inline bool GoogleSOCSCookie_extraData::_internal_has_unused2() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000008u) != 0;
+ return value;
+}
+inline bool GoogleSOCSCookie_extraData::has_unused2() const {
+ return _internal_has_unused2();
+}
+inline void GoogleSOCSCookie_extraData::clear_unused2() {
+ _impl_.unused2_ = 0u;
+ _impl_._has_bits_[0] &= ~0x00000008u;
+}
+inline uint32_t GoogleSOCSCookie_extraData::_internal_unused2() const {
+ return _impl_.unused2_;
+}
+inline uint32_t GoogleSOCSCookie_extraData::unused2() const {
+ // @@protoc_insertion_point(field_get:mozilla.cookieBanner.GoogleSOCSCookie.extraData.unused2)
+ return _internal_unused2();
+}
+inline void GoogleSOCSCookie_extraData::_internal_set_unused2(uint32_t value) {
+ _impl_._has_bits_[0] |= 0x00000008u;
+ _impl_.unused2_ = value;
+}
+inline void GoogleSOCSCookie_extraData::set_unused2(uint32_t value) {
+ _internal_set_unused2(value);
+ // @@protoc_insertion_point(field_set:mozilla.cookieBanner.GoogleSOCSCookie.extraData.unused2)
+}
+
+// -------------------------------------------------------------------
+
+// GoogleSOCSCookie_timeData
+
+// required uint64 timeStamp = 1;
+inline bool GoogleSOCSCookie_timeData::_internal_has_timestamp() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+ return value;
+}
+inline bool GoogleSOCSCookie_timeData::has_timestamp() const {
+ return _internal_has_timestamp();
+}
+inline void GoogleSOCSCookie_timeData::clear_timestamp() {
+ _impl_.timestamp_ = uint64_t{0u};
+ _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline uint64_t GoogleSOCSCookie_timeData::_internal_timestamp() const {
+ return _impl_.timestamp_;
+}
+inline uint64_t GoogleSOCSCookie_timeData::timestamp() const {
+ // @@protoc_insertion_point(field_get:mozilla.cookieBanner.GoogleSOCSCookie.timeData.timeStamp)
+ return _internal_timestamp();
+}
+inline void GoogleSOCSCookie_timeData::_internal_set_timestamp(uint64_t value) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.timestamp_ = value;
+}
+inline void GoogleSOCSCookie_timeData::set_timestamp(uint64_t value) {
+ _internal_set_timestamp(value);
+ // @@protoc_insertion_point(field_set:mozilla.cookieBanner.GoogleSOCSCookie.timeData.timeStamp)
+}
+
+// -------------------------------------------------------------------
+
+// GoogleSOCSCookie
+
+// required uint32 gdpr_choice = 1;
+inline bool GoogleSOCSCookie::_internal_has_gdpr_choice() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000004u) != 0;
+ return value;
+}
+inline bool GoogleSOCSCookie::has_gdpr_choice() const {
+ return _internal_has_gdpr_choice();
+}
+inline void GoogleSOCSCookie::clear_gdpr_choice() {
+ _impl_.gdpr_choice_ = 0u;
+ _impl_._has_bits_[0] &= ~0x00000004u;
+}
+inline uint32_t GoogleSOCSCookie::_internal_gdpr_choice() const {
+ return _impl_.gdpr_choice_;
+}
+inline uint32_t GoogleSOCSCookie::gdpr_choice() const {
+ // @@protoc_insertion_point(field_get:mozilla.cookieBanner.GoogleSOCSCookie.gdpr_choice)
+ return _internal_gdpr_choice();
+}
+inline void GoogleSOCSCookie::_internal_set_gdpr_choice(uint32_t value) {
+ _impl_._has_bits_[0] |= 0x00000004u;
+ _impl_.gdpr_choice_ = value;
+}
+inline void GoogleSOCSCookie::set_gdpr_choice(uint32_t value) {
+ _internal_set_gdpr_choice(value);
+ // @@protoc_insertion_point(field_set:mozilla.cookieBanner.GoogleSOCSCookie.gdpr_choice)
+}
+
+// required .mozilla.cookieBanner.GoogleSOCSCookie.extraData data = 2;
+inline bool GoogleSOCSCookie::_internal_has_data() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+ PROTOBUF_ASSUME(!value || _impl_.data_ != nullptr);
+ return value;
+}
+inline bool GoogleSOCSCookie::has_data() const {
+ return _internal_has_data();
+}
+inline void GoogleSOCSCookie::clear_data() {
+ if (_impl_.data_ != nullptr) _impl_.data_->Clear();
+ _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const ::mozilla::cookieBanner::GoogleSOCSCookie_extraData& GoogleSOCSCookie::_internal_data() const {
+ const ::mozilla::cookieBanner::GoogleSOCSCookie_extraData* p = _impl_.data_;
+ return p != nullptr ? *p : reinterpret_cast<const ::mozilla::cookieBanner::GoogleSOCSCookie_extraData&>(
+ ::mozilla::cookieBanner::_GoogleSOCSCookie_extraData_default_instance_);
+}
+inline const ::mozilla::cookieBanner::GoogleSOCSCookie_extraData& GoogleSOCSCookie::data() const {
+ // @@protoc_insertion_point(field_get:mozilla.cookieBanner.GoogleSOCSCookie.data)
+ return _internal_data();
+}
+inline void GoogleSOCSCookie::unsafe_arena_set_allocated_data(
+ ::mozilla::cookieBanner::GoogleSOCSCookie_extraData* data) {
+ if (GetArenaForAllocation() == nullptr) {
+ delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.data_);
+ }
+ _impl_.data_ = data;
+ if (data) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mozilla.cookieBanner.GoogleSOCSCookie.data)
+}
+inline ::mozilla::cookieBanner::GoogleSOCSCookie_extraData* GoogleSOCSCookie::release_data() {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ ::mozilla::cookieBanner::GoogleSOCSCookie_extraData* temp = _impl_.data_;
+ _impl_.data_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+ auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ if (GetArenaForAllocation() == nullptr) { delete old; }
+#else // PROTOBUF_FORCE_COPY_IN_RELEASE
+ if (GetArenaForAllocation() != nullptr) {
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ }
+#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE
+ return temp;
+}
+inline ::mozilla::cookieBanner::GoogleSOCSCookie_extraData* GoogleSOCSCookie::unsafe_arena_release_data() {
+ // @@protoc_insertion_point(field_release:mozilla.cookieBanner.GoogleSOCSCookie.data)
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ ::mozilla::cookieBanner::GoogleSOCSCookie_extraData* temp = _impl_.data_;
+ _impl_.data_ = nullptr;
+ return temp;
+}
+inline ::mozilla::cookieBanner::GoogleSOCSCookie_extraData* GoogleSOCSCookie::_internal_mutable_data() {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ if (_impl_.data_ == nullptr) {
+ auto* p = CreateMaybeMessage<::mozilla::cookieBanner::GoogleSOCSCookie_extraData>(GetArenaForAllocation());
+ _impl_.data_ = p;
+ }
+ return _impl_.data_;
+}
+inline ::mozilla::cookieBanner::GoogleSOCSCookie_extraData* GoogleSOCSCookie::mutable_data() {
+ ::mozilla::cookieBanner::GoogleSOCSCookie_extraData* _msg = _internal_mutable_data();
+ // @@protoc_insertion_point(field_mutable:mozilla.cookieBanner.GoogleSOCSCookie.data)
+ return _msg;
+}
+inline void GoogleSOCSCookie::set_allocated_data(::mozilla::cookieBanner::GoogleSOCSCookie_extraData* data) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+ if (message_arena == nullptr) {
+ delete _impl_.data_;
+ }
+ if (data) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+ ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(data);
+ if (message_arena != submessage_arena) {
+ data = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, data, submessage_arena);
+ }
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ _impl_.data_ = data;
+ // @@protoc_insertion_point(field_set_allocated:mozilla.cookieBanner.GoogleSOCSCookie.data)
+}
+
+// required .mozilla.cookieBanner.GoogleSOCSCookie.timeData time = 3;
+inline bool GoogleSOCSCookie::_internal_has_time() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+ PROTOBUF_ASSUME(!value || _impl_.time_ != nullptr);
+ return value;
+}
+inline bool GoogleSOCSCookie::has_time() const {
+ return _internal_has_time();
+}
+inline void GoogleSOCSCookie::clear_time() {
+ if (_impl_.time_ != nullptr) _impl_.time_->Clear();
+ _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline const ::mozilla::cookieBanner::GoogleSOCSCookie_timeData& GoogleSOCSCookie::_internal_time() const {
+ const ::mozilla::cookieBanner::GoogleSOCSCookie_timeData* p = _impl_.time_;
+ return p != nullptr ? *p : reinterpret_cast<const ::mozilla::cookieBanner::GoogleSOCSCookie_timeData&>(
+ ::mozilla::cookieBanner::_GoogleSOCSCookie_timeData_default_instance_);
+}
+inline const ::mozilla::cookieBanner::GoogleSOCSCookie_timeData& GoogleSOCSCookie::time() const {
+ // @@protoc_insertion_point(field_get:mozilla.cookieBanner.GoogleSOCSCookie.time)
+ return _internal_time();
+}
+inline void GoogleSOCSCookie::unsafe_arena_set_allocated_time(
+ ::mozilla::cookieBanner::GoogleSOCSCookie_timeData* time) {
+ if (GetArenaForAllocation() == nullptr) {
+ delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.time_);
+ }
+ _impl_.time_ = time;
+ if (time) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ }
+ // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mozilla.cookieBanner.GoogleSOCSCookie.time)
+}
+inline ::mozilla::cookieBanner::GoogleSOCSCookie_timeData* GoogleSOCSCookie::release_time() {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ ::mozilla::cookieBanner::GoogleSOCSCookie_timeData* temp = _impl_.time_;
+ _impl_.time_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+ auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ if (GetArenaForAllocation() == nullptr) { delete old; }
+#else // PROTOBUF_FORCE_COPY_IN_RELEASE
+ if (GetArenaForAllocation() != nullptr) {
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ }
+#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE
+ return temp;
+}
+inline ::mozilla::cookieBanner::GoogleSOCSCookie_timeData* GoogleSOCSCookie::unsafe_arena_release_time() {
+ // @@protoc_insertion_point(field_release:mozilla.cookieBanner.GoogleSOCSCookie.time)
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ ::mozilla::cookieBanner::GoogleSOCSCookie_timeData* temp = _impl_.time_;
+ _impl_.time_ = nullptr;
+ return temp;
+}
+inline ::mozilla::cookieBanner::GoogleSOCSCookie_timeData* GoogleSOCSCookie::_internal_mutable_time() {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ if (_impl_.time_ == nullptr) {
+ auto* p = CreateMaybeMessage<::mozilla::cookieBanner::GoogleSOCSCookie_timeData>(GetArenaForAllocation());
+ _impl_.time_ = p;
+ }
+ return _impl_.time_;
+}
+inline ::mozilla::cookieBanner::GoogleSOCSCookie_timeData* GoogleSOCSCookie::mutable_time() {
+ ::mozilla::cookieBanner::GoogleSOCSCookie_timeData* _msg = _internal_mutable_time();
+ // @@protoc_insertion_point(field_mutable:mozilla.cookieBanner.GoogleSOCSCookie.time)
+ return _msg;
+}
+inline void GoogleSOCSCookie::set_allocated_time(::mozilla::cookieBanner::GoogleSOCSCookie_timeData* time) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+ if (message_arena == nullptr) {
+ delete _impl_.time_;
+ }
+ if (time) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+ ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(time);
+ if (message_arena != submessage_arena) {
+ time = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, time, submessage_arena);
+ }
+ _impl_._has_bits_[0] |= 0x00000002u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ }
+ _impl_.time_ = time;
+ // @@protoc_insertion_point(field_set_allocated:mozilla.cookieBanner.GoogleSOCSCookie.time)
+}
+
+#ifdef __GNUC__
+ #pragma GCC diagnostic pop
+#endif // __GNUC__
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+
+// @@protoc_insertion_point(namespace_scope)
+
+} // namespace cookieBanner
+} // namespace mozilla
+
+// @@protoc_insertion_point(global_scope)
+
+#include <google/protobuf/port_undef.inc>
+#endif // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_cookieBanner_2eproto
diff --git a/toolkit/components/cookiebanners/cookieBanner.proto b/toolkit/components/cookiebanners/cookieBanner.proto
new file mode 100644
index 0000000000..ae20fed484
--- /dev/null
+++ b/toolkit/components/cookiebanners/cookieBanner.proto
@@ -0,0 +1,25 @@
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package mozilla.cookieBanner;
+
+// Represent the SOCS cookie used to store GDPR choice on the google search.
+message GoogleSOCSCookie {
+ // The GDPR choice. 1 means reject All and 2 means Accept or Custom.
+ required uint32 gdpr_choice = 1;
+ message extraData {
+ required uint32 unused1 = 1;
+ // The value Represents where does the consent is set. We will use this
+ // value to differentiate Accept and Custom.
+ required string platform = 2;
+ required string region = 3;
+ required uint32 unused2 = 4;
+ }
+ required extraData data = 2;
+
+ message timeData {
+ required uint64 timeStamp = 1;
+ }
+ required timeData time = 3;
+}
diff --git a/toolkit/components/cookiebanners/metrics.yaml b/toolkit/components/cookiebanners/metrics.yaml
index 8dabab39f3..98f86fd4a7 100644
--- a/toolkit/components/cookiebanners/metrics.yaml
+++ b/toolkit/components/cookiebanners/metrics.yaml
@@ -210,6 +210,72 @@ cookie.banners:
- pbz@mozilla.com
- tihuang@mozilla.com
expires: 128
+ google_gdpr_choice_cookie:
+ type: labeled_string
+ description: >
+ Records the GDPR choice on Google Search based on the "SOCS" cookie of the
+ Google Search domains. The value could be "Accept", "Reject" or "Custom".
+ We use the label to record different choices on different Google domains.
+ We only collect this if the default search engine is Google.
+ bugs:
+ - https://bugzilla.mozilla.org/1874741
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1874741#c10
+ data_sensitivity:
+ - stored_content
+ notification_emails:
+ - pbz@mozilla.com
+ - tihuang@mozilla.com
+ expires: never
+ google_gdpr_choice_cookie_event:
+ type: event
+ description: >
+ Recorded whenever a GDPR choice is made on a Google Search page. We assess
+ the "SOCS" cookie to know the GDPR choice.
+ bugs:
+ - https://bugzilla.mozilla.org/1874741
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1874741#c10
+ data_sensitivity:
+ - stored_content
+ notification_emails:
+ - pbz@mozilla.com
+ - tihuang@mozilla.com
+ expires: never
+ extra_keys:
+ search_domain:
+ description: The Google search domain where the GDPR choice was made.
+ type: string
+ choice:
+ description: >
+ The GDPR choice. The value could be "Accept", "Reject" or "Custom".
+ type: string
+ region:
+ description: >
+ The region where the GDPR consent is made. This is based on the IP
+ location.
+ type: string
+ google_gdpr_choice_cookie_event_pbm:
+ type: event
+ description: >
+ Recorded whenever a GDPR choice is made on a Google Search page on private
+ browsing windows. We assess the "SOCS" cookie to know the GDPR choice.
+ bugs:
+ - https://bugzilla.mozilla.org/1874741
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1874741#c10
+ data_sensitivity:
+ - stored_content
+ notification_emails:
+ - pbz@mozilla.com
+ - tihuang@mozilla.com
+ expires: never
+ extra_keys:
+ choice:
+ description: >
+ The GDPR choice. The value could be "Accept", "Reject" or "Custom".
+ type: string
+
cookie.banners.click:
handle_duration:
type: timing_distribution
diff --git a/toolkit/components/cookiebanners/moz.build b/toolkit/components/cookiebanners/moz.build
index 774c1dd96f..7dfbdb2c6b 100644
--- a/toolkit/components/cookiebanners/moz.build
+++ b/toolkit/components/cookiebanners/moz.build
@@ -14,11 +14,15 @@ XPIDL_SOURCES += [
"nsICookieBannerListService.idl",
"nsICookieBannerRule.idl",
"nsICookieBannerService.idl",
+ "nsICookieBannerTelemetryService.idl",
"nsICookieRule.idl",
]
XPIDL_MODULE = "toolkit_cookiebanners"
+DEFINES["GOOGLE_PROTOBUF_NO_RTTI"] = True
+DEFINES["GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER"] = True
+
EXTRA_JS_MODULES += [
"CookieBannerListService.sys.mjs",
]
@@ -31,15 +35,18 @@ EXPORTS.mozilla += [
"nsClickRule.h",
"nsCookieBannerRule.h",
"nsCookieBannerService.h",
+ "nsCookieBannerTelemetryService.h",
"nsCookieInjector.h",
"nsCookieRule.h",
]
UNIFIED_SOURCES += [
+ "cookieBanner.pb.cc",
"CookieBannerDomainPrefService.cpp",
"nsClickRule.cpp",
"nsCookieBannerRule.cpp",
"nsCookieBannerService.cpp",
+ "nsCookieBannerTelemetryService.cpp",
"nsCookieInjector.cpp",
"nsCookieRule.cpp",
]
diff --git a/toolkit/components/cookiebanners/nsCookieBannerTelemetryService.cpp b/toolkit/components/cookiebanners/nsCookieBannerTelemetryService.cpp
new file mode 100644
index 0000000000..23906650f4
--- /dev/null
+++ b/toolkit/components/cookiebanners/nsCookieBannerTelemetryService.cpp
@@ -0,0 +1,392 @@
+/* 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/. */
+
+#include "nsCookieBannerTelemetryService.h"
+
+#include "cookieBanner.pb.h"
+
+#include "mozilla/Base64.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/glean/GleanMetrics.h"
+#include "mozilla/OriginAttributes.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Logging.h"
+#include "Cookie.h"
+#include "nsCRT.h"
+#include "nsError.h"
+#include "nsICookie.h"
+#include "nsICookieManager.h"
+#include "nsICookieNotification.h"
+#include "nsTHashSet.h"
+#include "nsIObserverService.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsISearchService.h"
+#include "nsServiceManagerUtils.h"
+#include "nsStringFwd.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+
+NS_IMPL_ISUPPORTS(nsCookieBannerTelemetryService,
+ nsICookieBannerTelemetryService, nsIObserver)
+
+static LazyLogModule gCookieBannerTelemetryLog(
+ "nsCookieBannerTelemetryService");
+
+static StaticRefPtr<nsCookieBannerTelemetryService>
+ sCookieBannerTelemetryServiceSingleton;
+
+// A hash set used to tell whether a base domain is a Google domain.
+static StaticAutoPtr<nsTHashSet<nsCString>> sGoogleDomainsSet;
+
+namespace {
+
+// A helper function that decodes Google's SOCS cookie and returns the GDPR
+// choice and the region. The choice and be either "Accept", "Reject", or
+// "Custom".
+nsresult DecodeSOCSGoogleCookie(const nsACString& aCookie, nsACString& aChoice,
+ nsACString& aRegion) {
+ aChoice.Truncate();
+ aRegion.Truncate();
+
+ FallibleTArray<uint8_t> decoded;
+ nsresult rv =
+ Base64URLDecode(aCookie, Base64URLDecodePaddingPolicy::Ignore, decoded);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ std::string buf(reinterpret_cast<const char*>(decoded.Elements()),
+ decoded.Length());
+ cookieBanner::GoogleSOCSCookie socs;
+
+ if (!socs.ParseFromString(buf)) {
+ return NS_ERROR_CANNOT_CONVERT_DATA;
+ }
+
+ aRegion.Assign(socs.data().region().c_str(), socs.data().region().length());
+
+ // The first field represents the gdpr choice, 1 means "Reject" and 2 means
+ // either "Accept" or "Custom". We need to check a following field to decide.
+ if (socs.gdpr_choice() == 1) {
+ aChoice.AssignLiteral("Reject");
+ return NS_OK;
+ }
+
+ // The platform field represents where does this GDPR consent is selected. The
+ // value can be either "gws_*" or "boq_identityfrontenduiserver_*". If the
+ // field value starts with "gws_", it means the consent is from the Google
+ // search page. Otherwise, it's from the consent.google.com, which is used for
+ // the custom setting.
+ std::string prefix = "gws_";
+
+ if (socs.data().platform().compare(0, prefix.length(), prefix) == 0) {
+ aChoice.AssignLiteral("Accept");
+ return NS_OK;
+ }
+
+ aChoice.AssignLiteral("Custom");
+ return NS_OK;
+}
+
+} // anonymous namespace
+
+// static
+already_AddRefed<nsCookieBannerTelemetryService>
+nsCookieBannerTelemetryService::GetSingleton() {
+ MOZ_LOG(gCookieBannerTelemetryLog, LogLevel::Debug, ("GetSingleton."));
+
+ if (!sCookieBannerTelemetryServiceSingleton) {
+ sCookieBannerTelemetryServiceSingleton =
+ new nsCookieBannerTelemetryService();
+
+ RunOnShutdown([] {
+ DebugOnly<nsresult> rv =
+ sCookieBannerTelemetryServiceSingleton->Shutdown();
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+ "nsCookieBannerTelemetryService::Shutdown failed.");
+
+ sCookieBannerTelemetryServiceSingleton = nullptr;
+ });
+ }
+
+ return do_AddRef(sCookieBannerTelemetryServiceSingleton);
+}
+
+nsresult nsCookieBannerTelemetryService::Init() {
+ MOZ_LOG(gCookieBannerTelemetryLog, LogLevel::Debug, ("Init."));
+ if (mIsInitialized) {
+ return NS_OK;
+ }
+
+ mIsInitialized = true;
+
+ nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
+ NS_ENSURE_TRUE(obsSvc, NS_ERROR_FAILURE);
+
+ nsresult rv = obsSvc->AddObserver(this, "browser-search-service", false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = obsSvc->AddObserver(this, "idle-daily", false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = obsSvc->AddObserver(this, "cookie-changed", false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = obsSvc->AddObserver(this, "private-cookie-changed", false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+nsresult nsCookieBannerTelemetryService::Shutdown() {
+ MOZ_LOG(gCookieBannerTelemetryLog, LogLevel::Debug, ("Shutdown."));
+
+ if (!mIsInitialized) {
+ return NS_OK;
+ }
+
+ mIsInitialized = false;
+
+ sGoogleDomainsSet = nullptr;
+
+ nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
+ NS_ENSURE_TRUE(obsSvc, NS_ERROR_FAILURE);
+
+ nsresult rv = obsSvc->RemoveObserver(this, "browser-search-service");
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = obsSvc->RemoveObserver(this, "idle-daily");
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = obsSvc->RemoveObserver(this, "cookie-changed");
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = obsSvc->RemoveObserver(this, "private-cookie-changed");
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCookieBannerTelemetryService::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData) {
+ if (nsCRT::strcmp(aTopic, "profile-after-change") == 0) {
+ MOZ_LOG(gCookieBannerTelemetryLog, LogLevel::Debug,
+ ("Observe profile-after-change"));
+
+ return Init();
+ }
+
+ if (nsCRT::strcmp(aTopic, "idle-daily") == 0) {
+ MOZ_LOG(gCookieBannerTelemetryLog, LogLevel::Debug, ("idle-daily"));
+
+ return MaybeReportGoogleGDPRChoiceTelemetry();
+ }
+
+ if (nsCRT::strcmp(aTopic, "browser-search-service") == 0 &&
+ nsDependentString(aData).EqualsLiteral("init-complete")) {
+ MOZ_LOG(gCookieBannerTelemetryLog, LogLevel::Debug,
+ ("Observe browser-search-service::init-complete."));
+ mIsSearchServiceInitialized = true;
+
+ return MaybeReportGoogleGDPRChoiceTelemetry();
+ }
+
+ if (nsCRT::strcmp(aTopic, "cookie-changed") == 0 ||
+ nsCRT::strcmp(aTopic, "private-cookie-changed") == 0) {
+ MOZ_LOG(gCookieBannerTelemetryLog, LogLevel::Debug, ("Observe %s", aTopic));
+
+ nsCOMPtr<nsICookieNotification> notification = do_QueryInterface(aSubject);
+ NS_ENSURE_TRUE(notification, NS_ERROR_FAILURE);
+
+ if (notification->GetAction() != nsICookieNotification::COOKIE_ADDED &&
+ notification->GetAction() != nsICookieNotification::COOKIE_CHANGED) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsICookie> cookie;
+ nsresult rv = notification->GetCookie(getter_AddRefs(cookie));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString name;
+ rv = cookie->GetName(name);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Bail out early if this is not a SOCS cookie.
+ if (!name.EqualsLiteral("SOCS")) {
+ return NS_OK;
+ }
+
+ // A "SOCS" cookie is added. We record the GDPR choice as well as an event
+ // telemetry for recording a choice has been made at the moment.
+ return MaybeReportGoogleGDPRChoiceTelemetry(cookie, true);
+ }
+
+ return NS_OK;
+}
+
+nsresult nsCookieBannerTelemetryService::MaybeReportGoogleGDPRChoiceTelemetry(
+ nsICookie* aCookie, bool aReportEvent) {
+ MOZ_ASSERT(mIsInitialized);
+ nsresult rv;
+
+ // Don't report the telemetry if the search service is not yet initialized.
+ if (!mIsSearchServiceInitialized) {
+ MOZ_LOG(gCookieBannerTelemetryLog, LogLevel::Debug,
+ ("Search Service is not yet initialized."));
+ return NS_OK;
+ }
+
+ // We only collect Google GDPR choice if Google is the default search engine.
+ nsCOMPtr<nsISearchService> searchService(
+ do_GetService("@mozilla.org/browser/search-service;1", &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISearchEngine> engine;
+ rv = searchService->GetDefaultEngine(getter_AddRefs(engine));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Bail out early if no default search engine is available. This could happen
+ // if no search engine is shipped with the Gecko.
+ if (!engine) {
+ MOZ_LOG(gCookieBannerTelemetryLog, LogLevel::Debug,
+ ("No default search engine is available."));
+ return NS_OK;
+ }
+
+ nsAutoString id;
+ rv = engine->GetId(id);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Bail out early if the default search engine is not Google.
+ if (!id.EqualsLiteral("google@search.mozilla.orgdefault")) {
+ return NS_OK;
+ }
+
+ // Build up the Google domain set from the alternative Google search domains
+ if (!sGoogleDomainsSet) {
+ sGoogleDomainsSet = new nsTHashSet<nsCString>();
+
+ nsTArray<nsCString> googleDomains;
+ rv = searchService->GetAlternateDomains("www.google.com"_ns, googleDomains);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (const auto& domain : googleDomains) {
+ // We need to trim down the preceding "www" to get the host because the
+ // alternate domains provided by search service always have leading "www".
+ // However, the "SOCS" cookie is always set for all subdomains, e.g.
+ // ".google.com". Therefore, we need to trim down "www" to match the host
+ // of the cookie.
+ NS_ENSURE_TRUE(domain.Length() > 3, NS_ERROR_FAILURE);
+ sGoogleDomainsSet->Insert(Substring(domain, 3, domain.Length() - 3));
+ }
+ }
+
+ nsTArray<RefPtr<nsICookie>> cookies;
+ if (aCookie) {
+ const auto& attrs = aCookie->AsCookie().OriginAttributesRef();
+
+ // We only report cookies for the default originAttributes or private
+ // browsing mode.
+ if (attrs.mPrivateBrowsingId !=
+ nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID ||
+ attrs == OriginAttributes()) {
+ cookies.AppendElement(RefPtr<nsICookie>(aCookie));
+ }
+ } else {
+ // If no cookie is given, we will iterate all cookies under Google Search
+ // domains.
+ nsCOMPtr<nsICookieManager> cookieManager =
+ do_GetService("@mozilla.org/cookiemanager;1");
+ if (!cookieManager) {
+ return NS_ERROR_FAILURE;
+ }
+
+ for (const auto& domain : *sGoogleDomainsSet) {
+ // Getting Google Search cookies only for normal windows. We will exclude
+ // cookies of non-default originAttributes, like cookies from containers.
+ //
+ // Note that we need to trim the leading "." from the domain here.
+ nsTArray<RefPtr<nsICookie>> googleCookies;
+ rv = cookieManager->GetCookiesWithOriginAttributes(
+ u"{ \"privateBrowsingId\": 0, \"userContextId\": 0 }"_ns,
+ Substring(domain, 1, domain.Length() - 1), googleCookies);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ cookies.AppendElements(googleCookies);
+ }
+ }
+
+ for (const auto& cookie : cookies) {
+ nsAutoCString name;
+ rv = cookie->GetName(name);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!name.EqualsLiteral("SOCS")) {
+ continue;
+ }
+
+ nsAutoCString host;
+ rv = cookie->GetHost(host);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!sGoogleDomainsSet->Contains(host)) {
+ continue;
+ }
+
+ nsAutoCString value;
+ rv = cookie->GetValue(value);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString choice;
+ nsAutoCString region;
+ rv = DecodeSOCSGoogleCookie(value, choice, region);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool isPrivateBrowsing =
+ cookie->AsCookie().OriginAttributesRef().mPrivateBrowsingId !=
+ nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID;
+
+ MOZ_LOG(gCookieBannerTelemetryLog, LogLevel::Debug,
+ ("Record the Google GDPR choice %s on the host %s in region %s for "
+ "the %s window",
+ choice.get(), host.get(), region.get(),
+ isPrivateBrowsing ? "private" : "normal"));
+
+ // We rely on the dynamical labelled string which can send most 16 different
+ // labels per report. In average cases, 16 labels is sufficient because we
+ // expect a user might have only 1 or 2 "SOCS" cookies on different Google
+ // Search domains.
+ //
+ // Note that we only report event telemetry for private browsing windows
+ // because the private session is ephemeral. People can change GDPR
+ // choice across different private sessions, so it's hard to collect the
+ // state correctly.
+ if (!isPrivateBrowsing) {
+ glean::cookie_banners::google_gdpr_choice_cookie.Get(host).Set(choice);
+ }
+
+ if (aReportEvent) {
+ if (isPrivateBrowsing) {
+ glean::cookie_banners::GoogleGdprChoiceCookieEventPbmExtra extra = {
+ .choice = Some(choice),
+ };
+ glean::cookie_banners::google_gdpr_choice_cookie_event_pbm.Record(
+ Some(extra));
+ } else {
+ glean::cookie_banners::GoogleGdprChoiceCookieEventExtra extra = {
+ .choice = Some(choice),
+ .region = Some(region),
+ .searchDomain = Some(host),
+ };
+ glean::cookie_banners::google_gdpr_choice_cookie_event.Record(
+ Some(extra));
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+} // namespace mozilla
diff --git a/toolkit/components/cookiebanners/nsCookieBannerTelemetryService.h b/toolkit/components/cookiebanners/nsCookieBannerTelemetryService.h
new file mode 100644
index 0000000000..1236343ad9
--- /dev/null
+++ b/toolkit/components/cookiebanners/nsCookieBannerTelemetryService.h
@@ -0,0 +1,47 @@
+/* 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/. */
+
+#ifndef mozilla_nsCookieBannerTelemetryService_h__
+#define mozilla_nsCookieBannerTelemetryService_h__
+
+#include "nsICookieBannerTelemetryService.h"
+
+#include "nsIObserver.h"
+
+class nsICookie;
+
+namespace mozilla {
+class nsCookieBannerTelemetryService final
+ : public nsICookieBannerTelemetryService,
+ public nsIObserver {
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ NS_DECL_NSICOOKIEBANNERTELEMETRYSERVICE
+
+ public:
+ static already_AddRefed<nsCookieBannerTelemetryService> GetSingleton();
+
+ private:
+ nsCookieBannerTelemetryService() = default;
+ ~nsCookieBannerTelemetryService() = default;
+
+ bool mIsInitialized = false;
+ bool mIsSearchServiceInitialized = false;
+
+ [[nodiscard]] nsresult Init();
+
+ [[nodiscard]] nsresult Shutdown();
+
+ // Record the telemetry regarding the GDPR choice on Google Search domains.
+ // We only record if the default search engine is Google. When passing no
+ // cookie, we will walk through all cookies under Google Search domains.
+ // Otherwise, we will report the GDPR choice according to the given cookie.
+ [[nodiscard]] nsresult MaybeReportGoogleGDPRChoiceTelemetry(
+ nsICookie* aCookie = nullptr, bool aReportEvent = false);
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/toolkit/components/cookiebanners/nsICookieBannerRule.idl b/toolkit/components/cookiebanners/nsICookieBannerRule.idl
index 0ad557fe54..5ed456c90c 100644
--- a/toolkit/components/cookiebanners/nsICookieBannerRule.idl
+++ b/toolkit/components/cookiebanners/nsICookieBannerRule.idl
@@ -85,7 +85,7 @@ interface nsICookieBannerRule : nsISupports {
* aOptIn - The CSS selector for selecting the opt-in banner button
*/
void addClickRule(in ACString aPresence,
- [optional] in bool aSkipPresenceVisibilityCheck,
+ [optional] in boolean aSkipPresenceVisibilityCheck,
[optional] in nsIClickRule_RunContext aRunContext,
[optional] in ACString aHide,
[optional] in ACString aOptOut,
diff --git a/toolkit/components/cookiebanners/nsICookieBannerService.idl b/toolkit/components/cookiebanners/nsICookieBannerService.idl
index b58e726310..4d8db6f94e 100644
--- a/toolkit/components/cookiebanners/nsICookieBannerService.idl
+++ b/toolkit/components/cookiebanners/nsICookieBannerService.idl
@@ -58,13 +58,13 @@ interface nsICookieBannerService : nsISupports {
* this will return none, only reject rules or accept rules if there is no
* reject rule available.
*/
- Array<nsICookieRule> getCookiesForURI(in nsIURI aURI, in bool aIsPrivateBrowsing);
+ Array<nsICookieRule> getCookiesForURI(in nsIURI aURI, in boolean aIsPrivateBrowsing);
/**
* Look up the click rules for a given domain.
*/
Array<nsIClickRule> getClickRulesForDomain(in ACString aDomain,
- in bool aIsTopLevel);
+ in boolean aIsTopLevel);
/**
* Insert a cookie banner rule for a domain. If there was previously a rule
@@ -131,28 +131,28 @@ interface nsICookieBannerService : nsISupports {
* this session.
*/
boolean shouldStopBannerClickingForSite(in ACString aSite,
- in bool aIsTopLevel,
- in bool aIsPrivate);
+ in boolean aIsTopLevel,
+ in boolean aIsPrivate);
/**
* Mark that the cookie banner handling code was executed for the given site
* for this session.
*/
void markSiteExecuted(in ACString aSite,
- in bool aIsTopLevel,
- in bool aIsPrivate);
+ in boolean aIsTopLevel,
+ in boolean aIsPrivate);
/*
* Remove the executed record for a given site under the private browsing
* session or the normal session.
*/
- void removeExecutedRecordForSite(in ACString aSite, in bool aIsPrivate);
+ void removeExecutedRecordForSite(in ACString aSite, in boolean aIsPrivate);
/**
* Remove all the record of sites where cookie banner handling has been
* executed under the private browsing session or normal session.
*/
- void removeAllExecutedRecords(in bool aIsPrivate);
+ void removeAllExecutedRecords(in boolean aIsPrivate);
/**
* Clears the in-memory set that we use to maintain the domains that we have
diff --git a/toolkit/components/cookiebanners/nsICookieBannerTelemetryService.idl b/toolkit/components/cookiebanners/nsICookieBannerTelemetryService.idl
new file mode 100644
index 0000000000..9c027bf88e
--- /dev/null
+++ b/toolkit/components/cookiebanners/nsICookieBannerTelemetryService.idl
@@ -0,0 +1,13 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+#include "nsISupports.idl"
+
+/**
+ * Service singleton which collect cookie banner telemetry.
+ */
+[scriptable, builtinclass, uuid(56197e18-d144-45b5-9f77-84102f064462)]
+interface nsICookieBannerTelemetryService : nsISupports {};
diff --git a/toolkit/components/cookiebanners/test/browser/browser.toml b/toolkit/components/cookiebanners/test/browser/browser.toml
index 69728540d9..a3f8a96489 100644
--- a/toolkit/components/cookiebanners/test/browser/browser.toml
+++ b/toolkit/components/cookiebanners/test/browser/browser.toml
@@ -36,6 +36,8 @@ support-files = [
["browser_bannerClicking_visibilityOverride.js"]
support-files = ["file_banner_invisible.html"]
+["browser_cookiebanner_gdpr_telemetry.js"]
+
["browser_cookiebanner_telemetry.js"]
support-files = ["file_iframe_banner.html"]
diff --git a/toolkit/components/cookiebanners/test/browser/browser_cookiebanner_gdpr_telemetry.js b/toolkit/components/cookiebanners/test/browser/browser_cookiebanner_gdpr_telemetry.js
new file mode 100644
index 0000000000..2d6f3343b8
--- /dev/null
+++ b/toolkit/components/cookiebanners/test/browser/browser_cookiebanner_gdpr_telemetry.js
@@ -0,0 +1,189 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { SiteDataTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/SiteDataTestUtils.sys.mjs"
+);
+
+const TEST_CASES = [
+ {
+ value: "CAISHAgBEhJnd3NfMjAyNDAzMjUtMF9SQzEaAmRlIAEaBgiAw42wBg",
+ expected: "Accept",
+ region: "de",
+ host: ".google.de",
+ },
+ {
+ value: "CAESHAgBEhJnd3NfMjAyNDAzMjUtMF9SQzEaAmRlIAEaBgiAw42wBg",
+ expected: "Reject",
+ region: "de",
+ host: ".google.de",
+ },
+ {
+ value:
+ "CAISNQgBEitib3FfaWRlbnRpdHlmcm9udGVuZHVpc2VydmVyXzIwMjQwMzI0LjA4X3AwGgJkZSADGgYIgMONsAY",
+ expected: "Custom",
+ region: "de",
+ host: ".google.de",
+ },
+ {
+ value: "CAISHAgBEhJnd3NfMjAyNDAzMjUtMF9SQzEaAmVuIAEaBgiAw42wBg",
+ expected: "Accept",
+ region: "en",
+ host: ".google.com",
+ },
+];
+
+add_setup(async function () {
+ registerCleanupFunction(async () => {
+ // Clear cookies that have been set during testing.
+ await SiteDataTestUtils.clear();
+ });
+});
+
+add_task(async function test_google_gdpr_telemetry() {
+ // Clear telemetry before starting telemetry test.
+ Services.fog.testResetFOG();
+
+ for (let test of TEST_CASES) {
+ // Add a Google SOCS cookie to trigger recording telemetry.
+ SiteDataTestUtils.addToCookies({
+ name: "SOCS",
+ value: test.value,
+ host: test.host,
+ path: "/",
+ });
+
+ // Verify if the google GDPR choice cookie telemetry is recorded properly.
+ is(
+ Glean.cookieBanners.googleGdprChoiceCookie[test.host].testGetValue(),
+ test.expected,
+ "The Google GDPR telemetry is recorded properly."
+ );
+
+ // Verify the the event telemetry is recorded properly.
+ let events = Glean.cookieBanners.googleGdprChoiceCookieEvent.testGetValue();
+ let event = events[events.length - 1];
+
+ is(
+ event.extra.search_domain,
+ test.host,
+ "The Google GDPR event telemetry records the search domain properly."
+ );
+
+ is(
+ event.extra.choice,
+ test.expected,
+ "The Google GDPR event telemetry records the choice properly."
+ );
+
+ is(
+ event.extra.region,
+ test.region,
+ "The Google GDPR event telemetry records the region properly."
+ );
+ }
+
+ is(
+ Glean.cookieBanners.googleGdprChoiceCookieEvent.testGetValue().length,
+ TEST_CASES.length,
+ "The number of events is expected."
+ );
+});
+
+add_task(async function test_invalid_google_socs_cookies() {
+ // Clear telemetry before starting telemetry test.
+ Services.fog.testResetFOG();
+
+ // Add an invalid Google SOCS cookie which is not Base64 encoding.
+ SiteDataTestUtils.addToCookies({
+ name: "SOCS",
+ value: "invalid",
+ host: ".google.com",
+ path: "/",
+ });
+
+ // Ensure no GDPR telemetry is recorded.
+ is(
+ Glean.cookieBanners.googleGdprChoiceCookie[".google.com"].testGetValue(),
+ null,
+ "No Google GDPR telemetry is recorded."
+ );
+
+ is(
+ Glean.cookieBanners.googleGdprChoiceCookieEvent.testGetValue(),
+ null,
+ "No event telemetry is recorded."
+ );
+
+ // Add an invalid Google SOCS cookie which is Base64 encoding but in wrong
+ // protobuf format.
+ SiteDataTestUtils.addToCookies({
+ name: "SOCS",
+ value: "CAISAggBGgYIgMONsAY",
+ host: ".google.com",
+ path: "/",
+ });
+
+ // Ensure no GDPR telemetry is recorded.
+ is(
+ Glean.cookieBanners.googleGdprChoiceCookie[".google.com"].testGetValue(),
+ null,
+ "No Google GDPR telemetry is recorded."
+ );
+
+ is(
+ Glean.cookieBanners.googleGdprChoiceCookieEvent.testGetValue(),
+ null,
+ "No event telemetry is recorded."
+ );
+});
+
+add_task(async function test_google_gdpr_telemetry_pbm() {
+ // Clear telemetry before starting telemetry test.
+ Services.fog.testResetFOG();
+
+ for (let test of TEST_CASES) {
+ // Add a private Google SOCS cookie to trigger recording telemetry.
+ SiteDataTestUtils.addToCookies({
+ name: "SOCS",
+ value: test.value,
+ host: test.host,
+ path: "/",
+ originAttributes: { privateBrowsingId: 1 },
+ });
+
+ // Ensure we don't record google GDPR choice cookie telemetry.
+ is(
+ Glean.cookieBanners.googleGdprChoiceCookie[test.host].testGetValue(),
+ null,
+ "No Google GDPR telemetry is recorded."
+ );
+
+ // Verify the the event telemetry for PBM is recorded properly.
+ let events =
+ Glean.cookieBanners.googleGdprChoiceCookieEventPbm.testGetValue();
+ let event = events[events.length - 1];
+
+ is(
+ event.extra.choice,
+ test.expected,
+ "The Google GDPR event telemetry records the choice properly."
+ );
+
+ is(event.extra.search_domain, undefined, "No search domain is recorded.");
+ is(event.extra.region, undefined, "No region is recorded.");
+ }
+
+ is(
+ Glean.cookieBanners.googleGdprChoiceCookieEventPbm.testGetValue().length,
+ TEST_CASES.length,
+ "The number of events is expected."
+ );
+
+ // We need to notify the "last-pb-context-exited" tp explicitly remove private
+ // cookies. Otherwise, the remaining private cookies could affect following
+ // tests. The SiteDataTestUtils.clear() doesn't clear for private cookies.
+ Services.obs.notifyObservers(null, "last-pb-context-exited");
+});
diff --git a/toolkit/components/cookiebanners/test/browser/browser_cookiebanner_telemetry.js b/toolkit/components/cookiebanners/test/browser/browser_cookiebanner_telemetry.js
index 7366ae2ab6..8ac30770cb 100644
--- a/toolkit/components/cookiebanners/test/browser/browser_cookiebanner_telemetry.js
+++ b/toolkit/components/cookiebanners/test/browser/browser_cookiebanner_telemetry.js
@@ -86,7 +86,7 @@ async function reloadBrowser(browser, url) {
let reloaded = BrowserTestUtils.browserLoaded(browser, false, url);
// Reload as a user.
- window.BrowserReload();
+ window.BrowserCommands.reload();
await reloaded;
}
diff --git a/toolkit/components/corroborator/Corroborate.sys.mjs b/toolkit/components/corroborator/Corroborate.sys.mjs
deleted file mode 100644
index 51b447726c..0000000000
--- a/toolkit/components/corroborator/Corroborate.sys.mjs
+++ /dev/null
@@ -1,53 +0,0 @@
-/* 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/. */
-
-import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
-
-const lazy = {};
-
-XPCOMUtils.defineLazyServiceGetters(lazy, {
- gCertDB: ["@mozilla.org/security/x509certdb;1", "nsIX509CertDB"],
-});
-
-/**
- * Tools for verifying internal files in Mozilla products.
- */
-export const Corroborate = {
- async init() {},
-
- /**
- * Verify signed state of arbitrary JAR file. Currently only JAR files signed
- * with Mozilla-internal keys are supported.
- *
- * @argument file - an nsIFile pointing to the JAR to verify.
- *
- * @returns {Promise} - resolves true if file exists and is valid, false otherwise.
- * Never rejects.
- */
- verifyJar(file) {
- let root = Ci.nsIX509CertDB.AddonsPublicRoot;
- let expectedOrganizationalUnit = "Mozilla Components";
-
- return new Promise(resolve => {
- lazy.gCertDB.openSignedAppFileAsync(
- root,
- file,
- (rv, _zipReader, signatureInfos) => {
- // aSignatureInfos is an array of nsIAppSignatureInfo.
- // This implementation could be modified to iterate through the array to
- // determine if one or all of the verified signatures used a satisfactory
- // algorithm and signing certificate.
- // For now, though, it maintains existing behavior by inspecting the
- // first signing certificate encountered.
- resolve(
- Components.isSuccessCode(rv) &&
- signatureInfos.length &&
- signatureInfos[0].signerCert.organizationalUnit ==
- expectedOrganizationalUnit
- );
- }
- );
- });
- },
-};
diff --git a/toolkit/components/corroborator/moz.build b/toolkit/components/corroborator/moz.build
deleted file mode 100644
index 6db15ef997..0000000000
--- a/toolkit/components/corroborator/moz.build
+++ /dev/null
@@ -1,17 +0,0 @@
-# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-
-with Files("**"):
- BUG_COMPONENT = ("Toolkit", "General")
-
-EXTRA_JS_MODULES += [
- "Corroborate.sys.mjs",
-]
-
-XPCSHELL_TESTS_MANIFESTS += [
- "test/xpcshell/xpcshell.toml",
-]
diff --git a/toolkit/components/corroborator/test/xpcshell/data/privileged.xpi b/toolkit/components/corroborator/test/xpcshell/data/privileged.xpi
deleted file mode 100644
index c22acaacd2..0000000000
--- a/toolkit/components/corroborator/test/xpcshell/data/privileged.xpi
+++ /dev/null
Binary files differ
diff --git a/toolkit/components/corroborator/test/xpcshell/data/signed-amo.xpi b/toolkit/components/corroborator/test/xpcshell/data/signed-amo.xpi
deleted file mode 100644
index e2ba7d6fd8..0000000000
--- a/toolkit/components/corroborator/test/xpcshell/data/signed-amo.xpi
+++ /dev/null
Binary files differ
diff --git a/toolkit/components/corroborator/test/xpcshell/data/signed-components.xpi b/toolkit/components/corroborator/test/xpcshell/data/signed-components.xpi
deleted file mode 100644
index f27ac1fb73..0000000000
--- a/toolkit/components/corroborator/test/xpcshell/data/signed-components.xpi
+++ /dev/null
Binary files differ
diff --git a/toolkit/components/corroborator/test/xpcshell/data/signed-privileged.xpi b/toolkit/components/corroborator/test/xpcshell/data/signed-privileged.xpi
deleted file mode 100644
index c22acaacd2..0000000000
--- a/toolkit/components/corroborator/test/xpcshell/data/signed-privileged.xpi
+++ /dev/null
Binary files differ
diff --git a/toolkit/components/corroborator/test/xpcshell/data/unsigned.xpi b/toolkit/components/corroborator/test/xpcshell/data/unsigned.xpi
deleted file mode 100644
index 9e10be5db3..0000000000
--- a/toolkit/components/corroborator/test/xpcshell/data/unsigned.xpi
+++ /dev/null
Binary files differ
diff --git a/toolkit/components/corroborator/test/xpcshell/test_verify_jar.js b/toolkit/components/corroborator/test/xpcshell/test_verify_jar.js
deleted file mode 100644
index c1c98f4485..0000000000
--- a/toolkit/components/corroborator/test/xpcshell/test_verify_jar.js
+++ /dev/null
@@ -1,31 +0,0 @@
-/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set sts=2 sw=2 et tw=80: */
-"use strict";
-
-const { Corroborate } = ChromeUtils.importESModule(
- "resource://gre/modules/Corroborate.sys.mjs"
-);
-
-add_task(async function test_various_jars() {
- let result = await Corroborate.verifyJar(do_get_file("data/unsigned.xpi"));
- equal(result, false, "unsigned files do not verify");
-
- result = await Corroborate.verifyJar(do_get_file("data/signed-amo.xpi"));
- equal(result, false, "AMO signed files do not verify");
-
- result = await Corroborate.verifyJar(
- do_get_file("data/signed-privileged.xpi")
- );
- equal(result, false, "Privileged signed files do not verify");
-
- let missingFile = do_get_file("data");
- missingFile.append("missing.xpi");
-
- result = await Corroborate.verifyJar(missingFile);
- equal(result, false, "Missing (but expected) files do not verify");
-
- result = await Corroborate.verifyJar(
- do_get_file("data/signed-components.xpi")
- );
- equal(result, true, "Components signed files do verify");
-});
diff --git a/toolkit/components/corroborator/test/xpcshell/xpcshell.toml b/toolkit/components/corroborator/test/xpcshell/xpcshell.toml
deleted file mode 100644
index d30b8dfb17..0000000000
--- a/toolkit/components/corroborator/test/xpcshell/xpcshell.toml
+++ /dev/null
@@ -1,5 +0,0 @@
-[DEFAULT]
-tags = "corroborator"
-support-files = ["data/**"]
-
-["test_verify_jar.js"]
diff --git a/toolkit/components/crashes/CrashService.sys.mjs b/toolkit/components/crashes/CrashService.sys.mjs
index cedbca53b9..3634327b2e 100644
--- a/toolkit/components/crashes/CrashService.sys.mjs
+++ b/toolkit/components/crashes/CrashService.sys.mjs
@@ -66,7 +66,7 @@ function runMinidumpAnalyzer(minidumpPath, allThreads) {
args.unshift("--full");
}
- process.runAsync(args, args.length, (subject, topic, data) => {
+ process.runAsync(args, args.length, (subject, topic) => {
switch (topic) {
case "process-finished":
gRunningProcesses.delete(process);
@@ -211,7 +211,7 @@ CrashService.prototype = Object.freeze({
await blocker;
},
- observe(subject, topic, data) {
+ observe(subject, topic) {
switch (topic) {
case "profile-after-change":
// Side-effect is the singleton is instantiated.
diff --git a/toolkit/components/crashes/tests/xpcshell/test_crash_manager.js b/toolkit/components/crashes/tests/xpcshell/test_crash_manager.js
index 2f77ea5105..e776b8f87a 100644
--- a/toolkit/components/crashes/tests/xpcshell/test_crash_manager.js
+++ b/toolkit/components/crashes/tests/xpcshell/test_crash_manager.js
@@ -824,7 +824,7 @@ add_task(async function test_glean_crash_ping() {
// Test with additional fields
submitted = false;
- GleanPings.crash.testBeforeNextSubmit(reason => {
+ GleanPings.crash.testBeforeNextSubmit(() => {
submitted = true;
const MINUTES = new Date(DUMMY_DATE_2);
MINUTES.setSeconds(0);
diff --git a/toolkit/components/crashes/tests/xpcshell/test_crash_service.js b/toolkit/components/crashes/tests/xpcshell/test_crash_service.js
index 63ff3343d6..6648230785 100644
--- a/toolkit/components/crashes/tests/xpcshell/test_crash_service.js
+++ b/toolkit/components/crashes/tests/xpcshell/test_crash_service.js
@@ -170,7 +170,7 @@ add_task(async function test_addCrash_quitting() {
await setup(firstCrashId);
let minidumpAnalyzerKilledPromise = new Promise((resolve, reject) => {
- Services.obs.addObserver((subject, topic, data) => {
+ Services.obs.addObserver((subject, topic) => {
if (topic === "test-minidump-analyzer-killed") {
resolve();
}
diff --git a/toolkit/components/crashes/tests/xpcshell/test_crash_store.js b/toolkit/components/crashes/tests/xpcshell/test_crash_store.js
index 515aec86a0..3690c13922 100644
--- a/toolkit/components/crashes/tests/xpcshell/test_crash_store.js
+++ b/toolkit/components/crashes/tests/xpcshell/test_crash_store.js
@@ -2,7 +2,7 @@
* http://creativecommons.org/publicdomain/zero/1.0/ */
/*
- * This file tests the CrashStore type in CrashManager.jsm.
+ * This file tests the CrashStore type in CrashManager.sys.mjs.
*/
"use strict";
@@ -257,7 +257,7 @@ add_task(async function test_add_mixed_types() {
allAdd &&
s.addCrash(ptName, CRASH_TYPE_CRASH, ptName + "crash", new Date());
},
- (_, ptName) => {
+ _ => {
allAdd =
allAdd &&
s.addCrash(
diff --git a/toolkit/components/crashmonitor/CrashMonitor.sys.mjs b/toolkit/components/crashmonitor/CrashMonitor.sys.mjs
index c4dfdcbd83..a23446f45c 100644
--- a/toolkit/components/crashmonitor/CrashMonitor.sys.mjs
+++ b/toolkit/components/crashmonitor/CrashMonitor.sys.mjs
@@ -189,7 +189,7 @@ export var CrashMonitor = {
*
* Update checkpoint file for every new notification received.
*/
- observe(aSubject, aTopic, aData) {
+ observe(aSubject, aTopic) {
this.writeCheckpoint(aTopic);
if (
diff --git a/toolkit/components/crashmonitor/nsCrashMonitor.sys.mjs b/toolkit/components/crashmonitor/nsCrashMonitor.sys.mjs
index 0e91fc0859..008b0e7bc8 100644
--- a/toolkit/components/crashmonitor/nsCrashMonitor.sys.mjs
+++ b/toolkit/components/crashmonitor/nsCrashMonitor.sys.mjs
@@ -14,7 +14,7 @@ CrashMonitor.prototype = {
QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
- observe(aSubject, aTopic, aData) {
+ observe(aSubject, aTopic) {
switch (aTopic) {
case "profile-after-change":
MonitorAPI.init();
diff --git a/toolkit/components/crashmonitor/test/unit/test_register.js b/toolkit/components/crashmonitor/test/unit/test_register.js
index 1957af4ee1..22d3721df8 100644
--- a/toolkit/components/crashmonitor/test/unit/test_register.js
+++ b/toolkit/components/crashmonitor/test/unit/test_register.js
@@ -4,7 +4,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
- * Test that CrashMonitor.jsm is correctly loaded from XPCOM component
+ * Test that CrashMonitor.sys.mjs is correctly loaded from XPCOM component
*/
add_task(function test_register() {
let cm = Cc["@mozilla.org/toolkit/crashmonitor;1"].createInstance(
diff --git a/toolkit/components/credentialmanagement/tests/browser/browser.toml b/toolkit/components/credentialmanagement/tests/browser/browser.toml
index a7202c9190..e14e36b938 100644
--- a/toolkit/components/credentialmanagement/tests/browser/browser.toml
+++ b/toolkit/components/credentialmanagement/tests/browser/browser.toml
@@ -10,9 +10,7 @@ scheme = "https"
support-files = ["custom.svg"]
["browser_account_dialog.js"]
-fail-if = ["a11y_checks"] # Bug 1854509 clicked identity-credential-list-item may not be focusable
["browser_policy_dialog.js"]
["browser_provider_dialog.js"]
-fail-if = ["a11y_checks"] # Bug 1854509 clicked identity-credential-list-item may not be focusable
diff --git a/toolkit/components/ctypes/tests/chrome/xpcshellTestHarnessAdaptor.js b/toolkit/components/ctypes/tests/chrome/xpcshellTestHarnessAdaptor.js
index 9e90b22561..24f0cb2602 100644
--- a/toolkit/components/ctypes/tests/chrome/xpcshellTestHarnessAdaptor.js
+++ b/toolkit/components/ctypes/tests/chrome/xpcshellTestHarnessAdaptor.js
@@ -83,7 +83,7 @@ FileFaker.prototype = {
},
};
-function do_get_file(path, allowNonexistent) {
+function do_get_file(path) {
if (!_WORKINGDIR_) {
do_throw("No way to fake files if working directory is unknown!");
}
diff --git a/toolkit/components/ctypes/tests/unit/test_errno.js b/toolkit/components/ctypes/tests/unit/test_errno.js
index 24dba0597b..8905ec3a18 100644
--- a/toolkit/components/ctypes/tests/unit/test_errno.js
+++ b/toolkit/components/ctypes/tests/unit/test_errno.js
@@ -2,11 +2,10 @@
var ctypes = ctypes;
function run_test() {
- // Launch the test with regular loading of ctypes.jsm
+ // Launch the test with regular loading of ctypes.sys.mjs
main_test();
- // Relaunch the test with exotic loading of ctypes.jsm
- Cu.unload("resource://gre/modules/ctypes.jsm");
+ // Relaunch the test with exotic loading of ctypes.sys.mjs
let scope = ChromeUtils.importESModule(
"resource://gre/modules/ctypes.sys.mjs"
);
diff --git a/toolkit/components/ctypes/tests/unit/test_jsctypes.js b/toolkit/components/ctypes/tests/unit/test_jsctypes.js
index 7227ec9925..66255d85b1 100644
--- a/toolkit/components/ctypes/tests/unit/test_jsctypes.js
+++ b/toolkit/components/ctypes/tests/unit/test_jsctypes.js
@@ -3470,7 +3470,7 @@ function run_string_tests(library) {
Assert.equal(ptrValue(test_ansi_echo(null)), 0);
}
-function run_readstring_tests(library) {
+function run_readstring_tests() {
// ASCII decode test, "hello world"
let ascii_string = ctypes.unsigned_char.array(12)();
ascii_string[0] = 0x68;
diff --git a/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs b/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs
index 15fa2193c6..00a5381133 100644
--- a/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs
+++ b/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs
@@ -296,7 +296,7 @@ EnterprisePoliciesManager.prototype = {
},
// nsIObserver implementation
- observe: function BG_observe(subject, topic, data) {
+ observe: function BG_observe(subject, topic) {
switch (topic) {
case "policies-startup":
// Before the first set of policy callbacks runs, we must
diff --git a/toolkit/components/enterprisepolicies/nsIEnterprisePolicies.idl b/toolkit/components/enterprisepolicies/nsIEnterprisePolicies.idl
index 03c51af49f..24d4ae4dcf 100644
--- a/toolkit/components/enterprisepolicies/nsIEnterprisePolicies.idl
+++ b/toolkit/components/enterprisepolicies/nsIEnterprisePolicies.idl
@@ -15,7 +15,7 @@ interface nsIEnterprisePolicies : nsISupports
readonly attribute short status;
- bool isAllowed(in ACString feature);
+ boolean isAllowed(in ACString feature);
/**
* Get the active policies that have been successfully parsed.
@@ -56,7 +56,7 @@ interface nsIEnterprisePolicies : nsISupports
*
* @returns A boolean - true of the addon may be installed.
*/
- bool mayInstallAddon(in jsval addon);
+ boolean mayInstallAddon(in jsval addon);
/**
* Uses install_sources to determine if an addon can be installed
@@ -64,7 +64,7 @@ interface nsIEnterprisePolicies : nsISupports
*
* @returns A boolean - true of the addon may be installed.
*/
- bool allowedInstallSource(in nsIURI uri);
+ boolean allowedInstallSource(in nsIURI uri);
/**
* Uses ExemptDomainFileTypePairsFromFileTypeDownloadWarnings to determine
* if a given file extension is exempted from executable behavior and
@@ -72,5 +72,5 @@ interface nsIEnterprisePolicies : nsISupports
*
* @returns A boolean - true if the extension should be exempt.
*/
- bool isExemptExecutableExtension(in ACString url, in ACString extension);
+ boolean isExemptExecutableExtension(in ACString url, in ACString extension);
};
diff --git a/toolkit/components/enterprisepolicies/tests/EnterprisePolicyTesting.sys.mjs b/toolkit/components/enterprisepolicies/tests/EnterprisePolicyTesting.sys.mjs
index b877c6f738..dd0f5caf89 100644
--- a/toolkit/components/enterprisepolicies/tests/EnterprisePolicyTesting.sys.mjs
+++ b/toolkit/components/enterprisepolicies/tests/EnterprisePolicyTesting.sys.mjs
@@ -83,7 +83,7 @@ export var EnterprisePolicyTesting = {
/**
* This helper will track prefs that have been changed
* by the policy engine through the setAndLockPref and
- * setDefaultPref APIs (from Policies.jsm) and make sure
+ * setDefaultPref APIs (from Policies.sys.mjs) and make sure
* that they are restored to their original values when
* the test ends or another test case restarts the engine.
*/
diff --git a/toolkit/components/enterprisepolicies/tests/browser/browser_policies_enterprise_only.js b/toolkit/components/enterprisepolicies/tests/browser/browser_policies_enterprise_only.js
index 3da3d250c6..c0fd63870a 100644
--- a/toolkit/components/enterprisepolicies/tests/browser/browser_policies_enterprise_only.js
+++ b/toolkit/components/enterprisepolicies/tests/browser/browser_policies_enterprise_only.js
@@ -14,13 +14,13 @@ add_task(async function test_enterprise_only_policies() {
enterprisePolicyRan = false;
Policies.NormalPolicy = {
- onProfileAfterChange(manager, param) {
+ onProfileAfterChange() {
normalPolicyRan = true;
},
};
Policies.EnterpriseOnlyPolicy = {
- onProfileAfterChange(manager, param) {
+ onProfileAfterChange() {
enterprisePolicyRan = true;
},
};
diff --git a/toolkit/components/extensions/.eslintrc.js b/toolkit/components/extensions/.eslintrc.js
index 8cc2d2a16f..2009e44ec9 100644
--- a/toolkit/components/extensions/.eslintrc.js
+++ b/toolkit/components/extensions/.eslintrc.js
@@ -50,7 +50,7 @@ module.exports = {
"no-unused-vars": [
"error",
{
- args: "none",
+ argsIgnorePattern: "^_",
vars: "all",
varsIgnorePattern: "^console$",
},
@@ -223,7 +223,7 @@ module.exports = {
"no-unused-vars": [
"error",
{
- args: "none",
+ argsIgnorePattern: "^_",
vars: "local",
},
],
diff --git a/toolkit/components/extensions/ConduitsChild.sys.mjs b/toolkit/components/extensions/ConduitsChild.sys.mjs
index 598804f74a..d69a57cc19 100644
--- a/toolkit/components/extensions/ConduitsChild.sys.mjs
+++ b/toolkit/components/extensions/ConduitsChild.sys.mjs
@@ -12,6 +12,9 @@
* @property {ConduitID} [sender]
* @property {boolean} query
* @property {object} arg
+ *
+ * @typedef {import("ConduitsParent.sys.mjs").ConduitAddress} ConduitAddress
+ * @typedef {import("ConduitsParent.sys.mjs").ConduitID} ConduitID
*/
/**
@@ -46,15 +49,15 @@ export class BaseConduit {
}
/**
- * Internal, partially @abstract, uses the actor to send the message/query.
+ * Internal, uses the actor to send the message/query.
*
* @param {string} method
* @param {boolean} query Flag indicating a response is expected.
- * @param {JSWindowActor} actor
+ * @param {JSWindowActorChild|JSWindowActorParent} actor
* @param {MessageData} data
* @returns {Promise?}
*/
- _send(method, query, actor, data) {
+ _doSend(method, query, actor, data) {
if (query) {
return actor.sendQuery(method, data);
}
@@ -62,6 +65,16 @@ export class BaseConduit {
}
/**
+ * Internal @abstract, used by sendX stubs.
+ *
+ * @param {string} _name
+ * @param {boolean} _query
+ */
+ _send(_name, _query, ..._args) {
+ throw new Error(`_send not implemented for ${this.constructor.name}`);
+ }
+
+ /**
* Internal, calls the specific recvX method based on the message.
*
* @param {string} name Message/method name.
@@ -89,6 +102,7 @@ export class BaseConduit {
* one specific ConduitsChild actor.
*/
export class PointConduit extends BaseConduit {
+ /** @type {ConduitGen} */
constructor(subject, address, actor) {
super(subject, address);
this.actor = actor;
@@ -108,7 +122,7 @@ export class PointConduit extends BaseConduit {
throw new Error(`send${method} on closed conduit ${this.id}`);
}
let sender = this.id;
- return super._send(method, query, this.actor, { arg, query, sender });
+ return super._doSend(method, query, this.actor, { arg, query, sender });
}
/**
@@ -156,9 +170,7 @@ export class ConduitsChild extends JSWindowActorChild {
/**
* Public entry point a child-side subject uses to open a conduit.
*
- * @param {object} subject
- * @param {ConduitAddress} address
- * @returns {PointConduit}
+ * @type {ConduitGen}
*/
openConduit(subject, address) {
let conduit = new PointConduit(subject, address, this);
@@ -211,6 +223,5 @@ export class ProcessConduitsChild extends JSProcessActorChild {
openConduit = ConduitsChild.prototype.openConduit;
receiveMessage = ConduitsChild.prototype.receiveMessage;
- willDestroy = ConduitsChild.prototype.willDestroy;
didDestroy = ConduitsChild.prototype.didDestroy;
}
diff --git a/toolkit/components/extensions/ConduitsParent.sys.mjs b/toolkit/components/extensions/ConduitsParent.sys.mjs
index d90bc4afd7..afd81cafc2 100644
--- a/toolkit/components/extensions/ConduitsParent.sys.mjs
+++ b/toolkit/components/extensions/ConduitsParent.sys.mjs
@@ -29,6 +29,7 @@
* @property {string} [url]
* @property {number} [frameId]
* @property {string} [workerScriptURL]
+ * @property {number} [workerDescriptorId]
* @property {string} [extensionId]
* @property {string} [envType]
* @property {string} [instanceId]
@@ -299,7 +300,7 @@ export class BroadcastConduit extends BaseConduit {
if (method === "RunListener" && arg.path.startsWith("webRequest.")) {
return actor.batch(method, { target, arg, query, sender });
}
- return super._send(method, query, actor, { target, arg, query, sender });
+ return super._doSend(method, query, actor, { target, arg, query, sender });
}
/**
@@ -482,6 +483,5 @@ export class ConduitsParent extends JSWindowActorParent {
*/
export class ProcessConduitsParent extends JSProcessActorParent {
receiveMessage = ConduitsParent.prototype.receiveMessage;
- willDestroy = ConduitsParent.prototype.willDestroy;
didDestroy = ConduitsParent.prototype.didDestroy;
}
diff --git a/toolkit/components/extensions/Extension.sys.mjs b/toolkit/components/extensions/Extension.sys.mjs
index de6d4c8bfd..8ab3c30234 100644
--- a/toolkit/components/extensions/Extension.sys.mjs
+++ b/toolkit/components/extensions/Extension.sys.mjs
@@ -31,7 +31,9 @@ import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
import { ExtensionCommon } from "resource://gre/modules/ExtensionCommon.sys.mjs";
import { ExtensionParent } from "resource://gre/modules/ExtensionParent.sys.mjs";
import { ExtensionUtils } from "resource://gre/modules/ExtensionUtils.sys.mjs";
+import { Log } from "resource://gre/modules/Log.sys.mjs";
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -54,7 +56,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
ExtensionTelemetry: "resource://gre/modules/ExtensionTelemetry.sys.mjs",
LightweightThemeManager:
"resource://gre/modules/LightweightThemeManager.sys.mjs",
- Log: "resource://gre/modules/Log.sys.mjs",
NetUtil: "resource://gre/modules/NetUtil.sys.mjs",
SITEPERMS_ADDON_TYPE:
"resource://gre/modules/addons/siteperms-addon-utils.sys.mjs",
@@ -684,14 +685,13 @@ export var ExtensionProcessCrashObserver = {
// `processCrashTimeframe` milliseconds.
lastCrashTimestamps: [],
+ logger: Log.repository.getLogger("addons.process-crash-observer"),
+
init() {
if (!this.initialized) {
Services.obs.addObserver(this, "ipc:content-created");
Services.obs.addObserver(this, "process-type-set");
Services.obs.addObserver(this, "ipc:content-shutdown");
- this.logger = lazy.Log.repository.getLogger(
- "addons.process-crash-observer"
- );
if (this._isAndroid) {
Services.obs.addObserver(this, "geckoview-initial-foreground");
Services.obs.addObserver(this, "application-foreground");
@@ -948,7 +948,7 @@ export class ExtensionData {
get logger() {
let id = this.id || "<unknown>";
- return lazy.Log.repository.getLogger(LOGGER_ID_BASE + id);
+ return Log.repository.getLogger(LOGGER_ID_BASE + id);
}
/**
@@ -1120,6 +1120,24 @@ export class ExtensionData {
return !(this.isPrivileged && this.hasPermission("mozillaAddons"));
}
+ get optionsPageProperties() {
+ let page = this.manifest.options_ui?.page ?? this.manifest.options_page;
+ if (!page) {
+ return null;
+ }
+ return {
+ page,
+ open_in_tab: this.manifest.options_ui
+ ? this.manifest.options_ui.open_in_tab ?? false
+ : true,
+ // `options_ui.browser_style` is assigned the proper default value
+ // (true for MV2 and false for MV3 when not explicitly set),
+ // in `#parseBrowserStyleInManifest` (called when we are loading
+ // and parse manifest data from the `parseManifest` method).
+ browser_style: this.manifest.options_ui?.browser_style ?? false,
+ };
+ }
+
/**
* Given an array of host and permissions, generate a structured permissions object
* that contains seperate host origins and permissions arrays.
@@ -1658,6 +1676,8 @@ export class ExtensionData {
);
}
+ // manifest.options_page opens the extension page in a new tab
+ // and so we will not need to special handling browser_style.
if (manifest.options_ui) {
if (manifest.options_ui.open_in_tab) {
// browser_style:true has no effect when open_in_tab is true.
@@ -2748,10 +2768,10 @@ class DictionaryBootstrapScope extends BootstrapScope {
install() {}
uninstall() {}
- startup(data, reason) {
+ startup(data) {
// eslint-disable-next-line no-use-before-define
this.dictionary = new Dictionary(data);
- return this.dictionary.startup(BootstrapScope.BOOTSTRAP_REASON_MAP[reason]);
+ return this.dictionary.startup();
}
async shutdown(data, reason) {
@@ -2765,10 +2785,10 @@ class LangpackBootstrapScope extends BootstrapScope {
uninstall() {}
async update() {}
- startup(data, reason) {
+ startup(data) {
// eslint-disable-next-line no-use-before-define
this.langpack = new Langpack(data);
- return this.langpack.startup(BootstrapScope.BOOTSTRAP_REASON_MAP[reason]);
+ return this.langpack.startup();
}
async shutdown(data, reason) {
@@ -2782,12 +2802,10 @@ class SitePermissionBootstrapScope extends BootstrapScope {
install() {}
uninstall() {}
- startup(data, reason) {
+ startup(data) {
// eslint-disable-next-line no-use-before-define
this.sitepermission = new SitePermission(data);
- return this.sitepermission.startup(
- BootstrapScope.BOOTSTRAP_REASON_MAP[reason]
- );
+ return this.sitepermission.startup();
}
async shutdown(data, reason) {
@@ -2810,6 +2828,15 @@ export class Extension extends ExtensionData {
/** @type {Map<string, Map<string, any>>} */
persistentListeners;
+ /** @type {import("ExtensionShortcuts.sys.mjs").ExtensionShortcuts} */
+ shortcuts;
+
+ /** @type {TabManagerBase} */
+ tabManager;
+
+ /** @type {(options?: { ignoreDevToolsAttached?: boolean, disableResetIdleForTest?: boolean }) => Promise} */
+ terminateBackground;
+
constructor(addonData, startupReason, updateReason) {
super(addonData.resourceURI, addonData.isPrivileged);
@@ -3203,9 +3230,11 @@ export class Extension extends ExtensionData {
};
}
- // Extended serialized data which is only needed in the extensions process,
- // and is never deserialized in web content processes.
- // Keep in sync with BrowserExtensionContent in ExtensionChild.sys.mjs
+ /**
+ * Extended serialized data which is only needed in the extensions process,
+ * and is never deserialized in web content processes.
+ * Keep in sync with @see {ExtensionChild}.
+ */
serializeExtended() {
return {
backgroundScripts: this.backgroundScripts,
@@ -3461,7 +3490,7 @@ export class Extension extends ExtensionData {
ignoreQuarantine: this.ignoreQuarantine,
temporarilyInstalled: this.temporarilyInstalled,
allowedOrigins: new MatchPatternSet([]),
- localizeCallback() {},
+ localizeCallback: () => "",
readyPromise,
});
diff --git a/toolkit/components/extensions/ExtensionActions.sys.mjs b/toolkit/components/extensions/ExtensionActions.sys.mjs
index 29a286442e..92ea9865e3 100644
--- a/toolkit/components/extensions/ExtensionActions.sys.mjs
+++ b/toolkit/components/extensions/ExtensionActions.sys.mjs
@@ -79,8 +79,8 @@ class PanelActionBase {
/**
* Set a global, window specific or tab specific property.
*
- * @param {XULElement|ChromeWindow|null} target
- * A XULElement tab, a ChromeWindow, or null for the global data.
+ * @param {NativeTab|ChromeWindow|null} target
+ * A NativeTab tab, a ChromeWindow, or null for the global data.
* @param {string} prop
* String property to set. Should should be one of "icon", "title", "badgeText",
* "popup", "badgeBackgroundColor", "badgeTextColor" or "enabled".
@@ -104,8 +104,8 @@ class PanelActionBase {
/**
* Gets the data associated with a tab, window, or the global one.
*
- * @param {XULElement|ChromeWindow|null} target
- * A XULElement tab, a ChromeWindow, or null for the global data.
+ * @param {NativeTab|ChromeWindow|null} target
+ * A NativeTab tab, a ChromeWindow, or null for the global data.
* @returns {object}
* The icon, title, badge, etc. associated with the target.
*/
@@ -119,8 +119,8 @@ class PanelActionBase {
/**
* Retrieve the value of a global, window specific or tab specific property.
*
- * @param {XULElement|ChromeWindow|null} target
- * A XULElement tab, a ChromeWindow, or null for the global data.
+ * @param {NativeTab|ChromeWindow|null} target
+ * A NativeTab tab, a ChromeWindow, or null for the global data.
* @param {string} prop
* Name of property to retrieve. Should should be one of "icon",
* "title", "badgeText", "popup", "badgeBackgroundColor" or "enabled".
@@ -161,7 +161,7 @@ class PanelActionBase {
*
* @param {string} eventType
* The type of the event, should be "location-change".
- * @param {XULElement} tab
+ * @param {NativeTab} tab
* The tab whose location changed, or which has become selected.
* @param {boolean} [fromBrowse]
* - `true` if navigation occurred in `tab`.
@@ -178,7 +178,7 @@ class PanelActionBase {
/**
* Gets the popup url for a given tab.
*
- * @param {XULElement} tab
+ * @param {NativeTab} tab
* The tab the popup refers to.
* @param {boolean} strict
* If errors should be thrown if a URL is not available.
@@ -208,7 +208,7 @@ class PanelActionBase {
* Will clear any existing activeTab permissions previously granted for any
* other tab.
*
- * @param {XULElement} tab
+ * @param {NativeTab} tab
* The tab that should be granted activeTab permission for. Set to
* null to clear previously granted activeTab permission.
*/
@@ -229,7 +229,7 @@ class PanelActionBase {
/**
* Triggers this action and sends the appropriate event if needed.
*
- * @param {XULElement} tab
+ * @param {NativeTab} tab
* The tab on which the action was fired.
* @param {object} clickInfo
* Extra data passed to the second parameter to the action API's
@@ -322,7 +322,7 @@ class PanelActionBase {
* If it only changes a parameter for a single window, `target` will be that window.
* Otherwise `target` will be null.
*
- * @param {XULElement|ChromeWindow|null} _target
+ * @param {NativeTab|ChromeWindow} [_target]
* Browser tab or browser chrome window, may be null.
*/
updateOnChange(_target) {}
@@ -332,16 +332,22 @@ class PanelActionBase {
*
* @param {string} _tabId
* Internal id of the tab to get.
+ * @returns {NativeTab}
*/
- getTab(_tabId) {}
+ getTab(_tabId) {
+ throw new Error("Not implemented.");
+ }
/**
* Get window object from windowId
*
* @param {string} _windowId
* Internal id of the window to get.
+ * @returns {ChromeWindow}
*/
- getWindow(_windowId) {}
+ getWindow(_windowId) {
+ throw new Error("Not implemented.");
+ }
/**
* Gets the target object corresponding to the `details` parameter of the various
@@ -352,19 +358,19 @@ class PanelActionBase {
* @param {number} [_details.tabId]
* @param {number} [_details.windowId]
* @throws if both `tabId` and `windowId` are specified, or if they are invalid.
- * @returns {XULElement|ChromeWindow|null}
- * If a `tabId` was specified, the corresponding XULElement tab.
+ * @returns {NativeTab|ChromeWindow|null}
+ * If a `tabId` was specified, the corresponding NativeTab tab.
* If a `windowId` was specified, the corresponding ChromeWindow.
* Otherwise, `null`.
*/
getTargetFromDetails(_details) {
- return null;
+ throw new Error("Not Implemented");
}
/**
* Triggers a click event.
*
- * @param {XULElement} _tab
+ * @param {NativeTab} _tab
* The tab where this event should be fired.
* @param {object} _clickInfo
* Extra data passed to the second parameter to the action API's
@@ -375,7 +381,7 @@ class PanelActionBase {
/**
* Checks whether this action is shown.
*
- * @param {XULElement} _tab
+ * @param {NativeTab} _tab
* The tab to be checked
* @returns {boolean}
*/
@@ -442,7 +448,7 @@ export class PageActionBase extends PanelActionBase {
// Checks whether the tab action is shown when the specified tab becomes active.
// Does pattern matching if necessary, and caches the result as a tab-specific value.
- // @param {XULElement} tab
+ // @param {NativeTab} tab
// The tab to be checked
// @return boolean
isShownForTab(tab) {
@@ -571,6 +577,8 @@ export class BrowserActionBase extends PanelActionBase {
/**
* Determines the text badge color to be used in a tab, window, or globally.
*
+ * @typedef {number[]} ColorArray from schemas/browser_action.json.
+ *
* @param {object} values
* The values associated with the tab or window, or global values.
* @returns {ColorArray}
diff --git a/toolkit/components/extensions/ExtensionChild.sys.mjs b/toolkit/components/extensions/ExtensionChild.sys.mjs
index 70774db395..232d5cc659 100644
--- a/toolkit/components/extensions/ExtensionChild.sys.mjs
+++ b/toolkit/components/extensions/ExtensionChild.sys.mjs
@@ -15,6 +15,7 @@ import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
+/** @type {Lazy} */
const lazy = {};
XPCOMUtils.defineLazyServiceGetter(
@@ -144,7 +145,7 @@ const StrongPromise = {
Services.obs.addObserver(StrongPromise, "extensions-onMessage-witness");
// Simple single-event emitter-like helper, exposes the EventManager api.
-class SimpleEventAPI extends EventManager {
+export class SimpleEventAPI extends EventManager {
constructor(context, name) {
let fires = new Set();
let register = fire => {
@@ -162,7 +163,7 @@ class SimpleEventAPI extends EventManager {
}
// runtime.OnMessage event helper, handles custom async/sendResponse logic.
-class MessageEvent extends SimpleEventAPI {
+export class MessageEvent extends SimpleEventAPI {
emit(holder, sender) {
if (!this.fires.size || !this.context.active) {
return { received: false };
@@ -229,7 +230,7 @@ function holdMessage(name, anonymizedName, data, native = null) {
}
// Implements the runtime.Port extension API object.
-class Port {
+export class Port {
/**
* @param {BaseContext} context The context that owns this port.
* @param {number} portId Uniquely identifies this port's channel.
@@ -310,7 +311,7 @@ class Port {
* Each extension context gets its own Messenger object. It handles the
* basics of sendMessage, onMessage, connect and onConnect.
*/
-class Messenger {
+export class Messenger {
constructor(context) {
this.context = context;
this.conduit = context.openConduit(this, {
@@ -382,8 +383,11 @@ var ExtensionManager = {
extensions: new Map(),
};
-// Represents a browser extension in the content process.
-class BrowserExtensionContent extends EventEmitter {
+/**
+ * Represents an extension instance in the child process.
+ * Corresponds to the @see {Extension} instance in the parent.
+ */
+export class ExtensionChild extends EventEmitter {
constructor(policy) {
super();
@@ -580,7 +584,7 @@ class BrowserExtensionContent extends EventEmitter {
/**
* An object that runs an remote implementation of an API.
*/
-class ProxyAPIImplementation extends SchemaAPIInterface {
+export class ProxyAPIImplementation extends SchemaAPIInterface {
/**
* @param {string} namespace The full path to the namespace that contains the
* `name` member. This may contain dots, e.g. "storage.local".
@@ -680,7 +684,7 @@ class ProxyAPIImplementation extends SchemaAPIInterface {
}
}
-class ChildLocalAPIImplementation extends LocalAPIImplementation {
+export class ChildLocalAPIImplementation extends LocalAPIImplementation {
constructor(pathObj, namespace, name, childApiManager) {
super(pathObj, name, childApiManager.context);
this.childApiManagerId = childApiManager.id;
@@ -730,7 +734,7 @@ class ChildLocalAPIImplementation extends LocalAPIImplementation {
// JSProcessActor Conduits actors (see ConduitsChild.sys.mjs) to communicate
// with the ParentAPIManager singleton in ExtensionParent.sys.mjs.
// It handles asynchronous function calls as well as event listeners.
-class ChildAPIManager {
+export class ChildAPIManager {
constructor(context, messageManager, localAPICan, contextData) {
this.context = context;
this.messageManager = messageManager;
@@ -1012,14 +1016,3 @@ class ChildAPIManager {
this.permissionsChangedCallbacks.add(callback);
}
}
-
-export var ExtensionChild = {
- BrowserExtensionContent,
- ChildAPIManager,
- ChildLocalAPIImplementation,
- MessageEvent,
- Messenger,
- Port,
- ProxyAPIImplementation,
- SimpleEventAPI,
-};
diff --git a/toolkit/components/extensions/ExtensionCommon.sys.mjs b/toolkit/components/extensions/ExtensionCommon.sys.mjs
index 512d1444a5..c06cf37a6a 100644
--- a/toolkit/components/extensions/ExtensionCommon.sys.mjs
+++ b/toolkit/components/extensions/ExtensionCommon.sys.mjs
@@ -14,6 +14,7 @@ import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -234,7 +235,7 @@ class NoCloneSpreadArgs {
const LISTENERS = Symbol("listeners");
const ONCE_MAP = Symbol("onceMap");
-class EventEmitter {
+export class EventEmitter {
constructor() {
this[LISTENERS] = new Map();
this[ONCE_MAP] = new WeakMap();
@@ -353,7 +354,7 @@ class EventEmitter {
* that inherits from this class, the derived class is instantiated
* once for each extension that uses the API.
*/
-class ExtensionAPI extends EventEmitter {
+export class ExtensionAPI extends EventEmitter {
constructor(extension) {
super();
@@ -388,7 +389,7 @@ class ExtensionAPI extends EventEmitter {
/**
* @param {string} _id
- * @param {Record<string, JSONValue>} _manifest
+ * @param {object} _manifest
*/
static onUpdate(_id, _manifest) {}
}
@@ -468,7 +469,7 @@ class ExtensionAPIPersistent extends ExtensionAPI {
*
* @abstract
*/
-class BaseContext {
+export class BaseContext {
/** @type {boolean} */
isTopContext;
/** @type {string} */
@@ -553,10 +554,7 @@ class BaseContext {
* Opens a conduit linked to this context, populating related address fields.
* Only available in child contexts with an associated contentWindow.
*
- * @param {object} subject
- * @param {ConduitAddress} address
- * @returns {import("ConduitsChild.sys.mjs").PointConduit}
- * @type {ConduitOpen}
+ * @type {ConduitGen}
*/
openConduit(subject, address) {
let wgc = this.contentWindow.windowGlobalChild;
@@ -614,7 +612,7 @@ class BaseContext {
// All child contexts must implement logActivity. This is handled if the child
// context subclasses ExtensionBaseContextChild. ProxyContextParent overrides
// this with a noop for parent contexts.
- logActivity() {
+ logActivity(_type, _name, _data) {
throw new Error(`Not implemented for ${this.envType}`);
}
@@ -822,7 +820,7 @@ class BaseContext {
* exception error.
*
* @param {Error|object} error
- * @param {SavedFrame?} [caller]
+ * @param {nsIStackFrame?} [caller]
* @returns {Error}
*/
normalizeError(error, caller) {
@@ -864,7 +862,7 @@ class BaseContext {
*
* @param {object} error An object with a `message` property. May
* optionally be an `Error` object belonging to the target scope.
- * @param {SavedFrame?} caller
+ * @param {nsIStackFrame?} caller
* The optional caller frame which triggered this callback, to be used
* in error reporting.
* @param {Function} callback The callback to call.
@@ -885,7 +883,7 @@ class BaseContext {
/**
* Captures the most recent stack frame which belongs to the extension.
*
- * @returns {SavedFrame?}
+ * @returns {nsIStackFrame?}
*/
getCaller() {
return ChromeUtils.getCallerLocation(this.principal);
@@ -1037,7 +1035,7 @@ class BaseContext {
*
* @interface
*/
-class SchemaAPIInterface {
+export class SchemaAPIInterface {
/**
* Calls this as a function that returns its return value.
*
@@ -1483,7 +1481,7 @@ class SchemaAPIManager extends EventEmitter {
* "addon" - An addon process.
* "content" - A content process.
* "devtools" - A devtools process.
- * @param {import("Schemas.sys.mjs").SchemaRoot} [schema]
+ * @param {import("Schemas.sys.mjs").SchemaInject} [schema]
*/
constructor(processType, schema) {
super();
@@ -2023,10 +2021,14 @@ export function LocaleData(data) {
this.locales = data.locales || new Map();
this.warnedMissingKeys = new Set();
- // Map(locale-name -> Map(message-key -> localized-string))
- //
- // Contains a key for each loaded locale, each of which is a
- // Map of message keys to their localized strings.
+ /**
+ * Map(locale-name -> Map(message-key -> localized-string))
+ *
+ * Contains a key for each loaded locale, each of which is a
+ * Map of message keys to their localized strings.
+ *
+ * @type {Map<string, Map<string, string>>}
+ */
this.messages = data.messages || new Map();
if (data.builtinMessages) {
diff --git a/toolkit/components/extensions/ExtensionContent.sys.mjs b/toolkit/components/extensions/ExtensionContent.sys.mjs
index 015d1bc7c6..a2fce282ee 100644
--- a/toolkit/components/extensions/ExtensionContent.sys.mjs
+++ b/toolkit/components/extensions/ExtensionContent.sys.mjs
@@ -7,6 +7,7 @@
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -39,8 +40,10 @@ const ScriptError = Components.Constructor(
);
import {
+ ChildAPIManager,
ExtensionChild,
ExtensionActivityLogChild,
+ Messenger,
} from "resource://gre/modules/ExtensionChild.sys.mjs";
import { ExtensionCommon } from "resource://gre/modules/ExtensionCommon.sys.mjs";
import { ExtensionUtils } from "resource://gre/modules/ExtensionUtils.sys.mjs";
@@ -63,8 +66,6 @@ const {
runSafeSyncWithoutClone,
} = ExtensionCommon;
-const { BrowserExtensionContent, ChildAPIManager, Messenger } = ExtensionChild;
-
ChromeUtils.defineLazyGetter(lazy, "isContentScriptProcess", () => {
return (
Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_CONTENT ||
@@ -164,6 +165,7 @@ class ScriptCache extends CacheMap {
super(
SCRIPT_EXPIRY_TIMEOUT_MS,
url => {
+ /** @type {Promise<PrecompiledScript> & { script?: PrecompiledScript }} */
let promise = ChromeUtils.compileScript(url, options);
promise.then(script => {
promise.script = script;
@@ -282,49 +284,37 @@ class CSSCodeCache extends BaseCSSCache {
}
}
-defineLazyGetter(
- BrowserExtensionContent.prototype,
- "staticScripts",
- function () {
- return new ScriptCache({ hasReturnValue: false }, this);
- }
-);
+defineLazyGetter(ExtensionChild.prototype, "staticScripts", function () {
+ return new ScriptCache({ hasReturnValue: false }, this);
+});
-defineLazyGetter(
- BrowserExtensionContent.prototype,
- "dynamicScripts",
- function () {
- return new ScriptCache({ hasReturnValue: true }, this);
- }
-);
+defineLazyGetter(ExtensionChild.prototype, "dynamicScripts", function () {
+ return new ScriptCache({ hasReturnValue: true }, this);
+});
-defineLazyGetter(BrowserExtensionContent.prototype, "userCSS", function () {
+defineLazyGetter(ExtensionChild.prototype, "userCSS", function () {
return new CSSCache(Ci.nsIStyleSheetService.USER_SHEET, this);
});
-defineLazyGetter(BrowserExtensionContent.prototype, "authorCSS", function () {
+defineLazyGetter(ExtensionChild.prototype, "authorCSS", function () {
return new CSSCache(Ci.nsIStyleSheetService.AUTHOR_SHEET, this);
});
// These two caches are similar to the above but specialized to cache the cssCode
// using an hash computed from the cssCode string as the key (instead of the generated data
// URI which can be pretty long for bigger injected cssCode).
-defineLazyGetter(BrowserExtensionContent.prototype, "userCSSCode", function () {
+defineLazyGetter(ExtensionChild.prototype, "userCSSCode", function () {
return new CSSCodeCache(Ci.nsIStyleSheetService.USER_SHEET, this);
});
-defineLazyGetter(
- BrowserExtensionContent.prototype,
- "authorCSSCode",
- function () {
- return new CSSCodeCache(Ci.nsIStyleSheetService.AUTHOR_SHEET, this);
- }
-);
+defineLazyGetter(ExtensionChild.prototype, "authorCSSCode", function () {
+ return new CSSCodeCache(Ci.nsIStyleSheetService.AUTHOR_SHEET, this);
+});
// Represents a content script.
class Script {
/**
- * @param {BrowserExtensionContent} extension
+ * @param {ExtensionChild} extension
* @param {WebExtensionContentScript|object} matcher
* An object with a "matchesWindowGlobal" method and content script
* execution details. This is usually a plain WebExtensionContentScript
@@ -611,7 +601,7 @@ class Script {
* @param {ContentScriptContextChild} context
* The document to block the parsing on, if the scripts are not yet precompiled and cached.
*
- * @returns {Array<PreloadedScript> | Promise<Array<PreloadedScript>>}
+ * @returns {PrecompiledScript[] | Promise<PrecompiledScript[]>}
* Returns an array of preloaded scripts if they are already available, or a promise which
* resolves to the array of the preloaded scripts once they are precompiled and cached.
*/
@@ -667,7 +657,7 @@ class Script {
// Represents a user script.
class UserScript extends Script {
/**
- * @param {BrowserExtensionContent} extension
+ * @param {ExtensionChild} extension
* @param {WebExtensionContentScript|object} matcher
* An object with a "matchesWindowGlobal" method and content script
* execution details.
@@ -1014,7 +1004,7 @@ class ContentScriptContextChild extends BaseContext {
// Responsible for creating ExtensionContexts and injecting content
// scripts into them when new documents are created.
DocumentManager = {
- // Map[windowId -> Map[ExtensionChild -> ContentScriptContextChild]]
+ /** @type {Map<number, Map<ExtensionChild, ContentScriptContextChild>>} */
contexts: new Map(),
initialized: false,
@@ -1115,8 +1105,6 @@ DocumentManager = {
};
export var ExtensionContent = {
- BrowserExtensionContent,
-
contentScripts,
shutdownExtension(extension) {
diff --git a/toolkit/components/extensions/ExtensionDNR.sys.mjs b/toolkit/components/extensions/ExtensionDNR.sys.mjs
index d18856a2b8..afc7d30751 100644
--- a/toolkit/components/extensions/ExtensionDNR.sys.mjs
+++ b/toolkit/components/extensions/ExtensionDNR.sys.mjs
@@ -71,6 +71,7 @@ const gRuleManagers = [];
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -182,6 +183,8 @@ export class Rule {
class Ruleset {
/**
+ * @typedef {number} integer
+ *
* @param {string} rulesetId - extension-defined ruleset ID.
* @param {integer} rulesetPrecedence
* @param {Rule[]} rules - extension-defined rules
@@ -1316,7 +1319,7 @@ class RequestDetails {
* @param {string} options.type - ResourceType (MozContentPolicyType).
* @param {string} [options.method] - HTTP method
* @param {integer} [options.tabId]
- * @param {BrowsingContext} [options.browsingContext] - The BrowsingContext
+ * @param {CanonicalBrowsingContext} [options.browsingContext] - The CBC
* associated with the request. Typically the bc for which the subresource
* request is initiated, if any. For document requests, this is the parent
* (i.e. the parent frame for sub_frame, null for main_frame).
@@ -1975,7 +1978,10 @@ const NetworkIntegration = {
/**
* Applies the actions of the DNR rules.
*
- * @param {ChannelWrapper} channel
+ * @typedef {ChannelWrapper & { _dnrMatchedRules?: MatchedRule[] }}
+ * ChannelWrapperViaDNR
+ *
+ * @param {ChannelWrapperViaDNR} channel
* @returns {boolean} Whether to ignore any responses from the webRequest API.
*/
onBeforeRequest(channel) {
@@ -1993,7 +1999,7 @@ const NetworkIntegration = {
this.applyRedirect(channel, finalMatch);
return true;
case "upgradeScheme":
- this.applyUpgradeScheme(channel, finalMatch);
+ this.applyUpgradeScheme(channel);
return true;
}
// If there are multiple rules, then it may be a combination of allow,
@@ -2294,7 +2300,7 @@ function beforeWebRequestEvent(channel, kind) {
/**
* Applies matching DNR rules, some of which may potentially cancel the request.
*
- * @param {ChannelWrapper} channel
+ * @param {ChannelWrapperViaDNR} channel
* @param {string} kind - The name of the webRequest event.
* @returns {boolean} Whether to ignore any responses from the webRequest API.
*/
diff --git a/toolkit/components/extensions/ExtensionPageChild.sys.mjs b/toolkit/components/extensions/ExtensionPageChild.sys.mjs
index 17c208572b..f0ac5ed229 100644
--- a/toolkit/components/extensions/ExtensionPageChild.sys.mjs
+++ b/toolkit/components/extensions/ExtensionPageChild.sys.mjs
@@ -22,8 +22,9 @@ const CATEGORY_EXTENSION_SCRIPTS_DEVTOOLS = "webextension-scripts-devtools";
import { ExtensionCommon } from "resource://gre/modules/ExtensionCommon.sys.mjs";
import {
- ExtensionChild,
+ ChildAPIManager,
ExtensionActivityLogChild,
+ Messenger,
} from "resource://gre/modules/ExtensionChild.sys.mjs";
import { ExtensionUtils } from "resource://gre/modules/ExtensionUtils.sys.mjs";
@@ -32,8 +33,6 @@ const { getInnerWindowID, promiseEvent } = ExtensionUtils;
const { BaseContext, CanOfAPIs, SchemaAPIManager, redefineGetter } =
ExtensionCommon;
-const { ChildAPIManager, Messenger } = ExtensionChild;
-
const initializeBackgroundPage = context => {
// Override the `alert()` method inside background windows;
// we alias it to console.log().
@@ -188,7 +187,7 @@ export class ExtensionBaseContextChild extends BaseContext {
* This ExtensionBaseContextChild represents an addon execution environment
* that is running in an addon or devtools child process.
*
- * @param {BrowserExtensionContent} extension This context's owner.
+ * @param {ExtensionChild} extension This context's owner.
* @param {object} params
* @param {string} params.envType One of "addon_child" or "devtools_child".
* @param {nsIDOMWindow} params.contentWindow The window where the addon runs.
@@ -301,7 +300,7 @@ class ExtensionPageContextChild extends ExtensionBaseContextChild {
* This is the child side of the ExtensionPageContextParent class
* defined in ExtensionParent.sys.mjs.
*
- * @param {BrowserExtensionContent} extension This context's owner.
+ * @param {ExtensionChild} extension This context's owner.
* @param {object} params
* @param {nsIDOMWindow} params.contentWindow The window where the addon runs.
* @param {string} params.viewType One of "background", "popup", "sidebar" or "tab".
@@ -340,7 +339,7 @@ export class DevToolsContextChild extends ExtensionBaseContextChild {
* environment that has access to the devtools API namespace and to the same subset
* of APIs available in a content script execution environment.
*
- * @param {BrowserExtensionContent} extension This context's owner.
+ * @param {ExtensionChild} extension This context's owner.
* @param {object} params
* @param {nsIDOMWindow} params.contentWindow The window where the addon runs.
* @param {string} params.viewType One of "devtools_page" or "devtools_panel".
@@ -424,7 +423,7 @@ export var ExtensionPageChild = {
/**
* Create a privileged context at initial-document-element-inserted.
*
- * @param {BrowserExtensionContent} extension
+ * @param {ExtensionChild} extension
* The extension for which the context should be created.
* @param {nsIDOMWindow} contentWindow The global of the page.
*/
diff --git a/toolkit/components/extensions/ExtensionParent.sys.mjs b/toolkit/components/extensions/ExtensionParent.sys.mjs
index b4812a702a..f951433713 100644
--- a/toolkit/components/extensions/ExtensionParent.sys.mjs
+++ b/toolkit/components/extensions/ExtensionParent.sys.mjs
@@ -14,6 +14,7 @@ import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -245,8 +246,10 @@ let apiManager = new (class extends SchemaAPIManager {
// to relevant child messengers. Also handles Native messaging and GeckoView.
/** @typedef {typeof ProxyMessenger} NativeMessenger */
const ProxyMessenger = {
- /** @type {Map<number, Partial<ParentPort>&Promise<ParentPort>>} */
+ /** @type {Map<number, ParentPort>} */
ports: new Map(),
+ /** @type {Map<number, Promise>} */
+ portPromises: new Map(),
init() {
this.conduit = new lazy.BroadcastConduit(ProxyMessenger, {
@@ -300,7 +303,8 @@ const ProxyMessenger = {
};
if (JSWindowActorParent.isInstance(source.actor)) {
- let browser = source.actor.browsingContext.top.embedderElement;
+ let { currentWindowContext, top } = source.actor.browsingContext;
+ let browser = top.embedderElement;
let data =
browser && apiManager.global.tabTracker.getBrowserData(browser);
if (data?.tabId > 0) {
@@ -308,6 +312,13 @@ const ProxyMessenger = {
// frameId is documented to only be set if sender.tab is set.
sender.frameId = source.frameId;
}
+
+ let principal = currentWindowContext.documentPrincipal;
+ // We intend the serialization of null principals *and* file scheme to be
+ // "null".
+ sender.origin = new URL(principal.originNoSuffix).origin;
+ } else if (source.verified) {
+ sender.origin = `moz-extension://${extension.uuid}`;
}
return sender;
@@ -363,17 +374,24 @@ const ProxyMessenger = {
}
// PortMessages that follow will need to wait for the port to be opened.
- /** @type {callback} */
- let resolvePort;
- this.ports.set(arg.portId, new Promise(res => (resolvePort = res)));
+ let { promise, resolve, reject } = Promise.withResolvers();
+ this.portPromises.set(arg.portId, promise);
- let kind = await this.normalizeArgs(arg, sender);
- let all = await this.conduit.castPortConnect(kind, arg);
- resolvePort();
+ try {
+ let kind = await this.normalizeArgs(arg, sender);
+ let all = await this.conduit.castPortConnect(kind, arg);
+ resolve();
- // If there are no active onConnect listeners.
- if (!all.some(x => x.value)) {
- throw new ExtensionError(ERROR_NO_RECEIVERS);
+ // If there are no active onConnect listeners.
+ if (!all.some(x => x.value)) {
+ throw new ExtensionError(ERROR_NO_RECEIVERS);
+ }
+ } catch (err) {
+ // Throw _and_ reject with error, so everything awaiting this port fails.
+ reject(err);
+ throw err;
+ } finally {
+ this.portPromises.delete(arg.portId);
}
},
@@ -387,7 +405,7 @@ const ProxyMessenger = {
// NOTE: the following await make sure we await for promised ports
// (ports that were not yet open when added to the Map,
// see recvPortConnect).
- await this.ports.get(sender.portId);
+ await this.portPromises.get(sender.portId);
this.sendPortMessage(sender.portId, holder, !sender.source);
},
@@ -448,7 +466,7 @@ GlobalManager = {
extensionMap: new Map(),
initialized: false,
- /** @type {WeakMap<Browser, object>} Extension Context init data. */
+ /** @type {WeakMap<XULBrowserElement, object>} Extension Context init data. */
frameData: new WeakMap(),
init(extension) {
@@ -961,7 +979,6 @@ ParentAPIManager = {
throw new Error(`Bad sender context envType: ${sender.envType}`);
}
- let isBackgroundWorker = false;
if (JSWindowActorParent.isInstance(actor)) {
const target = actor.browsingContext.top.embedderElement;
let processMessageManager =
@@ -979,6 +996,22 @@ ParentAPIManager = {
"Attempt to create privileged extension parent from incorrect child process"
);
}
+
+ if (envType == "addon_parent") {
+ context = new ExtensionPageContextParent(
+ envType,
+ extension,
+ data,
+ actor.browsingContext
+ );
+ } else if (envType == "devtools_parent") {
+ context = new DevToolsExtensionPageContextParent(
+ envType,
+ extension,
+ data,
+ actor.browsingContext
+ );
+ }
} else if (JSProcessActorParent.isInstance(actor)) {
if (actor.manager.remoteType !== extension.remoteType) {
throw new Error(
@@ -996,7 +1029,7 @@ ParentAPIManager = {
`Unexpected viewType ${data.viewType} on an extension process actor`
);
}
- isBackgroundWorker = true;
+ context = new BackgroundWorkerContextParent(envType, extension, data);
} else {
// Unreacheable: JSWindowActorParent and JSProcessActorParent are the
// only actors.
@@ -1004,24 +1037,6 @@ ParentAPIManager = {
"Attempt to create privileged extension parent via incorrect actor"
);
}
-
- if (isBackgroundWorker) {
- context = new BackgroundWorkerContextParent(envType, extension, data);
- } else if (envType == "addon_parent") {
- context = new ExtensionPageContextParent(
- envType,
- extension,
- data,
- actor.browsingContext
- );
- } else if (envType == "devtools_parent") {
- context = new DevToolsExtensionPageContextParent(
- envType,
- extension,
- data,
- actor.browsingContext
- );
- }
} else if (envType == "content_parent") {
// Note: actor is always a JSWindowActorParent, with a browsingContext.
context = new ContentScriptContextParent(
@@ -1340,11 +1355,9 @@ class HiddenXULWindow {
// The windowless browser is a thin wrapper around a docShell that keeps
// its related resources alive. It implements nsIWebNavigation and
- // forwards its methods to the underlying docShell. That .docShell
- // needs `QueryInterface(nsIWebNavigation)` to give us access to the
- // webNav methods that are already available on the windowless browser.
+ // forwards its methods to the underlying docShell.
let chromeShell = windowlessBrowser.docShell;
- chromeShell.QueryInterface(Ci.nsIWebNavigation);
+ let webNav = chromeShell.QueryInterface(Ci.nsIWebNavigation);
if (lazy.PrivateBrowsingUtils.permanentPrivateBrowsing) {
let attrs = chromeShell.getOriginAttributes();
@@ -1353,13 +1366,13 @@ class HiddenXULWindow {
}
windowlessBrowser.browsingContext.useGlobalHistory = false;
- chromeShell.loadURI(DUMMY_PAGE_URI, {
+ webNav.loadURI(DUMMY_PAGE_URI, {
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
});
await promiseObserved(
"chrome-document-global-created",
- win => win.document == chromeShell.document
+ win => win.document == webNav.document
);
await promiseDocumentLoaded(windowlessBrowser.document);
if (this.unloaded) {
@@ -1376,7 +1389,7 @@ class HiddenXULWindow {
* An object that contains the xul attributes to set of the newly
* created browser XUL element.
*
- * @returns {Promise<XULElement>}
+ * @returns {Promise<XULBrowserElement>}
* A Promise which resolves to the newly created browser XUL element.
*/
async createBrowserElement(xulAttributes) {
@@ -1458,16 +1471,17 @@ const SharedWindow = {
* to inherits the shared boilerplate code needed to create a parent document for the hidden
* extension pages (e.g. the background page, the devtools page) in the BackgroundPage and
* DevToolsPage classes.
- *
- * @param {Extension} extension
- * The Extension which owns the hidden extension page created (used to decide
- * if the hidden extension page parent doc is going to be a windowlessBrowser or
- * a visible XUL window).
- * @param {string} viewType
- * The viewType of the WebExtension page that is going to be loaded
- * in the created browser element (e.g. "background" or "devtools_page").
*/
class HiddenExtensionPage {
+ /**
+ * @param {Extension} extension
+ * The Extension which owns the hidden extension page created (used to decide
+ * if the hidden extension page parent doc is going to be a windowlessBrowser or
+ * a visible XUL window).
+ * @param {string} viewType
+ * The viewType of the WebExtension page that is going to be loaded
+ * in the created browser element (e.g. "background" or "devtools_page").
+ */
constructor(extension, viewType) {
if (!extension || !viewType) {
throw new Error("extension and viewType parameters are mandatory");
@@ -1535,6 +1549,9 @@ class HiddenExtensionPage {
}
}
+/** @typedef {import("resource://devtools/server/actors/descriptors/webextension.js")
+ .WebExtensionDescriptorActor} WebExtensionDescriptorActor */
+
/**
* This object provides utility functions needed by the devtools actors to
* be able to connect and debug an extension (which can run in the main or in
@@ -1545,9 +1562,9 @@ const DebugUtils = {
// which are used to connect the webextension patent actor to the extension process.
hiddenXULWindow: null,
- // Map<extensionId, Promise<XULElement>>
+ /** @type {Map<string, Promise<XULBrowserElement> & { browser: XULBrowserElement }>} */
debugBrowserPromises: new Map(),
- // DefaultWeakMap<Promise<browser XULElement>, Set<WebExtensionParentActor>>
+ /** @type {WeakMap<Promise<XULBrowserElement>, Set<WebExtensionDescriptorActor>>} */
debugActors: new DefaultWeakMap(() => new Set()),
_extensionUpdatedWatcher: null,
@@ -1696,10 +1713,10 @@ const DebugUtils = {
* Retrieve a XUL browser element which has been configured to be able to connect
* the devtools actor with the process where the extension is running.
*
- * @param {WebExtensionParentActor} webExtensionParentActor
+ * @param {WebExtensionDescriptorActor} webExtensionParentActor
* The devtools actor that is retrieving the browser element.
*
- * @returns {Promise<XULElement>}
+ * @returns {Promise<XULBrowserElement>}
* A promise which resolves to the configured browser XUL element.
*/
async getExtensionProcessBrowser(webExtensionParentActor) {
@@ -1753,7 +1770,7 @@ const DebugUtils = {
* it destroys the XUL browser element, and it also destroy the hidden XUL window
* if it is not currently needed.
*
- * @param {WebExtensionParentActor} webExtensionParentActor
+ * @param {WebExtensionDescriptorActor} webExtensionParentActor
* The devtools actor that has retrieved an addon debug browser element.
*/
async releaseExtensionProcessBrowser(webExtensionParentActor) {
@@ -1783,7 +1800,7 @@ const DebugUtils = {
* was received by the message manager. The promise is rejected if the message
* manager was closed before a message was received.
*
- * @param {nsIMessageListenerManager} messageManager
+ * @param {MessageListenerManager} messageManager
* The message manager on which to listen for messages.
* @param {string} messageName
* The message to listen for.
diff --git a/toolkit/components/extensions/ExtensionPermissions.sys.mjs b/toolkit/components/extensions/ExtensionPermissions.sys.mjs
index 1ee9afdc32..964503d8f6 100644
--- a/toolkit/components/extensions/ExtensionPermissions.sys.mjs
+++ b/toolkit/components/extensions/ExtensionPermissions.sys.mjs
@@ -8,11 +8,13 @@ import { computeSha1HashAsString } from "resource://gre/modules/addons/crypto-ut
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
AddonManager: "resource://gre/modules/AddonManager.sys.mjs",
AddonManagerPrivate: "resource://gre/modules/AddonManager.sys.mjs",
+ Extension: "resource://gre/modules/Extension.sys.mjs",
ExtensionParent: "resource://gre/modules/ExtensionParent.sys.mjs",
FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
JSONFile: "resource://gre/modules/JSONFile.sys.mjs",
@@ -347,6 +349,50 @@ export var ExtensionPermissions = {
return this._getCached(extensionId);
},
+ /**
+ * Validate and normalize passed in `perms`, including a fixup to
+ * include all possible "all sites" permissions when appropriate.
+ *
+ * @throws if an origin or permission is not part of optional permissions.
+ *
+ * @typedef {object} Perms
+ * @property {string[]} origins
+ * @property {string[]} permissions
+ *
+ * @param {Perms} perms api permissions and origins to be added/removed.
+ * @param {Perms} optional permissions and origins from the manifest.
+ * @returns {Perms} normalized
+ */
+ normalizeOptional(perms, optional) {
+ let allSites = false;
+ let patterns = new MatchPatternSet(optional.origins, { ignorePath: true });
+ let normalized = Object.assign({}, perms);
+
+ for (let o of perms.origins) {
+ if (!patterns.subsumes(new MatchPattern(o))) {
+ throw new Error(`${o} was not declared in the manifest`);
+ }
+ // If this is one of the "all sites" permissions
+ allSites ||= lazy.Extension.isAllSitesPermission(o);
+ }
+
+ if (allSites) {
+ // Grant/revoke ALL "all sites" optional permissions from the manifest.
+ let origins = perms.origins.concat(
+ optional.origins.filter(o => lazy.Extension.isAllSitesPermission(o))
+ );
+ normalized.origins = Array.from(new Set(origins));
+ }
+
+ for (let p of perms.permissions) {
+ if (!optional.permissions.includes(p)) {
+ throw new Error(`${p} was not declared in optional_permissions`);
+ }
+ }
+
+ return normalized;
+ },
+
_fixupAllUrlsPerms(perms) {
// Unfortunately, we treat <all_urls> as an API permission as well.
// If it is added to either, ensure it is added to both.
@@ -361,8 +407,10 @@ export var ExtensionPermissions = {
* Add new permissions for the given extension. `permissions` is
* in the format that is passed to browser.permissions.request().
*
+ * @typedef {import("ExtensionCommon.sys.mjs").EventEmitter} EventEmitter
+ *
* @param {string} extensionId The extension id
- * @param {object} perms Object with permissions and origins array.
+ * @param {Perms} perms Object with permissions and origins array.
* @param {EventEmitter} [emitter] optional object implementing emitter interfaces
*/
async add(extensionId, perms, emitter) {
@@ -401,7 +449,7 @@ export var ExtensionPermissions = {
* in the format that is passed to browser.permissions.request().
*
* @param {string} extensionId The extension id
- * @param {object} perms Object with permissions and origins array.
+ * @param {Perms} perms Object with permissions and origins array.
* @param {EventEmitter} [emitter] optional object implementing emitter interfaces
*/
async remove(extensionId, perms, emitter) {
diff --git a/toolkit/components/extensions/ExtensionProcessScript.sys.mjs b/toolkit/components/extensions/ExtensionProcessScript.sys.mjs
index 93746cb0ca..ef5694be9f 100644
--- a/toolkit/components/extensions/ExtensionProcessScript.sys.mjs
+++ b/toolkit/components/extensions/ExtensionProcessScript.sys.mjs
@@ -11,6 +11,7 @@
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -49,7 +50,7 @@ ChromeUtils.defineLazyGetter(lazy, "isContentScriptProcess", () => {
});
var extensions = new DefaultWeakMap(policy => {
- return new lazy.ExtensionChild.BrowserExtensionContent(policy);
+ return new lazy.ExtensionChild(policy);
});
var pendingExtensions = new Map();
@@ -342,7 +343,7 @@ ExtensionManager = {
perms.delete(perm);
}
}
- policy.permissions = perms;
+ policy.permissions = Array.from(perms);
}
}
diff --git a/toolkit/components/extensions/ExtensionShortcuts.sys.mjs b/toolkit/components/extensions/ExtensionShortcuts.sys.mjs
index 7b30fa2cdf..17cff67eb9 100644
--- a/toolkit/components/extensions/ExtensionShortcuts.sys.mjs
+++ b/toolkit/components/extensions/ExtensionShortcuts.sys.mjs
@@ -6,6 +6,7 @@ import { ExtensionCommon } from "resource://gre/modules/ExtensionCommon.sys.mjs"
import { ExtensionUtils } from "resource://gre/modules/ExtensionUtils.sys.mjs";
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -462,7 +463,6 @@ export class ExtensionShortcuts {
let win = event.target.ownerGlobal;
action.triggerAction(win);
} else {
- this.extension.tabManager.addActiveTabPermission();
this.onCommand(name);
}
});
diff --git a/toolkit/components/extensions/ExtensionStorage.sys.mjs b/toolkit/components/extensions/ExtensionStorage.sys.mjs
index b1b09d137f..5317fb2a91 100644
--- a/toolkit/components/extensions/ExtensionStorage.sys.mjs
+++ b/toolkit/components/extensions/ExtensionStorage.sys.mjs
@@ -8,6 +8,7 @@ import { ExtensionUtils } from "resource://gre/modules/ExtensionUtils.sys.mjs";
const { DefaultWeakMap, ExtensionError } = ExtensionUtils;
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -89,7 +90,7 @@ function serialize(name, anonymizedName, value) {
}
export var ExtensionStorage = {
- // Map<extension-id, Promise<JSONFile>>
+ /** @type {Map<string, Promise<typeof lazy.JSONFile>>} */
jsonFilePromises: new Map(),
listeners: new Map(),
@@ -157,7 +158,7 @@ export var ExtensionStorage = {
*
* @param {any} value
* The value to sanitize.
- * @param {Context} context
+ * @param {BaseContext} context
* The extension context in which to sanitize the value
* @returns {value}
* The sanitized value.
diff --git a/toolkit/components/extensions/ExtensionStorageIDB.sys.mjs b/toolkit/components/extensions/ExtensionStorageIDB.sys.mjs
index 604d29b4cf..5223d57466 100644
--- a/toolkit/components/extensions/ExtensionStorageIDB.sys.mjs
+++ b/toolkit/components/extensions/ExtensionStorageIDB.sys.mjs
@@ -5,6 +5,7 @@
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
import { IndexedDB } from "resource://gre/modules/IndexedDB.sys.mjs";
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -801,6 +802,8 @@ export var ExtensionStorageIDB = {
* from the internal IndexedDB operations have to be converted into an ExtensionError
* to be accessible to the extension code).
*
+ * @typedef {import("ExtensionUtils.sys.mjs").ExtensionError} ExtensionError
+ *
* @param {object} params
* @param {Error|ExtensionError|DOMException} params.error
* The error object to normalize.
diff --git a/toolkit/components/extensions/ExtensionStorageSync.sys.mjs b/toolkit/components/extensions/ExtensionStorageSync.sys.mjs
index 3f82d91fac..06f0cc4310 100644
--- a/toolkit/components/extensions/ExtensionStorageSync.sys.mjs
+++ b/toolkit/components/extensions/ExtensionStorageSync.sys.mjs
@@ -10,6 +10,7 @@ import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
const NS_ERROR_DOM_QUOTA_EXCEEDED_ERR = 0x80530016;
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -55,6 +56,7 @@ ExtensionStorageApiCallback.prototype = {
},
handleError(code, message) {
+ /** @type {Error & { code?: number }} */
let e = new Error(message);
e.code = code;
Cu.reportError(e);
diff --git a/toolkit/components/extensions/ExtensionStorageSyncKinto.sys.mjs b/toolkit/components/extensions/ExtensionStorageSyncKinto.sys.mjs
index ace6e16c2c..62493e3b07 100644
--- a/toolkit/components/extensions/ExtensionStorageSyncKinto.sys.mjs
+++ b/toolkit/components/extensions/ExtensionStorageSyncKinto.sys.mjs
@@ -32,6 +32,7 @@ import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
import { ExtensionUtils } from "resource://gre/modules/ExtensionUtils.sys.mjs";
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -120,11 +121,6 @@ function throwIfNoFxA(fxAccounts, action) {
}
}
-// Global ExtensionStorageSyncKinto instance that extensions and Fx Sync use.
-// On Android, because there's no FXAccounts instance, any syncing
-// operations will fail.
-export var extensionStorageSyncKinto = null;
-
/**
* Utility function to enforce an order of fields when computing an HMAC.
*
@@ -558,6 +554,8 @@ class CryptoCollection {
* "characters" are values, each within [0, 255]. You can produce
* such a bytestring using e.g. CommonUtils.encodeUTF8.
*
+ * @typedef {string} bytestring
+ *
* The returned value is a base64url-encoded string of the hash.
*
* @param {bytestring} value The value to be hashed.
@@ -696,7 +694,7 @@ let CollectionKeyEncryptionRemoteTransformer = class extends EncryptionRemoteTra
*
* @param {Extension} extension
* The extension whose context just ended.
- * @param {Context} context
+ * @param {BaseContext} context
* The context that just ended.
*/
function cleanUpForContext(extension, context) {
@@ -1199,7 +1197,7 @@ export class ExtensionStorageSyncKinto {
* @param {Extension} extension
* The extension for which we are seeking
* a collection.
- * @param {Context} context
+ * @param {BaseContext} context
* The context of the extension, so that we can
* stop syncing the collection when the extension ends.
* @returns {Promise<Collection>}
@@ -1370,7 +1368,14 @@ export class ExtensionStorageSyncKinto {
}
}
-extensionStorageSyncKinto = new ExtensionStorageSyncKinto(_fxaService);
+/**
+ * Global ExtensionStorageSyncKinto instance that extensions and Fx Sync use.
+ * On Android, because there's no FXAccounts instance, any syncing
+ * operations will fail.
+ */
+export const extensionStorageSyncKinto = new ExtensionStorageSyncKinto(
+ _fxaService
+);
// For test use only.
export const KintoStorageTestUtils = {
diff --git a/toolkit/components/extensions/ExtensionTelemetry.sys.mjs b/toolkit/components/extensions/ExtensionTelemetry.sys.mjs
index 57f052372c..95b71ce007 100644
--- a/toolkit/components/extensions/ExtensionTelemetry.sys.mjs
+++ b/toolkit/components/extensions/ExtensionTelemetry.sys.mjs
@@ -68,7 +68,7 @@ export function getTrimmedString(str) {
* If the resulting string is longer than 80 characters it is going to be
* trimmed using the `getTrimmedString` helper function.
*
- * @param {Error | DOMException | Components.Exception} error
+ * @param {Error | DOMException | ReturnType<typeof Components.Exception>} error
* The error object to convert into a string representation.
*
* @returns {string}
@@ -146,7 +146,7 @@ class ExtensionTelemetryMetric {
* @param {string} metric
* The Glean timing_distribution metric to record (used to retrieve the Glean metric type from the
* GLEAN_METRICS_TYPES map).
- * @param {Extension | BrowserExtensionContent} extension
+ * @param {Extension | ExtensionChild} extension
* The extension to record the telemetry for.
* @param {any | undefined} [obj = extension]
* An optional object the timing_distribution method call should be related to
@@ -207,7 +207,7 @@ class ExtensionTelemetryMetric {
* The stopwatch method to call ("start", "finish" or "cancel").
* @param {string} metric
* The stopwatch metric to record (used to retrieve the base histogram id from the HISTOGRAMS_IDS object).
- * @param {Extension | BrowserExtensionContent} extension
+ * @param {Extension | ExtensionChild} extension
* The extension to record the telemetry for.
* @param {any | undefined} [obj = extension]
* An optional telemetry stopwatch object (which defaults to the extension parameter when missing).
@@ -242,7 +242,7 @@ class ExtensionTelemetryMetric {
* @param {string} metric
* The metric to record (used to retrieve the base histogram id from the _histogram object).
* @param {object} options
- * @param {Extension | BrowserExtensionContent} options.extension
+ * @param {Extension | ExtensionChild} options.extension
* The extension to record the telemetry for.
* @param {string | undefined} [options.category]
* An optional histogram category.
@@ -290,7 +290,7 @@ class ExtensionTelemetryMetric {
// NOTE: extensionsTiming may become a property of the GLEAN_METRICS_TYPES
// map once we may introduce new histograms that are not part of the
// extensionsTiming Glean metrics category.
- Glean.extensionsTiming[metric].accumulateSamples([value]);
+ Glean.extensionsTiming[metric].accumulateSingleSample(value);
break;
}
case "labeled_counter": {
@@ -324,6 +324,8 @@ const metricsCache = new Map();
* ExtensionTelemetry.extensionStartup.stopwatchStart(extension);
* ExtensionTelemetry.browserActionPreloadResult.histogramAdd({category: "Shown", extension});
*/
+/** @type {Record<string, ExtensionTelemetryMetric>} */
+// @ts-ignore no easy way in TS to say Proxy is a different type from target.
export var ExtensionTelemetry = new Proxy(metricsCache, {
get(target, prop) {
// NOTE: if we would be start adding glean probes that do not have a unified
diff --git a/toolkit/components/extensions/ExtensionUtils.sys.mjs b/toolkit/components/extensions/ExtensionUtils.sys.mjs
index 45f22aa530..84f7b25c01 100644
--- a/toolkit/components/extensions/ExtensionUtils.sys.mjs
+++ b/toolkit/components/extensions/ExtensionUtils.sys.mjs
@@ -43,7 +43,7 @@ function promiseTimeout(delay) {
* An Error subclass for which complete error messages are always passed
* to extensions, rather than being interpreted as an unknown error.
*/
-class ExtensionError extends DOMException {
+export class ExtensionError extends DOMException {
constructor(message) {
super(message, "ExtensionError");
}
@@ -67,7 +67,7 @@ function filterStack(error) {
* only logged internally and raised to the worker script as
* the generic unexpected error).
*/
-class WorkerExtensionError extends DOMException {
+export class WorkerExtensionError extends DOMException {
constructor(message) {
super(message, "Error");
}
@@ -122,9 +122,9 @@ function getInnerWindowID(window) {
* A set with a limited number of slots, which flushes older entries as
* newer ones are added.
*
- * @param {integer} limit
+ * @param {number} limit
* The maximum size to trim the set to after it grows too large.
- * @param {integer} [slop = limit * .25]
+ * @param {number} [slop = limit * .25]
* The number of extra entries to allow in the set after it
* reaches the size limit, before it is truncated to the limit.
* @param {Iterable} [iterable]
@@ -345,5 +345,4 @@ export var ExtensionUtils = {
DefaultWeakMap,
ExtensionError,
LimitedSet,
- WorkerExtensionError,
};
diff --git a/toolkit/components/extensions/ExtensionWorkerChild.sys.mjs b/toolkit/components/extensions/ExtensionWorkerChild.sys.mjs
index 44188d7ddb..d3fd477b10 100644
--- a/toolkit/components/extensions/ExtensionWorkerChild.sys.mjs
+++ b/toolkit/components/extensions/ExtensionWorkerChild.sys.mjs
@@ -10,8 +10,14 @@
*/
import {
- ExtensionChild,
+ ChildAPIManager,
+ ChildLocalAPIImplementation,
ExtensionActivityLogChild,
+ MessageEvent,
+ Messenger,
+ Port,
+ ProxyAPIImplementation,
+ SimpleEventAPI,
} from "resource://gre/modules/ExtensionChild.sys.mjs";
import { ExtensionCommon } from "resource://gre/modules/ExtensionCommon.sys.mjs";
@@ -19,20 +25,13 @@ import {
ExtensionPageChild,
getContextChildManagerGetter,
} from "resource://gre/modules/ExtensionPageChild.sys.mjs";
-import { ExtensionUtils } from "resource://gre/modules/ExtensionUtils.sys.mjs";
+import {
+ ExtensionUtils,
+ WorkerExtensionError,
+} from "resource://gre/modules/ExtensionUtils.sys.mjs";
const { BaseContext, redefineGetter } = ExtensionCommon;
-const {
- ChildAPIManager,
- ChildLocalAPIImplementation,
- MessageEvent,
- Messenger,
- Port,
- ProxyAPIImplementation,
- SimpleEventAPI,
-} = ExtensionChild;
-
const { DefaultMap, getUniqueId } = ExtensionUtils;
/**
@@ -341,7 +340,7 @@ class WebIDLChildAPIManager extends ChildAPIManager {
* The object that represents the API request received
* (including arguments, an event listener wrapper etc)
*
- * @returns {mozIExtensionAPIRequestResult}
+ * @returns {Partial<mozIExtensionAPIRequestResult>}
* Result for the API request, either a value to be returned
* (which has to be a value that can be structure cloned
* if the request was originated from the worker thread) or
@@ -373,9 +372,8 @@ class WebIDLChildAPIManager extends ChildAPIManager {
* into the expected mozIExtensionAPIRequestResult.
*
* @param {Error | WorkerExtensionError} error
- * @returns {mozIExtensionAPIRequestResult}
+ * @returns {Partial<mozIExtensionAPIRequestResult>}
*/
-
handleExtensionError(error) {
// Propagate an extension error to the caller on the worker thread.
if (error instanceof this.context.Error) {
@@ -402,7 +400,6 @@ class WebIDLChildAPIManager extends ChildAPIManager {
* @returns {any}
* @throws {Error | WorkerExtensionError}
*/
-
callAPIImplementation(request, impl) {
const { requestType, normalizedArgs } = request;
@@ -480,7 +477,7 @@ class WebIDLChildAPIManager extends ChildAPIManager {
* Return an ExtensionAPI class instance given its namespace.
*
* @param {string} namespace
- * @returns {ExtensionAPI}
+ * @returns {import("ExtensionCommon.sys.mjs").ExtensionAPI}
*/
getExtensionAPIInstance(namespace) {
return this.apiCan.apis.get(namespace);
@@ -569,7 +566,7 @@ class WorkerContextChild extends BaseContext {
* This WorkerContextChild represents an addon execution environment
* that is running on the worker thread in an extension child process.
*
- * @param {BrowserExtensionContent} extension This context's owner.
+ * @param {ExtensionChild} extension This context's owner.
* @param {object} params
* @param {mozIExtensionServiceWorkerInfo} params.serviceWorkerInfo
*/
@@ -607,7 +604,7 @@ class WorkerContextChild extends BaseContext {
// ExtensionAPIRequestHandler as errors that should be propagated to
// the worker thread and received by extension code that originated
// the API request.
- Error: ExtensionUtils.WorkerExtensionError,
+ Error: WorkerExtensionError,
};
}
@@ -616,6 +613,7 @@ class WorkerContextChild extends BaseContext {
return { workerDescriptorId };
}
+ /** @type {ConduitGen} */
openConduit(subject, address) {
let proc = ChromeUtils.domProcessChild;
let conduit = proc.getActor("ProcessConduits").openConduit(subject, {
@@ -658,7 +656,7 @@ class WorkerContextChild extends BaseContext {
* Captures the most recent stack frame from the WebIDL API request being
* processed.
*
- * @returns {SavedFrame?}
+ * @returns {nsIStackFrame}
*/
getCaller() {
return this.webidlAPIRequest?.callerSavedFrame;
@@ -719,7 +717,7 @@ export var ExtensionWorkerChild = {
* Create an extension worker context (on a mozExtensionAPIRequest with
* requestType "initWorkerContext").
*
- * @param {BrowserExtensionContent} extension
+ * @param {ExtensionChild} extension
* The extension for which the context should be created.
* @param {mozIExtensionServiceWorkerInfo} serviceWorkerInfo
*/
@@ -751,7 +749,7 @@ export var ExtensionWorkerChild = {
* Get an existing extension worker context for the given extension and
* service worker.
*
- * @param {BrowserExtensionContent} extension
+ * @param {ExtensionChild} extension
* The extension for which the context should be created.
* @param {mozIExtensionServiceWorkerInfo} serviceWorkerInfo
*
diff --git a/toolkit/components/extensions/ExtensionXPCShellUtils.sys.mjs b/toolkit/components/extensions/ExtensionXPCShellUtils.sys.mjs
index 27323dc8b3..3489a2caba 100644
--- a/toolkit/components/extensions/ExtensionXPCShellUtils.sys.mjs
+++ b/toolkit/components/extensions/ExtensionXPCShellUtils.sys.mjs
@@ -7,6 +7,7 @@
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
import { XPCShellContentUtils } from "resource://testing-common/XPCShellContentUtils.sys.mjs";
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -32,9 +33,9 @@ let BASE_MANIFEST = Object.freeze({
});
class ExtensionWrapper {
- /** @type {AddonWrapper} */
+ /** @type {import("resource://gre/modules/addons/XPIDatabase.sys.mjs").AddonWrapper} */
addon;
- /** @type {Promise<AddonWrapper>} */
+ /** @type {Promise} */
addonPromise;
/** @type {nsIFile[]} */
cleanupFiles;
diff --git a/toolkit/components/extensions/Schemas.sys.mjs b/toolkit/components/extensions/Schemas.sys.mjs
index e98dfb36f0..b107036355 100644
--- a/toolkit/components/extensions/Schemas.sys.mjs
+++ b/toolkit/components/extensions/Schemas.sys.mjs
@@ -11,6 +11,7 @@ import { ExtensionUtils } from "resource://gre/modules/ExtensionUtils.sys.mjs";
var { DefaultMap, DefaultWeakMap } = ExtensionUtils;
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -262,6 +263,23 @@ const POSTPROCESSORS = {
return string;
},
+ webRequestBlockingOrAuthProviderPermissionRequired(string, context) {
+ if (
+ string === "blocking" &&
+ !(
+ context.hasPermission("webRequestBlocking") ||
+ context.hasPermission("webRequestAuthProvider")
+ )
+ ) {
+ throw new context.cloneScope.Error(
+ "Using webRequest.onAuthRequired.addListener with the " +
+ "blocking option requires either the 'webRequestBlocking' " +
+ "or 'webRequestAuthProvider' permission."
+ );
+ }
+
+ return string;
+ },
requireBackgroundServiceWorkerEnabled(value, context) {
if (WebExtensionPolicy.backgroundServiceWorkerEnabled) {
return value;
@@ -904,7 +922,8 @@ class InjectionContext extends Context {
* @param {string} _namespace The full path to the namespace of the API, minus
* the name of the method or property. E.g. "storage.local".
* @param {string} _name The name of the method, property or event.
- * @returns {SchemaAPIInterface} The implementation of the API.
+ * @returns {import("ExtensionCommon.sys.mjs").SchemaAPIInterface}
+ * The implementation of the API.
*/
getImplementation(_namespace, _name) {
throw new Error("Not implemented");
@@ -1125,7 +1144,7 @@ const FORMATS = {
},
strictRelativeUrl(string, context) {
- void FORMATS.unresolvedRelativeUrl(string, context);
+ void FORMATS.unresolvedRelativeUrl(string);
return FORMATS.relativeUrl(string, context);
},
@@ -1220,8 +1239,8 @@ const FORMATS = {
throw new Error(errorMessage);
},
- manifestShortcutKeyOrEmpty(string, context) {
- return string === "" ? "" : FORMATS.manifestShortcutKey(string, context);
+ manifestShortcutKeyOrEmpty(string) {
+ return string === "" ? "" : FORMATS.manifestShortcutKey(string);
},
versionString(string, context) {
@@ -1470,26 +1489,33 @@ class Type extends Entry {
}
}
- // Takes a value, checks that it has the correct type, and returns a
- // "normalized" version of the value. The normalized version will
- // include "nulls" in place of omitted optional properties. The
- // result of this function is either {error: "Some type error"} or
- // {value: <normalized-value>}.
+ /**
+ * Takes a value, checks that it has the correct type, and returns a
+ * "normalized" version of the value. The normalized version will
+ * include "nulls" in place of omitted optional properties. The
+ * result of this function is either {error: "Some type error"} or
+ * {value: <normalized-value>}.
+ */
normalize(value, context) {
return context.error("invalid type");
}
- // Unlike normalize, this function does a shallow check to see if
- // |baseType| (one of the possible getValueBaseType results) is
- // valid for this type. It returns true or false. It's used to fill
- // in optional arguments to functions before actually type checking
-
- checkBaseType() {
+ /**
+ * Unlike normalize, this function does a shallow check to see if
+ * |baseType| (one of the possible getValueBaseType results) is
+ * valid for this type. It returns true or false. It's used to fill
+ * in optional arguments to functions before actually type checking
+ *
+ * @param {string} _baseType
+ */
+ checkBaseType(_baseType) {
return false;
}
- // Helper method that simply relies on checkBaseType to implement
- // normalize. Subclasses can choose to use it or not.
+ /**
+ * Helper method that simply relies on checkBaseType to implement
+ * normalize. Subclasses can choose to use it or not.
+ */
normalizeBase(type, value, context) {
if (this.checkBaseType(getValueBaseType(value))) {
this.checkDeprecated(context, value);
@@ -3458,22 +3484,26 @@ class SchemaRoots extends Namespaces {
* other schema roots. May extend a base namespace, in which case schemas in
* this root may refer to types in a base, but not vice versa.
*
- * @param {SchemaRoot|Array<SchemaRoot>|null} base
- * A base schema root (or roots) from which to derive, or null.
- * @param {Map<string, Array|StructuredCloneHolder>} schemaJSON
- * A map of schema URLs and corresponding JSON blobs from which to
- * populate this root namespace.
+ * @implements {SchemaInject}
*/
export class SchemaRoot extends Namespace {
+ /**
+ * @param {SchemaRoot|SchemaRoot[]} base
+ * A base schema root (or roots) from which to derive, or null.
+ * @param {Map<string, Array|StructuredCloneHolder>} schemaJSON
+ * A map of schema URLs and corresponding JSON blobs from which to
+ * populate this root namespace.
+ */
constructor(base, schemaJSON) {
super(null, "", []);
if (Array.isArray(base)) {
- base = new SchemaRoots(this, base);
+ this.base = new SchemaRoots(this, base);
+ } else {
+ this.base = base;
}
this.root = this;
- this.base = base;
this.schemaJSON = schemaJSON;
}
@@ -3555,7 +3585,7 @@ export class SchemaRoot extends Namespace {
parseSchemas() {
for (let [key, schema] of this.schemaJSON.entries()) {
try {
- if (typeof schema.deserialize === "function") {
+ if (StructuredCloneHolder.isInstance(schema)) {
schema = schema.deserialize(globalThis, isParentProcess);
// If we're in the parent process, we need to keep the
@@ -3607,7 +3637,7 @@ export class SchemaRoot extends Namespace {
*
* @param {object} dest The root namespace for the APIs.
* This object is usually exposed to extensions as "chrome" or "browser".
- * @param {object} wrapperFuncs An implementation of the InjectionContext
+ * @param {InjectionContext} wrapperFuncs An implementation of the InjectionContext
* interface, which runs the actual functionality of the generated API.
*/
inject(dest, wrapperFuncs) {
@@ -3651,6 +3681,11 @@ export class SchemaRoot extends Namespace {
}
}
+/**
+ * @typedef {{ inject: typeof Schemas.inject }} SchemaInject
+ * Interface SchemaInject as used by SchemaApiManager,
+ * with the one method shared across Schemas and SchemaRoot.
+ */
export var Schemas = {
initialized: false,
@@ -3676,6 +3711,7 @@ export var Schemas = {
extContext => new Context(extContext)
),
+ /** @returns {SchemaRoot} */
get rootSchema() {
if (!this.initialized) {
this.init();
@@ -3833,7 +3869,7 @@ export var Schemas = {
*
* @param {object} dest The root namespace for the APIs.
* This object is usually exposed to extensions as "chrome" or "browser".
- * @param {object} wrapperFuncs An implementation of the InjectionContext
+ * @param {InjectionContext} wrapperFuncs An implementation of the InjectionContext
* interface, which runs the actual functionality of the generated API.
*/
inject(dest, wrapperFuncs) {
@@ -3898,6 +3934,7 @@ export var Schemas = {
? `${apiNamespace}.${apiName}.${requestType}`
: `${apiNamespace}.${apiName}`
).split(".");
+ /** @type {Namespace|CallEntry} */
let apiSchema = this.getNamespace(ns);
// Keep track of the current schema path, populated while navigating the nested API schema
@@ -3926,7 +3963,7 @@ export var Schemas = {
throw new Error(`API Schema not found for ${schemaPath.join(".")}`);
}
- if (!apiSchema.checkParameters) {
+ if (!(apiSchema instanceof CallEntry)) {
throw new Error(
`Unexpected API Schema type for ${schemaPath.join(
"."
diff --git a/toolkit/components/extensions/WebNavigation.sys.mjs b/toolkit/components/extensions/WebNavigation.sys.mjs
index 7235aaeb4e..c33a45db81 100644
--- a/toolkit/components/extensions/WebNavigation.sys.mjs
+++ b/toolkit/components/extensions/WebNavigation.sys.mjs
@@ -4,6 +4,7 @@
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
+/** @type {Lazy} */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -23,9 +24,12 @@ function getBrowser(bc) {
}
export var WebNavigationManager = {
- // Map[string -> Map[listener -> URLFilter]]
+ /** @type {Map<string, Set<callback>>} */
listeners: new Map(),
+ /** @type {WeakMap<XULBrowserElement, object>} */
+ recentTabTransitionData: new WeakMap(),
+
init() {
// Collect recent tab transition data in a WeakMap:
// browser -> tabTransitionData
@@ -123,9 +127,9 @@ export var WebNavigationManager = {
* The data for the autocompleted item.
* @param {object} [acData.result]
* The result information associated with the navigation action.
- * @param {UrlbarUtils.RESULT_TYPE} [acData.result.type]
+ * @param {typeof lazy.UrlbarUtils.RESULT_TYPE} [acData.result.type]
* The result type associated with the navigation action.
- * @param {UrlbarUtils.RESULT_SOURCE} [acData.result.source]
+ * @param {typeof lazy.UrlbarUtils.RESULT_SOURCE} [acData.result.source]
* The result source associated with the navigation action.
*/
onURLBarUserStartNavigation(acData) {
diff --git a/toolkit/components/extensions/WebNavigationFrames.sys.mjs b/toolkit/components/extensions/WebNavigationFrames.sys.mjs
index 211698a88e..20db413bc0 100644
--- a/toolkit/components/extensions/WebNavigationFrames.sys.mjs
+++ b/toolkit/components/extensions/WebNavigationFrames.sys.mjs
@@ -45,7 +45,7 @@ function getParentFrameId(bc) {
/**
* Convert a BrowsingContext into internal FrameDetail json.
*
- * @param {BrowsingContext} bc
+ * @param {CanonicalBrowsingContext} bc
* @returns {FrameDetail}
*/
function getFrameDetail(bc) {
diff --git a/toolkit/components/extensions/extIWebNavigation.idl b/toolkit/components/extensions/extIWebNavigation.idl
index 3095d93d9f..1db9e73808 100644
--- a/toolkit/components/extensions/extIWebNavigation.idl
+++ b/toolkit/components/extensions/extIWebNavigation.idl
@@ -17,8 +17,8 @@ interface extIWebNavigation : nsISupports
void onHistoryChange(in BrowsingContext bc,
in jsval transitionData,
in nsIURI location,
- in bool isHistoryStateUpdated,
- in bool isReferenceFragmentUpdated);
+ in boolean isHistoryStateUpdated,
+ in boolean isReferenceFragmentUpdated);
void onStateChange(in BrowsingContext bc,
in nsIURI requestURI,
diff --git a/toolkit/components/extensions/mozIExtensionAPIRequestHandling.idl b/toolkit/components/extensions/mozIExtensionAPIRequestHandling.idl
index 0a2e3c7a5d..d7eb40345b 100644
--- a/toolkit/components/extensions/mozIExtensionAPIRequestHandling.idl
+++ b/toolkit/components/extensions/mozIExtensionAPIRequestHandling.idl
@@ -35,7 +35,7 @@ interface mozIExtensionListenerCallOptions : nsISupports
// An optional boolean to be set to true if the api object should be
// prepended to the rest of the call arguments (by default it is appended).
- readonly attribute bool apiObjectPrepended;
+ readonly attribute boolean apiObjectPrepended;
cenum CallbackType: 8 {
// Default: no callback argument is passed to the call to the event listener.
diff --git a/toolkit/components/extensions/mozIExtensionProcessScript.idl b/toolkit/components/extensions/mozIExtensionProcessScript.idl
index 84b33a9d02..47e442149c 100644
--- a/toolkit/components/extensions/mozIExtensionProcessScript.idl
+++ b/toolkit/components/extensions/mozIExtensionProcessScript.idl
@@ -17,5 +17,5 @@ interface mozIExtensionProcessScript : nsISupports
in mozIDOMWindow window);
void initExtensionDocument(in nsISupports extension, in Document doc,
- in bool privileged);
+ in boolean privileged);
};
diff --git a/toolkit/components/extensions/parent/ext-identity.js b/toolkit/components/extensions/parent/ext-identity.js
index bd53163305..f0f63dbf34 100644
--- a/toolkit/components/extensions/parent/ext-identity.js
+++ b/toolkit/components/extensions/parent/ext-identity.js
@@ -12,7 +12,7 @@ var { promiseDocumentLoaded } = ExtensionUtils;
const checkRedirected = (url, redirectURI) => {
return new Promise((resolve, reject) => {
- let xhr = new XMLHttpRequest();
+ let xhr = new XMLHttpRequest({ mozAnon: false });
xhr.open("GET", url);
// We expect this if the user has not authenticated.
xhr.onload = () => {
diff --git a/toolkit/components/extensions/parent/ext-runtime.js b/toolkit/components/extensions/parent/ext-runtime.js
index d1c03d9e0d..3f9c0f8857 100644
--- a/toolkit/components/extensions/parent/ext-runtime.js
+++ b/toolkit/components/extensions/parent/ext-runtime.js
@@ -248,7 +248,7 @@ this.runtime = class extends ExtensionAPIPersistent {
},
openOptionsPage: function () {
- if (!extension.manifest.options_ui) {
+ if (!extension.optionsPageProperties) {
return Promise.reject({ message: "No `options_ui` declared" });
}
diff --git a/toolkit/components/extensions/parent/ext-webRequest.js b/toolkit/components/extensions/parent/ext-webRequest.js
index 4f0ea90abd..f94c773e2e 100644
--- a/toolkit/components/extensions/parent/ext-webRequest.js
+++ b/toolkit/components/extensions/parent/ext-webRequest.js
@@ -64,7 +64,11 @@ function registerEvent(
filter2.incognito = filter.incognito;
}
- let blockingAllowed = extension.hasPermission("webRequestBlocking");
+ let blockingAllowed =
+ eventName == "onAuthRequired"
+ ? extension.hasPermission("webRequestBlocking") ||
+ extension.hasPermission("webRequestAuthProvider")
+ : extension.hasPermission("webRequestBlocking");
let info2 = [];
if (info) {
diff --git a/toolkit/components/extensions/schemas/manifest.json b/toolkit/components/extensions/schemas/manifest.json
index 14f78ba564..384b168e39 100644
--- a/toolkit/components/extensions/schemas/manifest.json
+++ b/toolkit/components/extensions/schemas/manifest.json
@@ -173,11 +173,15 @@
"optional": true
},
+ "options_page": {
+ "$ref": "ExtensionURL",
+ "optional": true,
+ "description": "Alias property for options_ui.page, ignored when options_ui.page is set. When using this property the options page is always opened in a new tab."
+ },
+
"options_ui": {
"type": "object",
-
"optional": true,
-
"properties": {
"page": { "$ref": "ExtensionURL" },
"browser_style": {
diff --git a/toolkit/components/extensions/schemas/web_request.json b/toolkit/components/extensions/schemas/web_request.json
index e4405f24c3..a1528d87f6 100644
--- a/toolkit/components/extensions/schemas/web_request.json
+++ b/toolkit/components/extensions/schemas/web_request.json
@@ -9,6 +9,7 @@
"type": "string",
"enum": [
"webRequest",
+ "webRequestAuthProvider",
"webRequestBlocking",
"webRequestFilterResponse",
"webRequestFilterResponse.serviceWorkerScript"
@@ -82,7 +83,7 @@
"id": "OnAuthRequiredOptions",
"type": "string",
"enum": ["responseHeaders", "blocking", "asyncBlocking"],
- "postprocess": "webRequestBlockingPermissionRequired"
+ "postprocess": "webRequestBlockingOrAuthProviderPermissionRequired"
},
{
"id": "OnResponseStartedOptions",
diff --git a/toolkit/components/extensions/storage/webext_storage_bridge/src/area.rs b/toolkit/components/extensions/storage/webext_storage_bridge/src/area.rs
index 1418ccca29..ec4f2057b9 100644
--- a/toolkit/components/extensions/storage/webext_storage_bridge/src/area.rs
+++ b/toolkit/components/extensions/storage/webext_storage_bridge/src/area.rs
@@ -13,7 +13,7 @@ use std::{
};
use golden_gate::{ApplyTask, BridgedEngine, FerryTask};
-use moz_task::{self, DispatchOptions, TaskRunnable};
+use moz_task::{DispatchOptions, TaskRunnable};
use nserror::{nsresult, NS_OK};
use nsstring::{nsACString, nsCString, nsString};
use thin_vec::ThinVec;
diff --git a/toolkit/components/extensions/test/browser/browser.toml b/toolkit/components/extensions/test/browser/browser.toml
index 33d54bddc2..33a3a6dc64 100644
--- a/toolkit/components/extensions/test/browser/browser.toml
+++ b/toolkit/components/extensions/test/browser/browser.toml
@@ -9,7 +9,6 @@ support-files = [
["browser_ext_downloads_filters.js"]
["browser_ext_downloads_referrer.js"]
-https_first_disabled = true
["browser_ext_eventpage_disableResetIdleForTest.js"]
diff --git a/toolkit/components/extensions/test/browser/browser_ext_extension_page_tab_navigated.js b/toolkit/components/extensions/test/browser/browser_ext_extension_page_tab_navigated.js
index 429d584a17..f85bbfe595 100644
--- a/toolkit/components/extensions/test/browser/browser_ext_extension_page_tab_navigated.js
+++ b/toolkit/components/extensions/test/browser/browser_ext_extension_page_tab_navigated.js
@@ -3,10 +3,6 @@
"use strict";
-const { AddonTestUtils } = ChromeUtils.importESModule(
- "resource://testing-common/AddonTestUtils.sys.mjs"
-);
-
// The test tasks in this test file tends to trigger an intermittent
// exception raised from JSActor::AfterDestroy, because of a race between
// when the WebExtensions API event is being emitted from the parent process
@@ -18,23 +14,6 @@ PromiseTestUtils.allowMatchingRejectionsGlobally(
/Actor 'Conduits' destroyed before query 'RunListener' was resolved/
);
-AddonTestUtils.initMochitest(this);
-
-const server = AddonTestUtils.createHttpServer({
- hosts: ["example.com", "anotherwebpage.org"],
-});
-
-server.registerPathHandler("/", (request, response) => {
- response.write(`<!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8">
- <title>test webpage</title>
- </head>
- </html>
- `);
-});
-
function createTestExtPage({ script }) {
return `<!DOCTYPE html>
<html>
@@ -55,7 +34,7 @@ function createTestExtPageScript(name) {
);
browser.test.sendMessage(`event-received:${pageName}`);
},
- { types: ["main_frame"], urls: ["http://example.com/*"] }
+ { types: ["main_frame"], urls: ["https://example.com/*"] }
);
/* eslint-disable mozilla/balanced-listeners */
window.addEventListener("pageshow", () => {
@@ -93,7 +72,7 @@ async function triggerWebRequestListener(webPageURL) {
add_task(async function test_extension_page_sameprocess_navigation() {
const extension = ExtensionTestUtils.loadExtension({
manifest: {
- permissions: ["webRequest", "http://example.com/*"],
+ permissions: ["webRequest", "https://example.com/*"],
},
files: {
"extpage1.html": createTestExtPage({ script: "extpage1.js" }),
@@ -116,7 +95,7 @@ add_task(async function test_extension_page_sameprocess_navigation() {
info("Wait for the extension page to be loaded");
await extension.awaitMessage("pageshow:extpage1");
- await triggerWebRequestListener("http://example.com");
+ await triggerWebRequestListener("https://example.com");
await extension.awaitMessage("event-received:extpage1");
ok(true, "extpage1 got a webRequest event as expected");
@@ -131,7 +110,7 @@ add_task(async function test_extension_page_sameprocess_navigation() {
info(
"Trigger a web request event and expect extpage2 to be the only one receiving it"
);
- await triggerWebRequestListener("http://example.com");
+ await triggerWebRequestListener("https://example.com");
await extension.awaitMessage("event-received:extpage2");
ok(true, "extpage2 got a webRequest event as expected");
@@ -146,7 +125,7 @@ add_task(async function test_extension_page_sameprocess_navigation() {
await extension.awaitMessage("pagehide:extpage2");
// We only expect extpage1 to be able to receive API events.
- await triggerWebRequestListener("http://example.com");
+ await triggerWebRequestListener("https://example.com");
await extension.awaitMessage("event-received:extpage1");
ok(true, "extpage1 got a webRequest event as expected");
@@ -159,7 +138,7 @@ add_task(async function test_extension_page_sameprocess_navigation() {
add_task(async function test_extension_page_context_navigated_to_web_page() {
const extension = ExtensionTestUtils.loadExtension({
manifest: {
- permissions: ["webRequest", "http://example.com/*"],
+ permissions: ["webRequest", "https://example.com/*"],
},
files: {
"extpage.html": createTestExtPage({ script: "extpage.js" }),
@@ -178,8 +157,8 @@ add_task(async function test_extension_page_context_navigated_to_web_page() {
// navigated will be intermittently able to receive an event before it
// is navigated to the webpage url (and moved into the BFCache or destroyed)
// and trigger an intermittent failure of this test.
- const webPageURL = "http://anotherwebpage.org/";
- const triggerWebRequestURL = "http://example.com/";
+ const webPageURL = "https://example.net/";
+ const triggerWebRequestURL = "https://example.com/";
info("Opening extension page in a new tab");
const extPageTab1 = await BrowserTestUtils.addTab(gBrowser, extPageURL);
diff --git a/toolkit/components/extensions/test/browser/browser_ext_themes_ntp_colors.js b/toolkit/components/extensions/test/browser/browser_ext_themes_ntp_colors.js
index 8e2f5446c9..4ee92bf798 100644
--- a/toolkit/components/extensions/test/browser/browser_ext_themes_ntp_colors.js
+++ b/toolkit/components/extensions/test/browser/browser_ext_themes_ntp_colors.js
@@ -26,17 +26,20 @@ async function test_ntp_theme(theme, isBrightText) {
});
let browser = gBrowser.selectedBrowser;
-
let { originalBackground, originalCardBackground, originalColor } =
await SpecialPowers.spawn(browser, [], function () {
let doc = content.document;
ok(
!doc.documentElement.hasAttribute("lwt-newtab"),
- "New tab page should not have lwt-newtab attribute"
+ `New tab page should not have lwt-newtab attribute`
+ );
+ ok(
+ !doc.documentElement.hasAttribute("lwtheme"),
+ `New tab page should not have lwtheme attribute`
);
ok(
!doc.documentElement.hasAttribute("lwt-newtab-brighttext"),
- `New tab page should not have lwt-newtab-brighttext attribute`
+ `New tab page not should have lwt-newtab-brighttext attribute`
);
return {
@@ -64,22 +67,39 @@ async function test_ntp_theme(theme, isBrightText) {
Services.ppmm.sharedData.flush();
+ let hasNtpColors = !!(
+ theme.colors.ntp_background ||
+ theme.colors.ntp_text ||
+ theme.colors.ntp_card_background
+ );
await SpecialPowers.spawn(
browser,
[
{
isBrightText,
+ hasNtpColors,
background: hexToCSS(theme.colors.ntp_background),
card_background: hexToCSS(theme.colors.ntp_card_background),
color: hexToCSS(theme.colors.ntp_text),
},
],
- async function ({ isBrightText, background, card_background, color }) {
+ async function ({
+ isBrightText,
+ hasNtpColors,
+ background,
+ card_background,
+ color,
+ }) {
let doc = content.document;
- ok(
+ is(
doc.documentElement.hasAttribute("lwt-newtab"),
+ hasNtpColors,
"New tab page should have lwt-newtab attribute"
);
+ ok(
+ doc.documentElement.hasAttribute("lwtheme"),
+ "New tab page should have lwtheme attribute"
+ );
is(
doc.documentElement.hasAttribute("lwt-newtab-brighttext"),
isBrightText,
@@ -88,22 +108,24 @@ async function test_ntp_theme(theme, isBrightText) {
} have lwt-newtab-brighttext attribute`
);
- is(
- content.getComputedStyle(doc.body).backgroundColor,
- background,
- "New tab page background should be set."
- );
- is(
- content.getComputedStyle(doc.querySelector(".top-site-outer .tile"))
- .backgroundColor,
- card_background,
- "New tab page card background should be set."
- );
- is(
- content.getComputedStyle(doc.querySelector(".outer-wrapper")).color,
- color,
- "New tab page text color should be set."
- );
+ if (hasNtpColors) {
+ is(
+ content.getComputedStyle(doc.body).backgroundColor,
+ background,
+ "New tab page background should be set."
+ );
+ is(
+ content.getComputedStyle(doc.querySelector(".top-site-outer .tile"))
+ .backgroundColor,
+ card_background,
+ "New tab page card background should be set."
+ );
+ is(
+ content.getComputedStyle(doc.querySelector(".outer-wrapper")).color,
+ color,
+ "New tab page text color should be set."
+ );
+ }
}
);
@@ -127,6 +149,10 @@ async function test_ntp_theme(theme, isBrightText) {
"New tab page should not have lwt-newtab attribute"
);
ok(
+ !doc.documentElement.hasAttribute("lwtheme"),
+ "New tab page should not have lwtheme attribute"
+ );
+ ok(
!doc.documentElement.hasAttribute("lwt-newtab-brighttext"),
`New tab page should not have lwt-newtab-brighttext attribute`
);
@@ -151,6 +177,17 @@ async function test_ntp_theme(theme, isBrightText) {
);
}
+async function waitForDarkMode(value) {
+ info(`waiting for dark mode: ${value}`);
+ const mq = matchMedia("(prefers-color-scheme: dark)");
+ if (mq.matches == value) {
+ return;
+ }
+ await new Promise(r => {
+ mq.addEventListener("change", r, { once: true });
+ });
+}
+
add_task(async function test_support_ntp_colors() {
await SpecialPowers.pushPrefEnv({
set: [
@@ -163,11 +200,13 @@ add_task(async function test_support_ntp_colors() {
["layout.css.prefers-color-scheme.content-override", 1],
// Override the system color scheme to light so this test passes on
// machines with dark system color scheme.
+ // FIXME(emilio): This doesn't seem working reliably, at least on macOS.
["ui.systemUsesDarkTheme", 0],
],
});
NewTabPagePreloading.removePreloadedBrowser(window);
for (let url of ["about:newtab", "about:home"]) {
+ await waitForDarkMode(false);
info("Opening url: " + url);
await BrowserTestUtils.withNewTab({ gBrowser, url }, async browser => {
await waitForAboutNewTabReady(browser, url);
@@ -185,6 +224,7 @@ add_task(async function test_support_ntp_colors() {
url
);
+ await waitForDarkMode(false);
await test_ntp_theme(
{
colors: {
@@ -198,6 +238,19 @@ add_task(async function test_support_ntp_colors() {
true,
url
);
+
+ // Test a theme without any new tab page colors
+ await waitForDarkMode(false);
+ await test_ntp_theme(
+ {
+ colors: {
+ frame: ACCENT_COLOR,
+ tab_background_text: TEXT_COLOR,
+ },
+ },
+ false,
+ url
+ );
});
}
});
diff --git a/toolkit/components/extensions/test/browser/browser_ext_themes_ntp_colors_perwindow.js b/toolkit/components/extensions/test/browser/browser_ext_themes_ntp_colors_perwindow.js
index 3b739322d6..54152d005c 100644
--- a/toolkit/components/extensions/test/browser/browser_ext_themes_ntp_colors_perwindow.js
+++ b/toolkit/components/extensions/test/browser/browser_ext_themes_ntp_colors_perwindow.js
@@ -39,6 +39,10 @@ function test_ntp_theme(browser, theme, isBrightText) {
doc.documentElement.hasAttribute("lwt-newtab"),
"New tab page should have lwt-newtab attribute"
);
+ ok(
+ doc.documentElement.hasAttribute("lwtheme"),
+ "New tab page should have lwtheme attribute"
+ );
is(
doc.documentElement.hasAttribute("lwt-newtab-brighttext"),
isBrightText,
@@ -90,6 +94,10 @@ function test_ntp_default_theme(browser) {
"New tab page should not have lwt-newtab attribute"
);
ok(
+ !doc.documentElement.hasAttribute("lwtheme"),
+ "New tab page should not have lwtheme attribute"
+ );
+ ok(
!doc.documentElement.hasAttribute("lwt-newtab-brighttext"),
`New tab page should not have lwt-newtab-brighttext attribute`
);
diff --git a/toolkit/components/extensions/test/browser/browser_ext_themes_pbm.js b/toolkit/components/extensions/test/browser/browser_ext_themes_pbm.js
index 3b36a256d0..d2dfb16e72 100644
--- a/toolkit/components/extensions/test/browser/browser_ext_themes_pbm.js
+++ b/toolkit/components/extensions/test/browser/browser_ext_themes_pbm.js
@@ -336,7 +336,7 @@ add_task(async function test_pbm_dark_page_info() {
await BrowserTestUtils.withNewTab(
{ gBrowser: win.gBrowser, url: "https://example.com" },
async () => {
- let pageInfo = win.BrowserPageInfo(null, "securityTab");
+ let pageInfo = win.BrowserCommands.pageInfo(null, "securityTab");
await BrowserTestUtils.waitForEvent(pageInfo, "page-info-init");
let prefersColorScheme = await getPrefersColorSchemeInfo({
diff --git a/toolkit/components/extensions/test/browser/browser_ext_themes_sanitization.js b/toolkit/components/extensions/test/browser/browser_ext_themes_sanitization.js
index 89ebd3ae68..fdee1eb72d 100644
--- a/toolkit/components/extensions/test/browser/browser_ext_themes_sanitization.js
+++ b/toolkit/components/extensions/test/browser/browser_ext_themes_sanitization.js
@@ -114,8 +114,9 @@ add_task(async function test_sanitization_transparent() {
await extension.startup();
let navbar = document.querySelector("#nav-bar");
- Assert.ok(
- window.getComputedStyle(navbar).boxShadow.includes("rgba(0, 0, 0, 0)"),
+ Assert.equal(
+ window.getComputedStyle(navbar).borderTopColor,
+ "rgba(0, 0, 0, 0)",
"Top separator should be transparent"
);
diff --git a/toolkit/components/extensions/test/browser/browser_ext_themes_separators.js b/toolkit/components/extensions/test/browser/browser_ext_themes_separators.js
index 4da4927ccf..1b953269b6 100644
--- a/toolkit/components/extensions/test/browser/browser_ext_themes_separators.js
+++ b/toolkit/components/extensions/test/browser/browser_ext_themes_separators.js
@@ -48,10 +48,9 @@ add_task(async function test_support_separator_properties() {
await deprecatedMessagePromise;
let navbar = document.querySelector("#nav-bar");
- Assert.ok(
- window
- .getComputedStyle(navbar)
- .boxShadow.includes(`rgb(${hexToRGB(SEPARATOR_TOP_COLOR).join(", ")})`),
+ Assert.equal(
+ window.getComputedStyle(navbar).borderTopColor,
+ `rgb(${hexToRGB(SEPARATOR_TOP_COLOR).join(", ")})`,
"Top separator color properly set"
);
diff --git a/toolkit/components/extensions/test/mochitest/test_check_startupcache.html b/toolkit/components/extensions/test/mochitest/test_check_startupcache.html
index 8cb529d18d..d1157472ec 100644
--- a/toolkit/components/extensions/test/mochitest/test_check_startupcache.html
+++ b/toolkit/components/extensions/test/mochitest/test_check_startupcache.html
@@ -41,7 +41,7 @@ add_task(async function check_ExtensionParent_StartupCache_is_non_empty() {
let map = await chromeScript.promiseOneMessage("StartupCache_data");
chromeScript.destroy();
- // "manifests" is populated by Extension's parseManifest in Extension.jsm.
+ // "manifests" is populated by Extension's parseManifest in Extension.sys.mjs.
const keys = ["manifests", "mochikit@mozilla.org", "2.0", "en-US"];
for (let key of keys) {
map = map.get(key);
diff --git a/toolkit/components/extensions/test/mochitest/test_ext_async_clipboard.html b/toolkit/components/extensions/test/mochitest/test_ext_async_clipboard.html
index 708b5522c3..e7fdd6aad0 100644
--- a/toolkit/components/extensions/test/mochitest/test_ext_async_clipboard.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_async_clipboard.html
@@ -354,11 +354,11 @@ add_task(async function test_contentscript_clipboard_nocontents_readtext() {
await extension.unload();
});
-// Test that performing read(...) when the clipboard is empty returns an empty ClipboardItem
+// Test that performing read(...) when the clipboard is empty returns no ClipboardItem
add_task(async function test_contentscript_clipboard_nocontents_read() {
function contentScript() {
clipboardRead().then(function(items) {
- if (items[0].types.length) {
+ if (items.length) {
browser.test.fail("Read read the wrong thing from clipboard, " +
"ClipboardItem has this many entries: " + items[0].types.length);
} else {
diff --git a/toolkit/components/extensions/test/mochitest/test_ext_contentscript_securecontext.html b/toolkit/components/extensions/test/mochitest/test_ext_contentscript_securecontext.html
index 093c26898f..f9230c9a5f 100644
--- a/toolkit/components/extensions/test/mochitest/test_ext_contentscript_securecontext.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_contentscript_securecontext.html
@@ -14,6 +14,8 @@
await SpecialPowers.pushPrefEnv({
"set": [
["dom.w3c_pointer_events.getcoalescedevents_only_in_securecontext", true],
+ // Test is intentionally testing in non-secure contexts.
+ ["dom.security.https_first", false]
]
});
});
diff --git a/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect.html b/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect.html
index 85f98d5034..d474a7caee 100644
--- a/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect.html
@@ -18,6 +18,7 @@ function background() {
browser.test.assertTrue(port.sender.url.endsWith("file_sample.html"), "URL correct");
browser.test.assertTrue(port.sender.tab.url.endsWith("file_sample.html"), "tab URL correct");
browser.test.assertEq(port.sender.frameId, 0, "frameId of top frame");
+ browser.test.assertEq(new URL(port.sender.url).origin, port.sender.origin, "sender origin correct");
let expected = "message 1";
port.onMessage.addListener(msg => {
diff --git a/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect2.html b/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect2.html
index 13b9029c48..32620b5b3b 100644
--- a/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect2.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect2.html
@@ -21,6 +21,7 @@ function backgroundScript(token) {
browser.runtime.onConnect.addListener(port => {
browser.test.assertTrue(port.sender.url.endsWith("file_sample.html"), "sender url correct");
browser.test.assertTrue(port.sender.tab.url.endsWith("file_sample.html"), "sender url correct");
+ browser.test.assertEq(new URL(port.sender.url).origin, port.sender.origin, "sender origin correct");
let tabId = port.sender.tab.id;
browser.tabs.connect(tabId, {name: token});
diff --git a/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect_iframe.html b/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect_iframe.html
index 9c64635063..e3e0e80f2a 100644
--- a/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect_iframe.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect_iframe.html
@@ -30,12 +30,15 @@ add_task(async function connect_from_background_frame() {
}
async function background() {
const FRAME_URL = "https://example.com/tests/toolkit/components/extensions/test/mochitest/file_sample.html";
+ const FRAME_ORIGIN = new URL(FRAME_URL).origin;
+
browser.runtime.onConnect.addListener(port => {
// The next two assertions are the reason for this being a mochitest
// instead of a xpcshell test.
browser.test.assertEq(port.sender.tab, undefined, "Sender is not a tab");
browser.test.assertEq(port.sender.frameId, undefined, "frameId unset");
browser.test.assertEq(port.sender.url, FRAME_URL, "Expected sender URL");
+ browser.test.assertEq(port.sender.origin, FRAME_ORIGIN, "Expected sender origin");
port.onMessage.addListener(msg => {
browser.test.assertEq("pong", msg, "Reply from content script");
port.disconnect();
@@ -88,6 +91,8 @@ add_task(async function connect_from_content_script_in_frame() {
async function background() {
const TAB_URL = "https://example.org/tests/toolkit/components/extensions/test/mochitest/file_contains_iframe.html";
const FRAME_URL = "https://example.org/tests/toolkit/components/extensions/test/mochitest/file_contains_img.html";
+ const FRAME_ORIGIN = new URL(FRAME_URL).origin;
+
let createdTab;
browser.runtime.onConnect.addListener(port => {
// The next two assertions are the reason for this being a mochitest
@@ -95,6 +100,7 @@ add_task(async function connect_from_content_script_in_frame() {
browser.test.assertEq(port.sender.tab.url, TAB_URL, "Sender is the tab");
browser.test.assertTrue(port.sender.frameId > 0, "frameId is set");
browser.test.assertEq(port.sender.url, FRAME_URL, "Expected sender URL");
+ browser.test.assertEq(port.sender.origin, FRAME_ORIGIN, "Expected sender origin");
browser.test.assertEq(createdTab.id, port.sender.tab.id, "Tab to close");
browser.tabs.remove(port.sender.tab.id).then(() => {
diff --git a/toolkit/components/extensions/test/mochitest/test_ext_tabs_captureTab.html b/toolkit/components/extensions/test/mochitest/test_ext_tabs_captureTab.html
index ab06a965ed..20f368a0ae 100644
--- a/toolkit/components/extensions/test/mochitest/test_ext_tabs_captureTab.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_tabs_captureTab.html
@@ -319,6 +319,141 @@ add_task(async function testCaptureVisibleTabPermissions() {
await extension.awaitFinish("captureVisibleTabPermissions");
await extension.unload();
});
+
+add_task(async function testCaptureVisibleTabWithActiveTab() {
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ browser_action: {
+ default_area: "navbar",
+ },
+ permissions: ["webNavigation", "tabs", "activeTab"],
+ },
+
+ async background() {
+ // Wait for the page (in the test window) to load.
+ await new Promise(resolve => {
+ browser.webNavigation.onCompleted.addListener(
+ () => resolve(),
+ {url: [{schemes: ["data"]}]});
+ });
+
+ browser.browserAction.onClicked.addListener(async tab => {
+ await browser.tabs.captureVisibleTab(tab.windowId);
+ browser.test.notifyPass("captureVisibleTabPermissions");
+ });
+
+ browser.test.sendMessage("ready");
+ },
+ });
+
+ let html = `
+ <!DOCTYPE html>
+ <html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ </head>
+ <body><h1>hello</h1></body>
+ </html>
+ `;
+
+ await extension.startup();
+
+ let testWindow = window.open(`data:text/html,${encodeURIComponent(html)}#scroll`);
+ await extension.awaitMessage("ready");
+ await AppTestDelegate.clickBrowserAction(testWindow, extension);
+ await extension.awaitFinish("captureVisibleTabPermissions");
+ await AppTestDelegate.closeBrowserAction(testWindow, extension);
+ testWindow.close();
+
+ await extension.unload();
+});
+
+add_task(async function testCaptureVisibleTabWithActiveTabAndNotUserInteraction() {
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ permissions: ["webNavigation", "tabs", "activeTab"],
+ },
+
+ async background() {
+ // Wait for the page (in the test window) to load.
+ await new Promise(resolve => {
+ browser.webNavigation.onCompleted.addListener(
+ () => resolve(),
+ {url: [{schemes: ["data"]}]});
+ });
+
+ let [tab] = await browser.tabs.query({ currentWindow: true, active: true });
+ await browser.test.assertRejects(
+ browser.tabs.captureVisibleTab(tab.windowId),
+ /Missing activeTab permission/,
+ "Expected rejection because activeTab permission isn't set"
+ );
+
+ browser.test.notifyPass("captureVisibleTabPermissions");
+ },
+ });
+
+ let html = `
+ <!DOCTYPE html>
+ <html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ </head>
+ <body><h1>hello</h1></body>
+ </html>
+ `;
+
+ await extension.startup();
+
+ let testWindow = window.open(`data:text/html,${encodeURIComponent(html)}#scroll`);
+ await extension.awaitFinish("captureVisibleTabPermissions");
+ testWindow.close();
+
+ await extension.unload();
+});
+
+add_task(async function testCaptureVisibleTabWithActiveTabAndAllURLs() {
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ permissions: ["webNavigation", "tabs", "activeTab", "<all_urls>"],
+ },
+
+ async background() {
+ // Wait for the page (in the test window) to load.
+ await new Promise(resolve => {
+ browser.webNavigation.onCompleted.addListener(
+ () => resolve(),
+ {url: [{schemes: ["data"]}]});
+ });
+
+ let [tab] = await browser.tabs.query({ currentWindow: true, active: true });
+ await browser.tabs.captureVisibleTab(tab.windowId);
+
+ browser.test.notifyPass("captureVisibleTabPermissions");
+ },
+ });
+
+ let html = `
+ <!DOCTYPE html>
+ <html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ </head>
+ <body><h1>hello</h1></body>
+ </html>
+ `;
+
+ await extension.startup();
+
+ let testWindow = window.open(`data:text/html,${encodeURIComponent(html)}#scroll`);
+ await extension.awaitFinish("captureVisibleTabPermissions");
+ testWindow.close();
+
+ await extension.unload();
+});
</script>
</body>
</html>
diff --git a/toolkit/components/extensions/test/mochitest/test_ext_tabs_sendMessage.html b/toolkit/components/extensions/test/mochitest/test_ext_tabs_sendMessage.html
index 4b230c258c..8c6dfeee7c 100644
--- a/toolkit/components/extensions/test/mochitest/test_ext_tabs_sendMessage.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_tabs_sendMessage.html
@@ -11,6 +11,10 @@
<script>
"use strict";
+const {
+ WebExtensionPolicy
+} = SpecialPowers.Cu.getGlobalForObject(SpecialPowers.Services);
+
add_task(async function test_tabs_sendMessage_to_extension_page_frame() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
@@ -27,6 +31,10 @@ add_task(async function test_tabs_sendMessage_to_extension_page_frame() {
browser.runtime.onMessage.addListener(async (msg, sender) => {
browser.test.assertEq(msg, "page-script-ready");
browser.test.assertEq(sender.url, browser.runtime.getURL("page.html"));
+ browser.test.assertEq(
+ sender.origin,
+ new URL(browser.runtime.getURL("/")).origin
+ );
let tabId = sender.tab.id;
let response = await browser.tabs.sendMessage(tabId, "tab-sendMessage");
@@ -147,6 +155,178 @@ add_task(async function test_tabs_sendMessage_using_frameId() {
await extension.unload();
});
+add_task(async function test_tabs_sendMessage_aboutBlank() {
+ const PATH = "tests/toolkit/components/extensions/test/mochitest/file_with_about_blank.html";
+
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ content_scripts: [
+ {
+ match_about_blank: true,
+ matches: ["*://mochi.test/*/file_with_about_blank.html"],
+ all_frames: true,
+ js: ["cs.js"],
+ },
+ ],
+ },
+
+ background() {
+ browser.runtime.onMessage.addListener((msg, { url, origin }) => {
+ browser.test.assertEq("cs", msg, "expected message from cs.js");
+
+ const kind = url.startsWith("about:") ? url : "top";
+ switch (kind) {
+ case "top":
+ browser.test.assertTrue(
+ url.endsWith("/file_with_about_blank.html"),
+ "expected correct url"
+ );
+ browser.test.assertEq(
+ "http://mochi.test:8888",
+ origin,
+ "expected correct origin"
+ );
+ break;
+
+ case "about:blank":
+ browser.test.assertEq("about:blank", url, "expected correct url");
+ browser.test.assertEq(
+ "http://mochi.test:8888",
+ origin,
+ "expected correct origin"
+ );
+ break;
+
+ case "about:srcdoc":
+ browser.test.assertEq("about:srcdoc", url, "expected correct url");
+ browser.test.assertEq(
+ "http://mochi.test:8888",
+ origin,
+ "expected correct origin"
+ );
+ break;
+
+ default:
+ browser.test.fail(`Unexpected kind: ${kind}`);
+ }
+
+ browser.test.sendMessage(`done:${kind}`);
+ });
+
+ browser.test.sendMessage("ready");
+ },
+
+ files: {
+ "cs.js"() {
+ browser.runtime.sendMessage("cs");
+ },
+ },
+ });
+
+ await extension.startup();
+ await extension.awaitMessage("ready");
+
+ let win = window.open("http://mochi.test:8888/" + PATH);
+ await extension.awaitMessage("done:top");
+ await extension.awaitMessage("done:about:blank");
+ await extension.awaitMessage("done:about:srcdoc");
+ win.close();
+
+ await extension.unload();
+});
+
+add_task(async function test_tabs_sendMessage_opaqueOrigin() {
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ content_scripts: [
+ {
+ match_about_blank: true,
+ // The combination of `matches` and `exclude_matches` below allows us
+ // to only inject in the top-level about:blank.
+ matches: ["*://*/*"],
+ exclude_matches: ["<all_urls>"],
+ js: ["cs.js"],
+ },
+ ],
+ },
+
+ async background() {
+ let tab;
+
+ browser.runtime.onMessage.addListener(async (msg, { url, origin }) => {
+ browser.test.assertEq("cs", msg, "expected message from cs.js");
+ browser.test.assertEq("about:blank", url, "expected correct url");
+ browser.test.assertEq("null", origin, "expected correct origin");
+
+ await browser.tabs.remove(tab.id);
+ browser.test.sendMessage("done");
+ });
+
+ tab = await browser.tabs.create({ url: "about:blank" });
+ },
+
+ files: {
+ "cs.js"() {
+ browser.runtime.sendMessage("cs");
+ },
+ },
+ });
+
+ await extension.startup();
+ await extension.awaitMessage("done");
+ await extension.unload();
+});
+
+add_task(async function test_tabs_sendMessage_blob() {
+ // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1724099
+ if (!WebExtensionPolicy.useRemoteWebExtensions) {
+ await SpecialPowers.pushPrefEnv({
+ set: [["security.allow_unsafe_parent_loads", true]],
+ });
+ }
+
+ let extension = ExtensionTestUtils.loadExtension({
+ async background() {
+ browser.runtime.onMessage.addListener(async (msg, { url, origin }) => {
+ browser.test.assertEq("script", msg, "expected message from script.js");
+ browser.test.assertTrue(
+ url.startsWith("blob:moz-extension://"),
+ "expected correct url"
+ );
+ browser.test.assertEq(
+ new URL(browser.runtime.getURL("/")).origin,
+ origin,
+ "expected correct origin"
+ );
+
+ browser.test.sendMessage("done");
+ });
+
+ const blob = new Blob(
+ [`<script src="${browser.runtime.getURL("script.js")}"><\/script>`],
+ { type: "text/html" }
+ );
+ const iframe = document.createElement("iframe");
+ iframe.src = URL.createObjectURL(blob);
+ document.body.appendChild(iframe);
+ },
+
+ files: {
+ "script.js"() {
+ browser.runtime.sendMessage("script");
+ },
+ },
+ });
+
+ await extension.startup();
+ await extension.awaitMessage("done");
+ await extension.unload();
+
+ if (!WebExtensionPolicy.useRemoteWebExtensions) {
+ await SpecialPowers.popPrefEnv();
+ }
+});
+
</script>
</body>
</html>
diff --git a/toolkit/components/extensions/test/mochitest/test_ext_test.html b/toolkit/components/extensions/test/mochitest/test_ext_test.html
index bf68786465..21093c9abf 100644
--- a/toolkit/components/extensions/test/mochitest/test_ext_test.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_test.html
@@ -126,7 +126,7 @@ function testScript() {
// The WebIDL version of assertDeepEq structurally clones before sending the
// params to the main thread. This check verifies that the behavior is
- // consistent between the WebIDL and Schemas.jsm-generated API bindings.
+ // consistent between the WebIDL and Schemas.sys.mjs-generated API bindings.
browser.test.assertThrows(
() => browser.test.assertDeepEq(obj, obj, "obj with func"),
/An unexpected error occurred/,
diff --git a/toolkit/components/extensions/test/xpcshell/test_csp_validator.js b/toolkit/components/extensions/test/xpcshell/test_csp_validator.js
index 011628f027..0e665b9730 100644
--- a/toolkit/components/extensions/test/xpcshell/test_csp_validator.js
+++ b/toolkit/components/extensions/test/xpcshell/test_csp_validator.js
@@ -202,8 +202,8 @@ add_task(async function test_csp_validator_extension_pages() {
let checkPolicy = (policy, expectedResult) => {
info(`Checking policy: ${policy}`);
- // While Schemas.jsm uses Ci.nsIAddonContentPolicy.CSP_ALLOW_WASM, we don't
- // pass that here because we are only verifying that remote scripts are
+ // While Schemas.sys.mjs uses Ci.nsIAddonContentPolicy.CSP_ALLOW_WASM, we
+ // don't pass that here because we are only verifying that remote scripts are
// blocked here.
let result = cps.validateAddonCSP(policy, 0);
equal(result, expectedResult);
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_adoption_with_private_field_xrays.js b/toolkit/components/extensions/test/xpcshell/test_ext_adoption_with_private_field_xrays.js
index 2d8b02bcd9..76b6644d44 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_adoption_with_private_field_xrays.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_adoption_with_private_field_xrays.js
@@ -1,6 +1,6 @@
"use strict";
-// ExtensionContent.jsm needs to know when it's running from xpcshell,
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell,
// to use the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
const server = createHttpServer();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_adoption_with_xrays.js b/toolkit/components/extensions/test/xpcshell/test_ext_adoption_with_xrays.js
index 9655c157d1..b909223302 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_adoption_with_xrays.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_adoption_with_xrays.js
@@ -1,6 +1,6 @@
"use strict";
-// ExtensionContent.jsm needs to know when it's running from xpcshell,
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell,
// to use the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
const server = createHttpServer();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_clear_cached_resources.js b/toolkit/components/extensions/test/xpcshell/test_ext_clear_cached_resources.js
index 95bef23383..bd05398736 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_clear_cached_resources.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_clear_cached_resources.js
@@ -297,7 +297,7 @@ add_task(
// This temporary directory is going to be removed from the
// cleanup function, but also make it unique as we do for the
// other temporary files (e.g. like getTemporaryFile as defined
- // in XPInstall.jsm).
+ // in XPIInstall.sys.mjs).
const random = Math.round(Math.random() * 36 ** 3).toString(36);
const tmpDirName = `xpcshelltest_unpacked_addons_${random}`;
let tmpExtPath = FileUtils.getDir("TmpD", [tmpDirName]);
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_dynamic_registration.js b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_dynamic_registration.js
index 0133b5d86c..20f6ece95a 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_dynamic_registration.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_dynamic_registration.js
@@ -5,7 +5,7 @@ server.registerDirectory("/data/", do_get_file("data"));
const BASE_URL = `http://localhost:${server.identity.primaryPort}/data`;
-// ExtensionContent.jsm needs to know when it's running from xpcshell, to use
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell, to use
// the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_triggeringPrincipal.js b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_triggeringPrincipal.js
index 4ebe6df636..ce7f293142 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_triggeringPrincipal.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_triggeringPrincipal.js
@@ -30,7 +30,7 @@ Services.prefs.setBoolPref(
false
);
-// ExtensionContent.jsm needs to know when it's running from xpcshell,
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell,
// to use the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_xorigin_frame.js b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_xorigin_frame.js
index 8a58b2475c..420fa7689c 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_xorigin_frame.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_xorigin_frame.js
@@ -5,6 +5,12 @@ const server = createHttpServer({
});
server.registerDirectory("/data/", do_get_file("data"));
+// By default, fission.webContentIsolationStrategy=1 (IsolateHighValue). When
+// Fission is enabled on Android, the pref value 2 (IsolateHighValue) will be
+// used instead. Set to 1 (IsolateEverything) to make sure the subframe in this
+// test gets its own process, independently of the default prefs on Android.
+Services.prefs.setIntPref("fission.webContentIsolationStrategy", 1);
+
add_task(async function test_process_switch_cross_origin_frame() {
const extension = ExtensionTestUtils.loadExtension({
manifest: {
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_xrays.js b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_xrays.js
index 7b92d5c4b7..30ec8ceced 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_xrays.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_xrays.js
@@ -1,6 +1,6 @@
"use strict";
-// ExtensionContent.jsm needs to know when it's running from xpcshell,
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell,
// to use the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_contexts_gc.js b/toolkit/components/extensions/test/xpcshell/test_ext_contexts_gc.js
index 2a36f51637..b6f955f7ba 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_contexts_gc.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_contexts_gc.js
@@ -5,7 +5,7 @@ server.registerDirectory("/data/", do_get_file("data"));
const BASE_URL = `http://localhost:${server.identity.primaryPort}/data`;
-// ExtensionContent.jsm needs to know when it's running from xpcshell,
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell,
// to use the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_dnr_modifyHeaders.js b/toolkit/components/extensions/test/xpcshell/test_ext_dnr_modifyHeaders.js
index 4b7bebe188..df9e9d77ef 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_dnr_modifyHeaders.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_dnr_modifyHeaders.js
@@ -29,6 +29,7 @@ server.registerPathHandler("/echoheaders", (req, res) => {
dropDefaultHeader("accept-language");
dropDefaultHeader("accept-encoding");
dropDefaultHeader("connection");
+ dropDefaultHeader("priority");
res.write(JSON.stringify(headers));
});
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_file_access.js b/toolkit/components/extensions/test/xpcshell/test_ext_file_access.js
index c05188cd38..bea1e76c0f 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_file_access.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_file_access.js
@@ -4,7 +4,7 @@ const FILE_DUMMY_URL = Services.io.newFileURI(
do_get_file("data/dummy_page.html")
).spec;
-// ExtensionContent.jsm needs to know when it's running from xpcshell,
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell,
// to use the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_i18n.js b/toolkit/components/extensions/test/xpcshell/test_ext_i18n.js
index 1e46e19527..69ba326f75 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_i18n.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_i18n.js
@@ -4,7 +4,7 @@ const { Preferences } = ChromeUtils.importESModule(
"resource://gre/modules/Preferences.sys.mjs"
);
-// ExtensionContent.jsm needs to know when it's running from xpcshell,
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell,
// to use the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_ipcBlob.js b/toolkit/components/extensions/test/xpcshell/test_ext_ipcBlob.js
index dd90d9bbc8..2c5b3378fd 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_ipcBlob.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_ipcBlob.js
@@ -5,7 +5,7 @@ server.registerDirectory("/data/", do_get_file("data"));
const BASE_URL = `http://localhost:${server.identity.primaryPort}/data`;
-// ExtensionContent.jsm needs to know when it's running from xpcshell,
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell,
// to use the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_permissions.js b/toolkit/components/extensions/test/xpcshell/test_ext_permissions.js
index 948b75978a..ae6ce3d27e 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_permissions.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_permissions.js
@@ -12,8 +12,8 @@ const { ExtensionPermissions } = ChromeUtils.importESModule(
Services.prefs.setBoolPref("extensions.manifestV3.enabled", true);
-// ExtensionParent.jsm is being imported lazily because when it is imported Services.appinfo will be
-// retrieved and cached (as a side-effect of Schemas.jsm being imported), and so Services.appinfo
+// ExtensionParent.sys.mjs is being imported lazily because when it is imported Services.appinfo will be
+// retrieved and cached (as a side-effect of Schemas.sys.mjs being imported), and so Services.appinfo
// will not be returning the version set by AddonTestUtils.createAppInfo and this test will
// fail on non-nightly builds (because the cached appinfo.version will be undefined and
// AddonManager startup will fail).
@@ -704,6 +704,7 @@ const GRANTED_WITHOUT_USER_PROMPT = [
"theme",
"unlimitedStorage",
"webRequest",
+ "webRequestAuthProvider",
"webRequestBlocking",
"webRequestFilterResponse",
"webRequestFilterResponse.serviceWorkerScript",
@@ -1058,3 +1059,47 @@ add_task(async function test_internal_permissions() {
await extension.unload();
});
+
+add_task(function test_normalizeOptional() {
+ const optional1 = {
+ origins: ["*://site.com/", "*://*.domain.com/"],
+ permissions: ["downloads", "tabs"],
+ };
+
+ function normalize(perms, optional) {
+ perms = { origins: [], permissions: [], ...perms };
+ optional = { origins: [], permissions: [], ...optional };
+ return ExtensionPermissions.normalizeOptional(perms, optional);
+ }
+
+ normalize({ origins: ["http://site.com/"] }, optional1);
+ normalize({ origins: ["https://site.com/"] }, optional1);
+ normalize({ origins: ["*://blah.domain.com/"] }, optional1);
+ normalize({ permissions: ["downloads", "tabs"] }, optional1);
+
+ Assert.throws(
+ () => normalize({ origins: ["http://www.example.com/"] }, optional1),
+ /was not declared in the manifest/
+ );
+ Assert.throws(
+ () => normalize({ permissions: ["proxy"] }, optional1),
+ /was not declared in optional_permissions/
+ );
+
+ const optional2 = {
+ origins: ["<all_urls>", "*://*/*"],
+ permissions: ["idle", "clipboardWrite"],
+ };
+
+ normalize({ origins: ["http://site.com/"] }, optional2);
+ normalize({ origins: ["https://site.com/"] }, optional2);
+ normalize({ origins: ["*://blah.domain.com/"] }, optional2);
+ normalize({ permissions: ["idle", "clipboardWrite"] }, optional2);
+
+ let perms = normalize({ origins: ["<all_urls>"] }, optional2);
+ equal(
+ perms.origins.sort().join(),
+ optional2.origins.sort().join(),
+ `Expect both "all sites" permissions`
+ );
+});
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_permissions_api.js b/toolkit/components/extensions/test/xpcshell/test_ext_permissions_api.js
index 0211787fee..97db78cf81 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_permissions_api.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_permissions_api.js
@@ -58,6 +58,7 @@ add_task(async function setup() {
"search",
"tabHide",
"tabs",
+ "webRequestAuthProvider",
"webRequestBlocking",
"webRequestFilterResponse",
"webRequestFilterResponse.serviceWorkerScript",
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_proxy_onauthrequired.js b/toolkit/components/extensions/test/xpcshell/test_ext_proxy_onauthrequired.js
index 4b1128a349..719fd219fe 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_proxy_onauthrequired.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_proxy_onauthrequired.js
@@ -251,7 +251,7 @@ add_task(async function test_webRequest_auth_proxy_system() {
() => {
browser.test.sendMessage("onAuthRequired");
// cancel is silently ignored, if it were not (e.g someone messes up in
- // WebRequest.jsm and allows cancel) this test would fail.
+ // WebRequest.sys.mjs and allows cancel) this test would fail.
return {
cancel: true,
authCredentials: { username: "puser", password: "ppass" },
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_schemas_manifest_permissions.js b/toolkit/components/extensions/test/xpcshell/test_ext_schemas_manifest_permissions.js
index 85e645e67b..e5fc746e60 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_schemas_manifest_permissions.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_schemas_manifest_permissions.js
@@ -86,7 +86,7 @@ add_task(async function () {
a_manifest_property: {},
},
background() {
- // Test hasPermission method implemented in ExtensionChild.jsm.
+ // Test hasPermission method implemented in ExtensionChild.sys.mjs.
browser.test.assertTrue(
"testManifestPermission" in browser,
"The API namespace is defined as expected"
@@ -105,7 +105,7 @@ add_task(async function () {
"test-extension-manifest-without-nested-prop"
);
- // Test hasPermission method implemented in Extension.jsm.
+ // Test hasPermission method implemented in Extension.sys.mjs.
equal(
extension.extension.hasPermission("manifest:a_manifest_property"),
true,
@@ -129,7 +129,7 @@ add_task(async function () {
},
},
background() {
- // Test hasPermission method implemented in ExtensionChild.jsm.
+ // Test hasPermission method implemented in ExtensionChild.sys.mjs.
browser.test.assertTrue(
"testManifestPermission" in browser,
"The API namespace is defined as expected"
@@ -146,7 +146,7 @@ add_task(async function () {
async extension => {
await extension.awaitFinish("test-extension-manifest-with-nested-prop");
- // Test hasPermission method implemented in Extension.jsm.
+ // Test hasPermission method implemented in Extension.sys.mjs.
equal(
extension.extension.hasPermission("manifest:a_manifest_property"),
true,
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_schemas_privileged.js b/toolkit/components/extensions/test/xpcshell/test_ext_schemas_privileged.js
index 14cbca7443..496607968e 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_schemas_privileged.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_schemas_privileged.js
@@ -144,7 +144,7 @@ add_task(
}
);
-// Test that Extension.jsm and schema correctly match.
+// Test that Extension.sys.mjs and schema correctly match.
add_task(function test_privileged_permissions_match() {
const { PRIVILEGED_PERMS } = ChromeUtils.importESModule(
"resource://gre/modules/Extension.sys.mjs"
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts.js b/toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts.js
index a06f34a1b4..c47cdcad4d 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts.js
@@ -5,7 +5,7 @@ server.registerDirectory("/data/", do_get_file("data"));
const BASE_URL = `http://localhost:${server.identity.primaryPort}/data`;
-// ExtensionContent.jsm needs to know when it's running from xpcshell, to use
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell, to use
// the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts_css.js b/toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts_css.js
index 21190d2d59..a3c79b1cc0 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts_css.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts_css.js
@@ -5,7 +5,7 @@ server.registerDirectory("/data/", do_get_file("data"));
const BASE_URL = `http://localhost:${server.identity.primaryPort}/data`;
-// ExtensionContent.jsm needs to know when it's running from xpcshell, to use
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell, to use
// the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts_file.js b/toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts_file.js
index 3c806439ce..02ef9b0fa5 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts_file.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_scripting_contentScripts_file.js
@@ -4,7 +4,7 @@ const FILE_DUMMY_URL = Services.io.newFileURI(
do_get_file("data/dummy_page.html")
).spec;
-// ExtensionContent.jsm needs to know when it's running from xpcshell, to use
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell, to use
// the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_scripting_updateContentScripts.js b/toolkit/components/extensions/test/xpcshell/test_ext_scripting_updateContentScripts.js
index 9d3bf1576c..85c2376715 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_scripting_updateContentScripts.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_scripting_updateContentScripts.js
@@ -5,7 +5,7 @@ server.registerDirectory("/data/", do_get_file("data"));
const BASE_URL = `http://localhost:${server.identity.primaryPort}/data`;
-// ExtensionContent.jsm needs to know when it's running from xpcshell, to use
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell, to use
// the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_shadowdom.js b/toolkit/components/extensions/test/xpcshell/test_ext_shadowdom.js
index 626d8de22d..e8316aa652 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_shadowdom.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_shadowdom.js
@@ -1,6 +1,6 @@
"use strict";
-// ExtensionContent.jsm needs to know when it's running from xpcshell,
+// ExtensionContent.sys.mjs needs to know when it's running from xpcshell,
// to use the right timeout for content scripts executed at document_idle.
ExtensionTestUtils.mockAppInfo();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_userScripts.js b/toolkit/components/extensions/test/xpcshell/test_ext_userScripts.js
index 3108c7b9b4..57ca08aca3 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_userScripts.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_userScripts.js
@@ -200,7 +200,7 @@ add_task(async function test_userScripts_no_webext_apis() {
let script = await browser.userScripts.register(userScriptOptions);
// Unregister and then register the same js code again, to verify that the last registered
- // userScript doesn't get assigned a revoked blob url (otherwise Extensioncontent.jsm
+ // userScript doesn't get assigned a revoked blob url (otherwise Extensioncontent.sys.mjs
// ScriptCache raises an error because it fails to compile the revoked blob url and the user
// script will never be loaded).
script.unregister();
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_auth.js b/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_auth.js
index 578e69ebdf..ef07817d00 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_auth.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_auth.js
@@ -63,7 +63,7 @@ server.registerPathHandler("/authenticate.sjs", (request, response) => {
}
});
-function getExtension(bgConfig) {
+function getExtension(bgConfig, permissions = ["webRequestBlocking"]) {
function background(config) {
let path = config.path;
browser.webRequest.onBeforeRequest.addListener(
@@ -125,18 +125,18 @@ function getExtension(bgConfig) {
return ExtensionTestUtils.loadExtension({
manifest: {
- permissions: ["webRequest", "webRequestBlocking", bgConfig.path],
+ permissions: [bgConfig.path, "webRequest", ...permissions],
},
background: `(${background})(${JSON.stringify(bgConfig)})`,
});
}
-add_task(async function test_webRequest_auth() {
+async function test_webRequest_auth(permissions) {
let config = {
path: `${BASE_URL}/*`,
realm: `webRequest_auth${Math.random()}`,
onBeforeRequest: {
- extra: ["blocking"],
+ extra: permissions.includes("webRequestBlocking") ? ["blocking"] : [],
},
onAuthRequired: {
extra: ["blocking"],
@@ -149,7 +149,7 @@ add_task(async function test_webRequest_auth() {
},
};
- let extension = getExtension(config);
+ let extension = getExtension(config, permissions);
await extension.startup();
let requestUrl = `${BASE_URL}/authenticate.sjs?realm=${config.realm}`;
@@ -174,6 +174,14 @@ add_task(async function test_webRequest_auth() {
await contentPage.close();
await extension.unload();
+}
+
+add_task(async function test_webRequest_auth_with_webRequestBlocking() {
+ await test_webRequest_auth(["webRequestBlocking"]);
+});
+
+add_task(async function test_webRequest_auth_with_webRequestAuthProvider() {
+ await test_webRequest_auth(["webRequestAuthProvider"]);
});
add_task(async function test_webRequest_auth_cancelled() {
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_permission.js b/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_permission.js
index 17c22e156d..97e44498c1 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_permission.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_permission.js
@@ -18,6 +18,18 @@ function sendMessage(page, msg, data) {
return MessageChannel.sendMessage(page.browser.messageManager, msg, data);
}
+add_setup(() => {
+ // Make sure to invalidate WebExtensions API schemas that may be cached
+ // in the StartupCache when this test runs with conditioned-profiles.
+ //
+ // These tests are subject to be hitting failures consistently on
+ // landing API schema changes to the WebExtensions API permissions.
+ // or other API schema properties that are explicitly covered by
+ // this tests (e.g. errors expected to be emitted by postprocess
+ // helper functions).
+ Services.obs.notifyObservers(null, "startupcache-invalidate");
+});
+
add_task(async function test_permissions() {
function background() {
browser.webRequest.onBeforeRequest.addListener(
@@ -113,11 +125,14 @@ add_task(async function test_permissions() {
await contentPage.close();
});
-add_task(async function test_no_webRequestBlocking_error() {
+add_task(async function test_missing_required_perm_for_blocking_error() {
function background() {
const expectedError =
"Using webRequest.addListener with the blocking option " +
"requires the 'webRequestBlocking' permission.";
+ const expectedErrorOnAuthRequired =
+ "Using webRequest.onAuthRequired.addListener with the blocking option " +
+ "requires either the 'webRequestBlocking' or 'webRequestAuthProvider' permission.";
const blockingEvents = [
"onBeforeRequest",
@@ -135,7 +150,9 @@ add_task(async function test_no_webRequestBlocking_error() {
["blocking"]
);
},
- expectedError,
+ eventName === "onAuthRequired"
+ ? expectedErrorOnAuthRequired
+ : expectedError,
`Got the expected exception for a blocking webRequest.${eventName} listener`
);
}
diff --git a/toolkit/components/extensions/test/xpcshell/test_load_all_api_modules.js b/toolkit/components/extensions/test/xpcshell/test_load_all_api_modules.js
index 32299fb04e..3d28971352 100644
--- a/toolkit/components/extensions/test/xpcshell/test_load_all_api_modules.js
+++ b/toolkit/components/extensions/test/xpcshell/test_load_all_api_modules.js
@@ -18,7 +18,7 @@ let schemaURLs = new Set();
schemaURLs.add("chrome://extensions/content/schemas/experiments.json");
// Helper class used to load the API modules similarly to the apiManager
-// defined in ExtensionParent.jsm.
+// defined in ExtensionParent.sys.mjs.
class FakeAPIManager extends ExtensionCommon.SchemaAPIManager {
constructor(processType = "main") {
super(processType, Schemas);
@@ -102,7 +102,8 @@ class FakeAPIManager extends ExtensionCommon.SchemaAPIManager {
}
// Specialized helper class used to test loading "child process" modules (similarly to the
-// SchemaAPIManagers sub-classes defined in ExtensionPageChild.jsm and ExtensionContent.jsm).
+// SchemaAPIManagers sub-classes defined in ExtensionPageChild.sys.mjs and
+// ExtensionContent.sys.mjs).
class FakeChildProcessAPIManager extends FakeAPIManager {
constructor({ processType, categoryScripts }) {
super(processType, Schemas);
diff --git a/toolkit/components/extensions/test/xpcshell/test_native_manifests.js b/toolkit/components/extensions/test/xpcshell/test_native_manifests.js
index 1f5bc88740..d4f3ae7243 100644
--- a/toolkit/components/extensions/test/xpcshell/test_native_manifests.js
+++ b/toolkit/components/extensions/test/xpcshell/test_native_manifests.js
@@ -248,7 +248,7 @@ add_task(
"lookupApplication returns the correct path with platform-native slash"
);
// Side note: manifest.path does not contain a platform-native path,
- // but it is normalized when used in NativeMessaging.jsm.
+ // but it is normalized when used in NativeMessaging.sys.mjs.
deepEqual(
result.manifest,
manifest,
diff --git a/toolkit/components/extensions/test/xpcshell/test_webRequest_ancestors.js b/toolkit/components/extensions/test/xpcshell/test_webRequest_ancestors.js
index 509f821828..abbb814ac7 100644
--- a/toolkit/components/extensions/test/xpcshell/test_webRequest_ancestors.js
+++ b/toolkit/components/extensions/test/xpcshell/test_webRequest_ancestors.js
@@ -11,7 +11,7 @@ const server = createHttpServer({ hosts: ["example.com"] });
server.registerDirectory("/data/", do_get_file("data"));
add_task(async function setup() {
- // When WebRequest.jsm is used directly instead of through ext-webRequest.js,
+ // When WebRequest.sys.mjs is used directly instead of through ext-webRequest.js,
// ExtensionParent.apiManager is not automatically initialized. Do it here.
await ExtensionParent.apiManager.lazyInit();
});
diff --git a/toolkit/components/extensions/test/xpcshell/test_webRequest_cookies.js b/toolkit/components/extensions/test/xpcshell/test_webRequest_cookies.js
index 53ed465786..f4c8d75690 100644
--- a/toolkit/components/extensions/test/xpcshell/test_webRequest_cookies.js
+++ b/toolkit/components/extensions/test/xpcshell/test_webRequest_cookies.js
@@ -75,7 +75,7 @@ function onResponseStarted(details) {
}
add_task(async function setup() {
- // When WebRequest.jsm is used directly instead of through ext-webRequest.js,
+ // When WebRequest.sys.mjs is used directly instead of through ext-webRequest.js,
// ExtensionParent.apiManager is not automatically initialized. Do it here.
await ExtensionParent.apiManager.lazyInit();
});
diff --git a/toolkit/components/extensions/test/xpcshell/test_webRequest_filtering.js b/toolkit/components/extensions/test/xpcshell/test_webRequest_filtering.js
index 46a72a5926..86b2410e33 100644
--- a/toolkit/components/extensions/test/xpcshell/test_webRequest_filtering.js
+++ b/toolkit/components/extensions/test/xpcshell/test_webRequest_filtering.js
@@ -89,7 +89,7 @@ add_task(async function setup() {
// Disable rcwn to make cache behavior deterministic.
Services.prefs.setBoolPref("network.http.rcwn.enabled", false);
- // When WebRequest.jsm is used directly instead of through ext-webRequest.js,
+ // When WebRequest.sys.mjs is used directly instead of through ext-webRequest.js,
// ExtensionParent.apiManager is not automatically initialized. Do it here.
await ExtensionParent.apiManager.lazyInit();
});
diff --git a/toolkit/components/extensions/test/xpcshell/xpcshell-common.toml b/toolkit/components/extensions/test/xpcshell/xpcshell-common.toml
index 7cf8d79409..6d47012eca 100644
--- a/toolkit/components/extensions/test/xpcshell/xpcshell-common.toml
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell-common.toml
@@ -533,7 +533,7 @@ skip-if = ["os == 'android'"] # Bug 1564871
["test_ext_storage_sanitizer.js"]
skip-if = [
"appname == 'thunderbird'",
- "os == 'android'", # Sanitizer.jsm is not in toolkit.
+ "os == 'android'", # Sanitizer.sys.mjs is not in toolkit.
]
["test_ext_storage_session.js"]
@@ -587,7 +587,9 @@ skip-if = [
["test_ext_wasm.js"]
["test_ext_webRequest_auth.js"]
-skip-if = ["os == 'android' && debug"]
+skip-if = [
+ "os == 'android' && debug",
+]
["test_ext_webRequest_cached.js"]
skip-if = ["os == 'android'"] # Bug 1573511
@@ -616,7 +618,9 @@ skip-if = ["os == 'android' && debug"]
skip-if = ["tsan"] # Bug 1683730
["test_ext_webRequest_permission.js"]
-skip-if = ["os == 'android' && debug"]
+skip-if = [
+ "os == 'android' && debug",
+]
["test_ext_webRequest_redirectProperty.js"]
skip-if = ["os == 'android' && processor == 'x86_64'"] # Bug 1683253
diff --git a/toolkit/components/extensions/tsconfig.json b/toolkit/components/extensions/tsconfig.json
index d569ba9eca..992018f4a8 100644
--- a/toolkit/components/extensions/tsconfig.json
+++ b/toolkit/components/extensions/tsconfig.json
@@ -1,15 +1,11 @@
{
- "include": ["*.mjs", "types/globals.ts"],
- "exclude": [],
+ "include": ["*.mjs", "types/*.ts"],
"compilerOptions": {
"checkJs": true,
- "target": "ESNEXT",
-
- "declaration": true,
- "outDir": "./types",
- "typeRoots": [],
"noEmit": true,
+ "target": "es2022",
+ "types": ["gecko"],
// prettier-ignore
"paths": {
@@ -39,11 +35,12 @@
"resource://gre/modules/Schemas.sys.mjs": ["./Schemas.sys.mjs"],
"resource://gre/modules/WebNavigationFrames.sys.mjs": ["./WebNavigationFrames.sys.mjs"],
"resource://gre/modules/WebRequest.sys.mjs": ["./webrequest/WebRequest.sys.mjs"],
+ "resource://testing-common/ExtensionTestCommon.sys.mjs": ["./ExtensionTestCommon.sys.mjs"],
// External.
"resource://gre/modules/addons/crypto-utils.sys.mjs": ["../../mozapps/extensions/internal/crypto-utils.sys.mjs"],
"resource://gre/modules/XPCOMUtils.sys.mjs": ["../../../js/xpconnect/loader/XPCOMUtils.sys.mjs"],
- "resource://testing-common/ExtensionTestCommon.sys.mjs": ["./ExtensionTestCommon.sys.mjs"],
+ "resource://devtools/server/actors/descriptors/webextension.js": ["./types/globals.ts"],
// Types for external modules which need fixing, but we don't wanna touch.
"resource://testing-common/XPCShellContentUtils.sys.mjs": ["./types/XPCShellContentUtils.sys.d.mts"],
diff --git a/toolkit/components/extensions/types/README.md b/toolkit/components/extensions/types/README.md
index ebd01dec60..1d4588389c 100644
--- a/toolkit/components/extensions/types/README.md
+++ b/toolkit/components/extensions/types/README.md
@@ -21,11 +21,11 @@ viability and benefits of doing this for a "typical" component.
## How to use and expectations
-Use [npm or yarn to install][download] TypeScript.
-Then run `tsc` in the extensions directory to check types:
+Mach now comes with a `ts` command for type checking code using the built-in
+gecko typelibs:
```
-mozilla-central/toolkit/components/extensions $ tsc
+mozilla-central $ ./mach ts check toolkit/components/extensions/
```
You can also use an editor which supports the [language server][langserv].
@@ -64,6 +64,9 @@ These fall under 5 main categories:
5) Don't re-use local variables unnecessarily with different types.
* (general good practice, local variables are "free")
+ 6) Use `export` on individual classes instead of grouping into "namespaces".
+ * (idiomatic/ergonomic/modern JS, grouping was a leftover from JSMs)
+
### @ts-ignore recommendations
*Don't* use `@ts-ignore` for class fields and function or method signatures.
@@ -75,7 +78,7 @@ These fall under 5 main categories:
parts of the codebase.
-[handbook]: https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html
+[handbook]: https://www.typescriptlang.org/docs/handbook/intro-to-js-ts.html
[jsdoc]: https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html
diff --git a/toolkit/components/extensions/types/ext-tabs-base.d.ts b/toolkit/components/extensions/types/ext-tabs-base.d.ts
new file mode 100644
index 0000000000..c889fd6620
--- /dev/null
+++ b/toolkit/components/extensions/types/ext-tabs-base.d.ts
@@ -0,0 +1,1447 @@
+// @ts-nocheck
+
+declare namespace tabs_base {
+
+declare function getUserContextIdForCookieStoreId(extension: any, cookieStoreId: any, isPrivateBrowsing: any): any;
+declare var DefaultMap: any;
+declare var DefaultWeakMap: any;
+declare var ExtensionError: any;
+declare var parseMatchPatterns: any;
+declare var defineLazyGetter: any;
+/**
+ * The platform-specific type of native tab objects, which are wrapped by
+ * TabBase instances.
+ *
+ * @typedef {object | XULElement} NativeTab
+ */
+/**
+ * @typedef {object} MutedInfo
+ * @property {boolean} muted
+ * True if the tab is currently muted, false otherwise.
+ * @property {string} [reason]
+ * The reason the tab is muted. Either "user", if the tab was muted by a
+ * user, or "extension", if it was muted by an extension.
+ * @property {string} [extensionId]
+ * If the tab was muted by an extension, contains the internal ID of that
+ * extension.
+ */
+/**
+ * A platform-independent base class for extension-specific wrappers around
+ * native tab objects.
+ *
+ * @param {Extension} extension
+ * The extension object for which this wrapper is being created. Used to
+ * determine permissions for access to certain properties and
+ * functionality.
+ * @param {NativeTab} nativeTab
+ * The native tab object which is being wrapped. The type of this object
+ * varies by platform.
+ * @param {integer} id
+ * The numeric ID of this tab object. This ID should be the same for
+ * every extension, and for the lifetime of the tab.
+ */
+declare class TabBase {
+ constructor(extension: any, nativeTab: any, id: any);
+ extension: any;
+ tabManager: any;
+ id: any;
+ nativeTab: any;
+ activeTabWindowID: any;
+ /**
+ * Capture the visible area of this tab, and return the result as a data: URI.
+ *
+ * @param {BaseContext} context
+ * The extension context for which to perform the capture.
+ * @param {number} zoom
+ * The current zoom for the page.
+ * @param {object} [options]
+ * The options with which to perform the capture.
+ * @param {string} [options.format = "png"]
+ * The image format in which to encode the captured data. May be one of
+ * "png" or "jpeg".
+ * @param {integer} [options.quality = 92]
+ * The quality at which to encode the captured image data, ranging from
+ * 0 to 100. Has no effect for the "png" format.
+ * @param {DOMRectInit} [options.rect]
+ * Area of the document to render, in CSS pixels, relative to the page.
+ * If null, the currently visible viewport is rendered.
+ * @param {number} [options.scale]
+ * The scale to render at, defaults to devicePixelRatio.
+ * @returns {Promise<string>}
+ */
+ capture(context: BaseContext, zoom: number, options?: {
+ format?: string;
+ quality?: integer;
+ rect?: DOMRectInit;
+ scale?: number;
+ }): Promise<string>;
+ /**
+ * @property {integer | null} innerWindowID
+ * The last known innerWindowID loaded into this tab's docShell. This
+ * property must remain in sync with the last known values of
+ * properties such as `url` and `title`. Any operations on the content
+ * of an out-of-process tab will automatically fail if the
+ * innerWindowID of the tab when the message is received does not match
+ * the value of this property when the message was sent.
+ * @readonly
+ */
+ readonly get innerWindowID(): any;
+ /**
+ * @property {boolean} hasTabPermission
+ * Returns true if the extension has permission to access restricted
+ * properties of this tab, such as `url`, `title`, and `favIconUrl`.
+ * @readonly
+ */
+ readonly get hasTabPermission(): any;
+ /**
+ * @property {boolean} hasActiveTabPermission
+ * Returns true if the extension has the "activeTab" permission, and
+ * has been granted access to this tab due to a user executing an
+ * extension action.
+ *
+ * If true, the extension may load scripts and CSS into this tab, and
+ * access restricted properties, such as its `url`.
+ * @readonly
+ */
+ readonly get hasActiveTabPermission(): boolean;
+ /**
+ * @property {boolean} matchesHostPermission
+ * Returns true if the extensions host permissions match the current tab url.
+ * @readonly
+ */
+ readonly get matchesHostPermission(): any;
+ /**
+ * @property {boolean} incognito
+ * Returns true if this is a private browsing tab, false otherwise.
+ * @readonly
+ */
+ readonly get _incognito(): any;
+ /**
+ * @property {string} _url
+ * Returns the current URL of this tab. Does not do any permission
+ * checks.
+ * @readonly
+ */
+ readonly get _url(): any;
+ /**
+ * @property {string | null} url
+ * Returns the current URL of this tab if the extension has permission
+ * to read it, or null otherwise.
+ * @readonly
+ */
+ readonly get url(): any;
+ /**
+ * @property {nsIURI} _uri
+ * Returns the current URI of this tab.
+ * @readonly
+ */
+ readonly get _uri(): any;
+ /**
+ * @property {string} _title
+ * Returns the current title of this tab. Does not do any permission
+ * checks.
+ * @readonly
+ */
+ readonly get _title(): any;
+ /**
+ * @property {nsIURI | null} title
+ * Returns the current title of this tab if the extension has permission
+ * to read it, or null otherwise.
+ * @readonly
+ */
+ readonly get title(): any;
+ /**
+ * @property {string} _favIconUrl
+ * Returns the current favicon URL of this tab. Does not do any permission
+ * checks.
+ * @readonly
+ * @abstract
+ */
+ readonly get _favIconUrl(): void;
+ /**
+ * @property {nsIURI | null} faviconUrl
+ * Returns the current faviron URL of this tab if the extension has permission
+ * to read it, or null otherwise.
+ * @readonly
+ */
+ readonly get favIconUrl(): void;
+ /**
+ * @property {integer} lastAccessed
+ * Returns the last time the tab was accessed as the number of
+ * milliseconds since epoch.
+ * @readonly
+ * @abstract
+ */
+ readonly get lastAccessed(): void;
+ /**
+ * @property {boolean} audible
+ * Returns true if the tab is currently playing audio, false otherwise.
+ * @readonly
+ * @abstract
+ */
+ readonly get audible(): void;
+ /**
+ * @property {boolean} autoDiscardable
+ * Returns true if the tab can be discarded on memory pressure, false otherwise.
+ * @readonly
+ * @abstract
+ */
+ readonly get autoDiscardable(): void;
+ /**
+ * @property {XULElement} browser
+ * Returns the XUL browser for the given tab.
+ * @readonly
+ * @abstract
+ */
+ readonly get browser(): XULBrowserElement;
+ /**
+ * @property {BrowsingContext} browsingContext
+ * Returns the BrowsingContext for the given tab.
+ * @readonly
+ */
+ readonly get browsingContext(): any;
+ /**
+ * @property {FrameLoader} frameLoader
+ * Returns the frameloader for the given tab.
+ * @readonly
+ */
+ readonly get frameLoader(): void;
+ /**
+ * @property {string} cookieStoreId
+ * Returns the cookie store identifier for the given tab.
+ * @readonly
+ * @abstract
+ */
+ readonly get cookieStoreId(): void;
+ /**
+ * @property {integer} openerTabId
+ * Returns the ID of the tab which opened this one.
+ * @readonly
+ */
+ readonly get openerTabId(): any;
+ /**
+ * @property {integer} discarded
+ * Returns true if the tab is discarded.
+ * @readonly
+ * @abstract
+ */
+ readonly get discarded(): void;
+ /**
+ * @property {integer} height
+ * Returns the pixel height of the visible area of the tab.
+ * @readonly
+ * @abstract
+ */
+ readonly get height(): void;
+ /**
+ * @property {integer} hidden
+ * Returns true if the tab is hidden.
+ * @readonly
+ * @abstract
+ */
+ readonly get hidden(): void;
+ /**
+ * @property {integer} index
+ * Returns the index of the tab in its window's tab list.
+ * @readonly
+ * @abstract
+ */
+ readonly get index(): void;
+ /**
+ * @property {MutedInfo} mutedInfo
+ * Returns information about the tab's current audio muting status.
+ * @readonly
+ * @abstract
+ */
+ readonly get mutedInfo(): void;
+ /**
+ * @property {SharingState} sharingState
+ * Returns object with tab sharingState.
+ * @readonly
+ * @abstract
+ */
+ readonly get sharingState(): void;
+ /**
+ * @property {boolean} pinned
+ * Returns true if the tab is pinned, false otherwise.
+ * @readonly
+ * @abstract
+ */
+ readonly get pinned(): void;
+ /**
+ * @property {boolean} active
+ * Returns true if the tab is the currently-selected tab, false
+ * otherwise.
+ * @readonly
+ * @abstract
+ */
+ readonly get active(): void;
+ /**
+ * @property {boolean} highlighted
+ * Returns true if the tab is highlighted.
+ * @readonly
+ * @abstract
+ */
+ readonly get highlighted(): void;
+ /**
+ * @property {string} status
+ * Returns the current loading status of the tab. May be either
+ * "loading" or "complete".
+ * @readonly
+ * @abstract
+ */
+ readonly get status(): void;
+ /**
+ * @property {integer} height
+ * Returns the pixel height of the visible area of the tab.
+ * @readonly
+ * @abstract
+ */
+ readonly get width(): void;
+ /**
+ * @property {DOMWindow} window
+ * Returns the browser window to which the tab belongs.
+ * @readonly
+ * @abstract
+ */
+ readonly get window(): void;
+ /**
+ * @property {integer} window
+ * Returns the numeric ID of the browser window to which the tab belongs.
+ * @readonly
+ * @abstract
+ */
+ readonly get windowId(): void;
+ /**
+ * @property {boolean} attention
+ * Returns true if the tab is drawing attention.
+ * @readonly
+ * @abstract
+ */
+ readonly get attention(): void;
+ /**
+ * @property {boolean} isArticle
+ * Returns true if the document in the tab can be rendered in reader
+ * mode.
+ * @readonly
+ * @abstract
+ */
+ readonly get isArticle(): void;
+ /**
+ * @property {boolean} isInReaderMode
+ * Returns true if the document in the tab is being rendered in reader
+ * mode.
+ * @readonly
+ * @abstract
+ */
+ readonly get isInReaderMode(): void;
+ /**
+ * @property {integer} successorTabId
+ * @readonly
+ * @abstract
+ */
+ readonly get successorTabId(): void;
+ /**
+ * Returns true if this tab matches the the given query info object. Omitted
+ * or null have no effect on the match.
+ *
+ * @param {object} queryInfo
+ * The query info against which to match.
+ * @param {boolean} [queryInfo.active]
+ * Matches against the exact value of the tab's `active` attribute.
+ * @param {boolean} [queryInfo.audible]
+ * Matches against the exact value of the tab's `audible` attribute.
+ * @param {boolean} [queryInfo.autoDiscardable]
+ * Matches against the exact value of the tab's `autoDiscardable` attribute.
+ * @param {string} [queryInfo.cookieStoreId]
+ * Matches against the exact value of the tab's `cookieStoreId` attribute.
+ * @param {boolean} [queryInfo.discarded]
+ * Matches against the exact value of the tab's `discarded` attribute.
+ * @param {boolean} [queryInfo.hidden]
+ * Matches against the exact value of the tab's `hidden` attribute.
+ * @param {boolean} [queryInfo.highlighted]
+ * Matches against the exact value of the tab's `highlighted` attribute.
+ * @param {integer} [queryInfo.index]
+ * Matches against the exact value of the tab's `index` attribute.
+ * @param {boolean} [queryInfo.muted]
+ * Matches against the exact value of the tab's `mutedInfo.muted` attribute.
+ * @param {boolean} [queryInfo.pinned]
+ * Matches against the exact value of the tab's `pinned` attribute.
+ * @param {string} [queryInfo.status]
+ * Matches against the exact value of the tab's `status` attribute.
+ * @param {string} [queryInfo.title]
+ * Matches against the exact value of the tab's `title` attribute.
+ * @param {string|boolean } [queryInfo.screen]
+ * Matches against the exact value of the tab's `sharingState.screen` attribute, or use true to match any screen sharing tab.
+ * @param {boolean} [queryInfo.camera]
+ * Matches against the exact value of the tab's `sharingState.camera` attribute.
+ * @param {boolean} [queryInfo.microphone]
+ * Matches against the exact value of the tab's `sharingState.microphone` attribute.
+ *
+ * Note: Per specification, this should perform a pattern match, rather
+ * than an exact value match, and will do so in the future.
+ * @param {MatchPattern} [queryInfo.url]
+ * Requires the tab's URL to match the given MatchPattern object.
+ *
+ * @returns {boolean}
+ * True if the tab matches the query.
+ */
+ matches(queryInfo: {
+ active?: boolean;
+ audible?: boolean;
+ autoDiscardable?: boolean;
+ cookieStoreId?: string;
+ discarded?: boolean;
+ hidden?: boolean;
+ highlighted?: boolean;
+ index?: integer;
+ muted?: boolean;
+ pinned?: boolean;
+ status?: string;
+ title?: string;
+ screen?: string | boolean;
+ camera?: boolean;
+ microphone?: boolean;
+ url?: MatchPattern;
+ }): boolean;
+ /**
+ * Converts this tab object to a JSON-compatible object containing the values
+ * of its properties which the extension is permitted to access, in the format
+ * required to be returned by WebExtension APIs.
+ *
+ * @param {object} [fallbackTabSize]
+ * A geometry data if the lazy geometry data for this tab hasn't been
+ * initialized yet.
+ * @returns {object}
+ */
+ convert(fallbackTabSize?: object): object;
+ /**
+ * Query each content process hosting subframes of the tab, return results.
+ *
+ * @param {string} message
+ * @param {object} options
+ * These options are also sent to the message handler in the
+ * `ExtensionContentChild`.
+ * @param {number[]} options.frameIds
+ * When omitted, all frames will be queried.
+ * @param {boolean} options.returnResultsWithFrameIds
+ * @returns {Promise[]}
+ */
+ queryContent(message: string, options: {
+ frameIds: number[];
+ returnResultsWithFrameIds: boolean;
+ }): Promise<any>[];
+ /**
+ * Inserts a script or stylesheet in the given tab, and returns a promise
+ * which resolves when the operation has completed.
+ *
+ * @param {BaseContext} context
+ * The extension context for which to perform the injection.
+ * @param {InjectDetails} details
+ * The InjectDetails object, specifying what to inject, where, and
+ * when.
+ * @param {string} kind
+ * The kind of data being injected. Either "script" or "css".
+ * @param {string} method
+ * The name of the method which was called to trigger the injection.
+ * Used to generate appropriate error messages on failure.
+ *
+ * @returns {Promise}
+ * Resolves to the result of the execution, once it has completed.
+ * @private
+ */
+ private _execute;
+ /**
+ * Executes a script in the tab's content window, and returns a Promise which
+ * resolves to the result of the evaluation, or rejects to the value of any
+ * error the injection generates.
+ *
+ * @param {BaseContext} context
+ * The extension context for which to inject the script.
+ * @param {InjectDetails} details
+ * The InjectDetails object, specifying what to inject, where, and
+ * when.
+ *
+ * @returns {Promise}
+ * Resolves to the result of the evaluation of the given script, once
+ * it has completed, or rejects with any error the evaluation
+ * generates.
+ */
+ executeScript(context: BaseContext, details: InjectDetails): Promise<any>;
+ /**
+ * Injects CSS into the tab's content window, and returns a Promise which
+ * resolves when the injection is complete.
+ *
+ * @param {BaseContext} context
+ * The extension context for which to inject the script.
+ * @param {InjectDetails} details
+ * The InjectDetails object, specifying what to inject, and where.
+ *
+ * @returns {Promise}
+ * Resolves when the injection has completed.
+ */
+ insertCSS(context: BaseContext, details: InjectDetails): Promise<any>;
+ /**
+ * Removes CSS which was previously into the tab's content window via
+ * `insertCSS`, and returns a Promise which resolves when the operation is
+ * complete.
+ *
+ * @param {BaseContext} context
+ * The extension context for which to remove the CSS.
+ * @param {InjectDetails} details
+ * The InjectDetails object, specifying what to remove, and from where.
+ *
+ * @returns {Promise}
+ * Resolves when the operation has completed.
+ */
+ removeCSS(context: BaseContext, details: InjectDetails): Promise<any>;
+}
+declare const WINDOW_ID_NONE: -1;
+declare const WINDOW_ID_CURRENT: -2;
+/**
+ * A platform-independent base class for extension-specific wrappers around
+ * native browser windows
+ *
+ * @param {Extension} extension
+ * The extension object for which this wrapper is being created.
+ * @param {DOMWindow} window
+ * The browser DOM window which is being wrapped.
+ * @param {integer} id
+ * The numeric ID of this DOM window object. This ID should be the same for
+ * every extension, and for the lifetime of the window.
+ */
+declare class WindowBase {
+ /**
+ * Returns the window state of the given window.
+ *
+ * @param {DOMWindow} window
+ * The window for which to return a state.
+ *
+ * @returns {string}
+ * The window's state. One of "normal", "minimized", "maximized",
+ * "fullscreen", or "docked".
+ * @static
+ * @abstract
+ */
+ static getState(window: DOMWindow): string;
+ constructor(extension: any, window: any, id: any);
+ extension: any;
+ window: any;
+ id: any;
+ /**
+ * @property {nsIAppWindow} appWindow
+ * The nsIAppWindow object for this browser window.
+ * @readonly
+ */
+ readonly get appWindow(): any;
+ /**
+ * Returns true if this window is the current window for the given extension
+ * context, false otherwise.
+ *
+ * @param {BaseContext} context
+ * The extension context for which to perform the check.
+ *
+ * @returns {boolean}
+ */
+ isCurrentFor(context: BaseContext): boolean;
+ /**
+ * @property {string} type
+ * The type of the window, as defined by the WebExtension API. May be
+ * either "normal" or "popup".
+ * @readonly
+ */
+ readonly get type(): "popup" | "normal";
+ /**
+ * Converts this window object to a JSON-compatible object which may be
+ * returned to an extension, in the format required to be returned by
+ * WebExtension APIs.
+ *
+ * @param {object} [getInfo]
+ * An optional object, the properties of which determine what data is
+ * available on the result object.
+ * @param {boolean} [getInfo.populate]
+ * Of true, the result object will contain a `tabs` property,
+ * containing an array of converted Tab objects, one for each tab in
+ * the window.
+ *
+ * @returns {object}
+ */
+ convert(getInfo?: {
+ populate?: boolean;
+ }): object;
+ /**
+ * Returns true if this window matches the the given query info object. Omitted
+ * or null have no effect on the match.
+ *
+ * @param {object} queryInfo
+ * The query info against which to match.
+ * @param {boolean} [queryInfo.currentWindow]
+ * Matches against against the return value of `isCurrentFor()` for the
+ * given context.
+ * @param {boolean} [queryInfo.lastFocusedWindow]
+ * Matches against the exact value of the window's `isLastFocused` attribute.
+ * @param {boolean} [queryInfo.windowId]
+ * Matches against the exact value of the window's ID, taking into
+ * account the special WINDOW_ID_CURRENT value.
+ * @param {string} [queryInfo.windowType]
+ * Matches against the exact value of the window's `type` attribute.
+ * @param {BaseContext} context
+ * The extension context for which the matching is being performed.
+ * Used to determine the current window for relevant properties.
+ *
+ * @returns {boolean}
+ * True if the window matches the query.
+ */
+ matches(queryInfo: {
+ currentWindow?: boolean;
+ lastFocusedWindow?: boolean;
+ windowId?: boolean;
+ windowType?: string;
+ }, context: BaseContext): boolean;
+ /**
+ * @property {boolean} focused
+ * Returns true if the browser window is currently focused.
+ * @readonly
+ * @abstract
+ */
+ readonly get focused(): void;
+ /**
+ * @property {integer} top
+ * Returns the pixel offset of the top of the window from the top of
+ * the screen.
+ * @readonly
+ * @abstract
+ */
+ readonly get top(): void;
+ /**
+ * @property {integer} left
+ * Returns the pixel offset of the left of the window from the left of
+ * the screen.
+ * @readonly
+ * @abstract
+ */
+ readonly get left(): void;
+ /**
+ * @property {integer} width
+ * Returns the pixel width of the window.
+ * @readonly
+ * @abstract
+ */
+ readonly get width(): void;
+ /**
+ * @property {integer} height
+ * Returns the pixel height of the window.
+ * @readonly
+ * @abstract
+ */
+ readonly get height(): void;
+ /**
+ * @property {boolean} incognito
+ * Returns true if this is a private browsing window, false otherwise.
+ * @readonly
+ * @abstract
+ */
+ readonly get incognito(): void;
+ /**
+ * @property {boolean} alwaysOnTop
+ * Returns true if this window is constrained to always remain above
+ * other windows.
+ * @readonly
+ * @abstract
+ */
+ readonly get alwaysOnTop(): void;
+ /**
+ * @property {boolean} isLastFocused
+ * Returns true if this is the browser window which most recently had
+ * focus.
+ * @readonly
+ * @abstract
+ */
+ readonly get isLastFocused(): void;
+ set state(state: void);
+ /**
+ * @property {string} state
+ * Returns or sets the current state of this window, as determined by
+ * `getState()`.
+ * @abstract
+ */
+ get state(): void;
+ /**
+ * @property {nsIURI | null} title
+ * Returns the current title of this window if the extension has permission
+ * to read it, or null otherwise.
+ * @readonly
+ */
+ readonly get title(): any;
+ /**
+ * Returns an iterator of TabBase objects for each tab in this window.
+ *
+ * @returns {Iterator<TabBase>}
+ */
+ getTabs(): Iterator<TabBase>;
+ /**
+ * Returns an iterator of TabBase objects for each highlighted tab in this window.
+ *
+ * @returns {Iterator<TabBase>}
+ */
+ getHighlightedTabs(): Iterator<TabBase>;
+ /**
+ * @property {TabBase} The window's currently active tab.
+ */
+ get activeTab(): void;
+ /**
+ * Returns the window's tab at the specified index.
+ *
+ * @param {integer} index
+ * The index of the desired tab.
+ *
+ * @returns {TabBase|undefined}
+ */
+ getTabAtIndex(index: integer): TabBase | undefined;
+}
+/**
+ * The parameter type of "tab-attached" events, which are emitted when a
+ * pre-existing tab is attached to a new window.
+ *
+ * @typedef {object} TabAttachedEvent
+ * @property {NativeTab} tab
+ * The native tab object in the window to which the tab is being
+ * attached. This may be a different object than was used to represent
+ * the tab in the old window.
+ * @property {integer} tabId
+ * The ID of the tab being attached.
+ * @property {integer} newWindowId
+ * The ID of the window to which the tab is being attached.
+ * @property {integer} newPosition
+ * The position of the tab in the tab list of the new window.
+ */
+/**
+ * The parameter type of "tab-detached" events, which are emitted when a
+ * pre-existing tab is detached from a window, in order to be attached to a new
+ * window.
+ *
+ * @typedef {object} TabDetachedEvent
+ * @property {NativeTab} tab
+ * The native tab object in the window from which the tab is being
+ * detached. This may be a different object than will be used to
+ * represent the tab in the new window.
+ * @property {NativeTab} adoptedBy
+ * The native tab object in the window to which the tab will be attached,
+ * and is adopting the contents of this tab. This may be a different
+ * object than the tab in the previous window.
+ * @property {integer} tabId
+ * The ID of the tab being detached.
+ * @property {integer} oldWindowId
+ * The ID of the window from which the tab is being detached.
+ * @property {integer} oldPosition
+ * The position of the tab in the tab list of the window from which it is
+ * being detached.
+ */
+/**
+ * The parameter type of "tab-created" events, which are emitted when a
+ * new tab is created.
+ *
+ * @typedef {object} TabCreatedEvent
+ * @property {NativeTab} tab
+ * The native tab object for the tab which is being created.
+ */
+/**
+ * The parameter type of "tab-removed" events, which are emitted when a
+ * tab is removed and destroyed.
+ *
+ * @typedef {object} TabRemovedEvent
+ * @property {NativeTab} tab
+ * The native tab object for the tab which is being removed.
+ * @property {integer} tabId
+ * The ID of the tab being removed.
+ * @property {integer} windowId
+ * The ID of the window from which the tab is being removed.
+ * @property {boolean} isWindowClosing
+ * True if the tab is being removed because the window is closing.
+ */
+/**
+ * An object containing basic, extension-independent information about the window
+ * and tab that a XUL <browser> belongs to.
+ *
+ * @typedef {object} BrowserData
+ * @property {integer} tabId
+ * The numeric ID of the tab that a <browser> belongs to, or -1 if it
+ * does not belong to a tab.
+ * @property {integer} windowId
+ * The numeric ID of the browser window that a <browser> belongs to, or -1
+ * if it does not belong to a browser window.
+ */
+/**
+ * A platform-independent base class for the platform-specific TabTracker
+ * classes, which track the opening and closing of tabs, and manage the mapping
+ * of them between numeric IDs and native tab objects.
+ *
+ * Instances of this class are EventEmitters which emit the following events,
+ * each with an argument of the given type:
+ *
+ * - "tab-attached" {@link TabAttacheEvent}
+ * - "tab-detached" {@link TabDetachedEvent}
+ * - "tab-created" {@link TabCreatedEvent}
+ * - "tab-removed" {@link TabRemovedEvent}
+ */
+declare class TabTrackerBase {
+ on(...args: any[]): any;
+ /**
+ * Called to initialize the tab tracking listeners the first time that an
+ * event listener is added.
+ *
+ * @protected
+ * @abstract
+ */
+ protected init(): void;
+ /**
+ * Returns the numeric ID for the given native tab.
+ *
+ * @param {NativeTab} nativeTab
+ * The native tab for which to return an ID.
+ *
+ * @returns {integer}
+ * The tab's numeric ID.
+ * @abstract
+ */
+ getId(nativeTab: NativeTab): integer;
+ /**
+ * Returns the native tab with the given numeric ID.
+ *
+ * @param {integer} tabId
+ * The numeric ID of the tab to return.
+ * @param {*} default_
+ * The value to return if no tab exists with the given ID.
+ *
+ * @returns {NativeTab}
+ * @throws {ExtensionError}
+ * If no tab exists with the given ID and a default return value is not
+ * provided.
+ * @abstract
+ */
+ getTab(tabId: integer, default_?: any): NativeTab;
+ /**
+ * Returns basic information about the tab and window that the given browser
+ * belongs to.
+ *
+ * @param {XULElement} browser
+ * The XUL browser element for which to return data.
+ *
+ * @returns {BrowserData}
+ * @abstract
+ */
+ getBrowserData(browser: XULElement): BrowserData;
+ /**
+ * @property {NativeTab} activeTab
+ * Returns the native tab object for the active tab in the
+ * most-recently focused window, or null if no live tabs currently
+ * exist.
+ * @abstract
+ */
+ get activeTab(): void;
+}
+/**
+ * A browser progress listener instance which calls a given listener function
+ * whenever the status of the given browser changes.
+ *
+ * @param {function(object): void} listener
+ * A function to be called whenever the status of a tab's top-level
+ * browser. It is passed an object with a `browser` property pointing to
+ * the XUL browser, and a `status` property with a string description of
+ * the browser's status.
+ * @private
+ */
+declare class StatusListener {
+ constructor(listener: any);
+ listener: any;
+ onStateChange(browser: any, webProgress: any, request: any, stateFlags: any, statusCode: any): void;
+ onLocationChange(browser: any, webProgress: any, request: any, locationURI: any, flags: any): void;
+}
+/**
+ * A platform-independent base class for the platform-specific WindowTracker
+ * classes, which track the opening and closing of windows, and manage the
+ * mapping of them between numeric IDs and native tab objects.
+ */
+declare class WindowTrackerBase {
+ /**
+ * A private method which is called whenever a new browser window is opened,
+ * and adds the necessary listeners to it.
+ *
+ * @param {DOMWindow} window
+ * The window being opened.
+ * @private
+ */
+ private _handleWindowOpened;
+ _openListeners: Set<any>;
+ _closeListeners: Set<any>;
+ _listeners: any;
+ _statusListeners: any;
+ _windowIds: any;
+ isBrowserWindow(window: any): boolean;
+ /**
+ * Returns an iterator for all currently active browser windows.
+ *
+ * @param {boolean} [includeInomplete = false]
+ * If true, include browser windows which are not yet fully loaded.
+ * Otherwise, only include windows which are.
+ *
+ * @returns {Iterator<DOMWindow>}
+ */
+ browserWindows(includeIncomplete?: boolean): Iterator<DOMWindow>;
+ /**
+ * @property {DOMWindow|null} topWindow
+ * The currently active, or topmost, browser window, or null if no
+ * browser window is currently open.
+ * @readonly
+ */
+ readonly get topWindow(): any;
+ /**
+ * @property {DOMWindow|null} topWindow
+ * The currently active, or topmost, browser window that is not
+ * private browsing, or null if no browser window is currently open.
+ * @readonly
+ */
+ readonly get topNonPBWindow(): any;
+ /**
+ * Returns the top window accessible by the extension.
+ *
+ * @param {BaseContext} context
+ * The extension context for which to return the current window.
+ *
+ * @returns {DOMWindow|null}
+ */
+ getTopWindow(context: BaseContext): DOMWindow | null;
+ /**
+ * Returns the numeric ID for the given browser window.
+ *
+ * @param {DOMWindow} window
+ * The DOM window for which to return an ID.
+ *
+ * @returns {integer}
+ * The window's numeric ID.
+ */
+ getId(window: DOMWindow): integer;
+ /**
+ * Returns the browser window to which the given context belongs, or the top
+ * browser window if the context does not belong to a browser window.
+ *
+ * @param {BaseContext} context
+ * The extension context for which to return the current window.
+ *
+ * @returns {DOMWindow|null}
+ */
+ getCurrentWindow(context: BaseContext): DOMWindow | null;
+ /**
+ * Returns the browser window with the given ID.
+ *
+ * @param {integer} id
+ * The ID of the window to return.
+ * @param {BaseContext} context
+ * The extension context for which the matching is being performed.
+ * Used to determine the current window for relevant properties.
+ * @param {boolean} [strict = true]
+ * If false, undefined will be returned instead of throwing an error
+ * in case no window exists with the given ID.
+ *
+ * @returns {DOMWindow|undefined}
+ * @throws {ExtensionError}
+ * If no window exists with the given ID and `strict` is true.
+ */
+ getWindow(id: integer, context: BaseContext, strict?: boolean): DOMWindow | undefined;
+ /**
+ * @property {boolean} _haveListeners
+ * Returns true if any window open or close listeners are currently
+ * registered.
+ * @private
+ */
+ private get _haveListeners();
+ /**
+ * Register the given listener function to be called whenever a new browser
+ * window is opened.
+ *
+ * @param {function(DOMWindow): void} listener
+ * The listener function to register.
+ */
+ addOpenListener(listener: (arg0: DOMWindow) => void): void;
+ /**
+ * Unregister a listener function registered in a previous addOpenListener
+ * call.
+ *
+ * @param {function(DOMWindow): void} listener
+ * The listener function to unregister.
+ */
+ removeOpenListener(listener: (arg0: DOMWindow) => void): void;
+ /**
+ * Register the given listener function to be called whenever a browser
+ * window is closed.
+ *
+ * @param {function(DOMWindow): void} listener
+ * The listener function to register.
+ */
+ addCloseListener(listener: (arg0: DOMWindow) => void): void;
+ /**
+ * Unregister a listener function registered in a previous addCloseListener
+ * call.
+ *
+ * @param {function(DOMWindow): void} listener
+ * The listener function to unregister.
+ */
+ removeCloseListener(listener: (arg0: DOMWindow) => void): void;
+ /**
+ * Handles load events for recently-opened windows, and adds additional
+ * listeners which may only be safely added when the window is fully loaded.
+ *
+ * @param {Event} event
+ * A DOM event to handle.
+ * @private
+ */
+ private handleEvent;
+ /**
+ * Observes "domwindowopened" and "domwindowclosed" events, notifies the
+ * appropriate listeners, and adds necessary additional listeners to the new
+ * windows.
+ *
+ * @param {DOMWindow} window
+ * A DOM window.
+ * @param {string} topic
+ * The topic being observed.
+ * @private
+ */
+ private observe;
+ /**
+ * Add an event listener to be called whenever the given DOM event is received
+ * at the top level of any browser window.
+ *
+ * @param {string} type
+ * The type of event to listen for. May be any valid DOM event name, or
+ * one of the following special cases:
+ *
+ * - "progress": Adds a tab progress listener to every browser window.
+ * - "status": Adds a StatusListener to every tab of every browser
+ * window.
+ * - "domwindowopened": Acts as an alias for addOpenListener.
+ * - "domwindowclosed": Acts as an alias for addCloseListener.
+ * @param {Function | object} listener
+ * The listener to invoke in response to the given events.
+ *
+ * @returns {undefined}
+ */
+ addListener(type: string, listener: Function | object): undefined;
+ /**
+ * Removes an event listener previously registered via an addListener call.
+ *
+ * @param {string} type
+ * The type of event to stop listening for.
+ * @param {Function | object} listener
+ * The listener to remove.
+ *
+ * @returns {undefined}
+ */
+ removeListener(type: string, listener: Function | object): undefined;
+ /**
+ * Adds a listener for the given event to the given window.
+ *
+ * @param {DOMWindow} window
+ * The browser window to which to add the listener.
+ * @param {string} eventType
+ * The type of DOM event to listen for, or "progress" to add a tab
+ * progress listener.
+ * @param {Function | object} listener
+ * The listener to add.
+ * @private
+ */
+ private _addWindowListener;
+ /**
+ * Adds a tab progress listener to the given browser window.
+ *
+ * @param {DOMWindow} window
+ * The browser window to which to add the listener.
+ * @param {object} listener
+ * The tab progress listener to add.
+ * @abstract
+ */
+ addProgressListener(window: DOMWindow, listener: object): void;
+ /**
+ * Removes a tab progress listener from the given browser window.
+ *
+ * @param {DOMWindow} window
+ * The browser window from which to remove the listener.
+ * @param {object} listener
+ * The tab progress listener to remove.
+ * @abstract
+ */
+ removeProgressListener(window: DOMWindow, listener: object): void;
+}
+/**
+ * Manages native tabs, their wrappers, and their dynamic permissions for a
+ * particular extension.
+ *
+ * @param {Extension} extension
+ * The extension for which to manage tabs.
+ */
+declare class TabManagerBase {
+ constructor(extension: any);
+ extension: any;
+ _tabs: any;
+ /**
+ * If the extension has requested activeTab permission, grant it those
+ * permissions for the current inner window in the given native tab.
+ *
+ * @param {NativeTab} nativeTab
+ * The native tab for which to grant permissions.
+ */
+ addActiveTabPermission(nativeTab: NativeTab): void;
+ /**
+ * Revoke the extension's activeTab permissions for the current inner window
+ * of the given native tab.
+ *
+ * @param {NativeTab} nativeTab
+ * The native tab for which to revoke permissions.
+ */
+ revokeActiveTabPermission(nativeTab: NativeTab): void;
+ /**
+ * Returns true if the extension has requested activeTab permission, and has
+ * been granted permissions for the current inner window if this tab.
+ *
+ * @param {NativeTab} nativeTab
+ * The native tab for which to check permissions.
+ * @returns {boolean}
+ * True if the extension has activeTab permissions for this tab.
+ */
+ hasActiveTabPermission(nativeTab: NativeTab): boolean;
+ /**
+ * Activate MV3 content scripts if the extension has activeTab or an
+ * (ungranted) host permission.
+ *
+ * @param {NativeTab} nativeTab
+ */
+ activateScripts(nativeTab: NativeTab): void;
+ /**
+ * Returns true if the extension has permissions to access restricted
+ * properties of the given native tab. In practice, this means that it has
+ * either requested the "tabs" permission or has activeTab permissions for the
+ * given tab.
+ *
+ * NOTE: Never use this method on an object that is not a native tab
+ * for the current platform: this method implicitly generates a wrapper
+ * for the passed nativeTab parameter and the platform-specific tabTracker
+ * instance is likely to store it in a map which is cleared only when the
+ * tab is closed (and so, if nativeTab is not a real native tab, it will
+ * never be cleared from the platform-specific tabTracker instance),
+ * See Bug 1458918 for a rationale.
+ *
+ * @param {NativeTab} nativeTab
+ * The native tab for which to check permissions.
+ * @returns {boolean}
+ * True if the extension has permissions for this tab.
+ */
+ hasTabPermission(nativeTab: NativeTab): boolean;
+ /**
+ * Returns this extension's TabBase wrapper for the given native tab. This
+ * method will always return the same wrapper object for any given native tab.
+ *
+ * @param {NativeTab} nativeTab
+ * The tab for which to return a wrapper.
+ *
+ * @returns {TabBase|undefined}
+ * The wrapper for this tab.
+ */
+ getWrapper(nativeTab: NativeTab): TabBase | undefined;
+ /**
+ * Determines access using extension context.
+ *
+ * @param {NativeTab} nativeTab
+ * The tab to check access on.
+ * @returns {boolean}
+ * True if the extension has permissions for this tab.
+ * @protected
+ * @abstract
+ */
+ protected canAccessTab(nativeTab: NativeTab): boolean;
+ /**
+ * Converts the given native tab to a JSON-compatible object, in the format
+ * required to be returned by WebExtension APIs, which may be safely passed to
+ * extension code.
+ *
+ * @param {NativeTab} nativeTab
+ * The native tab to convert.
+ * @param {object} [fallbackTabSize]
+ * A geometry data if the lazy geometry data for this tab hasn't been
+ * initialized yet.
+ *
+ * @returns {object}
+ */
+ convert(nativeTab: NativeTab, fallbackTabSize?: object): object;
+ /**
+ * Returns an iterator of TabBase objects which match the given query info.
+ *
+ * @param {object | null} [queryInfo = null]
+ * An object containing properties on which to filter. May contain any
+ * properties which are recognized by {@link TabBase#matches} or
+ * {@link WindowBase#matches}. Unknown properties will be ignored.
+ * @param {BaseContext|null} [context = null]
+ * The extension context for which the matching is being performed.
+ * Used to determine the current window for relevant properties.
+ *
+ * @returns {Iterator<TabBase>}
+ */
+ query(queryInfo?: object | null, context?: BaseContext | null): Iterator<TabBase>;
+ /**
+ * Returns a TabBase wrapper for the tab with the given ID.
+ *
+ * @param {integer} tabId
+ * The ID of the tab for which to return a wrapper.
+ *
+ * @returns {TabBase}
+ * @throws {ExtensionError}
+ * If no tab exists with the given ID.
+ * @abstract
+ */
+ get(tabId: integer): TabBase;
+ /**
+ * Returns a new TabBase instance wrapping the given native tab.
+ *
+ * @param {NativeTab} nativeTab
+ * The native tab for which to return a wrapper.
+ *
+ * @returns {TabBase}
+ * @protected
+ * @abstract
+ */
+ protected wrapTab(nativeTab: NativeTab): TabBase;
+}
+/**
+ * Manages native browser windows and their wrappers for a particular extension.
+ *
+ * @param {Extension} extension
+ * The extension for which to manage windows.
+ */
+declare class WindowManagerBase {
+ constructor(extension: any);
+ extension: any;
+ _windows: any;
+ /**
+ * Converts the given browser window to a JSON-compatible object, in the
+ * format required to be returned by WebExtension APIs, which may be safely
+ * passed to extension code.
+ *
+ * @param {DOMWindow} window
+ * The browser window to convert.
+ * @param {*} args
+ * Additional arguments to be passed to {@link WindowBase#convert}.
+ *
+ * @returns {object}
+ */
+ convert(window: DOMWindow, ...args: any): object;
+ /**
+ * Returns this extension's WindowBase wrapper for the given browser window.
+ * This method will always return the same wrapper object for any given
+ * browser window.
+ *
+ * @param {DOMWindow} window
+ * The browser window for which to return a wrapper.
+ *
+ * @returns {WindowBase|undefined}
+ * The wrapper for this tab.
+ */
+ getWrapper(window: DOMWindow): WindowBase | undefined;
+ /**
+ * Returns whether this window can be accessed by the extension in the given
+ * context.
+ *
+ * @param {DOMWindow} window
+ * The browser window that is being tested
+ * @param {BaseContext|null} context
+ * The extension context for which this test is being performed.
+ * @returns {boolean}
+ */
+ canAccessWindow(window: DOMWindow, context: BaseContext | null): boolean;
+ /**
+ * Returns an iterator of WindowBase objects which match the given query info.
+ *
+ * @param {object | null} [queryInfo = null]
+ * An object containing properties on which to filter. May contain any
+ * properties which are recognized by {@link WindowBase#matches}.
+ * Unknown properties will be ignored.
+ * @param {BaseContext|null} [context = null]
+ * The extension context for which the matching is being performed.
+ * Used to determine the current window for relevant properties.
+ *
+ * @returns {Iterator<WindowBase>}
+ */
+ query(queryInfo?: object | null, context?: BaseContext | null): Iterator<WindowBase>;
+ /**
+ * Returns a WindowBase wrapper for the browser window with the given ID.
+ *
+ * @param {integer} windowId
+ * The ID of the browser window for which to return a wrapper.
+ * @param {BaseContext} context
+ * The extension context for which the matching is being performed.
+ * Used to determine the current window for relevant properties.
+ *
+ * @returns {WindowBase}
+ * @throws {ExtensionError}
+ * If no window exists with the given ID.
+ * @abstract
+ */
+ get(windowId: integer, context: BaseContext): WindowBase;
+ /**
+ * Returns an iterator of WindowBase wrappers for each currently existing
+ * browser window.
+ *
+ * @returns {Iterator<WindowBase>}
+ * @abstract
+ */
+ getAll(): Iterator<WindowBase>;
+ /**
+ * Returns a new WindowBase instance wrapping the given browser window.
+ *
+ * @param {DOMWindow} window
+ * The browser window for which to return a wrapper.
+ *
+ * @returns {WindowBase}
+ * @protected
+ * @abstract
+ */
+ protected wrapWindow(window: DOMWindow): WindowBase;
+}
+/**
+ * The platform-specific type of native tab objects, which are wrapped by
+ * TabBase instances.
+ */
+type NativeTab = object | XULElement;
+type MutedInfo = {
+ /**
+ * True if the tab is currently muted, false otherwise.
+ */
+ muted: boolean;
+ /**
+ * The reason the tab is muted. Either "user", if the tab was muted by a
+ * user, or "extension", if it was muted by an extension.
+ */
+ reason?: string;
+ /**
+ * If the tab was muted by an extension, contains the internal ID of that
+ * extension.
+ */
+ extensionId?: string;
+};
+/**
+ * The parameter type of "tab-attached" events, which are emitted when a
+ * pre-existing tab is attached to a new window.
+ */
+type TabAttachedEvent = {
+ /**
+ * The native tab object in the window to which the tab is being
+ * attached. This may be a different object than was used to represent
+ * the tab in the old window.
+ */
+ tab: NativeTab;
+ /**
+ * The ID of the tab being attached.
+ */
+ tabId: integer;
+ /**
+ * The ID of the window to which the tab is being attached.
+ */
+ newWindowId: integer;
+ /**
+ * The position of the tab in the tab list of the new window.
+ */
+ newPosition: integer;
+};
+/**
+ * The parameter type of "tab-detached" events, which are emitted when a
+ * pre-existing tab is detached from a window, in order to be attached to a new
+ * window.
+ */
+type TabDetachedEvent = {
+ /**
+ * The native tab object in the window from which the tab is being
+ * detached. This may be a different object than will be used to
+ * represent the tab in the new window.
+ */
+ tab: NativeTab;
+ /**
+ * The native tab object in the window to which the tab will be attached,
+ * and is adopting the contents of this tab. This may be a different
+ * object than the tab in the previous window.
+ */
+ adoptedBy: NativeTab;
+ /**
+ * The ID of the tab being detached.
+ */
+ tabId: integer;
+ /**
+ * The ID of the window from which the tab is being detached.
+ */
+ oldWindowId: integer;
+ /**
+ * The position of the tab in the tab list of the window from which it is
+ * being detached.
+ */
+ oldPosition: integer;
+};
+/**
+ * The parameter type of "tab-created" events, which are emitted when a
+ * new tab is created.
+ */
+type TabCreatedEvent = {
+ /**
+ * The native tab object for the tab which is being created.
+ */
+ tab: NativeTab;
+};
+/**
+ * The parameter type of "tab-removed" events, which are emitted when a
+ * tab is removed and destroyed.
+ */
+type TabRemovedEvent = {
+ /**
+ * The native tab object for the tab which is being removed.
+ */
+ tab: NativeTab;
+ /**
+ * The ID of the tab being removed.
+ */
+ tabId: integer;
+ /**
+ * The ID of the window from which the tab is being removed.
+ */
+ windowId: integer;
+ /**
+ * True if the tab is being removed because the window is closing.
+ */
+ isWindowClosing: boolean;
+};
+/**
+ * An object containing basic, extension-independent information about the window
+ * and tab that a XUL <browser> belongs to.
+ */
+type BrowserData = {
+ /**
+ * The numeric ID of the tab that a <browser> belongs to, or -1 if it
+ * does not belong to a tab.
+ */
+ tabId: integer;
+ /**
+ * The numeric ID of the browser window that a <browser> belongs to, or -1
+ * if it does not belong to a browser window.
+ */
+ windowId: integer;
+};
+
+} // namespace tabs_base
+
+declare global {
+ type TabTrackerBase = tabs_base.TabTrackerBase;
+ type TabManagerBase = tabs_base.TabManagerBase;
+ type TabBase = tabs_base.TabBase;
+ type WindowTrackerBase = tabs_base.WindowTrackerBase;
+ type WindowManagerBase = tabs_base.WindowManagerBase;
+ type WindowBase = tabs_base.WindowBase;
+ type getUserContextIdForCookieStoreId = tabs_base.getUserContextIdForCookieStoreId;
+ type NativeTab = tabs_base.NativeTab;
+}
+
+export {};
diff --git a/toolkit/components/extensions/types/extensions.ts b/toolkit/components/extensions/types/extensions.ts
index 8f9555421b..e232ab4a3f 100644
--- a/toolkit/components/extensions/types/extensions.ts
+++ b/toolkit/components/extensions/types/extensions.ts
@@ -1,5 +1,5 @@
/**
- * Type declarations for WebExtensions framework code.
+ * Types specific to toolkit/extensions code.
*/
// This has every possible property we import from all modules, which is not
@@ -42,39 +42,37 @@ type LazyAll = {
getTrimmedString: typeof import("ExtensionTelemetry.sys.mjs").getTrimmedString,
};
-// Utility type to extract all strings from a const array, to use as keys.
-type Items<A> = A extends ReadonlyArray<infer U extends string> ? U : never;
-
declare global {
- type Lazy<Keys extends keyof LazyAll = keyof LazyAll> = Pick<LazyAll, Keys> & { [k: string]: any };
+ type Lazy = Partial<LazyAll> & { [k: string]: any };
- // Export JSDoc types, and make other classes available globally.
- type ConduitAddress = import("ConduitsParent.sys.mjs").ConduitAddress;
- type ConduitID = import("ConduitsParent.sys.mjs").ConduitID;
+ type BaseContext = import("ExtensionCommon.sys.mjs").BaseContext;
+ type ExtensionChild = import("ExtensionChild.sys.mjs").ExtensionChild;
type Extension = import("Extension.sys.mjs").Extension;
+ type callback = (...any) => any;
+
+ interface nsIDOMProcessChild {
+ getActor(name: "ProcessConduits"): ProcessConduitsChild;
+ }
- // Something about Class type not being exported when nested in a namespace?
- type BaseContext = InstanceType<typeof import("ExtensionCommon.sys.mjs").ExtensionCommon.BaseContext>;
- type BrowserExtensionContent = InstanceType<typeof import("ExtensionContent.sys.mjs").ExtensionContent.BrowserExtensionContent>;
- type EventEmitter = InstanceType<typeof import("ExtensionCommon.sys.mjs").ExtensionCommon.EventEmitter>;
- type ExtensionAPI = InstanceType<typeof import("ExtensionCommon.sys.mjs").ExtensionCommon.ExtensionAPI>;
- type ExtensionError = InstanceType<typeof import("ExtensionUtils.sys.mjs").ExtensionUtils.ExtensionError>;
- type LocaleData = InstanceType<typeof import("ExtensionCommon.sys.mjs").ExtensionCommon.LocaleData>;
- type ProxyAPIImplementation = InstanceType<typeof import("ExtensionChild.sys.mjs").ExtensionChild.ProxyAPIImplementation>;
- type SchemaAPIInterface = InstanceType<typeof import("ExtensionCommon.sys.mjs").ExtensionCommon.SchemaAPIInterface>;
- type WorkerExtensionError = InstanceType<typeof import("ExtensionUtils.sys.mjs").ExtensionUtils.WorkerExtensionError>;
+ interface WebExtensionContentScript {
+ userScriptOptions: { scriptMetadata: object };
+ }
- // Other misc types.
- type AddonWrapper = any;
- type Context = BaseContext;
- type NativeTab = Element;
- type SavedFrame = object;
+ interface WebExtensionPolicy {
+ extension: Extension;
+ debugName: string;
+ instanceId: string;
+ optionalPermissions: string[];
+ }
// Can't define a const generic parameter in jsdocs yet.
// https://github.com/microsoft/TypeScript/issues/56634
- type ConduitInit<Send> = ConduitAddress & { send: Send; };
- type Conduit<Send> = import("../ConduitsChild.sys.mjs").PointConduit & { [s in `send${Items<Send>}`]: callback };
- type ConduitOpen = <const Send>(subject: object, address: ConduitInit<Send>) => Conduit<Send>;
+ function ConduitGen<const Send>(_, init: Init<Send>, _actor?): Conduit<Send>;
+ type Items<A> = A extends ReadonlyArray<infer U extends string> ? U : never;
}
-export {}
+import { PointConduit, ProcessConduitsChild } from "ConduitsChild.sys.mjs";
+import { ConduitAddress } from "ConduitsParent.sys.mjs";
+
+type Conduit<Send> = PointConduit & { [s in `send${Items<Send>}`]: callback };
+type Init<Send> = ConduitAddress & { send: Send; };
diff --git a/toolkit/components/extensions/types/gecko.ts b/toolkit/components/extensions/types/gecko.ts
index f6b5190f8d..720919d794 100644
--- a/toolkit/components/extensions/types/gecko.ts
+++ b/toolkit/components/extensions/types/gecko.ts
@@ -1,163 +1,94 @@
/**
- * Global Gecko type declarations.
+ * Gecko generic/specialized adjustments for xpcom and webidl types.
*/
-// @ts-ignore
-import type { CiClass } from "lib.gecko.xpidl"
-
-declare global {
- // Other misc types.
- type Browser = InstanceType<typeof XULBrowserElement>;
- type bytestring = string;
- type callback = (...args: any[]) => any;
- type ColorArray = number[];
- type integer = number;
- type JSONValue = null | boolean | number | string | JSONValue[] | { [key: string]: JSONValue };
-
- interface Document {
- createXULElement(name: string): Element;
- documentReadyForIdle: Promise<void>;
- }
- interface EventTarget {
- ownerGlobal: Window;
- }
- interface Error {
- code;
- }
- interface ErrorConstructor {
- new (message?: string, options?: ErrorOptions, lineNo?: number): Error;
- }
- interface Window {
- gBrowser;
- }
- // HACK to get the static isInstance for DOMException and Window?
- interface Object {
- isInstance(object: any): boolean;
- }
+// More specific types for parent process browsing contexts.
+interface CanonicalBrowsingContext extends LoadContextMixin {
+ embedderElement: XULBrowserElement;
+ currentWindowContext: WindowGlobalParent;
+ parent: CanonicalBrowsingContext;
+ parentWindowContext: WindowGlobalParent;
+ top: CanonicalBrowsingContext;
+ topWindowContext: WindowGlobalParent;
+}
- // XPIDL additions/overrides.
+interface ChromeWindow extends Window {
+ isChromeWindow: true;
+}
- interface nsISupports {
- // OMG it works!
- QueryInterface?<T extends CiClass<nsISupports>>(aCiClass: T): T['prototype'];
- wrappedJSObject?: object;
- }
- interface nsIProperties {
- get<T extends CiClass<nsISupports>>(prop: string, aCiClass: T): T['prototype'];
- }
- interface nsIPrefBranch {
- getComplexValue<T extends CiClass<nsISupports>>(aPrefName: string, aCiClass: T): T['prototype'];
- }
- // TODO: incorporate above into lib.xpidl.d.ts generation, somehow?
+interface Document {
+ createXULElement(name: "browser"): XULBrowserElement;
+}
- type Sandbox = typeof globalThis;
- interface nsIXPCComponents_utils_Sandbox {
- (principal: nsIPrincipal | nsIPrincipal[], options: object): Sandbox;
- }
- interface nsIXPCComponents_Utils {
- cloneInto<T>(obj: T, ...args: any[]): T;
- createObjectIn<T>(Sandbox, options?: T): T;
- exportFunction<T extends callback>(f: T, ...args: any[]): T;
- getWeakReference<T extends object>(T): { get(): T };
- readonly Sandbox: nsIXPCComponents_utils_Sandbox;
- waiveXrays<T>(obj: T): T;
- }
- interface nsIDOMWindow extends Window {
- docShell: nsIDocShell;
- }
- interface Document {
- documentURIObject: nsIURI;
- createXULElement(name: string): Element;
- }
+interface MessageListenerManagerMixin {
+ // Overloads that define `data` arg as required, since it's ~always expected.
+ addMessageListener(msg: string, listener: { receiveMessage(_: ReceiveMessageArgument & { data })});
+ removeMessageListener(msg: string, listener: { receiveMessage(_: ReceiveMessageArgument & { data })});
+}
+
+interface MozQueryInterface {
+ <T>(iid: T): nsQIResult<T>;
+}
- // nsDocShell is the only thing implementing nsIDocShell, but it also
- // implements nsIWebNavigation, and a few others, so this is "ok".
- interface nsIDocShell extends nsIWebNavigation {}
- interface nsISimpleEnumerator extends Iterable<any> {}
+interface nsICryptoHash extends nsISupports {
+ // Accepts a TypedArray.
+ update(aData: ArrayLike<number>, aLen: number): void;
+}
- namespace Components {
- type Exception = Error;
- }
- namespace UrlbarUtils {
- type RESULT_TYPE = any;
- type RESULT_SOURCE = any;
+interface nsIDOMWindow extends Window {}
+
+interface nsISimpleEnumerator extends Iterable<any> {}
+
+interface nsISupports {
+ wrappedJSObject?: object;
+}
+
+interface nsIXPCComponents_Constructor {
+ <const T, IIDs = nsIXPCComponents_Interfaces>(cid, id: T, init?): {
+ new (...any): nsQIResult<T extends keyof IIDs ? IIDs[T] : T>;
+ (...any): nsQIResult<T extends keyof IIDs ? IIDs[T] : T>;
}
+}
+
+interface nsIXPCComponents_Exception {
+ (...args: ConstructorParameters<typeof Error>): Error;
+}
+
+interface nsIXPCComponents_utils_Sandbox {
+ (principal: nsIPrincipal | nsIPrincipal[], options: object): typeof globalThis;
+}
- // Various mozilla globals.
- var Cc, Cr, ChromeUtils, Components, dump, uneval;
-
- // [ChromeOnly] WebIDL, to be generated.
- var BrowsingContext, ChannelWrapper, ChromeWindow, ChromeWorker,
- ClonedErrorHolder, Glean, InspectorUtils, IOUtils, JSProcessActorChild,
- JSProcessActorParent, JSWindowActor, JSWindowActorChild,
- JSWindowActorParent, L10nRegistry, L10nFileSource, Localization,
- MatchGlob, MatchPattern, MatchPatternSet, PathUtils, PreloadedScript,
- StructuredCloneHolder, TelemetryStopwatch, WindowGlobalChild,
- WebExtensionContentScript, WebExtensionParentActor, WebExtensionPolicy,
- XULBrowserElement, nsIMessageListenerManager;
-
- interface XULElement extends Element {}
-
- // nsIServices is not a thing.
- interface nsIServices {
- scriptloader: mozIJSSubScriptLoader;
- locale: mozILocaleService;
- intl: mozIMozIntl;
- storage: mozIStorageService;
- appShell: nsIAppShellService;
- startup: nsIAppStartup;
- blocklist: nsIBlocklistService;
- cache2: nsICacheStorageService;
- catMan: nsICategoryManager;
- clearData: nsIClearDataService;
- clipboard: nsIClipboard;
- console: nsIConsoleService;
- cookieBanners: nsICookieBannerService;
- cookies: nsICookieManager & nsICookieService;
- appinfo: nsICrashReporter & nsIXULAppInfo & nsIXULRuntime;
- DAPTelemetry: nsIDAPTelemetry;
- DOMRequest: nsIDOMRequestService;
- dns: nsIDNSService;
- dirsvc: nsIDirectoryService & nsIProperties;
- droppedLinkHandler: nsIDroppedLinkHandler;
- eTLD: nsIEffectiveTLDService;
- policies: nsIEnterprisePolicies;
- env: nsIEnvironment;
- els: nsIEventListenerService;
- fog: nsIFOG;
- focus: nsIFocusManager;
- io: nsIIOService & nsINetUtil & nsISpeculativeConnect;
- loadContextInfo: nsILoadContextInfoFactory;
- domStorageManager: nsIDOMStorageManager & nsILocalStorageManager;
- logins: nsILoginManager;
- obs: nsIObserverService;
- perms: nsIPermissionManager;
- prefs: nsIPrefBranch & nsIPrefService;
- profiler: nsIProfiler;
- prompt: nsIPromptService;
- sysinfo: nsISystemInfo & nsIPropertyBag2;
- qms: nsIQuotaManagerService;
- rfp: nsIRFPService;
- scriptSecurityManager: nsIScriptSecurityManager;
- search: nsISearchService;
- sessionStorage: nsISessionStorageService;
- strings: nsIStringBundleService;
- telemetry: nsITelemetry;
- textToSubURI: nsITextToSubURI;
- tm: nsIThreadManager;
- uriFixup: nsIURIFixup;
- urlFormatter: nsIURLFormatter;
- uuid: nsIUUIDGenerator;
- vc: nsIVersionComparator;
- wm: nsIWindowMediator;
- ww: nsIWindowWatcher;
- xulStore: nsIXULStore;
- ppmm: any;
- cpmm: any;
- mm: any;
+interface nsXPCComponents_Classes {
+ [cid: string]: {
+ createInstance<T>(aID: T): nsQIResult<T>;
+ getService<T>(aID?: T): unknown extends T ? nsISupports : nsQIResult<T>;
}
+}
- var Ci: nsIXPCComponents_Interfaces;
- var Cu: nsIXPCComponents_Utils;
- var Services: nsIServices;
+// Generic overloads.
+interface nsXPCComponents_Utils {
+ cloneInto<T>(value: T, ...any): T;
+ createObjectIn<T = object>(_, object?: T): T;
+ exportFunction<T>(func: T, ...any): T;
+ getWeakReference<T>(value: T): { get(): T };
+ waiveXrays<T>(object: T): T;
}
+
+// TODO: remove after next TS update.
+interface PromiseConstructor {
+ withResolvers<T>(): {
+ promise: Promise<T>;
+ resolve: (value: T | PromiseLike<T>) => void;
+ reject: (reason?: any) => void;
+ };
+}
+
+// Hand-crafted artisanal types.
+interface XULBrowserElement extends XULFrameElement, FrameLoader {
+ currentURI: nsIURI;
+ docShellIsActive: boolean;
+ isRemoteBrowser: boolean;
+ remoteType: string;
+}
+
+type nsQIResult<iid> = import("gecko/lib.gecko.xpcom").nsQIResult<iid>;
diff --git a/toolkit/components/extensions/types/glean.d.ts b/toolkit/components/extensions/types/glean.d.ts
new file mode 100644
index 0000000000..842a5a56de
--- /dev/null
+++ b/toolkit/components/extensions/types/glean.d.ts
@@ -0,0 +1,79 @@
+/**
+ * NOTE: Do not modify this file by hand.
+ * Content was generated from source metrics.yaml files.
+ */
+
+interface GleanImpl {
+
+ // toolkit/mozapps/extensions/metrics.yaml
+
+ addonsManager: {
+ install: GleanEvent;
+ update: GleanEvent;
+ installStats: GleanEvent;
+ manage: GleanEvent;
+ report: GleanEvent;
+ reportSuspiciousSite: GleanEvent;
+ }
+
+ blocklist: {
+ lastModifiedRsAddonsMblf: GleanDatetime;
+ mlbfSource: GleanString;
+ mlbfGenerationTime: GleanDatetime;
+ mlbfStashTimeOldest: GleanDatetime;
+ mlbfStashTimeNewest: GleanDatetime;
+ addonBlockChange: GleanEvent;
+ }
+
+ // toolkit/components/extensions/metrics.yaml
+
+ extensions: {
+ useRemotePref: GleanBoolean;
+ useRemotePolicy: GleanBoolean;
+ startupCacheLoadTime: GleanTimespan;
+ startupCacheReadErrors: Record<string, GleanCounter>;
+ startupCacheWriteBytelength: GleanQuantity;
+ processEvent: Record<string, GleanCounter>;
+ }
+
+ extensionsApisDnr: {
+ startupCacheReadSize: GleanMemoryDistribution;
+ startupCacheReadTime: GleanTimingDistribution;
+ startupCacheWriteSize: GleanMemoryDistribution;
+ startupCacheWriteTime: GleanTimingDistribution;
+ startupCacheEntries: Record<string, GleanCounter>;
+ validateRulesTime: GleanTimingDistribution;
+ evaluateRulesTime: GleanTimingDistribution;
+ evaluateRulesCountMax: GleanQuantity;
+ }
+
+ extensionsData: {
+ migrateResult: GleanEvent;
+ storageLocalError: GleanEvent;
+ }
+
+ extensionsQuarantinedDomains: {
+ listsize: GleanQuantity;
+ listhash: GleanString;
+ remotehash: GleanString;
+ }
+
+ extensionsCounters: {
+ browserActionPreloadResult: Record<string, GleanCounter>;
+ eventPageIdleResult: Record<string, GleanCounter>;
+ }
+
+ extensionsTiming: {
+ backgroundPageLoad: GleanTimingDistribution;
+ browserActionPopupOpen: GleanTimingDistribution;
+ contentScriptInjection: GleanTimingDistribution;
+ eventPageRunningTime: GleanCustomDistribution;
+ extensionStartup: GleanTimingDistribution;
+ pageActionPopupOpen: GleanTimingDistribution;
+ storageLocalGetJson: GleanTimingDistribution;
+ storageLocalSetJson: GleanTimingDistribution;
+ storageLocalGetIdb: GleanTimingDistribution;
+ storageLocalSetIdb: GleanTimingDistribution;
+ }
+
+}
diff --git a/toolkit/components/extensions/types/globals.ts b/toolkit/components/extensions/types/globals.ts
index 45722828e2..dee9de0cf4 100644
--- a/toolkit/components/extensions/types/globals.ts
+++ b/toolkit/components/extensions/types/globals.ts
@@ -1,33 +1,32 @@
/**
- * Support types for toolkit/components/extensions code.
+ * Gecko globals.
*/
+declare global {
+ const Cc: nsXPCComponents_Classes;
+ const Ci: nsIXPCComponents_Interfaces;
+ const Cr: nsIXPCComponents_Results;
+ const Components: nsIXPCComponents;
-/// <reference lib="dom" />
-/// <reference path="./gecko.ts" />
-/// <reference path="./extensions.ts" />
+ // Resolve typed generic overloads before the generated ones.
+ const Cu: nsXPCComponents_Utils & nsIXPCComponents_Utils;
-// This now relies on types generated in bug 1872918, or get the built
-// artifact tslib directly and put it in your src/node_modules/@types:
-// https://phabricator.services.mozilla.com/D197620
-/// <reference types="lib.gecko.xpidl" />
+ const Glean: GleanImpl;
+ const Services: JSServices;
+ const uneval: (any) => string;
+}
-// Exports for all other external modules redirected to globals.ts.
-export var AppConstants,
- GeckoViewConnection, GeckoViewWebExtension, IndexedDB, JSONFile, Log;
+// Exports for all modules redirected here by a catch-all rule in tsconfig.json.
+export var
+ AddonWrapper, AppConstants, GeckoViewConnection, GeckoViewWebExtension,
+ IndexedDB, JSONFile, Log, UrlbarUtils, WebExtensionDescriptorActor;
/**
- * This is a mock for the "class" from EventEmitter.sys.mjs. When we import
- * it in extensions code using resource://gre/modules/EventEmitter.sys.mjs,
- * the catch-all rule from tsconfig.json redirects it to this file. The export
- * of the class below fulfills the import. The mock is needed when we subclass
- * that EventEmitter, typescript gets confused because it's an old style
- * function-and-prototype-based "class", and some types don't match up.
- *
+ * A stub type for the "class" from EventEmitter.sys.mjs.
* TODO: Convert EventEmitter.sys.mjs into a proper class.
*/
export declare class EventEmitter {
+ emit(event: string, ...args: any[]): void;
on(event: string, listener: callback): void;
once(event: string, listener: callback): Promise<any>;
off(event: string, listener: callback): void;
- emit(event: string, ...args: any[]): void;
}
diff --git a/toolkit/components/extensions/webrequest/StreamFilterParent.cpp b/toolkit/components/extensions/webrequest/StreamFilterParent.cpp
index 467616ed0f..ba8700e7f1 100644
--- a/toolkit/components/extensions/webrequest/StreamFilterParent.cpp
+++ b/toolkit/components/extensions/webrequest/StreamFilterParent.cpp
@@ -230,7 +230,7 @@ StreamFilterParent::CheckListenerChain() {
if (trsl) {
return trsl->CheckListenerChain();
}
- return NS_ERROR_FAILURE;
+ return NS_ERROR_NO_INTERFACE;
}
NS_IMETHODIMP
diff --git a/toolkit/components/forgetaboutsite/test/unit/head_forgetaboutsite.js b/toolkit/components/forgetaboutsite/test/unit/head_forgetaboutsite.js
index 0e0e178e96..93d72aece3 100644
--- a/toolkit/components/forgetaboutsite/test/unit/head_forgetaboutsite.js
+++ b/toolkit/components/forgetaboutsite/test/unit/head_forgetaboutsite.js
@@ -21,7 +21,7 @@ async function cleanUp() {
await new Promise(resolve => {
Services.clearData.deleteData(
Ci.nsIClearDataService.CLEAR_PERMISSIONS,
- value => resolve()
+ () => resolve()
);
});
}
diff --git a/toolkit/components/formautofill/AutofillProfileAutoComplete.sys.mjs b/toolkit/components/formautofill/AutofillProfileAutoComplete.sys.mjs
index fc3f0454b0..7594fc8fcf 100644
--- a/toolkit/components/formautofill/AutofillProfileAutoComplete.sys.mjs
+++ b/toolkit/components/formautofill/AutofillProfileAutoComplete.sys.mjs
@@ -6,7 +6,10 @@
* Form Autofill content process module.
*/
-import { GenericAutocompleteItem } from "resource://gre/modules/FillHelpers.sys.mjs";
+import {
+ GenericAutocompleteItem,
+ sendFillRequestToParent,
+} from "resource://gre/modules/FillHelpers.sys.mjs";
/* eslint-disable no-use-before-define */
@@ -31,16 +34,6 @@ const autocompleteController = Cc[
ChromeUtils.defineLazyGetter(
lazy,
- "ADDRESSES_COLLECTION_NAME",
- () => lazy.FormAutofillUtils.ADDRESSES_COLLECTION_NAME
-);
-ChromeUtils.defineLazyGetter(
- lazy,
- "CREDITCARDS_COLLECTION_NAME",
- () => lazy.FormAutofillUtils.CREDITCARDS_COLLECTION_NAME
-);
-ChromeUtils.defineLazyGetter(
- lazy,
"FIELD_STATES",
() => lazy.FormAutofillUtils.FIELD_STATES
);
@@ -191,14 +184,8 @@ AutofillProfileAutoCompleteSearch.prototype = {
isInputAutofilled,
});
} else {
- let infoWithoutElement = { ...activeFieldDetail };
- delete infoWithoutElement.elementWeakRef;
-
- let data = {
- collectionName: isAddressField
- ? lazy.ADDRESSES_COLLECTION_NAME
- : lazy.CREDITCARDS_COLLECTION_NAME,
- info: infoWithoutElement,
+ const data = {
+ fieldName: activeFieldDetail.fieldName,
searchString,
};
@@ -284,12 +271,10 @@ AutofillProfileAutoCompleteSearch.prototype = {
* Input element for autocomplete.
* @param {object} data
* Parameters for querying the corresponding result.
- * @param {string} data.collectionName
- * The name used to specify which collection to retrieve records.
* @param {string} data.searchString
* The typed string for filtering out the matched records.
- * @param {string} data.info
- * The input autocomplete property's information.
+ * @param {string} data.fieldName
+ * The identified field name for the input
* @returns {Promise}
* Promise that resolves when addresses returned from parent process.
*/
@@ -368,44 +353,6 @@ export const ProfileAutocomplete = {
}
},
- fillRequestId: 0,
-
- async sendFillRequestToFormAutofillParent(input, comment) {
- if (!comment) {
- return false;
- }
-
- if (!input || input != autocompleteController?.input.focusedInput) {
- return false;
- }
-
- const { fillMessageName, fillMessageData } = JSON.parse(comment ?? "{}");
- if (!fillMessageName) {
- return false;
- }
-
- this.fillRequestId++;
- const fillRequestId = this.fillRequestId;
- const actor = getActorFromWindow(input.ownerGlobal, "FormAutofill");
- const value = await actor.sendQuery(fillMessageName, fillMessageData ?? {});
-
- // skip fill if another fill operation started during await
- if (fillRequestId != this.fillRequestId) {
- return false;
- }
-
- if (typeof value !== "string") {
- return false;
- }
-
- // If AutoFillParent returned a string to fill, we must do it here because
- // nsAutoCompleteController.cpp already finished it's work before we finished await.
- input.setUserInput(value);
- input.select(value.length, value.length);
-
- return true;
- },
-
_getSelectedIndex(contentWindow) {
let actor = getActorFromWindow(contentWindow, "AutoComplete");
if (!actor) {
@@ -431,18 +378,31 @@ export const ProfileAutocomplete = {
? this.lastProfileAutoCompleteResult.getCommentAt(selectedIndex)
: null;
+ let profile = JSON.parse(comment);
if (
selectedIndex == -1 ||
!this.lastProfileAutoCompleteResult ||
- this.lastProfileAutoCompleteResult.getStyleAt(selectedIndex) !=
- "autofill-profile"
+ this.lastProfileAutoCompleteResult.getStyleAt(selectedIndex) != "autofill"
) {
- await this.sendFillRequestToFormAutofillParent(focusedInput, comment);
+ if (
+ focusedInput &&
+ focusedInput == autocompleteController?.input.focusedInput
+ ) {
+ if (profile?.fillMessageName == "FormAutofill:ClearForm") {
+ // The child can do this directly.
+ getActorFromWindow(focusedInput.ownerGlobal)?.clearForm();
+ } else {
+ // Pass focusedInput as both input arguments.
+ await sendFillRequestToParent(
+ "FormAutofill",
+ autocompleteController.input,
+ comment
+ );
+ }
+ }
return;
}
- let profile = JSON.parse(comment);
-
await lazy.FormAutofillContent.activeHandler.autofillFormFields(profile);
},
@@ -468,8 +428,7 @@ export const ProfileAutocomplete = {
if (
!this.lastProfileAutoCompleteResult ||
- this.lastProfileAutoCompleteResult.getStyleAt(selectedIndex) !=
- "autofill-profile"
+ this.lastProfileAutoCompleteResult.getStyleAt(selectedIndex) != "autofill"
) {
return;
}
diff --git a/toolkit/components/formautofill/FormAutofillChild.sys.mjs b/toolkit/components/formautofill/FormAutofillChild.sys.mjs
index 8678a7bd45..af84459432 100644
--- a/toolkit/components/formautofill/FormAutofillChild.sys.mjs
+++ b/toolkit/components/formautofill/FormAutofillChild.sys.mjs
@@ -124,33 +124,18 @@ export class FormAutofillChild extends JSWindowActorChild {
lazy.AutoCompleteChild.removePopupStateListener(this);
}
- popupStateChanged(messageName, data, _target) {
- let docShell;
- try {
- docShell = this.docShell;
- } catch (ex) {
- lazy.AutoCompleteChild.removePopupStateListener(this);
- return;
- }
-
+ popupStateChanged(messageName, _data, _target) {
if (!lazy.FormAutofill.isAutofillEnabled) {
return;
}
- const { chromeEventHandler } = docShell;
-
switch (messageName) {
- case "FormAutoComplete:PopupClosed": {
- this.onPopupClosed(data.selectedRowStyle);
- Services.tm.dispatchToMainThread(() => {
- chromeEventHandler.removeEventListener("keydown", this, true);
- });
-
+ case "AutoComplete:PopupClosed": {
+ this.onPopupClosed();
break;
}
- case "FormAutoComplete:PopupOpened": {
+ case "AutoComplete:PopupOpened": {
this.onPopupOpened();
- chromeEventHandler.addEventListener("keydown", this, true);
break;
}
}
@@ -385,10 +370,6 @@ export class FormAutofillChild extends JSWindowActorChild {
}
switch (evt.type) {
- case "keydown": {
- this._onKeyDown(evt);
- break;
- }
case "focusin": {
if (lazy.FormAutofill.isAutofillEnabled) {
this.onFocusIn(evt);
@@ -497,11 +478,9 @@ export class FormAutofillChild extends JSWindowActorChild {
return;
}
- const doc = this.document;
-
switch (message.name) {
case "FormAutofill:PreviewProfile": {
- this.previewProfile(doc);
+ this.previewProfile(message.data.selectedIndex);
break;
}
case "FormAutofill:ClearForm": {
@@ -677,9 +656,7 @@ export class FormAutofillChild extends JSWindowActorChild {
}
}
- previewProfile(doc) {
- let docWin = doc.ownerGlobal;
- let selectedIndex = lazy.ProfileAutocomplete._getSelectedIndex(docWin);
+ previewProfile(selectedIndex) {
let lastAutoCompleteResult =
lazy.ProfileAutocomplete.lastProfileAutoCompleteResult;
let focusedInput = this.activeInput;
@@ -688,55 +665,17 @@ export class FormAutofillChild extends JSWindowActorChild {
selectedIndex === -1 ||
!focusedInput ||
!lastAutoCompleteResult ||
- lastAutoCompleteResult.getStyleAt(selectedIndex) != "autofill-profile"
+ lastAutoCompleteResult.getStyleAt(selectedIndex) != "autofill"
) {
- this.sendAsyncMessage("FormAutofill:UpdateWarningMessage", {});
-
lazy.ProfileAutocomplete._clearProfilePreview();
} else {
- let focusedInputDetails = this.activeFieldDetail;
- let profile = JSON.parse(
- lastAutoCompleteResult.getCommentAt(selectedIndex)
- );
- let allFieldNames = this.activeSection.allFieldNames;
- let profileFields = allFieldNames.filter(
- fieldName => !!profile[fieldName]
- );
-
- let focusedCategory = lazy.FormAutofillUtils.getCategoryFromFieldName(
- focusedInputDetails.fieldName
- );
- let categories =
- lazy.FormAutofillUtils.getCategoriesFromFieldNames(profileFields);
- this.sendAsyncMessage("FormAutofill:UpdateWarningMessage", {
- focusedCategory,
- categories,
- });
-
lazy.ProfileAutocomplete._previewSelectedProfile(selectedIndex);
}
}
- onPopupClosed(selectedRowStyle) {
+ onPopupClosed() {
this.debug("Popup has closed.");
lazy.ProfileAutocomplete._clearProfilePreview();
-
- let lastAutoCompleteResult =
- lazy.ProfileAutocomplete.lastProfileAutoCompleteResult;
- let focusedInput = this.activeInput;
- if (
- lastAutoCompleteResult &&
- this._keyDownEnterForInput &&
- focusedInput === this._keyDownEnterForInput &&
- focusedInput ===
- lazy.ProfileAutocomplete.lastProfileAutoCompleteFocusedInput
- ) {
- if (selectedRowStyle == "autofill-footer") {
- this.sendAsyncMessage("FormAutofill:OpenPreferences");
- } else if (selectedRowStyle == "autofill-clear-button") {
- this.clearForm();
- }
- }
}
onPopupOpened() {
@@ -764,21 +703,4 @@ export class FormAutofillChild extends JSWindowActorChild {
formFillController.markAsAutofillField(field);
}
-
- _onKeyDown(e) {
- delete this._keyDownEnterForInput;
- let lastAutoCompleteResult =
- lazy.ProfileAutocomplete.lastProfileAutoCompleteResult;
- let focusedInput = this.activeInput;
- if (
- e.keyCode != e.DOM_VK_RETURN ||
- !lastAutoCompleteResult ||
- !focusedInput ||
- focusedInput !=
- lazy.ProfileAutocomplete.lastProfileAutoCompleteFocusedInput
- ) {
- return;
- }
- this._keyDownEnterForInput = focusedInput;
- }
}
diff --git a/toolkit/components/formautofill/FormAutofillParent.sys.mjs b/toolkit/components/formautofill/FormAutofillParent.sys.mjs
index 61c4bd2943..34dac8ce15 100644
--- a/toolkit/components/formautofill/FormAutofillParent.sys.mjs
+++ b/toolkit/components/formautofill/FormAutofillParent.sys.mjs
@@ -84,21 +84,6 @@ export let FormAutofillStatus = {
Services.prefs.addObserver(ENABLED_AUTOFILL_CREDITCARDS_PREF, this);
}
- // We have to use empty window type to get all opened windows here because the
- // window type parameter may not be available during startup.
- for (let win of Services.wm.getEnumerator("")) {
- let { documentElement } = win.document;
- if (documentElement?.getAttribute("windowtype") == "navigator:browser") {
- this.injectElements(win.document);
- } else {
- // Manually call onOpenWindow for windows that are already opened but not
- // yet have the window type set. This ensures we inject the elements we need
- // when its docuemnt is ready.
- this.onOpenWindow(win);
- }
- }
- Services.wm.addListener(this);
-
Services.telemetry.setEventRecordingEnabled("creditcard", true);
Services.telemetry.setEventRecordingEnabled("address", true);
},
@@ -198,31 +183,6 @@ export let FormAutofillStatus = {
this.updateStatus();
},
- injectElements(doc) {
- Services.scriptloader.loadSubScript(
- "chrome://formautofill/content/customElements.js",
- doc.ownerGlobal
- );
- },
-
- onOpenWindow(xulWindow) {
- const win = xulWindow.docShell.domWindow;
- win.addEventListener(
- "load",
- () => {
- if (
- win.document.documentElement.getAttribute("windowtype") ==
- "navigator:browser"
- ) {
- this.injectElements(win.document);
- }
- },
- { once: true }
- );
- },
-
- onCloseWindow() {},
-
async observe(subject, topic, data) {
lazy.log.debug("observe:", topic, "with data:", data);
switch (topic) {
@@ -312,7 +272,7 @@ export class FormAutofillParent extends JSWindowActorParent {
scenarioName: data.scenarioName,
hasInput: !!data.searchString?.length,
});
- const recordsPromise = FormAutofillParent._getRecords(data);
+ const recordsPromise = FormAutofillParent.getRecords(data);
const [records, externalEntries] = await Promise.all([
recordsPromise,
relayPromise,
@@ -448,42 +408,42 @@ export class FormAutofillParent extends JSWindowActorParent {
*
* This is static as a unit test calls this.
*
- * @private
* @param {object} data
- * @param {string} data.collectionName
- * The name used to specify which collection to retrieve records.
* @param {string} data.searchString
* The typed string for filtering out the matched records.
- * @param {string} data.info
- * The input autocomplete property's information.
+ * @param {string} data.collectionName
+ * The name used to specify which collection to retrieve records.
+ * @param {string} data.fieldName
+ * The field name to search. If not specified, return all records in
+ * the collection
*/
- static async _getRecords({ collectionName, searchString, info }) {
- let collection = lazy.gFormAutofillStorage[collectionName];
+ static async getRecords({ searchString, collectionName, fieldName }) {
+ // Derive the collection name from field name if it doesn't exist
+ collectionName ||=
+ FormAutofillUtils.getCollectionNameFromFieldName(fieldName);
+
+ const collection = lazy.gFormAutofillStorage[collectionName];
if (!collection) {
return [];
}
- let recordsInCollection = await collection.getAll();
- if (!info || !info.fieldName || !recordsInCollection.length) {
- return recordsInCollection;
+ const records = await collection.getAll();
+ if (!fieldName || !records.length) {
+ return records;
}
- let isCC = collectionName == CREDITCARDS_COLLECTION_NAME;
// We don't filter "cc-number"
- if (isCC && info.fieldName == "cc-number") {
- recordsInCollection = recordsInCollection.filter(
- record => !!record["cc-number"]
- );
- return recordsInCollection;
+ if (collectionName == CREDITCARDS_COLLECTION_NAME) {
+ if (fieldName == "cc-number") {
+ return records.filter(record => !!record["cc-number"]);
+ }
}
- let records = [];
- let lcSearchString = searchString.toLowerCase();
-
- for (let record of recordsInCollection) {
- let fieldValue = record[info.fieldName];
+ const lcSearchString = searchString.toLowerCase();
+ return records.filter(record => {
+ const fieldValue = record[fieldName];
if (!fieldValue) {
- continue;
+ return false;
}
if (
@@ -493,19 +453,14 @@ export class FormAutofillParent extends JSWindowActorParent {
) {
// Address autofill isn't supported for the record's country so we don't
// want to attempt to potentially incorrectly fill the address fields.
- continue;
- }
-
- if (
- lcSearchString &&
- !String(fieldValue).toLowerCase().startsWith(lcSearchString)
- ) {
- continue;
+ return false;
}
- records.push(record);
- }
- return records;
+ return (
+ !lcSearchString ||
+ String(fieldValue).toLowerCase().startsWith(lcSearchString)
+ );
+ });
}
async _onAddressSubmit(address, browser) {
diff --git a/toolkit/components/formautofill/Helpers.ios.mjs b/toolkit/components/formautofill/Helpers.ios.mjs
index 56bb49f0e9..83137331f1 100644
--- a/toolkit/components/formautofill/Helpers.ios.mjs
+++ b/toolkit/components/formautofill/Helpers.ios.mjs
@@ -45,12 +45,6 @@ HTMLElement.prototype.getAutocompleteInfo = function () {
};
};
-// Bug 1835024. Webkit doesn't support `checkVisibility` API
-// https://drafts.csswg.org/cssom-view-1/#dom-element-checkvisibility
-HTMLElement.prototype.checkVisibility = function (_options) {
- throw new Error(`Not implemented: WebKit doesn't support checkVisibility `);
-};
-
// This function helps us debug better when an error occurs because a certain mock is missing
const withNotImplementedError = obj =>
new Proxy(obj, {
@@ -106,7 +100,7 @@ export const XPCOMUtils = withNotImplementedError({
prop,
pref,
defaultValue = null,
- onUpdate = null,
+ onUpdate,
transform = val => val
) => {
if (!Object.keys(IOSAppConstants.prefs).includes(pref)) {
diff --git a/toolkit/components/formautofill/ProfileAutoCompleteResult.sys.mjs b/toolkit/components/formautofill/ProfileAutoCompleteResult.sys.mjs
index 52ed8bed03..68df30f8b5 100644
--- a/toolkit/components/formautofill/ProfileAutoCompleteResult.sys.mjs
+++ b/toolkit/components/formautofill/ProfileAutoCompleteResult.sys.mjs
@@ -131,6 +131,12 @@ class ProfileAutoCompleteResult {
if (typeof label == "string") {
return label;
}
+
+ let type = this.getTypeOfIndex(index);
+ if (type == "clear" || type == "manage") {
+ return label.primary;
+ }
+
return JSON.stringify(label);
}
@@ -141,6 +147,16 @@ class ProfileAutoCompleteResult {
* @returns {string} The comment at the specified index
*/
getCommentAt(index) {
+ let type = this.getTypeOfIndex(index);
+ switch (type) {
+ case "clear":
+ return '{"fillMessageName": "FormAutofill:ClearForm"}';
+ case "manage":
+ return '{"fillMessageName": "FormAutofill:OpenPreferences"}';
+ case "insecure":
+ return '{"noLearnMore": true }';
+ }
+
const item = this.getAt(index);
return item.comment ?? JSON.stringify(this._matchingProfiles[index]);
}
@@ -157,14 +173,16 @@ class ProfileAutoCompleteResult {
return itemStyle;
}
- if (index == this._popupLabels.length - 1) {
- return "autofill-footer";
- }
- if (this._isInputAutofilled) {
- return "autofill-clear-button";
+ switch (this.getTypeOfIndex(index)) {
+ case "manage":
+ return "action";
+ case "clear":
+ return "action";
+ case "insecure":
+ return "insecureWarning";
+ default:
+ return "autofill";
}
-
- return "autofill-profile";
}
/**
@@ -205,6 +223,24 @@ class ProfileAutoCompleteResult {
removeValueAt(_index) {
// There is no plan to support removing profiles via autocomplete.
}
+
+ /**
+ * Returns a type string that identifies te type of row at the given index.
+ *
+ * @param {number} index The index of the result requested
+ * @returns {string} The type at the specified index
+ */
+ getTypeOfIndex(index) {
+ if (this._isInputAutofilled && index == 0) {
+ return "clear";
+ }
+
+ if (index == this._popupLabels.length - 1) {
+ return "manage";
+ }
+
+ return "item";
+ }
}
export class AddressResult extends ProfileAutoCompleteResult {
@@ -281,18 +317,26 @@ export class AddressResult extends ProfileAutoCompleteResult {
"autofill-manage-addresses-label"
);
+ let footerItem = {
+ primary: manageLabel,
+ secondary: "",
+ };
+
if (this._isInputAutofilled) {
- return [
- { primary: "", secondary: "" }, // Clear button
- // Footer
+ const clearLabel = lazy.l10n.formatValueSync("autofill-clear-form-label");
+
+ let labels = [
{
- primary: "",
- secondary: "",
- manageLabel,
+ primary: clearLabel,
},
];
+ labels.push(footerItem);
+ return labels;
}
+ let focusedCategory =
+ lazy.FormAutofillUtils.getCategoryFromFieldName(focusedFieldName);
+
// Skip results without a primary label.
let labels = profiles
.filter(profile => {
@@ -306,35 +350,88 @@ export class AddressResult extends ProfileAutoCompleteResult {
) {
primaryLabel = profile["-moz-street-address-one-line"];
}
+
+ let profileFields = allFieldNames.filter(
+ fieldName => !!profile[fieldName]
+ );
+
+ let categories =
+ lazy.FormAutofillUtils.getCategoriesFromFieldNames(profileFields);
+ let status = this.getStatusNote(categories, focusedCategory);
+ let secondary = this._getSecondaryLabel(
+ focusedFieldName,
+ allFieldNames,
+ profile
+ );
+ const ariaLabel = [primaryLabel, secondary, status]
+ .filter(chunk => !!chunk) // Exclude empty chunks.
+ .join(" ");
return {
primary: primaryLabel,
- secondary: this._getSecondaryLabel(
- focusedFieldName,
- allFieldNames,
- profile
- ),
+ secondary,
+ status,
+ ariaLabel,
};
});
- const focusedCategory =
- lazy.FormAutofillUtils.getCategoryFromFieldName(focusedFieldName);
+ let allCategories =
+ lazy.FormAutofillUtils.getCategoriesFromFieldNames(allFieldNames);
+
+ if (allCategories && allCategories.length) {
+ let statusItem = {
+ primary: "",
+ secondary: "",
+ status: this.getStatusNote(allCategories, focusedCategory),
+ style: "status",
+ };
+ labels.push(statusItem);
+ }
- // Add an empty result entry for footer. Its content will come from
- // the footer binding, so don't assign any value to it.
- // The additional properties: categories and focusedCategory are required of
- // the popup to generate autofill hint on the footer.
- labels.push({
- primary: "",
- secondary: "",
- manageLabel,
- categories: lazy.FormAutofillUtils.getCategoriesFromFieldNames(
- this._allFieldNames
- ),
- focusedCategory,
- });
+ labels.push(footerItem);
return labels;
}
+
+ getStatusNote(categories, focusedCategory) {
+ if (!categories || !categories.length) {
+ return "";
+ }
+
+ // If the length of categories is 1, that means all the fillable fields are in the same
+ // category. We will change the way to inform user according to this flag. When the value
+ // is true, we show "Also autofills ...", otherwise, show "Autofills ..." only.
+ let hasExtraCategories = categories.length > 1;
+ // Show the categories in certain order to conform with the spec.
+ let orderedCategoryList = [
+ "address",
+ "name",
+ "organization",
+ "tel",
+ "email",
+ ];
+ let showCategories = hasExtraCategories
+ ? orderedCategoryList.filter(
+ category =>
+ categories.includes(category) && category != focusedCategory
+ )
+ : [orderedCategoryList.find(category => category == focusedCategory)];
+
+ let formatter = new Intl.ListFormat(undefined, {
+ style: "narrow",
+ });
+
+ let categoriesText = showCategories.map(category =>
+ lazy.l10n.formatValueSync("autofill-category-" + category)
+ );
+ categoriesText = formatter.format(categoriesText);
+
+ let statusTextTmplKey = hasExtraCategories
+ ? "autofill-phishing-warningmessage-extracategory"
+ : "autofill-phishing-warningmessage";
+ return lazy.l10n.formatValueSync(statusTextTmplKey, {
+ categories: categoriesText,
+ });
+ }
}
export class CreditCardResult extends ProfileAutoCompleteResult {
@@ -401,16 +498,20 @@ export class CreditCardResult extends ProfileAutoCompleteResult {
"autofill-manage-payment-methods-label"
);
+ let footerItem = {
+ primary: manageLabel,
+ };
+
if (this._isInputAutofilled) {
- return [
- { primary: "", secondary: "" }, // Clear button
- // Footer
+ const clearLabel = lazy.l10n.formatValueSync("autofill-clear-form-label");
+
+ let labels = [
{
- primary: "",
- secondary: "",
- manageLabel,
+ primary: clearLabel,
},
];
+ labels.push(footerItem);
+ return labels;
}
// Skip results without a primary label.
@@ -446,37 +547,23 @@ export class CreditCardResult extends ProfileAutoCompleteResult {
.filter(chunk => !!chunk) // Exclude empty chunks.
.join(" ");
return {
- primary,
- secondary,
+ primary: primary.toString().replaceAll("*", "•"),
+ secondary: secondary.toString().replaceAll("*", "•"),
ariaLabel,
image,
};
});
- const focusedCategory =
- lazy.FormAutofillUtils.getCategoryFromFieldName(focusedFieldName);
-
- // Add an empty result entry for footer.
- labels.push({
- primary: "",
- secondary: "",
- manageLabel,
- focusedCategory,
- });
+ labels.push(footerItem);
return labels;
}
- getStyleAt(index) {
- const itemStyle = this.getAt(index).style;
- if (itemStyle) {
- return itemStyle;
- }
-
+ getTypeOfIndex(index) {
if (!this._isSecure) {
- return "autofill-insecureWarning";
+ return "insecure";
}
- return super.getStyleAt(index);
+ return super.getTypeOfIndex(index);
}
}
diff --git a/toolkit/components/formautofill/android/FormAutofillStorage.sys.mjs b/toolkit/components/formautofill/android/FormAutofillStorage.sys.mjs
index 964be31d06..17a50de7eb 100644
--- a/toolkit/components/formautofill/android/FormAutofillStorage.sys.mjs
+++ b/toolkit/components/formautofill/android/FormAutofillStorage.sys.mjs
@@ -68,7 +68,7 @@ class Addresses extends AddressesBase {
this._initializePromise = Promise.resolve();
}
- async _saveRecord(record, { sourceSync = false } = {}) {
+ async _saveRecord(record) {
lazy.GeckoViewAutocomplete.onAddressSave(lazy.Address.fromGecko(record));
}
@@ -136,7 +136,7 @@ class CreditCards extends CreditCardsBase {
this._initializePromise = Promise.resolve();
}
- async _saveRecord(record, { sourceSync = false } = {}) {
+ async _saveRecord(record) {
lazy.GeckoViewAutocomplete.onCreditCardSave(
lazy.CreditCard.fromGecko(record)
);
diff --git a/toolkit/components/formautofill/default/FormAutofillPrompter.sys.mjs b/toolkit/components/formautofill/default/FormAutofillPrompter.sys.mjs
index f166716de5..05dcf5bace 100644
--- a/toolkit/components/formautofill/default/FormAutofillPrompter.sys.mjs
+++ b/toolkit/components/formautofill/default/FormAutofillPrompter.sys.mjs
@@ -789,16 +789,15 @@ export class AddressEditDoorhanger extends AutofillDoorhanger {
input.setAttribute("id", inputId);
- const value = this.newRecord[fieldName] ?? "";
if (popup) {
- const menuitem = Array.from(popup.childNodes).find(
- item =>
- item.label.toLowerCase() === value?.toLowerCase() ||
- item.value.toLowerCase() === value?.toLowerCase()
- );
- input.selectedItem = menuitem;
+ input.selectedItem =
+ FormAutofillUtils.findAddressSelectOptionWithMenuPopup(
+ popup,
+ this.newRecord,
+ fieldName
+ );
} else {
- input.value = value;
+ input.value = this.newRecord[fieldName] ?? "";
}
div.appendChild(input);
diff --git a/toolkit/components/formautofill/shared/AddressComponent.sys.mjs b/toolkit/components/formautofill/shared/AddressComponent.sys.mjs
index 40e00b66a0..e83cd22251 100644
--- a/toolkit/components/formautofill/shared/AddressComponent.sys.mjs
+++ b/toolkit/components/formautofill/shared/AddressComponent.sys.mjs
@@ -412,7 +412,6 @@ class State extends AddressField {
const options = {
merge_whitespace: true,
- remove_punctuation: true,
};
this.#state = lazy.FormAutofillUtils.getAbbreviatedSubregionName(
this.normalizeUserValue(options),
@@ -991,7 +990,7 @@ export class AddressComparison {
* country, postal code, etc. The class provides a compare methods
* to compare another AddressComponent against the current instance.
*
- * Note. This class assumes records that pass to it have already been normalized.
+ * Note: This class assumes records that pass to it have already been normalized.
*/
export class AddressComponent {
/**
diff --git a/toolkit/components/formautofill/shared/FormAutofillSection.sys.mjs b/toolkit/components/formautofill/shared/FormAutofillSection.sys.mjs
index 7bda4c167b..1a5b3014c9 100644
--- a/toolkit/components/formautofill/shared/FormAutofillSection.sys.mjs
+++ b/toolkit/components/formautofill/shared/FormAutofillSection.sys.mjs
@@ -136,15 +136,15 @@ export class FormAutofillSection {
* specific case. Return the original value in the default case.
* @param {String} value
* The original field value.
- * @param {Object} fieldDetail
+ * @param {Object} _fieldName
* A fieldDetail of the related element.
- * @param {HTMLElement} element
+ * @param {HTMLElement} _element
* A element for checking converting value.
*
* @returns {String}
* A string of the converted value.
*/
- computeFillingValue(value, fieldName, element) {
+ computeFillingValue(value, _fieldName, _element) {
return value;
}
diff --git a/toolkit/components/formautofill/shared/FormAutofillUtils.sys.mjs b/toolkit/components/formautofill/shared/FormAutofillUtils.sys.mjs
index e86f14975c..c2b48a53a3 100644
--- a/toolkit/components/formautofill/shared/FormAutofillUtils.sys.mjs
+++ b/toolkit/components/formautofill/shared/FormAutofillUtils.sys.mjs
@@ -176,6 +176,12 @@ FormAutofillUtils = {
return Array.from(categories);
},
+ getCollectionNameFromFieldName(fieldName) {
+ return this.isCreditCardField(fieldName)
+ ? CREDITCARDS_COLLECTION_NAME
+ : ADDRESSES_COLLECTION_NAME;
+ },
+
getAddressSeparator() {
// The separator should be based on the L10N address format, and using a
// white space is a temporary solution.
@@ -799,6 +805,42 @@ FormAutofillUtils = {
return null;
},
+ /**
+ * Find the option element from xul menu popups, as used in address capture
+ * doorhanger.
+ *
+ * This is a proxy to `findAddressSelectOption`, which expects HTML select
+ * DOM nodes and operates on options instead of xul menuitems.
+ *
+ * NOTE: This is a temporary solution until Bug 1886949 is landed. This
+ * method will then be removed `findAddressSelectOption` will be used
+ * directly.
+ *
+ * @param {XULPopupElement} menupopup
+ * @param {object} address
+ * @param {string} fieldName
+ * @returns {XULElement}
+ */
+ findAddressSelectOptionWithMenuPopup(menupopup, address, fieldName) {
+ class MenuitemProxy {
+ constructor(menuitem) {
+ this.menuitem = menuitem;
+ }
+ get text() {
+ return this.menuitem.label;
+ }
+ get value() {
+ return this.menuitem.value;
+ }
+ }
+ const selectEl = {
+ options: Array.from(menupopup.childNodes).map(
+ menuitem => new MenuitemProxy(menuitem)
+ ),
+ };
+ return this.findAddressSelectOption(selectEl, address, fieldName)?.menuitem;
+ },
+
findCreditCardSelectOption(selectEl, creditCard, fieldName) {
let oneDigitMonth = creditCard["cc-exp-month"]
? creditCard["cc-exp-month"].toString()
diff --git a/toolkit/components/gfx/SanityTest.sys.mjs b/toolkit/components/gfx/SanityTest.sys.mjs
index f519735a1a..6fe7c4e0a4 100644
--- a/toolkit/components/gfx/SanityTest.sys.mjs
+++ b/toolkit/components/gfx/SanityTest.sys.mjs
@@ -397,7 +397,7 @@ SanityTest.prototype = {
return true;
},
- observe(subject, topic, data) {
+ observe(subject, topic) {
if (topic != "profile-after-change") {
return;
}
diff --git a/toolkit/components/gfx/content/gfxFrameScript.js b/toolkit/components/gfx/content/gfxFrameScript.js
index c423fb8a2d..0587f2fad7 100644
--- a/toolkit/components/gfx/content/gfxFrameScript.js
+++ b/toolkit/components/gfx/content/gfxFrameScript.js
@@ -45,7 +45,7 @@ const gfxFrameScript = {
return aUri.endsWith("/sanitytest.html");
},
- onStateChange(webProgress, req, flags, status) {
+ onStateChange(webProgress, req, flags) {
if (
webProgress.isTopLevel &&
flags & Ci.nsIWebProgressListener.STATE_STOP &&
diff --git a/toolkit/components/glean/Cargo.toml b/toolkit/components/glean/Cargo.toml
index 98b8d8a95b..ba563f514a 100644
--- a/toolkit/components/glean/Cargo.toml
+++ b/toolkit/components/glean/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2018"
license = "MPL-2.0"
[dependencies]
-glean = "58.1.0"
+glean = "59.0.0"
log = "0.4"
nserror = { path = "../../../xpcom/rust/nserror" }
nsstring = { path = "../../../xpcom/rust/nsstring" }
diff --git a/toolkit/components/glean/api/Cargo.toml b/toolkit/components/glean/api/Cargo.toml
index dc688dc41e..af507f499f 100644
--- a/toolkit/components/glean/api/Cargo.toml
+++ b/toolkit/components/glean/api/Cargo.toml
@@ -9,7 +9,7 @@ license = "MPL-2.0"
[dependencies]
bincode = "1.0"
chrono = "0.4.10"
-glean = "58.1.0"
+glean = "59.0.0"
inherent = "1.0.0"
log = "0.4"
nsstring = { path = "../../../../xpcom/rust/nsstring", optional = true }
diff --git a/toolkit/components/glean/api/src/common_test.rs b/toolkit/components/glean/api/src/common_test.rs
index 3c8c2c71e1..46062736f2 100644
--- a/toolkit/components/glean/api/src/common_test.rs
+++ b/toolkit/components/glean/api/src/common_test.rs
@@ -43,6 +43,7 @@ fn setup_glean(tempdir: Option<tempfile::TempDir>) -> tempfile::TempDir {
rate_limit: None,
enable_event_timestamps: false,
experimentation_id: None,
+ enable_internal_pings: true
};
let client_info = glean::ClientInfoMetrics {
diff --git a/toolkit/components/glean/api/src/ffi/custom_distribution.rs b/toolkit/components/glean/api/src/ffi/custom_distribution.rs
index 643ebfbff5..6f369fd268 100644
--- a/toolkit/components/glean/api/src/ffi/custom_distribution.rs
+++ b/toolkit/components/glean/api/src/ffi/custom_distribution.rs
@@ -69,6 +69,26 @@ pub extern "C" fn fog_custom_distribution_accumulate_samples_signed(
}
#[no_mangle]
+pub extern "C" fn fog_custom_distribution_accumulate_single_sample(id: u32, sample: u64) {
+ with_metric!(
+ CUSTOM_DISTRIBUTION_MAP,
+ id,
+ metric,
+ metric.accumulate_single_sample_signed(sample as i64)
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn fog_custom_distribution_accumulate_single_sample_signed(id: u32, sample: i64) {
+ with_metric!(
+ CUSTOM_DISTRIBUTION_MAP,
+ id,
+ metric,
+ metric.accumulate_single_sample_signed(sample)
+ );
+}
+
+#[no_mangle]
pub extern "C" fn fog_custom_distribution_test_get_error(
id: u32,
diff --git a/toolkit/components/glean/api/src/ffi/event.rs b/toolkit/components/glean/api/src/ffi/event.rs
index bd167021d6..5d1e2dc0f1 100644
--- a/toolkit/components/glean/api/src/ffi/event.rs
+++ b/toolkit/components/glean/api/src/ffi/event.rs
@@ -4,8 +4,6 @@
#![cfg(feature = "with_gecko")]
-use std::collections::HashMap;
-
use nsstring::{nsACString, nsCString};
use thin_vec::ThinVec;
@@ -59,7 +57,6 @@ pub extern "C" fn fog_event_record(
Some(m) => m.record_raw(extra),
None => panic!("No (dynamic) metric for event with id {}", id),
}
- return;
} else {
match metric_maps::record_event_by_id(id, extra) {
Ok(()) => {}
@@ -148,7 +145,7 @@ pub extern "C" fn fog_event_test_get_value(
};
for event in events {
- let extra = event.extra.unwrap_or_else(HashMap::new);
+ let extra = event.extra.unwrap_or_default();
let extra_len = extra.len();
let mut extras = ThinVec::with_capacity(extra_len * 2);
for (k, v) in extra.into_iter() {
diff --git a/toolkit/components/glean/api/src/private/custom_distribution.rs b/toolkit/components/glean/api/src/private/custom_distribution.rs
index aeaf9b58c2..85121fca1e 100644
--- a/toolkit/components/glean/api/src/private/custom_distribution.rs
+++ b/toolkit/components/glean/api/src/private/custom_distribution.rs
@@ -92,8 +92,21 @@ impl CustomDistribution for CustomDistributionMetric {
}
}
- pub fn accumulate_single_sample_signed(&self, _sample: i64) {
- unimplemented!("bug 1884183: expose this to FOG")
+ pub fn accumulate_single_sample_signed(&self, sample: i64) {
+ match self {
+ CustomDistributionMetric::Parent { inner, .. } => {
+ inner.accumulate_single_sample(sample)
+ }
+ CustomDistributionMetric::Child(c) => {
+ with_ipc_payload(move |payload| {
+ if let Some(v) = payload.custom_samples.get_mut(&c.0) {
+ v.push(sample);
+ } else {
+ payload.custom_samples.insert(c.0, vec![sample]);
+ }
+ });
+ }
+ }
}
pub fn test_get_value<'a, S: Into<Option<&'a str>>>(
diff --git a/toolkit/components/glean/api/src/private/timing_distribution.rs b/toolkit/components/glean/api/src/private/timing_distribution.rs
index 6707560e41..2807b87c4f 100644
--- a/toolkit/components/glean/api/src/private/timing_distribution.rs
+++ b/toolkit/components/glean/api/src/private/timing_distribution.rs
@@ -374,8 +374,16 @@ impl TimingDistribution for TimingDistributionMetric {
}
}
- pub fn accumulate_single_sample(&self, _sample: i64) {
- unimplemented!("bug 1884183: expose this to FOG")
+ pub fn accumulate_single_sample(&self, sample: i64) {
+ match self {
+ TimingDistributionMetric::Parent { id: _id, inner } => {
+ inner.accumulate_single_sample(sample)
+ }
+ TimingDistributionMetric::Child(_c) => {
+ // TODO: Instrument this error
+ log::error!("Can't record samples for a timing distribution from a child metric");
+ }
+ }
}
/// **Exported for test purposes.**
diff --git a/toolkit/components/glean/bindings/private/Boolean.cpp b/toolkit/components/glean/bindings/private/Boolean.cpp
index 8300990b49..168179f569 100644
--- a/toolkit/components/glean/bindings/private/Boolean.cpp
+++ b/toolkit/components/glean/bindings/private/Boolean.cpp
@@ -21,7 +21,7 @@ void BooleanMetric::Set(bool aValue) const {
if (scalarId) {
Telemetry::ScalarSet(scalarId.extract(), aValue);
} else if (IsSubmetricId(mId)) {
- GetLabeledMirrorLock().apply([&](auto& lock) {
+ GetLabeledMirrorLock().apply([&](const auto& lock) {
auto tuple = lock.ref()->MaybeGet(mId);
if (tuple) {
Telemetry::ScalarSet(std::get<0>(tuple.ref()), std::get<1>(tuple.ref()),
diff --git a/toolkit/components/glean/bindings/private/Counter.cpp b/toolkit/components/glean/bindings/private/Counter.cpp
index f7f70f29eb..4eeeb3de7a 100644
--- a/toolkit/components/glean/bindings/private/Counter.cpp
+++ b/toolkit/components/glean/bindings/private/Counter.cpp
@@ -22,7 +22,7 @@ void CounterMetric::Add(int32_t aAmount) const {
if (scalarId) {
Telemetry::ScalarAdd(scalarId.extract(), aAmount);
} else if (IsSubmetricId(mId)) {
- GetLabeledMirrorLock().apply([&](auto& lock) {
+ GetLabeledMirrorLock().apply([&](const auto& lock) {
auto tuple = lock.ref()->MaybeGet(mId);
if (tuple && aAmount > 0) {
Telemetry::ScalarAdd(std::get<0>(tuple.ref()),
diff --git a/toolkit/components/glean/bindings/private/CustomDistribution.cpp b/toolkit/components/glean/bindings/private/CustomDistribution.cpp
index a5a821a558..88e96236db 100644
--- a/toolkit/components/glean/bindings/private/CustomDistribution.cpp
+++ b/toolkit/components/glean/bindings/private/CustomDistribution.cpp
@@ -33,6 +33,15 @@ void CustomDistributionMetric::AccumulateSamples(
fog_custom_distribution_accumulate_samples(mId, &aSamples);
}
+void CustomDistributionMetric::AccumulateSingleSample(uint64_t aSample) const {
+ auto hgramId = HistogramIdForMetric(mId);
+ if (hgramId) {
+ auto id = hgramId.extract();
+ Telemetry::Accumulate(id, aSample);
+ }
+ fog_custom_distribution_accumulate_single_sample(mId, aSample);
+}
+
void CustomDistributionMetric::AccumulateSamplesSigned(
const nsTArray<int64_t>& aSamples) const {
auto hgramId = HistogramIdForMetric(mId);
@@ -47,6 +56,16 @@ void CustomDistributionMetric::AccumulateSamplesSigned(
fog_custom_distribution_accumulate_samples_signed(mId, &aSamples);
}
+void CustomDistributionMetric::AccumulateSingleSampleSigned(
+ int64_t aSample) const {
+ auto hgramId = HistogramIdForMetric(mId);
+ if (hgramId) {
+ auto id = hgramId.extract();
+ Telemetry::Accumulate(id, aSample);
+ }
+ fog_custom_distribution_accumulate_single_sample_signed(mId, aSample);
+}
+
Result<Maybe<DistributionData>, nsCString>
CustomDistributionMetric::TestGetValue(const nsACString& aPingName) const {
nsCString err;
@@ -78,6 +97,10 @@ void GleanCustomDistribution::AccumulateSamples(
mCustomDist.AccumulateSamplesSigned(aSamples);
}
+void GleanCustomDistribution::AccumulateSingleSample(const int64_t aSample) {
+ mCustomDist.AccumulateSingleSampleSigned(aSample);
+}
+
void GleanCustomDistribution::TestGetValue(
const nsACString& aPingName,
dom::Nullable<dom::GleanDistributionData>& aRetval, ErrorResult& aRv) {
diff --git a/toolkit/components/glean/bindings/private/CustomDistribution.h b/toolkit/components/glean/bindings/private/CustomDistribution.h
index 8227b024ad..8074a0542e 100644
--- a/toolkit/components/glean/bindings/private/CustomDistribution.h
+++ b/toolkit/components/glean/bindings/private/CustomDistribution.h
@@ -35,6 +35,13 @@ class CustomDistributionMetric {
void AccumulateSamples(const nsTArray<uint64_t>& aSamples) const;
/**
+ * Accumulates the provided sample in the metric.
+ *
+ * @param aSamples The sample to be recorded by the metric.
+ */
+ void AccumulateSingleSample(uint64_t aSample) const;
+
+ /**
* Accumulates the provided samples in the metric.
*
* @param aSamples The vector holding the samples to be recorded by the
@@ -46,6 +53,14 @@ class CustomDistributionMetric {
void AccumulateSamplesSigned(const nsTArray<int64_t>& aSamples) const;
/**
+ * Accumulates the provided sample in the metric.
+ *
+ * @param aSamples The signed integer sample to be recorded by the
+ * metric.
+ */
+ void AccumulateSingleSampleSigned(int64_t aSample) const;
+
+ /**
* **Test-only API**
*
* Gets the currently stored value as a DistributionData.
@@ -80,6 +95,8 @@ class GleanCustomDistribution final : public GleanMetric {
void AccumulateSamples(const dom::Sequence<int64_t>& aSamples);
+ void AccumulateSingleSample(const int64_t aSample);
+
void TestGetValue(const nsACString& aPingName,
dom::Nullable<dom::GleanDistributionData>& aRetval,
ErrorResult& aRv);
diff --git a/toolkit/components/glean/bindings/private/DistributionData.h b/toolkit/components/glean/bindings/private/DistributionData.h
index fb9bba720e..782fe17c98 100644
--- a/toolkit/components/glean/bindings/private/DistributionData.h
+++ b/toolkit/components/glean/bindings/private/DistributionData.h
@@ -27,6 +27,28 @@ struct DistributionData final {
this->values.InsertOrUpdate(aBuckets[i], aCounts[i]);
}
}
+
+ friend std::ostream& operator<<(std::ostream& aStream,
+ const DistributionData& aDist) {
+ aStream << "DistributionData(";
+ aStream << "sum=" << aDist.sum << ", ";
+ aStream << "count=" << aDist.count << ", ";
+ aStream << "values={";
+ bool first = true;
+ for (const auto& entry : aDist.values) {
+ if (!first) {
+ aStream << ", ";
+ }
+ first = false;
+
+ const uint64_t bucket = entry.GetKey();
+ const uint64_t count = entry.GetData();
+ aStream << bucket << "=" << count;
+ }
+ aStream << "}";
+ aStream << ")";
+ return aStream;
+ }
};
} // namespace mozilla::glean
diff --git a/toolkit/components/glean/bindings/private/Labeled.cpp b/toolkit/components/glean/bindings/private/Labeled.cpp
index 23527708e0..1af9b870ae 100644
--- a/toolkit/components/glean/bindings/private/Labeled.cpp
+++ b/toolkit/components/glean/bindings/private/Labeled.cpp
@@ -31,7 +31,7 @@ already_AddRefed<GleanMetric> GleanLabeled::NamedGetter(const nsAString& aName,
auto mirrorId = ScalarIdForMetric(mId);
if (mirrorId) {
- GetLabeledMirrorLock().apply([&](auto& lock) {
+ GetLabeledMirrorLock().apply([&](const auto& lock) {
auto tuple = std::make_tuple<Telemetry::ScalarID, nsString>(
mirrorId.extract(), nsString(aName));
lock.ref()->InsertOrUpdate(submetricId, std::move(tuple));
diff --git a/toolkit/components/glean/bindings/private/Labeled.h b/toolkit/components/glean/bindings/private/Labeled.h
index 65e31bd2bd..0e3aafba05 100644
--- a/toolkit/components/glean/bindings/private/Labeled.h
+++ b/toolkit/components/glean/bindings/private/Labeled.h
@@ -60,7 +60,7 @@ class Labeled {
static inline void UpdateLabeledMirror(Telemetry::ScalarID aMirrorId,
uint32_t aSubmetricId,
const nsACString& aLabel) {
- GetLabeledMirrorLock().apply([&](auto& lock) {
+ GetLabeledMirrorLock().apply([&](const auto& lock) {
auto tuple = std::make_tuple<Telemetry::ScalarID, nsString>(
std::move(aMirrorId), NS_ConvertUTF8toUTF16(aLabel));
lock.ref()->InsertOrUpdate(aSubmetricId, std::move(tuple));
diff --git a/toolkit/components/glean/bindings/private/Ping.cpp b/toolkit/components/glean/bindings/private/Ping.cpp
index 19f4fb5f77..8dbb316128 100644
--- a/toolkit/components/glean/bindings/private/Ping.cpp
+++ b/toolkit/components/glean/bindings/private/Ping.cpp
@@ -42,7 +42,7 @@ void Ping::Submit(const nsACString& aReason) const {
{
auto callback = Maybe<PingTestCallback>();
GetCallbackMapLock().apply(
- [&](auto& lock) { callback = lock.ref()->Extract(mId); });
+ [&](const auto& lock) { callback = lock.ref()->Extract(mId); });
// Calling the callback outside of the lock allows it to register a new
// callback itself.
if (callback) {
@@ -55,7 +55,7 @@ void Ping::Submit(const nsACString& aReason) const {
void Ping::TestBeforeNextSubmit(PingTestCallback&& aCallback) const {
{
GetCallbackMapLock().apply(
- [&](auto& lock) { lock.ref()->InsertOrUpdate(mId, aCallback); });
+ [&](const auto& lock) { lock.ref()->InsertOrUpdate(mId, aCallback); });
}
}
diff --git a/toolkit/components/glean/bindings/private/Timespan.cpp b/toolkit/components/glean/bindings/private/Timespan.cpp
index 2ab1f0dbba..7f154152eb 100644
--- a/toolkit/components/glean/bindings/private/Timespan.cpp
+++ b/toolkit/components/glean/bindings/private/Timespan.cpp
@@ -93,7 +93,7 @@ void TimespanMetric::Start() const {
auto optScalarId = ScalarIdForMetric(mId);
if (optScalarId) {
auto scalarId = optScalarId.extract();
- GetTimesToStartsLock().apply([&](auto& lock) {
+ GetTimesToStartsLock().apply([&](const auto& lock) {
(void)NS_WARN_IF(lock.ref()->Remove(scalarId));
lock.ref()->InsertOrUpdate(scalarId, TimeStamp::Now());
});
@@ -105,7 +105,7 @@ void TimespanMetric::Stop() const {
auto optScalarId = ScalarIdForMetric(mId);
if (optScalarId) {
auto scalarId = optScalarId.extract();
- GetTimesToStartsLock().apply([&](auto& lock) {
+ GetTimesToStartsLock().apply([&](const auto& lock) {
auto optStart = lock.ref()->Extract(scalarId);
if (!NS_WARN_IF(!optStart)) {
double delta = (TimeStamp::Now() - optStart.extract()).ToMilliseconds();
@@ -127,7 +127,7 @@ void TimespanMetric::Cancel() const {
if (optScalarId) {
auto scalarId = optScalarId.extract();
GetTimesToStartsLock().apply(
- [&](auto& lock) { lock.ref()->Remove(scalarId); });
+ [&](const auto& lock) { lock.ref()->Remove(scalarId); });
}
fog_timespan_cancel(mId);
}
diff --git a/toolkit/components/glean/bindings/private/TimingDistribution.cpp b/toolkit/components/glean/bindings/private/TimingDistribution.cpp
index 036db5f9db..7273d3fd2f 100644
--- a/toolkit/components/glean/bindings/private/TimingDistribution.cpp
+++ b/toolkit/components/glean/bindings/private/TimingDistribution.cpp
@@ -106,7 +106,7 @@ extern "C" NS_EXPORT void GIFFT_TimingDistributionStart(
uint32_t aMetricId, mozilla::glean::TimerId aTimerId) {
auto mirrorId = mozilla::glean::HistogramIdForMetric(aMetricId);
if (mirrorId) {
- mozilla::glean::GetTimerIdToStartsLock().apply([&](auto& lock) {
+ mozilla::glean::GetTimerIdToStartsLock().apply([&](const auto& lock) {
auto tuple = mozilla::glean::MetricTimerTuple{aMetricId, aTimerId};
// It should be all but impossible for anyone to have already inserted
// this timer for this metric given the monotonicity of timer ids.
@@ -121,7 +121,7 @@ extern "C" NS_EXPORT void GIFFT_TimingDistributionStopAndAccumulate(
uint32_t aMetricId, mozilla::glean::TimerId aTimerId) {
auto mirrorId = mozilla::glean::HistogramIdForMetric(aMetricId);
if (mirrorId) {
- mozilla::glean::GetTimerIdToStartsLock().apply([&](auto& lock) {
+ mozilla::glean::GetTimerIdToStartsLock().apply([&](const auto& lock) {
auto tuple = mozilla::glean::MetricTimerTuple{aMetricId, aTimerId};
auto optStart = lock.ref()->Extract(tuple);
// The timer might not be in the map to be removed if it's already been
@@ -147,7 +147,7 @@ extern "C" NS_EXPORT void GIFFT_TimingDistributionCancel(
uint32_t aMetricId, mozilla::glean::TimerId aTimerId) {
auto mirrorId = mozilla::glean::HistogramIdForMetric(aMetricId);
if (mirrorId) {
- mozilla::glean::GetTimerIdToStartsLock().apply([&](auto& lock) {
+ mozilla::glean::GetTimerIdToStartsLock().apply([&](const auto& lock) {
// The timer might not be in the map to be removed if it's already been
// cancelled or stop_and_accumulate'd.
auto tuple = mozilla::glean::MetricTimerTuple{aMetricId, aTimerId};
@@ -172,8 +172,20 @@ void TimingDistributionMetric::StopAndAccumulate(const TimerId&& aId) const {
// type.
void TimingDistributionMetric::AccumulateRawDuration(
const TimeDuration& aDuration) const {
+ // `* 1000.0` is an acceptable overflow risk as durations are unlikely to be
+ // on the order of (-)10^282 years.
+ double durationNs = aDuration.ToMicroseconds() * 1000.0;
+ double roundedDurationNs = std::round(durationNs);
+ if (MOZ_UNLIKELY(
+ roundedDurationNs <
+ static_cast<double>(std::numeric_limits<uint64_t>::min()) ||
+ roundedDurationNs >
+ static_cast<double>(std::numeric_limits<uint64_t>::max()))) {
+ // TODO(bug 1691073): Instrument this error.
+ return;
+ }
fog_timing_distribution_accumulate_raw_nanos(
- mId, uint64_t(aDuration.ToMicroseconds() * 1000.00));
+ mId, static_cast<uint64_t>(roundedDurationNs));
}
void TimingDistributionMetric::Cancel(const TimerId&& aId) const {
diff --git a/toolkit/components/glean/build_scripts/mach_commands.py b/toolkit/components/glean/build_scripts/mach_commands.py
index 4a0f6dbc68..b8d270c088 100644
--- a/toolkit/components/glean/build_scripts/mach_commands.py
+++ b/toolkit/components/glean/build_scripts/mach_commands.py
@@ -167,9 +167,18 @@ def update_glean(command_context, version):
topsrcdir = Path(command_context.topsrcdir)
replace_in_file_or_die(
- topsrcdir / "build.gradle",
- r'gleanVersion = "[0-9.]+"',
- f'gleanVersion = "{version}"',
+ topsrcdir
+ / "mobile"
+ / "android"
+ / "android-components"
+ / "plugins"
+ / "dependencies"
+ / "src"
+ / "main"
+ / "java"
+ / "DependenciesPlugin.kt",
+ r'mozilla_glean = "[0-9.]+"',
+ f'mozilla_glean = "{version}"',
)
replace_in_file_or_die(
topsrcdir / "toolkit" / "components" / "glean" / "Cargo.toml",
@@ -226,3 +235,47 @@ def update_glean(command_context, version):
"""
print(textwrap.dedent(instructions))
+
+
+@Command(
+ "event-into-legacy",
+ category="misc",
+ description="Create a Legacy Telemetry compatible event definition from an existing Glean Event metric.",
+)
+@CommandArgument(
+ "--append",
+ "-a",
+ action="store_true",
+ help="Append to toolkit/components/telemetry/Events.yaml (note: verify and make any necessary modifications before landing).",
+)
+@CommandArgument("event", default=None, nargs="?", type=str, help="Event name.")
+def event_into_legacy(command_context, event=None, append=False):
+ # Get the metrics_index's list of metrics indices
+ # by loading the index as a module.
+ import sys
+ from os import path
+
+ sys.path.append(path.join(path.dirname(__file__), path.pardir))
+
+ from metrics_index import metrics_yamls
+
+ sys.path.append(path.dirname(__file__))
+
+ from pathlib import Path
+
+ from translate_events import translate_event
+
+ legacy_yaml_path = path.join(
+ Path(command_context.topsrcdir),
+ "toolkit",
+ "components",
+ "telemetry",
+ "Events.yaml",
+ )
+
+ return translate_event(
+ event,
+ append,
+ [Path(command_context.topsrcdir) / x for x in metrics_yamls],
+ legacy_yaml_path,
+ )
diff --git a/toolkit/components/glean/build_scripts/translate_events.py b/toolkit/components/glean/build_scripts/translate_events.py
new file mode 100644
index 0000000000..5936a67132
--- /dev/null
+++ b/toolkit/components/glean/build_scripts/translate_events.py
@@ -0,0 +1,177 @@
+# -*- coding: utf-8 -*-
+
+# 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/.
+
+"""
+Create a Legacy Telemetry event definition for the provided, named Glean event metric.
+"""
+
+import re
+import sys
+from os import path
+from pathlib import Path
+from typing import Sequence
+
+import yaml
+from glean_parser import parser, util
+
+TC_ROOT_PATH = path.abspath(path.join(path.dirname(__file__), path.pardir, path.pardir))
+sys.path.append(TC_ROOT_PATH)
+# The parsers live in a subdirectory of "build_scripts", account for that.
+# NOTE: if the parsers are moved, this logic will need to be updated.
+sys.path.append(path.join(TC_ROOT_PATH, "telemetry", "build_scripts"))
+
+from mozparsers.parse_events import convert_to_cpp_identifier # noqa: E402
+
+bug_number_pattern = re.compile(r"\d+")
+
+
+class IndentingDumper(yaml.Dumper):
+ def increase_indent(self, flow=False, indentless=False):
+ return super(IndentingDumper, self).increase_indent(flow, False)
+
+
+def get_bug_number_from_url(url: str) -> int:
+ bug = bug_number_pattern.search(url)
+ # Python lacks a safe cast, so we will return 1 if we fail to bubble up.
+ if bug is not None:
+ try:
+ bug = int(bug[0])
+ except TypeError:
+ print(f"Failed to parse {bug[0]} to an integer")
+ return 1
+ return bug
+ print(f"Failed to find a valid bug in the url {url}")
+ return 1
+
+
+def create_legacy_mirror_def(category_name: str, metric_name: str, event_objects: list):
+ event_cpp_enum = convert_to_cpp_identifier(category_name, "_") + "_"
+ event_cpp_enum += convert_to_cpp_identifier(metric_name, ".") + "_"
+ event_cpp_enum += convert_to_cpp_identifier(event_objects[0], ".")
+
+ print(
+ f"""The Glean event {category_name}.{metric_name} has generated the {event_cpp_enum} Legacy Telemetry event. To link Glean to Legacy, please include in {category_name}.{metric_name}'s definition the following property:
+telemetry_mirror: {event_cpp_enum}
+See https://firefox-source-docs.mozilla.org/toolkit/components/glean/user/gifft.html#the-telemetry-mirror-property-in-metrics-yaml
+for more information.
+"""
+ )
+
+
+def translate_event(
+ event_category_and_name: str,
+ append: bool,
+ metrics_files: Sequence[Path],
+ legacy_yaml_file: Path,
+) -> int:
+ """
+ Commandline helper for translating Glean events to Legacy.
+
+ :param event_name: the event to look for
+ :param metrics_files: List of Path objects to load metrics from.
+ :return: Non-zero if there were any errors.
+ """
+
+ # Event objects are a Legacy Telemetry event field that is rarely used. We
+ # always broadcast into a single pre-defined object.
+ event_objects = ["events"]
+
+ if event_category_and_name is not None:
+ *event_category, event_name = event_category_and_name.rsplit(".", maxsplit=1)
+ else:
+ print("Please provide an event (in category.metricName format) to translate.")
+ return 1
+
+ if len(event_category) > 0:
+ event_category = util.snake_case(event_category[0])
+ event_name = util.snake_case(event_name)
+ else:
+ print(
+ f"Your event '{event_category_and_name}' did not conform to the a.category.a_metric_name format."
+ )
+ return 1
+
+ metrics_files = util.ensure_list(metrics_files)
+
+ # Accept any value of expires.
+ parser_options = {
+ "allow_reserved": True,
+ "custom_is_expired": lambda expires: False,
+ "custom_validate_expires": lambda expires: True,
+ }
+ all_objects = parser.parse_objects(metrics_files, parser_options)
+
+ if util.report_validation_errors(all_objects):
+ return 1
+
+ for category_name, metrics in all_objects.value.items():
+ for metric in metrics.values():
+ metric_name = util.snake_case(metric.name)
+ category_name = util.snake_case(category_name)
+
+ if metric_name != event_name or category_name != event_category:
+ continue
+
+ if metric.type != "event":
+ print(
+ f"Metric {event_category_and_name} was found, but was a {metric.type} metric, not an event metric."
+ )
+ return 1
+
+ bugs_list = [get_bug_number_from_url(m) for m in metric.bugs]
+ # Bail out if there was a parse error (error printed in get_bug_number_from_url)
+ if 1 in bugs_list:
+ return 1
+
+ metric_dict = {
+ "objects": event_objects,
+ "description": str(metric.description).strip(),
+ "bug_numbers": bugs_list,
+ "notification_emails": metric.notification_emails,
+ "expiry_version": str(metric.expires),
+ "products": ["firefox"],
+ "record_in_processes": ["all"],
+ }
+
+ if len(metric.extra_keys.items()) > 0:
+ # Convert extra keys into a nested dictionary that YAML can understand
+ extra_keys = {}
+ for extra_key_pair in metric.extra_keys.items():
+ description = (
+ extra_key_pair[1]["description"].replace("\n", " ").strip()
+ )
+ extra_keys[extra_key_pair[0]] = description
+
+ metric_dict["extra_keys"] = extra_keys
+
+ metric_dict = {metric_name: metric_dict}
+ metric_dict = {category_name: metric_dict}
+
+ metric_yaml = yaml.dump(
+ metric_dict,
+ default_flow_style=False,
+ width=78,
+ indent=2,
+ Dumper=IndentingDumper,
+ )
+
+ if append:
+ with open(legacy_yaml_file, "a") as file:
+ print("", file=file)
+ print(metric_yaml, file=file)
+ print(
+ f"Apended {event_category_and_name} to the Legacy Events.yaml file. Please confirm the details by opening"
+ )
+ print(legacy_yaml_file)
+ print("and checking that all fields are correct.")
+
+ create_legacy_mirror_def(event_name, category_name, event_objects)
+ return 0
+
+ print(metric_yaml)
+ create_legacy_mirror_def(event_name, category_name, event_objects)
+
+ return 0
diff --git a/toolkit/components/glean/metrics_index.py b/toolkit/components/glean/metrics_index.py
index 8e20658810..28759a15fa 100644
--- a/toolkit/components/glean/metrics_index.py
+++ b/toolkit/components/glean/metrics_index.py
@@ -18,6 +18,8 @@ gecko_metrics = [
"browser/base/content/metrics.yaml",
"docshell/base/metrics.yaml",
"dom/base/use_counter_metrics.yaml",
+ "dom/media/eme/metrics.yaml",
+ "dom/media/hls/metrics.yaml",
"dom/media/metrics.yaml",
"dom/media/webrtc/metrics.yaml",
"dom/metrics.yaml",
@@ -31,6 +33,7 @@ gecko_metrics = [
"mobile/android/modules/geckoview/metrics.yaml",
"netwerk/metrics.yaml",
"netwerk/protocol/http/metrics.yaml",
+ "security/certverifier/metrics.yaml",
"security/manager/ssl/metrics.yaml",
"toolkit/components/cookiebanners/metrics.yaml",
"toolkit/components/extensions/metrics.yaml",
diff --git a/toolkit/components/glean/src/init/mod.rs b/toolkit/components/glean/src/init/mod.rs
index f430cd7384..2c938899c0 100644
--- a/toolkit/components/glean/src/init/mod.rs
+++ b/toolkit/components/glean/src/init/mod.rs
@@ -38,6 +38,7 @@ use viaduct_uploader::ViaductUploader;
pub extern "C" fn fog_init(
data_path_override: &nsACString,
app_id_override: &nsACString,
+ disable_internal_pings: bool,
) -> nsresult {
let upload_enabled = static_prefs::pref!("datareporting.healthreport.uploadEnabled");
let recording_enabled = static_prefs::pref!("telemetry.fog.test.localhost_port") < 0;
@@ -48,6 +49,9 @@ pub extern "C" fn fog_init(
app_id_override,
upload_enabled || recording_enabled,
uploader,
+ // Flipping it around, because no value = defaults to false,
+ // so we take in `disable` but pass on `enable`.
+ !disable_internal_pings,
)
.into()
}
@@ -64,6 +68,7 @@ pub extern "C" fn fog_init(
pub extern "C" fn fog_init(
data_path_override: &nsACString,
app_id_override: &nsACString,
+ disable_internal_pings: bool,
) -> nsresult {
// On Android always enable Glean upload.
let upload_enabled = true;
@@ -75,6 +80,7 @@ pub extern "C" fn fog_init(
app_id_override,
upload_enabled,
uploader,
+ !disable_internal_pings,
)
.into()
}
@@ -84,6 +90,7 @@ fn fog_init_internal(
app_id_override: &nsACString,
upload_enabled: bool,
uploader: Option<Box<dyn glean::net::PingUploader>>,
+ enable_internal_pings: bool,
) -> Result<(), nsresult> {
metrics::fog::initialization.start();
@@ -95,6 +102,7 @@ fn fog_init_internal(
conf.upload_enabled = upload_enabled;
conf.uploader = uploader;
+ conf.enable_internal_pings = enable_internal_pings;
// If we're operating in automation without any specific source tags to set,
// set the tag "automation" so any pings that escape don't clutter the tables.
@@ -155,16 +163,12 @@ fn build_configuration(
extern "C" {
fn FOG_MaxPingLimit() -> u32;
- fn FOG_EventTimestampsEnabled() -> bool;
}
// SAFETY NOTE: Safe because it returns a primitive by value.
let pings_per_interval = unsafe { FOG_MaxPingLimit() };
metrics::fog::max_pings_per_minute.set(pings_per_interval.into());
- // SAFETY NOTE: Safe because it returns a primitive by value.
- let enable_event_timestamps = unsafe { FOG_EventTimestampsEnabled() };
-
let rate_limit = Some(glean::PingRateLimit {
seconds_per_interval: 60,
pings_per_interval,
@@ -182,8 +186,9 @@ fn build_configuration(
trim_data_to_registered_pings: true,
log_level: None,
rate_limit,
- enable_event_timestamps,
+ enable_event_timestamps: true,
experimentation_id: None,
+ enable_internal_pings: true,
};
Ok((configuration, client_info))
diff --git a/toolkit/components/glean/tests/gtest/TestFog.cpp b/toolkit/components/glean/tests/gtest/TestFog.cpp
index 0c1621911e..e6f4f7a2c2 100644
--- a/toolkit/components/glean/tests/gtest/TestFog.cpp
+++ b/toolkit/components/glean/tests/gtest/TestFog.cpp
@@ -11,6 +11,7 @@
#include "mozilla/Maybe.h"
#include "mozilla/Result.h"
#include "mozilla/ResultVariant.h"
+#include "mozilla/TimeStamp.h"
#include "nsTArray.h"
@@ -20,6 +21,7 @@
#include "prtime.h"
using mozilla::Preferences;
+using mozilla::TimeDuration;
using namespace mozilla::glean;
using namespace mozilla::glean::impl;
@@ -277,6 +279,15 @@ TEST_F(FOGFixture, TestCppTimingDistWorks) {
ASSERT_EQ(sampleCount, (uint64_t)2);
}
+TEST_F(FOGFixture, TestCppTimingDistNegativeDuration) {
+ // Intentionally a negative duration to test the error case.
+ auto negDuration = TimeDuration::FromSeconds(-1);
+ test_only::what_time_is_it.AccumulateRawDuration(negDuration);
+
+ ASSERT_EQ(mozilla::Nothing(),
+ test_only::what_time_is_it.TestGetValue().unwrap());
+}
+
TEST_F(FOGFixture, TestLabeledBooleanWorks) {
ASSERT_EQ(mozilla::Nothing(),
test_only::mabels_like_balloons.Get("hot_air"_ns)
diff --git a/toolkit/components/glean/xpcom/FOG.cpp b/toolkit/components/glean/xpcom/FOG.cpp
index d4c03a5b9e..955f2511b6 100644
--- a/toolkit/components/glean/xpcom/FOG.cpp
+++ b/toolkit/components/glean/xpcom/FOG.cpp
@@ -95,7 +95,7 @@ already_AddRefed<FOG> FOG::GetSingleton() {
glean::fog::inits_during_shutdown.Add(1);
// It's enough to call init before shutting down.
// We don't need to (and can't) wait for it to complete.
- glean::impl::fog_init(&VoidCString(), &VoidCString());
+ glean::impl::fog_init(&VoidCString(), &VoidCString(), false);
}
gFOG->Shutdown();
gFOG = nullptr;
@@ -123,19 +123,13 @@ extern "C" uint32_t FOG_MaxPingLimit(void) {
"gleanMaxPingsPerMinute"_ns, 15);
}
-// This allows us to pass whether to enable precise event timestamps to Rust.
-// Default is false.
-extern "C" bool FOG_EventTimestampsEnabled(void) {
- return NimbusFeatures::GetBool("gleanInternalSdk"_ns,
- "enableEventTimestamps"_ns, false);
-}
-
// Called when knowing if we're in automation is necessary.
extern "C" bool FOG_IPCIsInAutomation(void) { return xpc::IsInAutomation(); }
NS_IMETHODIMP
FOG::InitializeFOG(const nsACString& aDataPathOverride,
- const nsACString& aAppIdOverride) {
+ const nsACString& aAppIdOverride,
+ const bool aDisableInternalPings) {
MOZ_ASSERT(XRE_IsParentProcess());
gInitializeCalled = true;
RunOnShutdown(
@@ -147,7 +141,8 @@ FOG::InitializeFOG(const nsACString& aDataPathOverride,
},
ShutdownPhase::AppShutdownConfirmed);
- return glean::impl::fog_init(&aDataPathOverride, &aAppIdOverride);
+ return glean::impl::fog_init(&aDataPathOverride, &aAppIdOverride,
+ aDisableInternalPings);
}
NS_IMETHODIMP
diff --git a/toolkit/components/glean/xpcom/nsIFOG.idl b/toolkit/components/glean/xpcom/nsIFOG.idl
index dd5d0ec21a..67041d6228 100644
--- a/toolkit/components/glean/xpcom/nsIFOG.idl
+++ b/toolkit/components/glean/xpcom/nsIFOG.idl
@@ -17,8 +17,10 @@ interface nsIFOG : nsISupports
* instead of the profile dir.
* @param aAppIdOverride - The application_id to use instead of
* "firefox.desktop".
+ * @param aDisableInternalPings - Whether to disable internal pings (baseline, events, metrics).
+ * Default: false.
*/
- void initializeFOG([optional] in AUTF8String aDataPathOverride, [optional] in AUTF8String aAppIdOverride);
+ void initializeFOG([optional] in AUTF8String aDataPathOverride, [optional] in AUTF8String aAppIdOverride, [optional] in boolean aDisableInternalPings);
/**
* Register custom pings.
diff --git a/toolkit/components/httpsonlyerror/tests/browser/browser_exception.js b/toolkit/components/httpsonlyerror/tests/browser/browser_exception.js
index 7cf98b467f..0c2bd4fff5 100644
--- a/toolkit/components/httpsonlyerror/tests/browser/browser_exception.js
+++ b/toolkit/components/httpsonlyerror/tests/browser/browser_exception.js
@@ -139,7 +139,7 @@ function setupFileServer() {
"GET",
`${SECURE_ROOT_PATH}file_upgrade_insecure_server.sjs?queryresult=${INSECURE_ROOT_PATH}`
);
- xhrRequest.onload = function (e) {
+ xhrRequest.onload = function () {
var results = xhrRequest.responseText.split(",");
resolve(results);
};
diff --git a/toolkit/components/kvstore/nsIKeyValue.idl b/toolkit/components/kvstore/nsIKeyValue.idl
index 08cd548af2..3d890eaa96 100644
--- a/toolkit/components/kvstore/nsIKeyValue.idl
+++ b/toolkit/components/kvstore/nsIKeyValue.idl
@@ -174,7 +174,7 @@ interface nsIKeyValuePair : nsISupports {
*/
[scriptable, builtinclass, rust_sync, uuid(b9ba7116-b7ff-4717-9a28-a08e6879b199)]
interface nsIKeyValueEnumerator : nsISupports {
- bool hasMoreElements();
+ boolean hasMoreElements();
nsIKeyValuePair getNext();
};
diff --git a/toolkit/components/mediasniffer/test/unit/test_mediasniffer.js b/toolkit/components/mediasniffer/test/unit/test_mediasniffer.js
index 12aa56a855..dc92c90a45 100644
--- a/toolkit/components/mediasniffer/test/unit/test_mediasniffer.js
+++ b/toolkit/components/mediasniffer/test/unit/test_mediasniffer.js
@@ -71,7 +71,7 @@ var listener = {
);
},
- onDataAvailable(request, stream, offset, count) {
+ onDataAvailable(request, stream) {
try {
var bis = Cc["@mozilla.org/binaryinputstream;1"].createInstance(
Ci.nsIBinaryInputStream
@@ -83,7 +83,7 @@ var listener = {
}
},
- onStopRequest(request, status) {
+ onStopRequest() {
testRan++;
runNext();
},
diff --git a/toolkit/components/mediasniffer/test/unit/test_mediasniffer_ext.js b/toolkit/components/mediasniffer/test/unit/test_mediasniffer_ext.js
index f48ffbb75a..5b6b400ed1 100644
--- a/toolkit/components/mediasniffer/test/unit/test_mediasniffer_ext.js
+++ b/toolkit/components/mediasniffer/test/unit/test_mediasniffer_ext.js
@@ -61,7 +61,7 @@ var listener = {
);
},
- onDataAvailable(request, stream, offset, count) {
+ onDataAvailable(request, stream) {
try {
var bis = Cc["@mozilla.org/binaryinputstream;1"].createInstance(
Ci.nsIBinaryInputStream
@@ -73,7 +73,7 @@ var listener = {
}
},
- onStopRequest(request, status) {
+ onStopRequest() {
testRan++;
runNext();
},
diff --git a/toolkit/components/messaging-system/lib/SpecialMessageActions.sys.mjs b/toolkit/components/messaging-system/lib/SpecialMessageActions.sys.mjs
index 8ed488ff88..c1a229e9c3 100644
--- a/toolkit/components/messaging-system/lib/SpecialMessageActions.sys.mjs
+++ b/toolkit/components/messaging-system/lib/SpecialMessageActions.sys.mjs
@@ -198,6 +198,8 @@ export const SpecialMessageActions = {
"browser.firefox-view.feature-tour",
"browser.pdfjs.feature-tour",
"browser.newtab.feature-tour",
+ "browser.newtabpage.activity-stream.newtabWallpapers.wallpaper-light",
+ "browser.newtabpage.activity-stream.newtabWallpapers.wallpaper-dark",
"cookiebanners.service.mode",
"cookiebanners.service.mode.privateBrowsing",
"cookiebanners.service.detectOnly",
@@ -291,7 +293,7 @@ export const SpecialMessageActions = {
Ci.nsISupportsWeakReference,
]),
- observe(aSubject, aTopic, aData) {
+ observe() {
let state = lazy.UIState.get();
if (state.status === lazy.UIState.STATUS_SIGNED_IN) {
// We completed sign-in, so tear down our listener / observer and resolve
diff --git a/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/test/browser/browser_sma_open_protection_panel.js b/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/test/browser/browser_sma_open_protection_panel.js
index c9522426a2..bddc456539 100644
--- a/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/test/browser/browser_sma_open_protection_panel.js
+++ b/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/test/browser/browser_sma_open_protection_panel.js
@@ -4,7 +4,7 @@
"use strict";
add_task(async function test_OPEN_PROTECTION_PANEL() {
- await BrowserTestUtils.withNewTab(EXAMPLE_URL, async browser => {
+ await BrowserTestUtils.withNewTab(EXAMPLE_URL, async () => {
const popupshown = BrowserTestUtils.waitForEvent(
window,
"popupshown",
diff --git a/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/test/browser/browser_sma_pin_current_tab.js b/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/test/browser/browser_sma_pin_current_tab.js
index 4425325526..2cb0e3000f 100644
--- a/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/test/browser/browser_sma_pin_current_tab.js
+++ b/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/test/browser/browser_sma_pin_current_tab.js
@@ -4,7 +4,7 @@
"use strict";
add_task(async function test_PIN_CURRENT_TAB() {
- await BrowserTestUtils.withNewTab("about:blank", async browser => {
+ await BrowserTestUtils.withNewTab("about:blank", async () => {
await SMATestUtils.executeAndValidateAction({ type: "PIN_CURRENT_TAB" });
ok(gBrowser.selectedTab.pinned, "should pin current tab");
diff --git a/toolkit/components/messaging-system/schemas/TriggerActionSchemas/TriggerActionSchemas.json b/toolkit/components/messaging-system/schemas/TriggerActionSchemas/TriggerActionSchemas.json
index 6a7d2328d7..773b84aa4a 100644
--- a/toolkit/components/messaging-system/schemas/TriggerActionSchemas/TriggerActionSchemas.json
+++ b/toolkit/components/messaging-system/schemas/TriggerActionSchemas/TriggerActionSchemas.json
@@ -169,6 +169,18 @@
"properties": {
"id": {
"type": "string",
+ "enum": ["deeplinkedToWindowsSettingsUI"]
+ }
+ },
+ "additionalProperties": false,
+ "required": ["id"],
+ "description": "Occurs when user indicates they want to set Firefox to the default browser and they need to interact with Windows Settings to do so."
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
"enum": ["captivePortalLogin"]
}
},
diff --git a/toolkit/components/messaging-system/schemas/TriggerActionSchemas/index.md b/toolkit/components/messaging-system/schemas/TriggerActionSchemas/index.md
index 85be613392..45df09249a 100644
--- a/toolkit/components/messaging-system/schemas/TriggerActionSchemas/index.md
+++ b/toolkit/components/messaging-system/schemas/TriggerActionSchemas/index.md
@@ -44,6 +44,7 @@ let patterns: string[];
* [formAutofill](#formautofill)
* [contentBlocking](#contentblocking)
* [defaultBrowserCheck](#defaultbrowsercheck)
+* [deeplinkedToWindowsSettingsUI](#deeplinkedtowindowssettingsui)
* [captivePortalLogin](#captiveportallogin)
* [preferenceObserver](#preferenceobserver)
* [featureCalloutCheck](#featurecalloutcheck)
@@ -161,6 +162,12 @@ let willShowDefaultPrompt = boolean | undefined;
}
```
+### `deeplinkedToWindowsSettingsUI`
+
+Triggers when the user has indicated they want to set Firefox as the default web
+browser and interaction with Windows Settings is necessary to finish setting
+Firefox as default.
+
### `captivePortalLogin`
Happens when the user successfully goes through a captive portal authentication flow.
diff --git a/toolkit/components/ml/actors/MLEngineParent.sys.mjs b/toolkit/components/ml/actors/MLEngineParent.sys.mjs
index 10b4eed4fa..05203e5f69 100644
--- a/toolkit/components/ml/actors/MLEngineParent.sys.mjs
+++ b/toolkit/components/ml/actors/MLEngineParent.sys.mjs
@@ -91,7 +91,7 @@ export class MLEngineParent extends JSWindowActorParent {
}
// eslint-disable-next-line consistent-return
- async receiveMessage({ name, data }) {
+ async receiveMessage({ name }) {
switch (name) {
case "MLEngine:Ready":
if (lazy.EngineProcess.resolveMLEngineParent) {
diff --git a/toolkit/components/ml/content/ModelHub.sys.mjs b/toolkit/components/ml/content/ModelHub.sys.mjs
new file mode 100644
index 0000000000..4c2181ff14
--- /dev/null
+++ b/toolkit/components/ml/content/ModelHub.sys.mjs
@@ -0,0 +1,690 @@
+/* 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, {
+ clearTimeout: "resource://gre/modules/Timer.sys.mjs",
+ setTimeout: "resource://gre/modules/Timer.sys.mjs",
+});
+
+ChromeUtils.defineLazyGetter(lazy, "console", () => {
+ return console.createInstance({
+ maxLogLevelPref: "browser.ml.logLevel",
+ prefix: "ML",
+ });
+});
+
+const ALLOWED_HUBS = [
+ "chrome://*",
+ "resource://*",
+ "http://localhost",
+ "https://localhost",
+ "https://model-hub.mozilla.org",
+];
+
+const ALLOWED_HEADERS_KEYS = ["Content-Type", "ETag", "status"];
+const DEFAULT_URL_TEMPLATE =
+ "${organization}/${modelName}/resolve/${modelVersion}/${file}";
+
+/**
+ * Checks if a given URL string corresponds to an allowed hub.
+ *
+ * This function validates a URL against a list of allowed hubs, ensuring that it:
+ * - Is well-formed according to the URL standard.
+ * - Does not include a username or password.
+ * - Matches the allowed scheme and hostname.
+ *
+ * @param {string} urlString The URL string to validate.
+ * @returns {boolean} True if the URL is allowed; false otherwise.
+ */
+function allowedHub(urlString) {
+ try {
+ const url = new URL(urlString);
+ // Check for username or password in the URL
+ if (url.username !== "" || url.password !== "") {
+ return false; // Reject URLs with username or password
+ }
+ const scheme = url.protocol;
+ const host = url.hostname;
+ const fullPrefix = `${scheme}//${host}`;
+
+ return ALLOWED_HUBS.some(allowedHub => {
+ const [allowedScheme, allowedHost] = allowedHub.split("://");
+ if (allowedHost === "*") {
+ return `${allowedScheme}:` === scheme;
+ }
+ const allowedPrefix = `${allowedScheme}://${allowedHost}`;
+ return fullPrefix === allowedPrefix;
+ });
+ } catch (error) {
+ lazy.console.error("Error parsing URL:", error);
+ return false;
+ }
+}
+
+const NO_ETAG = "NO_ETAG";
+
+/**
+ * Class for managing a cache stored in IndexedDB.
+ */
+export class IndexedDBCache {
+ /**
+ * Reference to the IndexedDB database.
+ *
+ * @type {IDBDatabase|null}
+ */
+ db = null;
+
+ /**
+ * Version of the database. Null if not set.
+ *
+ * @type {number|null}
+ */
+ dbVersion = null;
+
+ /**
+ * Total size of the files stored in the cache.
+ *
+ * @type {number}
+ */
+ totalSize = 0;
+
+ /**
+ * Name of the database used by IndexedDB.
+ *
+ * @type {string}
+ */
+ dbName;
+
+ /**
+ * Name of the object store for storing files.
+ *
+ * @type {string}
+ */
+ fileStoreName;
+
+ /**
+ * Name of the object store for storing headers.
+ *
+ * @type {string}
+ */
+ headersStoreName;
+ /**
+ * Maximum size of the cache in bytes. Defaults to 1GB.
+ *
+ * @type {number}
+ */
+ #maxSize = 1_073_741_824; // 1GB in bytes
+
+ /**
+ * Private constructor to prevent direct instantiation.
+ * Use IndexedDBCache.init to create an instance.
+ *
+ * @param {string} dbName - The name of the database file.
+ * @param {number} version - The version number of the database.
+ */
+ constructor(dbName = "modelFiles", version = 1) {
+ this.dbName = dbName;
+ this.dbVersion = version;
+ this.fileStoreName = "files";
+ this.headersStoreName = "headers";
+ }
+
+ /**
+ * Static method to create and initialize an instance of IndexedDBCache.
+ *
+ * @param {string} [dbName="modelFiles"] - The name of the database.
+ * @param {number} [version=1] - The version number of the database.
+ * @returns {Promise<IndexedDBCache>} An initialized instance of IndexedDBCache.
+ */
+ static async init(dbName = "modelFiles", version = 1) {
+ const cacheInstance = new IndexedDBCache(dbName, version);
+ cacheInstance.db = await cacheInstance.#openDB();
+ const storedSize = await cacheInstance.#getData(
+ cacheInstance.headersStoreName,
+ "totalSize"
+ );
+ cacheInstance.totalSize = storedSize ? storedSize.size : 0;
+ return cacheInstance;
+ }
+
+ /**
+ * Called to close the DB connection and dispose the instance
+ *
+ */
+ async dispose() {
+ if (this.db) {
+ this.db.close();
+ this.db = null;
+ }
+ }
+
+ /**
+ * Opens or creates the IndexedDB database.
+ *
+ * @returns {Promise<IDBDatabase>}
+ */
+ async #openDB() {
+ return new Promise((resolve, reject) => {
+ const request = indexedDB.open(this.dbName, this.dbVersion);
+ request.onerror = event => reject(event.target.error);
+ request.onsuccess = event => resolve(event.target.result);
+ request.onupgradeneeded = event => {
+ const db = event.target.result;
+ if (!db.objectStoreNames.contains(this.fileStoreName)) {
+ db.createObjectStore(this.fileStoreName, { keyPath: "id" });
+ }
+ if (!db.objectStoreNames.contains(this.headersStoreName)) {
+ db.createObjectStore(this.headersStoreName, { keyPath: "id" });
+ }
+ };
+ });
+ }
+
+ /**
+ * Generic method to get the data from a specified object store.
+ *
+ * @param {string} storeName - The name of the object store.
+ * @param {string} key - The key within the object store to retrieve the data from.
+ * @returns {Promise<any>}
+ */
+ async #getData(storeName, key) {
+ return new Promise((resolve, reject) => {
+ const transaction = this.db.transaction([storeName], "readonly");
+ const store = transaction.objectStore(storeName);
+ const request = store.get(key);
+ request.onerror = event => reject(event.target.error);
+ request.onsuccess = event => resolve(event.target.result);
+ });
+ }
+
+ // Used in tests
+ async _testGetData(storeName, key) {
+ return this.#getData(storeName, key);
+ }
+
+ /**
+ * Generic method to update data in a specified object store.
+ *
+ * @param {string} storeName - The name of the object store.
+ * @param {object} data - The data to store.
+ * @returns {Promise<void>}
+ */
+ async #updateData(storeName, data) {
+ return new Promise((resolve, reject) => {
+ const transaction = this.db.transaction([storeName], "readwrite");
+ const store = transaction.objectStore(storeName);
+ const request = store.put(data);
+ request.onerror = event => reject(event.target.error);
+ request.onsuccess = () => resolve();
+ });
+ }
+
+ /**
+ * Deletes a specific cache entry.
+ *
+ * @param {string} storeName - The name of the object store.
+ * @param {string} key - The key of the entry to delete.
+ * @returns {Promise<void>}
+ */
+ async #deleteData(storeName, key) {
+ return new Promise((resolve, reject) => {
+ const transaction = this.db.transaction([storeName], "readwrite");
+ const store = transaction.objectStore(storeName);
+ const request = store.delete(key);
+ request.onerror = event => reject(event.target.error);
+ request.onsuccess = () => resolve();
+ });
+ }
+
+ /**
+ * Retrieves the headers for a specific cache entry.
+ *
+ * @param {string} organization - The organization name.
+ * @param {string} modelName - The model name.
+ * @param {string} modelVersion - The model version.
+ * @param {string} file - The file name.
+ * @returns {Promise<object|null>} The headers or null if not found.
+ */
+ async getHeaders(organization, modelName, modelVersion, file) {
+ const headersKey = `${organization}/${modelName}/${modelVersion}`;
+ const cacheKey = `${organization}/${modelName}/${modelVersion}/${file}`;
+ const headers = await this.#getData(this.headersStoreName, headersKey);
+ if (headers && headers.files[cacheKey]) {
+ return headers.files[cacheKey];
+ }
+ return null; // Return null if no headers is found
+ }
+
+ /**
+ * Retrieves the file for a specific cache entry.
+ *
+ * @param {string} organization - The organization name.
+ * @param {string} modelName - The model name.
+ * @param {string} modelVersion - The model version.
+ * @param {string} file - The file name.
+ * @returns {Promise<[ArrayBuffer, object]|null>} The file ArrayBuffer and its headers or null if not found.
+ */
+ async getFile(organization, modelName, modelVersion, file) {
+ const cacheKey = `${organization}/${modelName}/${modelVersion}/${file}`;
+ const stored = await this.#getData(this.fileStoreName, cacheKey);
+ if (stored) {
+ const headers = await this.getHeaders(
+ organization,
+ modelName,
+ modelVersion,
+ file
+ );
+ return [stored.data, headers];
+ }
+ return null; // Return null if no file is found
+ }
+
+ /**
+ * Adds or updates a cache entry.
+ *
+ * @param {string} organization - The organization name.
+ * @param {string} modelName - The model name.
+ * @param {string} modelVersion - The model version.
+ * @param {string} file - The file name.
+ * @param {ArrayBuffer} arrayBuffer - The data to cache.
+ * @param {object} [headers] - The headers for the file.
+ * @returns {Promise<void>}
+ */
+ async put(
+ organization,
+ modelName,
+ modelVersion,
+ file,
+ arrayBuffer,
+ headers = {}
+ ) {
+ const cacheKey = `${organization}/${modelName}/${modelVersion}/${file}`;
+ const newSize = this.totalSize + arrayBuffer.byteLength;
+ if (newSize > this.#maxSize) {
+ throw new Error("Exceeding total cache size limit of 1GB");
+ }
+
+ const headersKey = `${organization}/${modelName}/${modelVersion}`;
+ const data = { id: cacheKey, data: arrayBuffer };
+
+ // Store the file data
+ await this.#updateData(this.fileStoreName, data);
+
+ // Update headers store - whith defaults for ETag and Content-Type
+ headers = headers || {};
+ headers["Content-Type"] =
+ headers["Content-Type"] ?? "application/octet-stream";
+ headers.ETag = headers.ETag ?? NO_ETAG;
+
+ // filter out any keys that are not allowed
+ headers = Object.keys(headers)
+ .filter(key => ALLOWED_HEADERS_KEYS.includes(key))
+ .reduce((obj, key) => {
+ obj[key] = headers[key];
+ return obj;
+ }, {});
+
+ const headersStore = (await this.#getData(
+ this.headersStoreName,
+ headersKey
+ )) || {
+ id: headersKey,
+ files: {},
+ };
+ headersStore.files[cacheKey] = headers;
+ await this.#updateData(this.headersStoreName, headersStore);
+
+ // Update size
+ await this.#updateTotalSize(arrayBuffer.byteLength);
+ }
+
+ /**
+ * Updates the total size of the cache.
+ *
+ * @param {number} sizeToAdd - The size to add to the total.
+ * @returns {Promise<void>}
+ */
+ async #updateTotalSize(sizeToAdd) {
+ this.totalSize += sizeToAdd;
+ await this.#updateData(this.headersStoreName, {
+ id: "totalSize",
+ size: this.totalSize,
+ });
+ }
+ /**
+ * Deletes all data related to a specific model.
+ *
+ * @param {string} organization - The organization name.
+ * @param {string} modelName - The model name.
+ * @param {string} modelVersion - The model version.
+ * @returns {Promise<void>}
+ */
+ async deleteModel(organization, modelName, modelVersion) {
+ const headersKey = `${organization}/${modelName}/${modelVersion}`;
+ const headers = await this.#getData(this.headersStoreName, headersKey);
+ if (headers) {
+ for (const fileKey in headers.files) {
+ await this.#deleteData(this.fileStoreName, fileKey);
+ }
+ await this.#deleteData(this.headersStoreName, headersKey); // Remove headers entry after files are deleted
+ }
+ }
+
+ /**
+ * Lists all models stored in the cache.
+ *
+ * @returns {Promise<Array<string>>} An array of model identifiers.
+ */
+ async listModels() {
+ const models = [];
+ return new Promise((resolve, reject) => {
+ const transaction = this.db.transaction(
+ [this.headersStoreName],
+ "readonly"
+ );
+ const store = transaction.objectStore(this.headersStoreName);
+ const request = store.openCursor();
+ request.onerror = event => reject(event.target.error);
+ request.onsuccess = event => {
+ const cursor = event.target.result;
+ if (cursor) {
+ models.push(cursor.value.id); // Assuming id is the organization/modelName
+ cursor.continue();
+ } else {
+ resolve(models);
+ }
+ };
+ });
+ }
+}
+
+export class ModelHub {
+ constructor({ rootUrl, urlTemplate = DEFAULT_URL_TEMPLATE }) {
+ if (!allowedHub(rootUrl)) {
+ throw new Error(`Invalid model hub root url: ${rootUrl}`);
+ }
+ this.rootUrl = rootUrl;
+ this.cache = null;
+
+ // Ensures the URL template is well-formed and does not contain any invalid characters.
+ const pattern = /^(?:\$\{\w+\}|\w+)(?:\/(?:\$\{\w+\}|\w+))*$/;
+ // ^ $ Start and end of string
+ // (?:\$\{\w+\}|\w+) Match a ${placeholder} or alphanumeric characters
+ // (?:\/(?:\$\{\w+\}|\w+))* Zero or more groups of a forward slash followed by a ${placeholder} or alphanumeric characters
+ if (!pattern.test(urlTemplate)) {
+ throw new Error(`Invalid URL template: ${urlTemplate}`);
+ }
+ this.urlTemplate = urlTemplate;
+ }
+
+ async #initCache() {
+ if (this.cache) {
+ return;
+ }
+ this.cache = await IndexedDBCache.init();
+ }
+
+ /** Creates the file URL from the organization, model, and version.
+ *
+ * @param {string} organization
+ * @param {string} modelName
+ * @param {string} modelVersion
+ * @param {string} file
+ * @returns {string} The full URL
+ */
+ #fileUrl(organization, modelName, modelVersion, file) {
+ const baseUrl = new URL(this.rootUrl);
+ if (!baseUrl.pathname.endsWith("/")) {
+ baseUrl.pathname += "/";
+ }
+
+ // Replace placeholders in the URL template with the provided data.
+ // If some keys are missing in the data object, the placeholder is left as is.
+ // If the placeholder is not found in the data object, it is left as is.
+ const data = {
+ organization,
+ modelName,
+ modelVersion,
+ file,
+ };
+ const path = this.urlTemplate.replace(
+ /\$\{(\w+)\}/g,
+ (match, key) => data[key] || match
+ );
+ const fullPath = `${baseUrl.pathname}${
+ path.startsWith("/") ? path.slice(1) : path
+ }`;
+
+ const urlObject = new URL(fullPath, baseUrl.origin);
+ urlObject.searchParams.append("download", "true");
+ return urlObject.toString();
+ }
+
+ /** Checks the organization, model, and version inputs.
+ *
+ * @param { string } organization
+ * @param { string } modelName
+ * @param { string } modelVersion
+ * @param { string } file
+ * @returns { Error } The error instance(can be null)
+ */
+ #checkInput(organization, modelName, modelVersion, file) {
+ // Ensures string consists only of letters, digits, and hyphens without starting/ending
+ // with a hyphen or containing consecutive hyphens.
+ //
+ // ^ $ Start and end of string
+ // (?!-) (?<!-) Negative lookahead/behind for not starting or ending with hyphen
+ // (?!.*--) Negative lookahead for not containing consecutive hyphens
+ // [A-Za-z0-9-]+ Alphanum characters or hyphens, one or more
+ const orgRegex = /^(?!-)(?!.*--)[A-Za-z0-9-]+(?<!-)$/;
+
+ // Matches strings containing letters, digits, hyphens, underscores, or periods.
+ // ^ $ Start and end of string
+ // [A-Za-z0-9-_.]+ Alphanum characters, hyphens, underscores, or periods, one or more times
+ const modelRegex = /^[A-Za-z0-9-_.]+$/;
+
+ // Matches strings consisting of alphanumeric characters, hyphens, or periods.
+ //
+ // ^ $ Start and end of string
+ // [A-Za-z0-9-.]+ Alphanum characters, hyphens, or periods, one or more times
+ const versionRegex = /^[A-Za-z0-9-.]+$/;
+
+ // Matches filenames with subdirectories, starting with alphanumeric or underscore,
+ // and optionally ending with a dot followed by a 2-4 letter extension.
+ //
+ // ^ $ Start and end of string
+ // (?:\/)? Optional leading slash (for absolute paths or root directory)
+ // (?!\/) Negative lookahead for not starting with a slash
+ // [A-Za-z0-9-_]+ First directory or filename
+ // (?: Begin non-capturing group for additional directories or file
+ // \/ Directory separator
+ // [A-Za-z0-9-_]+ Directory or file name
+ // )* Zero or more times
+ // (?:[.][A-Za-z]{2,4})? Optional non-capturing group for file extension
+ const fileRegex =
+ /^(?:\/)?(?!\/)[A-Za-z0-9-_]+(?:\/[A-Za-z0-9-_]+)*(?:[.][A-Za-z]{2,4})?$/;
+
+ if (!orgRegex.test(organization) || !isNaN(parseInt(organization))) {
+ return new Error(`Invalid organization name ${organization}`);
+ }
+
+ if (!modelRegex.test(modelName)) {
+ return new Error("Invalid model name.");
+ }
+
+ if (
+ !versionRegex.test(modelVersion) ||
+ modelVersion.includes(" ") ||
+ /[\^$]/.test(modelVersion)
+ ) {
+ return new Error("Invalid version identifier.");
+ }
+
+ if (!fileRegex.test(file)) {
+ return new Error("Invalid file name");
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the ETag value given an URL
+ *
+ * @param {string} url
+ * @param {number} timeout in ms. Default is 1000
+ * @returns {Promise<string>} ETag (can be null)
+ */
+ async #getETag(url, timeout = 1000) {
+ const controller = new AbortController();
+ const id = lazy.setTimeout(() => controller.abort(), timeout);
+
+ try {
+ const headResponse = await fetch(url, {
+ method: "HEAD",
+ signal: controller.signal,
+ });
+ const currentEtag = headResponse.headers.get("ETag");
+ return currentEtag;
+ } catch (error) {
+ lazy.console.warn("An error occurred when calling HEAD:", error);
+ return null;
+ } finally {
+ lazy.clearTimeout(id);
+ }
+ }
+
+ /**
+ * Given an organization, model, and version, fetch a model file in the hub as a Response.
+ *
+ * @param {object} config
+ * @param {string} config.organization
+ * @param {string} config.modelName
+ * @param {string} config.modelVersion
+ * @param {string} config.file
+ * @returns {Promise<Response>} The file content
+ */
+ async getModelFileAsResponse({
+ organization,
+ modelName,
+ modelVersion,
+ file,
+ }) {
+ const [blob, headers] = await this.getModelFileAsBlob({
+ organization,
+ modelName,
+ modelVersion,
+ file,
+ });
+ return new Response(blob, { headers });
+ }
+
+ /**
+ * Given an organization, model, and version, fetch a model file in the hub as an ArrayBuffer.
+ *
+ * @param {object} config
+ * @param {string} config.organization
+ * @param {string} config.modelName
+ * @param {string} config.modelVersion
+ * @param {string} config.file
+ * @returns {Promise<[ArrayBuffer, headers]>} The file content
+ */
+ async getModelFileAsArrayBuffer({
+ organization,
+ modelName,
+ modelVersion,
+ file,
+ }) {
+ const [blob, headers] = await this.getModelFileAsBlob({
+ organization,
+ modelName,
+ modelVersion,
+ file,
+ });
+ return [await blob.arrayBuffer(), headers];
+ }
+
+ /**
+ * Given an organization, model, and version, fetch a model file in the hub as blob.
+ *
+ * @param {object} config
+ * @param {string} config.organization
+ * @param {string} config.modelName
+ * @param {string} config.modelVersion
+ * @param {string} config.file
+ * @returns {Promise<[Blob, object]>} The file content
+ */
+ async getModelFileAsBlob({ organization, modelName, modelVersion, file }) {
+ // Make sure inputs are clean. We don't sanitize them but throw an exception
+ let checkError = this.#checkInput(
+ organization,
+ modelName,
+ modelVersion,
+ file
+ );
+ if (checkError) {
+ throw checkError;
+ }
+
+ const url = this.#fileUrl(organization, modelName, modelVersion, file);
+ lazy.console.debug(`Getting model file from ${url}`);
+
+ await this.#initCache();
+
+ // this can be null if no ETag was found or there were a network error
+ const hubETag = await this.#getETag(url);
+
+ lazy.console.debug(
+ `Checking the cache for ${organization}/${modelName}/${modelVersion}/${file}`
+ );
+
+ // storage lookup
+ const cachedHeaders = await this.cache.getHeaders(
+ organization,
+ modelName,
+ modelVersion,
+ file
+ );
+ const cachedEtag = cachedHeaders ? cachedHeaders.ETag : null;
+
+ // If we have something in store, and the hub ETag is null or it matches the cached ETag, return the cached response
+ if (cachedEtag !== null && (hubETag === null || cachedEtag === hubETag)) {
+ lazy.console.debug(`Cache Hit`);
+ return await this.cache.getFile(
+ organization,
+ modelName,
+ modelVersion,
+ file
+ );
+ }
+
+ lazy.console.debug(`Fetching ${url}`);
+ try {
+ const response = await fetch(url);
+ if (response.ok) {
+ const clone = response.clone();
+ const headers = {
+ // We don't store the boundary or the charset, just the content type,
+ // so we drop what's after the semicolon.
+ "Content-Type": response.headers.get("Content-Type").split(";")[0],
+ ETag: hubETag,
+ };
+
+ await this.cache.put(
+ organization,
+ modelName,
+ modelVersion,
+ file,
+ await clone.blob(),
+ headers
+ );
+ return [await response.blob(), headers];
+ }
+ } catch (error) {
+ lazy.console.error(`Failed to fetch ${url}:`, error);
+ }
+
+ throw new Error(`Failed to fetch the model file: ${url}`);
+ }
+}
diff --git a/toolkit/components/ml/jar.mn b/toolkit/components/ml/jar.mn
index 56bfb0d469..c9c82e0b26 100644
--- a/toolkit/components/ml/jar.mn
+++ b/toolkit/components/ml/jar.mn
@@ -6,4 +6,5 @@ toolkit.jar:
content/global/ml/EngineProcess.sys.mjs (content/EngineProcess.sys.mjs)
content/global/ml/MLEngine.worker.mjs (content/MLEngine.worker.mjs)
content/global/ml/MLEngine.html (content/MLEngine.html)
+ content/global/ml/ModelHub.sys.mjs (content/ModelHub.sys.mjs)
content/global/ml/SummarizerModel.sys.mjs (content/SummarizerModel.sys.mjs)
diff --git a/toolkit/components/ml/tests/browser/browser.toml b/toolkit/components/ml/tests/browser/browser.toml
index 9ccda0beaa..57637c8bda 100644
--- a/toolkit/components/ml/tests/browser/browser.toml
+++ b/toolkit/components/ml/tests/browser/browser.toml
@@ -1,5 +1,9 @@
[DEFAULT]
support-files = [
"head.js",
+ "data/**/*.*"
]
+
+["browser_ml_cache.js"]
+
["browser_ml_engine.js"]
diff --git a/toolkit/components/ml/tests/browser/browser_ml_cache.js b/toolkit/components/ml/tests/browser/browser_ml_cache.js
new file mode 100644
index 0000000000..d8725368bd
--- /dev/null
+++ b/toolkit/components/ml/tests/browser/browser_ml_cache.js
@@ -0,0 +1,361 @@
+/* Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const { sinon } = ChromeUtils.importESModule(
+ "resource://testing-common/Sinon.sys.mjs"
+);
+
+// Root URL of the fake hub, see the `data` dir in the tests.
+const FAKE_HUB =
+ "chrome://mochitests/content/browser/toolkit/components/ml/tests/browser/data";
+
+const FAKE_MODEL_ARGS = {
+ organization: "acme",
+ modelName: "bert",
+ modelVersion: "main",
+ file: "config.json",
+};
+
+const FAKE_ONNX_MODEL_ARGS = {
+ organization: "acme",
+ modelName: "bert",
+ modelVersion: "main",
+ file: "onnx/config.json",
+};
+
+const badHubs = [
+ "https://my.cool.hub",
+ "https://sub.localhost/myhub", // Subdomain of allowed domain
+ "https://model-hub.mozilla.org.evil.com", // Manipulating path to mimic domain
+ "httpsz://localhost/myhub", // Similar-looking scheme
+ "https://localhost.", // Trailing dot in domain
+ "resource://user@localhost", // User info in URL
+ "ftp://localhost/myhub", // Disallowed scheme with allowed host
+ "https://model-hub.mozilla.org.hack", // Domain that contains allowed domain
+];
+
+add_task(async function test_bad_hubs() {
+ for (const badHub of badHubs) {
+ Assert.throws(
+ () => new ModelHub({ rootUrl: badHub }),
+ new RegExp(`Error: Invalid model hub root url: ${badHub}`),
+ `Should throw with ${badHub}`
+ );
+ }
+});
+
+let goodHubs = [
+ "https:///localhost/myhub", // Triple slashes, see https://stackoverflow.com/a/22775589
+ "https://localhost:8080/myhub",
+ "http://localhost/myhub",
+ "https://model-hub.mozilla.org",
+ "chrome://gre/somewhere/in/the/code/base",
+];
+
+add_task(async function test_allowed_hub() {
+ goodHubs.forEach(url => new ModelHub({ rootUrl: url }));
+});
+
+const badInputs = [
+ [
+ {
+ organization: "ac me",
+ modelName: "bert",
+ modelVersion: "main",
+ file: "config.json",
+ },
+ "Org can only contain letters, numbers, and hyphens",
+ ],
+ [
+ {
+ organization: "1111",
+ modelName: "bert",
+ modelVersion: "main",
+ file: "config.json",
+ },
+ "Org cannot contain only numbers",
+ ],
+ [
+ {
+ organization: "-acme",
+ modelName: "bert",
+ modelVersion: "main",
+ file: "config.json",
+ },
+ "Org start or end with a hyphen, or use consecutive hyphens",
+ ],
+ [
+ {
+ organization: "a-c-m-e",
+ modelName: "#bert",
+ modelVersion: "main",
+ file: "config.json",
+ },
+ "Models can only contain letters, numbers, and hyphens, underscord, periods",
+ ],
+ [
+ {
+ organization: "a-c-m-e",
+ modelName: "b$ert",
+ modelVersion: "main",
+ file: "config.json",
+ },
+ "Models cannot contain spaces or control characters",
+ ],
+ [
+ {
+ organization: "a-c-m-e",
+ modelName: "b$ert",
+ modelVersion: "main",
+ file: ".filename",
+ },
+ "File",
+ ],
+];
+
+add_task(async function test_bad_inputs() {
+ const hub = new ModelHub({ rootUrl: FAKE_HUB });
+
+ for (const badInput of badInputs) {
+ const params = badInput[0];
+ const errorMsg = badInput[1];
+ try {
+ await hub.getModelFileAsArrayBuffer(params);
+ } catch (error) {
+ continue;
+ }
+ throw new Error(errorMsg);
+ }
+});
+
+add_task(async function test_getting_file() {
+ const hub = new ModelHub({ rootUrl: FAKE_HUB });
+
+ let [array, headers] = await hub.getModelFileAsArrayBuffer(FAKE_MODEL_ARGS);
+
+ Assert.equal(headers["Content-Type"], "application/json");
+
+ // check the content of the file.
+ let jsonData = JSON.parse(
+ String.fromCharCode.apply(null, new Uint8Array(array))
+ );
+
+ Assert.equal(jsonData.hidden_size, 768);
+});
+
+add_task(async function test_getting_file_in_subdir() {
+ const hub = new ModelHub({ rootUrl: FAKE_HUB });
+
+ let [array, metadata] = await hub.getModelFileAsArrayBuffer(
+ FAKE_ONNX_MODEL_ARGS
+ );
+
+ Assert.equal(metadata["Content-Type"], "application/json");
+
+ // check the content of the file.
+ let jsonData = JSON.parse(
+ String.fromCharCode.apply(null, new Uint8Array(array))
+ );
+
+ Assert.equal(jsonData.hidden_size, 768);
+});
+
+add_task(async function test_getting_file_custom_path() {
+ const hub = new ModelHub({
+ rootUrl: FAKE_HUB,
+ urlTemplate: "${organization}/${modelName}/resolve/${modelVersion}/${file}",
+ });
+
+ let res = await hub.getModelFileAsArrayBuffer(FAKE_MODEL_ARGS);
+
+ Assert.equal(res[1]["Content-Type"], "application/json");
+});
+
+add_task(async function test_getting_file_custom_path_rogue() {
+ const urlTemplate =
+ "${organization}/${modelName}/resolve/${modelVersion}/${file}?some_id=bedqwdw";
+ Assert.throws(
+ () => new ModelHub({ rootUrl: FAKE_HUB, urlTemplate }),
+ /Invalid URL template/,
+ `Should throw with ${urlTemplate}`
+ );
+});
+
+add_task(async function test_getting_file_as_response() {
+ const hub = new ModelHub({ rootUrl: FAKE_HUB });
+
+ let response = await hub.getModelFileAsResponse(FAKE_MODEL_ARGS);
+
+ // check the content of the file.
+ let jsonData = await response.json();
+ Assert.equal(jsonData.hidden_size, 768);
+});
+
+add_task(async function test_getting_file_from_cache() {
+ const hub = new ModelHub({ rootUrl: FAKE_HUB });
+ let array = await hub.getModelFileAsArrayBuffer(FAKE_MODEL_ARGS);
+
+ // stub to verify that the data was retrieved from IndexDB
+ let matchMethod = hub.cache._testGetData;
+
+ sinon.stub(hub.cache, "_testGetData").callsFake(function () {
+ return matchMethod.apply(this, arguments).then(result => {
+ Assert.notEqual(result, null);
+ return result;
+ });
+ });
+
+ // exercises the cache
+ let array2 = await hub.getModelFileAsArrayBuffer(FAKE_MODEL_ARGS);
+ hub.cache._testGetData.restore();
+
+ Assert.deepEqual(array, array2);
+});
+
+// IndexedDB tests
+
+/**
+ * Helper function to initialize the cache
+ */
+async function initializeCache() {
+ const randomSuffix = Math.floor(Math.random() * 10000);
+ return await IndexedDBCache.init(`modelFiles-${randomSuffix}`);
+}
+
+/**
+ * Helper function to delete the cache database
+ */
+async function deleteCache(cache) {
+ await cache.dispose();
+ indexedDB.deleteDatabase(cache.dbName);
+}
+
+/**
+ * Test the initialization and creation of the IndexedDBCache instance.
+ */
+add_task(async function test_Init() {
+ const cache = await initializeCache();
+ Assert.ok(
+ cache instanceof IndexedDBCache,
+ "The cache instance should be created successfully."
+ );
+ Assert.ok(
+ IDBDatabase.isInstance(cache.db),
+ `The cache should have an IDBDatabase instance. Found ${cache.db}`
+ );
+ await deleteCache(cache);
+});
+
+/**
+ * Test adding data to the cache and retrieving it.
+ */
+add_task(async function test_PutAndGet() {
+ const cache = await initializeCache();
+ const testData = new ArrayBuffer(8); // Example data
+ await cache.put("org", "model", "v1", "file.txt", testData, {
+ ETag: "ETAG123",
+ });
+
+ const [retrievedData, headers] = await cache.getFile(
+ "org",
+ "model",
+ "v1",
+ "file.txt"
+ );
+ Assert.deepEqual(
+ retrievedData,
+ testData,
+ "The retrieved data should match the stored data."
+ );
+ Assert.equal(
+ headers.ETag,
+ "ETAG123",
+ "The retrieved ETag should match the stored ETag."
+ );
+
+ await deleteCache(cache);
+});
+
+/**
+ * Test retrieving the headers for a cache entry.
+ */
+add_task(async function test_GetHeaders() {
+ const cache = await initializeCache();
+ const testData = new ArrayBuffer(8);
+ const headers = {
+ ETag: "ETAG123",
+ status: 200,
+ extra: "extra",
+ };
+
+ await cache.put("org", "model", "v1", "file.txt", testData, headers);
+
+ const storedHeaders = await cache.getHeaders(
+ "org",
+ "model",
+ "v1",
+ "file.txt"
+ );
+
+ // The `extra` field should be removed from the stored headers because
+ // it's not part of the allowed keys.
+ // The content-type one is added when not present
+ Assert.deepEqual(
+ {
+ ETag: "ETAG123",
+ status: 200,
+ "Content-Type": "application/octet-stream",
+ },
+ storedHeaders,
+ "The retrieved headers should match the stored headers."
+ );
+ await deleteCache(cache);
+});
+
+/**
+ * Test listing all models stored in the cache.
+ */
+add_task(async function test_ListModels() {
+ const cache = await initializeCache();
+ await cache.put(
+ "org1",
+ "modelA",
+ "v1",
+ "file1.txt",
+ new ArrayBuffer(8),
+ null
+ );
+ await cache.put(
+ "org2",
+ "modelB",
+ "v1",
+ "file2.txt",
+ new ArrayBuffer(8),
+ null
+ );
+
+ const models = await cache.listModels();
+ Assert.ok(
+ models.includes("org1/modelA/v1") && models.includes("org2/modelB/v1"),
+ "All models should be listed."
+ );
+ await deleteCache(cache);
+});
+
+/**
+ * Test deleting a model and its data from the cache.
+ */
+add_task(async function test_DeleteModel() {
+ const cache = await initializeCache();
+ await cache.put("org", "model", "v1", "file.txt", new ArrayBuffer(8), null);
+ await cache.deleteModel("org", "model", "v1");
+
+ const dataAfterDelete = await cache.getFile("org", "model", "v1", "file.txt");
+ Assert.equal(
+ dataAfterDelete,
+ null,
+ "The data for the deleted model should not exist."
+ );
+ await deleteCache(cache);
+});
diff --git a/toolkit/components/ml/tests/browser/data/README.md b/toolkit/components/ml/tests/browser/data/README.md
new file mode 100644
index 0000000000..d826cf7ee6
--- /dev/null
+++ b/toolkit/components/ml/tests/browser/data/README.md
@@ -0,0 +1,5 @@
+# fake hub
+
+This directory is a fake hub that is served via chrome://global/content/ml/tests.
+
+All files in this directory are included with a wildcard in the component `jar.md`.
diff --git a/toolkit/components/ml/tests/browser/data/acme/bert/resolve/main/config.json b/toolkit/components/ml/tests/browser/data/acme/bert/resolve/main/config.json
new file mode 100644
index 0000000000..50dbb760bb
--- /dev/null
+++ b/toolkit/components/ml/tests/browser/data/acme/bert/resolve/main/config.json
@@ -0,0 +1,21 @@
+{
+ "architectures": ["BertForMaskedLM"],
+ "attention_probs_dropout_prob": 0.1,
+ "gradient_checkpointing": false,
+ "hidden_act": "gelu",
+ "hidden_dropout_prob": 0.1,
+ "hidden_size": 768,
+ "initializer_range": 0.02,
+ "intermediate_size": 3072,
+ "layer_norm_eps": 1e-12,
+ "max_position_embeddings": 512,
+ "model_type": "bert",
+ "num_attention_heads": 12,
+ "num_hidden_layers": 12,
+ "pad_token_id": 0,
+ "position_embedding_type": "absolute",
+ "transformers_version": "4.6.0.dev0",
+ "type_vocab_size": 2,
+ "use_cache": true,
+ "vocab_size": 30522
+}
diff --git a/toolkit/components/ml/tests/browser/data/acme/bert/resolve/main/onnx/config.json b/toolkit/components/ml/tests/browser/data/acme/bert/resolve/main/onnx/config.json
new file mode 100644
index 0000000000..50dbb760bb
--- /dev/null
+++ b/toolkit/components/ml/tests/browser/data/acme/bert/resolve/main/onnx/config.json
@@ -0,0 +1,21 @@
+{
+ "architectures": ["BertForMaskedLM"],
+ "attention_probs_dropout_prob": 0.1,
+ "gradient_checkpointing": false,
+ "hidden_act": "gelu",
+ "hidden_dropout_prob": 0.1,
+ "hidden_size": 768,
+ "initializer_range": 0.02,
+ "intermediate_size": 3072,
+ "layer_norm_eps": 1e-12,
+ "max_position_embeddings": 512,
+ "model_type": "bert",
+ "num_attention_heads": 12,
+ "num_hidden_layers": 12,
+ "pad_token_id": 0,
+ "position_embedding_type": "absolute",
+ "transformers_version": "4.6.0.dev0",
+ "type_vocab_size": 2,
+ "use_cache": true,
+ "vocab_size": 30522
+}
diff --git a/toolkit/components/ml/tests/browser/head.js b/toolkit/components/ml/tests/browser/head.js
index 99d27ce18a..9fc20c0e84 100644
--- a/toolkit/components/ml/tests/browser/head.js
+++ b/toolkit/components/ml/tests/browser/head.js
@@ -19,6 +19,10 @@ const { MLEngineParent } = ChromeUtils.importESModule(
"resource://gre/actors/MLEngineParent.sys.mjs"
);
+const { ModelHub, IndexedDBCache } = ChromeUtils.importESModule(
+ "chrome://global/content/ml/ModelHub.sys.mjs"
+);
+
// This test suite shares some utility functions with translations as they work in a very
// similar fashion. Eventually, the plan is to unify these two components.
Services.scriptloader.loadSubScript(
diff --git a/toolkit/components/moz.build b/toolkit/components/moz.build
index d574f1305b..c1785e21e5 100644
--- a/toolkit/components/moz.build
+++ b/toolkit/components/moz.build
@@ -30,6 +30,7 @@ DIRS += [
"commandlines",
"contentanalysis",
"contentprefs",
+ "contentrelevancy",
"contextualidentity",
"crashes",
"crashmonitor",
@@ -137,10 +138,6 @@ DIRS += ["nimbus"]
if CONFIG["MOZ_BACKGROUNDTASKS"]:
DIRS += ["backgroundtasks"]
-# This is only packaged for browser since corrupt JAR and XPI files tend to be a desktop-OS problem.
-if CONFIG["MOZ_BUILD_APP"] == "browser":
- DIRS += ["corroborator"]
-
if CONFIG["MOZ_UNIFFI_FIXTURES"]:
DIRS += ["uniffi-bindgen-gecko-js/fixtures"]
diff --git a/toolkit/components/narrate/NarrateControls.sys.mjs b/toolkit/components/narrate/NarrateControls.sys.mjs
index ed2f8ff124..19551fa59f 100644
--- a/toolkit/components/narrate/NarrateControls.sys.mjs
+++ b/toolkit/components/narrate/NarrateControls.sys.mjs
@@ -38,7 +38,7 @@ export function NarrateControls(win, languagePromise) {
toggleButton.dataset.telemetryId = "reader-listen";
let tip = win.document.createElement("span");
let shortcutNarrateKey = gStrings.GetStringFromName("narrate-key-shortcut");
- let labelText = gStrings.formatStringFromName("listen-label", [
+ let labelText = gStrings.formatStringFromName("read-aloud-label", [
shortcutNarrateKey,
]);
tip.textContent = labelText;
@@ -65,10 +65,6 @@ export function NarrateControls(win, languagePromise) {
narrateVoices.className = "narrate-row narrate-voices";
dropdownList.appendChild(narrateVoices);
- let dropdownArrow = win.document.createElement("div");
- dropdownArrow.className = "dropdown-arrow";
- dropdownList.appendChild(dropdownArrow);
-
let narrateSkipPrevious = win.document.createElement("button");
narrateSkipPrevious.className = "narrate-skip-previous";
narrateSkipPrevious.disabled = true;
diff --git a/toolkit/components/nimbus/FeatureManifest.yaml b/toolkit/components/nimbus/FeatureManifest.yaml
index 7612ef2dc2..c540e0e387 100644
--- a/toolkit/components/nimbus/FeatureManifest.yaml
+++ b/toolkit/components/nimbus/FeatureManifest.yaml
@@ -136,6 +136,14 @@ search:
branch: default
pref: browser.urlbar.trending.maxResultsNoSearchMode
description: The maximum number of trending results mode outside search mode.
+ newSearchConfigEnabled:
+ type: boolean
+ setPref:
+ branch: user
+ pref: browser.search.newSearchConfig.enabled
+ description: >-
+ Whether search-config-v2 is enabled for the user. This will only take
+ effect when the user restarts.
# `searchConfiguration` is for search experiment features for items that require
# isEarlyStartup to be true. These items may require a reload of the search
@@ -256,6 +264,13 @@ urlbar:
will be able to click the "Show less frequently" command for Pocket
suggestions. If undefined or zero, the user will be able to click the
command without any limit.
+ potentialExposureKeywords:
+ type: json
+ fallbackPref: browser.urlbar.potentialExposureKeywords
+ description: >-
+ An array of keyword strings that will trigger the
+ `urlbar-potential-exposure` ping when the user types one during a urlbar
+ session.
quickSuggestAllowPositionInSuggestions:
type: boolean
fallbackPref: browser.urlbar.quicksuggest.allowPositionInSuggestions
@@ -825,6 +840,13 @@ pocketNewtab:
browser.newtabpage.activity-stream.discoverystream.sendToPocket.enabled
description: >-
Decides what to do when a logged out user click "Save to Pocket" from a Pocket card.
+ wallpapers:
+ type: boolean
+ setPref:
+ branch: user
+ pref: browser.newtabpage.activity-stream.newtabWallpapers.enabled
+ description: >-
+ Turns on and off wallpaper support.
recsPersonalized:
type: boolean
fallbackPref: >-
@@ -926,27 +948,36 @@ saveToPocket:
description: The save to Pocket feature
owner: sdowne@getpocket.com
hasExposure: false
- isEarlyStartup: true
variables:
emailButton:
type: boolean
- fallbackPref: extensions.pocket.refresh.emailButton.enabled
+ setPref:
+ branch: user
+ pref: extensions.pocket.refresh.emailButton.enabled
description: Just for the new Pocket panels, enables the email signup button.
hideRecentSaves:
type: boolean
- fallbackPref: extensions.pocket.refresh.hideRecentSaves.enabled
+ setPref:
+ branch: user
+ pref: extensions.pocket.refresh.hideRecentSaves.enabled
description: Hides the recently saved section in the home panel.
bffRecentSaves:
type: boolean
- fallbackPref: "extensions.pocket.bffRecentSaves"
+ setPref:
+ branch: user
+ pref: extensions.pocket.bffRecentSaves
description: Use the new BFF Proxy Service instead of the legacy Pocket Service for Recent Saves
bffApi:
type: string
- fallbackPref: "extensions.pocket.bffApi"
+ setPref:
+ branch: user
+ pref: extensions.pocket.bffApi
description: BFF Proxy Service domain
oAuthConsumerKeyBff:
type: string
- fallbackPref: "extensions.pocket.oAuthConsumerKeyBff"
+ setPref:
+ branch: user
+ pref: extensions.pocket.oAuthConsumerKeyBff
description: BFF Proxy Service OAuth Consumer Key
password-autocomplete:
@@ -1378,9 +1409,6 @@ gleanInternalSdk:
type: int
description: >-
Maximum number of pings that can be sent in a 60 second interval
- enableEventTimestamps:
- type: "boolean"
- description: "Enables precise event timestamps for Glean events"
majorRelease2022:
description: Major Release 2022
@@ -1543,6 +1571,12 @@ dohPrefs:
setPref:
branch: default
pref: "network.trr_ui.show_fallback_warning_option"
+ nativeHTTPSRecords:
+ description: Whether we can perform native DNS HTTPS lookups
+ type: boolean
+ setPref:
+ branch: default
+ pref: "network.dns.native_https_query"
dooh:
description: "DNS over Oblivious HTTP"
@@ -2295,7 +2329,6 @@ updatePrompt:
exposureDescription: >-
Exposure is sent at most once per browsing session when an update
notification prompt is displayed.
- isEarlyStartup: true
variables:
showReleaseNotesLink:
type: boolean
@@ -2647,3 +2680,69 @@ httpsFirst:
setPref:
branch: default
pref: dom.security.https_only_fire_http_request_background_timer_ms
+
+contentRelevancy:
+ description: >-
+ A feature for interest-based content relevance ranking and personalization
+ for Firefox.
+ owner: disco-team@mozilla.com
+ hasExposure: false
+ variables:
+ enabled:
+ description: Enable this feature
+ type: boolean
+ fallbackPref: toolkit.contentRelevancy.enabled
+ maxInputUrls:
+ description: The maximum number of input URLs for interest classification
+ type: int
+ minInputUrls:
+ description: The minimal number of input URLs for interest classification
+ type: int
+ timerInterval:
+ description: >-
+ The interval (in seconds) of the background update timer for the content
+ relevancy manager
+ type: int
+ setPref:
+ branch: user
+ pref: toolkit.contentRelevancy.timerInterval
+
+tabPreview:
+ description: Prefs to control Tab Previews
+ owner: dwalker@mozilla.com
+ hasExposure: false
+ variables:
+ tabPreviewsEnabled:
+ type: boolean
+ setPref:
+ branch: default
+ pref: browser.tabs.cardPreview.enabled
+ description: >-
+ When true, users will see the new card preview when hovering a tab, instead of the standard tooltip
+
+backupService:
+ description: Prefs to control the profile backup service
+ owner: mconley@mozilla.com
+ hasExposure: false
+ variables:
+ enabled:
+ type: boolean
+ setPref:
+ branch: default
+ pref: browser.backup.enabled
+ description: >-
+ When true, the profile backup service will be initialized soon after
+ startup.
+
+pqcrypto:
+ description: Prefs that control the use of post-quantum cryptography.
+ owner: jschanck@mozilla.com
+ hasExposure: false
+ variables:
+ tlsEnableXyber:
+ type: boolean
+ setPref:
+ branch: default
+ pref: security.tls.enable_kyber
+ description: >-
+ Whether to enable Xyber768 for TLS.
diff --git a/toolkit/components/nimbus/generate/generate_feature_manifest.py b/toolkit/components/nimbus/generate/generate_feature_manifest.py
index 14057493c5..55e681fdf8 100644
--- a/toolkit/components/nimbus/generate/generate_feature_manifest.py
+++ b/toolkit/components/nimbus/generate/generate_feature_manifest.py
@@ -31,11 +31,9 @@ ALLOWED_ISEARLYSTARTUP_FEATURE_IDS = {
"majorRelease2022",
"newtab",
"pocketNewtab",
- "saveToPocket",
"searchConfiguration",
"shellService",
"testFeature",
- "updatePrompt",
"upgradeDialog",
}
diff --git a/toolkit/components/nimbus/lib/ExperimentManager.sys.mjs b/toolkit/components/nimbus/lib/ExperimentManager.sys.mjs
index d0f313a2ae..8bd847b067 100644
--- a/toolkit/components/nimbus/lib/ExperimentManager.sys.mjs
+++ b/toolkit/components/nimbus/lib/ExperimentManager.sys.mjs
@@ -95,8 +95,8 @@ export class _ExperimentManager {
get studiesEnabled() {
return (
- Services.prefs.getBoolPref(UPLOAD_ENABLED_PREF) &&
- Services.prefs.getBoolPref(STUDIES_OPT_OUT_PREF)
+ Services.prefs.getBoolPref(UPLOAD_ENABLED_PREF, false) &&
+ Services.prefs.getBoolPref(STUDIES_OPT_OUT_PREF, false)
);
}
@@ -974,7 +974,7 @@ export class _ExperimentManager {
// need to check if we have another enrollment for the same feature.
const conflictingEnrollment = getConflictingEnrollment(featureId);
- for (const [variable, value] of Object.entries(featureValue)) {
+ for (let [variable, value] of Object.entries(featureValue)) {
const setPref = feature.getSetPref(variable);
if (setPref) {
@@ -1012,6 +1012,13 @@ export class _ExperimentManager {
// An experiment takes precedence if there is already a pref set.
if (!isRollout || !conflictingPref) {
+ if (
+ lazy.NimbusFeatures[featureId].manifest.variables[variable]
+ .type === "json"
+ ) {
+ value = JSON.stringify(value);
+ }
+
prefsToSet.push({
name: prefName,
value,
@@ -1250,7 +1257,13 @@ export class _ExperimentManager {
}
}
- const value = featuresById[featureId].value[variable];
+ let value = featuresById[featureId].value[variable];
+ if (
+ lazy.NimbusFeatures[featureId].manifest.variables[variable].type ===
+ "json"
+ ) {
+ value = JSON.stringify(value);
+ }
if (prefBranch !== "user") {
lazy.PrefUtils.setPref(name, value, { branch: prefBranch });
diff --git a/toolkit/components/nimbus/test/unit/test_ExperimentManager_prefs.js b/toolkit/components/nimbus/test/unit/test_ExperimentManager_prefs.js
index 7ae0e1b76f..f2f5a25836 100644
--- a/toolkit/components/nimbus/test/unit/test_ExperimentManager_prefs.js
+++ b/toolkit/components/nimbus/test/unit/test_ExperimentManager_prefs.js
@@ -3348,3 +3348,207 @@ add_task(async function test_nested_prefs_enroll_both() {
branch: DEFAULT,
});
});
+
+const TYPED_FEATURE = new ExperimentFeature("test-typed-prefs", {
+ description: "Test feature that sets each type of pref",
+ owner: "test@test.test",
+ hasExposure: false,
+ variables: {
+ string: {
+ type: "string",
+ description: "test string variable",
+ setPref: {
+ branch: "default",
+ pref: "nimbus.test-only.types.string",
+ },
+ },
+ int: {
+ type: "int",
+ description: "test int variable",
+ setPref: {
+ branch: "default",
+ pref: "nimbus.test-only.types.int",
+ },
+ },
+ boolean: {
+ type: "boolean",
+ description: "test boolean variable",
+ setPref: {
+ branch: "default",
+ pref: "nimbus.test-only.types.boolean",
+ },
+ },
+ json: {
+ type: "json",
+ description: "test json variable",
+ setPref: {
+ branch: "default",
+ pref: "nimbus.test-only.types.json",
+ },
+ },
+ },
+});
+
+add_task(async function test_setPref_types() {
+ const featureCleanup = ExperimentTestUtils.addTestFeatures(TYPED_FEATURE);
+
+ const store = ExperimentFakes.store();
+ const manager = ExperimentFakes.manager(store);
+
+ await manager.onStartup();
+ await assertEmptyStore(store);
+
+ const json = {
+ foo: "foo",
+ bar: 12345,
+ baz: true,
+ qux: null,
+ quux: ["corge"],
+ };
+
+ const experimentCleanup = await ExperimentFakes.enrollWithFeatureConfig(
+ {
+ featureId: TYPED_FEATURE.featureId,
+ value: {
+ string: "hello, world",
+ int: 12345,
+ boolean: true,
+ json,
+ },
+ },
+ { manager }
+ );
+
+ const defaultBranch = Services.prefs.getDefaultBranch(null);
+
+ Assert.equal(
+ defaultBranch.getPrefType("nimbus.test-only.types.string"),
+ Services.prefs.PREF_STRING
+ );
+ Assert.equal(
+ defaultBranch.getStringPref("nimbus.test-only.types.string"),
+ "hello, world"
+ );
+
+ Assert.equal(
+ defaultBranch.getPrefType("nimbus.test-only.types.int"),
+ Services.prefs.PREF_INT
+ );
+ Assert.equal(defaultBranch.getIntPref("nimbus.test-only.types.int"), 12345);
+
+ Assert.equal(
+ defaultBranch.getPrefType("nimbus.test-only.types.boolean"),
+ Services.prefs.PREF_BOOL
+ );
+ Assert.equal(
+ defaultBranch.getBoolPref("nimbus.test-only.types.boolean"),
+ true
+ );
+
+ Assert.equal(
+ defaultBranch.getPrefType("nimbus.test-only.types.json"),
+ Services.prefs.PREF_STRING
+ );
+
+ const jsonPrefValue = JSON.parse(
+ defaultBranch.getStringPref("nimbus.test-only.types.json")
+ );
+
+ Assert.deepEqual(json, jsonPrefValue);
+
+ await experimentCleanup();
+ featureCleanup();
+
+ await assertEmptyStore(store, { cleanup: true });
+});
+
+add_task(async function test_setPref_types_restore() {
+ const featureCleanup = ExperimentTestUtils.addTestFeatures(TYPED_FEATURE);
+
+ const json = {
+ foo: "foo",
+ bar: 12345,
+ baz: true,
+ qux: null,
+ quux: ["corge"],
+ };
+
+ {
+ const store = ExperimentFakes.store();
+ const manager = ExperimentFakes.manager(store);
+
+ await manager.onStartup();
+ await assertEmptyStore(store);
+
+ await ExperimentFakes.enrollWithFeatureConfig(
+ {
+ featureId: TYPED_FEATURE.featureId,
+ value: {
+ string: "hello, world",
+ int: 12345,
+ boolean: true,
+ json,
+ },
+ },
+ { manager }
+ );
+
+ store._store.saveSoon();
+ await store._store.finalize();
+
+ for (const varDef of Object.values(TYPED_FEATURE.manifest.variables)) {
+ Services.prefs.deleteBranch(varDef.setPref.pref);
+ }
+
+ removePrefObservers(manager);
+ assertNoObservers(manager);
+ }
+
+ const store = ExperimentFakes.store();
+ const manager = ExperimentFakes.manager(store);
+
+ await manager.onStartup();
+
+ const defaultBranch = Services.prefs.getDefaultBranch(null);
+ Assert.equal(
+ defaultBranch.getPrefType("nimbus.test-only.types.string"),
+ Services.prefs.PREF_STRING
+ );
+ Assert.equal(
+ defaultBranch.getStringPref("nimbus.test-only.types.string"),
+ "hello, world"
+ );
+
+ Assert.equal(
+ defaultBranch.getPrefType("nimbus.test-only.types.int"),
+ Services.prefs.PREF_INT
+ );
+ Assert.equal(defaultBranch.getIntPref("nimbus.test-only.types.int"), 12345);
+
+ Assert.equal(
+ defaultBranch.getPrefType("nimbus.test-only.types.boolean"),
+ Services.prefs.PREF_BOOL
+ );
+ Assert.equal(
+ defaultBranch.getBoolPref("nimbus.test-only.types.boolean"),
+ true
+ );
+
+ Assert.equal(
+ defaultBranch.getPrefType("nimbus.test-only.types.json"),
+ Services.prefs.PREF_STRING
+ );
+
+ const jsonPrefValue = JSON.parse(
+ defaultBranch.getStringPref("nimbus.test-only.types.json")
+ );
+
+ Assert.deepEqual(json, jsonPrefValue);
+
+ const enrollment = store.getExperimentForFeature(TYPED_FEATURE.featureId);
+ manager.unenroll(enrollment.slug);
+ store._deleteForTests(enrollment.slug);
+
+ await assertEmptyStore(store, { cleanup: true });
+ featureCleanup();
+});
diff --git a/toolkit/components/normandy/lib/RecipeRunner.sys.mjs b/toolkit/components/normandy/lib/RecipeRunner.sys.mjs
index f9578b37d2..087e4ed51e 100644
--- a/toolkit/components/normandy/lib/RecipeRunner.sys.mjs
+++ b/toolkit/components/normandy/lib/RecipeRunner.sys.mjs
@@ -197,7 +197,7 @@ export var RecipeRunner = {
},
checkPrefs() {
- if (!Services.prefs.getBoolPref(SHIELD_ENABLED_PREF)) {
+ if (!Services.prefs.getBoolPref(SHIELD_ENABLED_PREF, false)) {
log.debug(
`Disabling Shield because ${SHIELD_ENABLED_PREF} is set to false`
);
@@ -205,7 +205,7 @@ export var RecipeRunner = {
return;
}
- const apiUrl = Services.prefs.getCharPref(API_URL_PREF);
+ const apiUrl = Services.prefs.getCharPref(API_URL_PREF, "");
if (!apiUrl) {
log.warn(`Disabling Shield because ${API_URL_PREF} is not set.`);
this.disable();
@@ -287,7 +287,7 @@ export var RecipeRunner = {
// Run once every `runInterval` wall-clock seconds. This is managed by setting a "last ran"
// timestamp, and running if it is more than `runInterval` seconds ago. Even with very short
// intervals, the timer will only fire at most once every few minutes.
- const runInterval = Services.prefs.getIntPref(RUN_INTERVAL_PREF);
+ const runInterval = Services.prefs.getIntPref(RUN_INTERVAL_PREF, 21600); // 6h
lazy.timerManager.registerTimer(TIMER_NAME, () => this.run(), runInterval);
},
@@ -597,7 +597,7 @@ export var RecipeRunner = {
* executed in the browser console.
*/
async testRun(baseApiUrl) {
- const oldApiUrl = Services.prefs.getCharPref(API_URL_PREF);
+ const oldApiUrl = Services.prefs.getCharPref(API_URL_PREF, "");
Services.prefs.setCharPref(API_URL_PREF, baseApiUrl);
try {
diff --git a/toolkit/components/normandy/test/unit/test_NormandyApi.js b/toolkit/components/normandy/test/unit/test_NormandyApi.js
index 5b0ede1701..cd15aff965 100644
--- a/toolkit/components/normandy/test/unit/test_NormandyApi.js
+++ b/toolkit/components/normandy/test/unit/test_NormandyApi.js
@@ -195,7 +195,7 @@ decorate_task(
// response that sets a cookie.
// send a request, to store a cookie in the cookie store
- await fetch(serverUrl);
+ await fetch(serverUrl, { credentials: "same-origin" });
// A normal request should send that cookie
const cookieExpectedDeferred = Promise.withResolvers();
@@ -218,7 +218,7 @@ decorate_task(
cookieExpectedDeferred.resolve();
}
Services.obs.addObserver(cookieExpectedObserver, "http-on-modify-request");
- await fetch(serverUrl);
+ await fetch(serverUrl, { credentials: "same-origin" });
await cookieExpectedDeferred.promise;
// A request through the NormandyApi method should not send that cookie
diff --git a/toolkit/components/normandy/test/unit/test_RecipeRunner.js b/toolkit/components/normandy/test/unit/test_RecipeRunner.js
index 710ac4d507..cfc83714f1 100644
--- a/toolkit/components/normandy/test/unit/test_RecipeRunner.js
+++ b/toolkit/components/normandy/test/unit/test_RecipeRunner.js
@@ -12,6 +12,9 @@ const { RecipeRunner } = ChromeUtils.importESModule(
// Test that new build IDs trigger immediate recipe runs
add_task(async () => {
+ // This test assumes normandy is enabled.
+ Services.prefs.setBoolPref("app.normandy.enabled", true);
+
updateAppInfo({
appBuildID: "new-build-id",
lastAppBuildID: "old-build-id",
diff --git a/toolkit/components/passwordmgr/LoginAutoComplete.sys.mjs b/toolkit/components/passwordmgr/LoginAutoComplete.sys.mjs
index 411d9249db..187caf9d62 100644
--- a/toolkit/components/passwordmgr/LoginAutoComplete.sys.mjs
+++ b/toolkit/components/passwordmgr/LoginAutoComplete.sys.mjs
@@ -7,7 +7,10 @@
*/
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
-import { GenericAutocompleteItem } from "resource://gre/modules/FillHelpers.sys.mjs";
+import {
+ GenericAutocompleteItem,
+ sendFillRequestToParent,
+} from "resource://gre/modules/FillHelpers.sys.mjs";
const lazy = {};
@@ -487,7 +490,7 @@ export class LoginAutoComplete {
* @param {string} aSearchString The value typed in the field.
* @param {nsIAutoCompleteResult} aPreviousResult
* @param {HTMLInputElement} aElement
- * @param {nsIFormAutoCompleteObserver} aCallback
+ * @param {nsIFormFillCompleteObserver} aCallback
*/
startSearch(aSearchString, aPreviousResult, aElement, aCallback) {
let { isNullPrincipal } = aElement.nodePrincipal;
@@ -717,7 +720,6 @@ export class LoginAutoComplete {
let gAutoCompleteListener = {
added: false,
- fillRequestId: 0,
init() {
if (!this.added) {
@@ -729,45 +731,11 @@ let gAutoCompleteListener = {
async observe(subject, topic, data) {
switch (topic) {
case "autocomplete-will-enter-text": {
- await this.sendFillRequestToLoginManagerParent(subject, data);
+ if (subject && subject == lazy.formFillController.controller.input) {
+ await sendFillRequestToParent("LoginManager", subject, data);
+ }
break;
}
}
},
-
- async sendFillRequestToLoginManagerParent(input, comment) {
- if (!comment) {
- return;
- }
-
- if (input != lazy.formFillController.controller.input) {
- return;
- }
-
- const { fillMessageName, fillMessageData } = JSON.parse(comment ?? "{}");
- if (!fillMessageName) {
- return;
- }
-
- this.fillRequestId++;
- const fillRequestId = this.fillRequestId;
- const child = lazy.LoginManagerChild.forWindow(
- input.focusedInput.ownerGlobal
- );
- const value = await child.sendQuery(fillMessageName, fillMessageData ?? {});
-
- // skip fill if another fill operation started during await
- if (fillRequestId != this.fillRequestId) {
- return;
- }
-
- if (typeof value !== "string") {
- return;
- }
-
- // If LoginManagerParent returned a string to fill, we must do it here because
- // nsAutoCompleteController.cpp already finished it's work before we finished await.
- input.textValue = value;
- input.selectTextRange(value.length, value.length);
- },
};
diff --git a/toolkit/components/passwordmgr/LoginManagerAuthPrompter.sys.mjs b/toolkit/components/passwordmgr/LoginManagerAuthPrompter.sys.mjs
index f56ec80d5e..8cc501b79d 100644
--- a/toolkit/components/passwordmgr/LoginManagerAuthPrompter.sys.mjs
+++ b/toolkit/components/passwordmgr/LoginManagerAuthPrompter.sys.mjs
@@ -711,7 +711,7 @@ LoginManagerAuthPrompter.prototype = {
ok = await Services.prompt.asyncPromptAuth(
this._browser?.browsingContext,
- LoginManagerAuthPrompter.promptAuthModalType,
+ Ci.nsIPrompt.MODAL_TYPE_TAB,
aChannel,
aLevel,
aAuthInfo
@@ -1115,10 +1115,3 @@ ChromeUtils.defineLazyGetter(LoginManagerAuthPrompter.prototype, "log", () => {
let logger = lazy.LoginHelper.createLogger("LoginManagerAuthPrompter");
return logger.log.bind(logger);
});
-
-XPCOMUtils.defineLazyPreferenceGetter(
- LoginManagerAuthPrompter,
- "promptAuthModalType",
- "prompts.modalType.httpAuth",
- Services.prompt.MODAL_TYPE_WINDOW
-);
diff --git a/toolkit/components/passwordmgr/LoginManagerChild.sys.mjs b/toolkit/components/passwordmgr/LoginManagerChild.sys.mjs
index c89852b56c..66f45416f8 100644
--- a/toolkit/components/passwordmgr/LoginManagerChild.sys.mjs
+++ b/toolkit/components/passwordmgr/LoginManagerChild.sys.mjs
@@ -2435,15 +2435,7 @@ export class LoginManagerChild extends JSWindowActorChild {
if (isSubmission) {
// Notify `PasswordManager:onFormSubmit` as long as we detect submission event on a
// valid form with a password field.
- this.sendAsyncMessage(
- "PasswordManager:onFormSubmit",
- {},
- {
- fields,
- isSubmission,
- triggeredByFillingGenerated,
- }
- );
+ this.sendAsyncMessage("PasswordManager:onFormSubmit", {});
}
}
diff --git a/toolkit/components/passwordmgr/nsILoginAutoCompleteSearch.idl b/toolkit/components/passwordmgr/nsILoginAutoCompleteSearch.idl
index 6c0e94b445..586292d04d 100644
--- a/toolkit/components/passwordmgr/nsILoginAutoCompleteSearch.idl
+++ b/toolkit/components/passwordmgr/nsILoginAutoCompleteSearch.idl
@@ -5,7 +5,7 @@
#include "nsISupports.idl"
interface nsIAutoCompleteResult;
-interface nsIFormAutoCompleteObserver;
+interface nsIFormFillCompleteObserver;
webidl HTMLInputElement;
@@ -22,7 +22,7 @@ interface nsILoginAutoCompleteSearch : nsISupports {
void startSearch(in AString aSearchString,
in nsIAutoCompleteResult aPreviousResult,
in HTMLInputElement aElement,
- in nsIFormAutoCompleteObserver aListener);
+ in nsIFormFillCompleteObserver aListener);
/**
* Stop a previously-started search.
diff --git a/toolkit/components/passwordmgr/test/LoginTestUtils.sys.mjs b/toolkit/components/passwordmgr/test/LoginTestUtils.sys.mjs
index 94e48aac6a..c16a44735e 100644
--- a/toolkit/components/passwordmgr/test/LoginTestUtils.sys.mjs
+++ b/toolkit/components/passwordmgr/test/LoginTestUtils.sys.mjs
@@ -67,6 +67,13 @@ export const LoginTestUtils = {
return Services.logins.addLoginAsync(login);
},
+ /**
+ * Removes a login from the store
+ */
+ async removeLogin(login) {
+ return Services.logins.removeLogin(login);
+ },
+
async modifyLogin(oldLogin, newLogin) {
const storageChangedPromise = TestUtils.topicObserved(
"passwordmgr-storage-changed",
diff --git a/toolkit/components/passwordmgr/test/browser/browser_basicAuth_multiTab.js b/toolkit/components/passwordmgr/test/browser/browser_basicAuth_multiTab.js
index 0069c653a5..2115272abe 100644
--- a/toolkit/components/passwordmgr/test/browser/browser_basicAuth_multiTab.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_basicAuth_multiTab.js
@@ -88,13 +88,6 @@ async function testTabAuthed(expectAuthed, { tab, loadPromise, authOptions }) {
);
}
-add_setup(async function () {
- await SpecialPowers.pushPrefEnv({
- // This test relies on tab auth prompts.
- set: [["prompts.modalType.httpAuth", Services.prompt.MODAL_TYPE_TAB]],
- });
-});
-
add_task(async function test() {
let tabA = await openTabWithAuthPrompt(ORIGIN1, {
user: "userA",
diff --git a/toolkit/components/passwordmgr/test/browser/browser_basicAuth_rateLimit.js b/toolkit/components/passwordmgr/test/browser/browser_basicAuth_rateLimit.js
index b0a5b8b335..214ae5d299 100644
--- a/toolkit/components/passwordmgr/test/browser/browser_basicAuth_rateLimit.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_basicAuth_rateLimit.js
@@ -5,12 +5,10 @@
// This tests that the basic auth dialog can not be used for DOS attacks
// and that the protections are reset on user-initiated navigation/reload.
-let promptModalType = Services.prefs.getIntPref("prompts.modalType.httpAuth");
-
function promiseAuthWindowShown() {
return PromptTestUtils.handleNextPrompt(
window,
- { modalType: promptModalType, promptType: "promptUserAndPass" },
+ { modalType: Ci.nsIPrompt.MODAL_TYPE_TAB, promptType: "promptUserAndPass" },
{ buttonNumClick: 1 }
);
}
diff --git a/toolkit/components/passwordmgr/test/browser/browser_basicAuth_switchTab.js b/toolkit/components/passwordmgr/test/browser/browser_basicAuth_switchTab.js
index 8fddea4c93..8e993388e7 100644
--- a/toolkit/components/passwordmgr/test/browser/browser_basicAuth_switchTab.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_basicAuth_switchTab.js
@@ -2,14 +2,12 @@
* 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/. */
-let modalType = Services.prefs.getIntPref("prompts.modalType.httpAuth");
-
-add_task(async function test() {
+add_task(async function test_auth_switchtab() {
let tab = BrowserTestUtils.addTab(gBrowser);
isnot(tab, gBrowser.selectedTab, "New tab shouldn't be selected");
let authPromptShown = PromptTestUtils.waitForPrompt(tab.linkedBrowser, {
- modalType,
+ modalType: Ci.nsIPrompt.MODAL_TYPE_TAB,
promptType: "promptUserAndPass",
});
diff --git a/toolkit/components/passwordmgr/test/browser/browser_doorhanger_form_password_edit.js b/toolkit/components/passwordmgr/test/browser/browser_doorhanger_form_password_edit.js
index 23afd2c6ab..ad91270eb5 100644
--- a/toolkit/components/passwordmgr/test/browser/browser_doorhanger_form_password_edit.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_doorhanger_form_password_edit.js
@@ -30,7 +30,7 @@ let testCases = [
doorhanger: {
type: "password-save",
dismissed: true,
- anchorExtraAttr: "",
+ anchorExtraAttr: null,
username: "",
password: "abcXYZ",
toggle: "visible",
@@ -56,7 +56,7 @@ let testCases = [
doorhanger: {
type: "password-save",
dismissed: true,
- anchorExtraAttr: "",
+ anchorExtraAttr: null,
username: "",
password: "pass-changed",
toggle: "visible",
@@ -80,7 +80,7 @@ let testCases = [
doorhanger: {
type: "password-change",
dismissed: true,
- anchorExtraAttr: "",
+ anchorExtraAttr: null,
username: "user1",
password: "autopass-changed",
},
@@ -104,7 +104,7 @@ let testCases = [
doorhanger: {
type: "password-save",
dismissed: true,
- anchorExtraAttr: "",
+ anchorExtraAttr: null,
username: "user2",
password: "pass2",
toggle: "visible",
@@ -147,7 +147,7 @@ let testCases = [
doorhanger: {
type: "password-save",
dismissed: true,
- anchorExtraAttr: "",
+ anchorExtraAttr: null,
username: "user2",
password: "pass1",
toggle: "visible",
@@ -174,7 +174,7 @@ let testCases = [
doorhanger: {
type: "password-change",
dismissed: true,
- anchorExtraAttr: "",
+ anchorExtraAttr: null,
username: "user-saved",
password: "pass2",
toggle: "visible",
@@ -198,7 +198,7 @@ let testCases = [
doorhanger: {
type: "password-change",
dismissed: true,
- anchorExtraAttr: "",
+ anchorExtraAttr: null,
username: "user1",
password: "pass1",
toggle: "visible",
@@ -244,7 +244,7 @@ let testCases = [
doorhanger: {
type: "password-save",
dismissed: true,
- anchorExtraAttr: "",
+ anchorExtraAttr: null,
username: "",
password: "a",
toggle: "visible",
@@ -271,7 +271,7 @@ let testCases = [
doorhanger: {
type: "password-save",
dismissed: true,
- anchorExtraAttr: "",
+ anchorExtraAttr: null,
username: "",
password: "abc",
toggle: "visible",
@@ -296,7 +296,7 @@ let testCases = [
doorhanger: {
type: "password-change",
dismissed: true,
- anchorExtraAttr: "",
+ anchorExtraAttr: null,
username: "",
password: "pass",
toggle: "visible",
diff --git a/toolkit/components/passwordmgr/test/browser/browser_doorhanger_generated_password.js b/toolkit/components/passwordmgr/test/browser/browser_doorhanger_generated_password.js
index 798337ddda..dd3a1a216e 100644
--- a/toolkit/components/passwordmgr/test/browser/browser_doorhanger_generated_password.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_doorhanger_generated_password.js
@@ -481,7 +481,7 @@ add_task(async function autocomplete_generated_password_saved_empty_username() {
info("Waiting to openAndVerifyDoorhanger");
await openAndVerifyDoorhanger(browser, "password-change", {
dismissed: true,
- anchorExtraAttr: "",
+ anchorExtraAttr: null,
usernameValue: "",
passwordLength: LoginTestUtils.generation.LENGTH,
});
@@ -493,7 +493,7 @@ add_task(async function autocomplete_generated_password_saved_empty_username() {
await submitForm(browser);
let notif = await openAndVerifyDoorhanger(browser, "password-change", {
dismissed: false,
- anchorExtraAttr: "",
+ anchorExtraAttr: null,
usernameValue: "",
passwordLength: LoginTestUtils.generation.LENGTH,
});
@@ -648,7 +648,7 @@ add_task(async function ac_gen_pw_saved_empty_un_stored_non_empty_un_in_form() {
info("Waiting to openAndVerifyDoorhanger");
await openAndVerifyDoorhanger(browser, "password-save", {
dismissed: true,
- anchorExtraAttr: "",
+ anchorExtraAttr: null,
usernameValue: "myusername",
passwordLength: LoginTestUtils.generation.LENGTH,
});
@@ -660,7 +660,7 @@ add_task(async function ac_gen_pw_saved_empty_un_stored_non_empty_un_in_form() {
await submitForm(browser);
let notif = await openAndVerifyDoorhanger(browser, "password-save", {
dismissed: false,
- anchorExtraAttr: "",
+ anchorExtraAttr: null,
usernameValue: "myusername",
passwordLength: LoginTestUtils.generation.LENGTH,
});
@@ -719,7 +719,7 @@ add_task(async function contextfill_generated_password_saved_empty_username() {
info("Waiting to openAndVerifyDoorhanger");
await openAndVerifyDoorhanger(browser, "password-change", {
dismissed: true,
- anchorExtraAttr: "",
+ anchorExtraAttr: null,
usernameValue: "",
passwordLength: LoginTestUtils.generation.LENGTH,
});
@@ -731,7 +731,7 @@ add_task(async function contextfill_generated_password_saved_empty_username() {
await submitForm(browser);
let notif = await openAndVerifyDoorhanger(browser, "password-change", {
dismissed: false,
- anchorExtraAttr: "",
+ anchorExtraAttr: null,
usernameValue: "",
passwordLength: LoginTestUtils.generation.LENGTH,
});
@@ -789,7 +789,7 @@ async function autocomplete_generated_password_edited_no_auto_save(
info("Waiting to openAndVerifyDoorhanger");
let notif = await openAndVerifyDoorhanger(browser, "password-change", {
dismissed: true,
- anchorExtraAttr: "",
+ anchorExtraAttr: null,
usernameValue: "",
passwordLength: LoginTestUtils.generation.LENGTH,
});
@@ -812,7 +812,7 @@ async function autocomplete_generated_password_edited_no_auto_save(
info("Waiting to openAndVerifyDoorhanger");
notif = await openAndVerifyDoorhanger(browser, "password-change", {
dismissed: true,
- anchorExtraAttr: "",
+ anchorExtraAttr: null,
usernameValue: "",
passwordLength: LoginTestUtils.generation.LENGTH + 2,
});
@@ -836,7 +836,7 @@ async function autocomplete_generated_password_edited_no_auto_save(
await submitForm(browser);
notif = await openAndVerifyDoorhanger(browser, "password-change", {
dismissed: false,
- anchorExtraAttr: "",
+ anchorExtraAttr: null,
usernameValue: "",
passwordLength: LoginTestUtils.generation.LENGTH + 2,
});
@@ -957,7 +957,7 @@ add_task(async function contextmenu_fill_generated_password_and_set_username() {
await submitForm(browser);
let notif = await openAndVerifyDoorhanger(browser, "password-change", {
dismissed: false,
- anchorExtraAttr: "",
+ anchorExtraAttr: null,
usernameValue: "differentuser",
passwordLength: LoginTestUtils.generation.LENGTH,
});
@@ -1386,7 +1386,7 @@ add_task(async function autosaved_login_updated_to_existing_login_onsubmit() {
await waitForDoorhanger(browser, "password-change");
notif = await openAndVerifyDoorhanger(browser, "password-change", {
dismissed: false,
- anchorExtraAttr: "",
+ anchorExtraAttr: null,
usernameValue: "user1",
password: autoSavedLogin.password,
});
@@ -1611,7 +1611,7 @@ add_task(async function form_change_from_autosaved_login_to_existing_login() {
// the previous doorhanger would have old values, verify it was updated/replaced with new values from the form
notif = await openAndVerifyDoorhanger(browser, "password-change", {
dismissed: true,
- anchorExtraAttr: "",
+ anchorExtraAttr: null,
usernameValue: user1LoginSnapshot.username,
passwordLength: user1LoginSnapshot.password.length,
});
@@ -1818,7 +1818,7 @@ add_task(async function form_edit_username_and_password_of_generated_login() {
info("Verifying the doorhanger");
notif = await openAndVerifyDoorhanger(browser, "password-change", {
dismissed: true,
- anchorExtraAttr: expectedConfirmation ? "attention" : "",
+ anchorExtraAttr: expectedConfirmation ? "attention" : null,
usernameValue: expectedDoorhangerUsername,
passwordLength: expectedDoorhangerPassword.length,
});
@@ -1835,7 +1835,7 @@ add_task(async function form_edit_username_and_password_of_generated_login() {
await passwordChangeDoorhangerPromise;
notif = await openAndVerifyDoorhanger(browser, "password-change", {
dismissed: false,
- anchorExtraAttr: "",
+ anchorExtraAttr: null,
usernameValue: "someuser",
passwordLength: LoginTestUtils.generation.LENGTH + 2,
});
diff --git a/toolkit/components/passwordmgr/test/browser/browser_entry_point_telemetry.js b/toolkit/components/passwordmgr/test/browser/browser_entry_point_telemetry.js
index 9241f18612..2fbca0381b 100644
--- a/toolkit/components/passwordmgr/test/browser/browser_entry_point_telemetry.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_entry_point_telemetry.js
@@ -68,7 +68,7 @@ add_task(async function pageInfo_entryPoint() {
},
async function (_browser) {
info("pageInfo_entryPoint, opening pageinfo");
- let pageInfo = BrowserPageInfo(TEST_ORIGIN, "securityTab", {});
+ let pageInfo = BrowserCommands.pageInfo(TEST_ORIGIN, "securityTab", {});
await BrowserTestUtils.waitForEvent(pageInfo, "page-info-init");
info(
"pageInfo_entryPoint, got pageinfo, wait until password button is visible"
diff --git a/toolkit/components/passwordmgr/test/browser/browser_private_window.js b/toolkit/components/passwordmgr/test/browser/browser_private_window.js
index 31fe82cf8b..8e1e22c20f 100644
--- a/toolkit/components/passwordmgr/test/browser/browser_private_window.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_private_window.js
@@ -77,7 +77,7 @@ async function loadAccessRestrictedURL(browser, url, username, password) {
// Wait for the auth prompt, enter the login details and close the prompt
await PromptTestUtils.handleNextPrompt(
browser,
- { modalType: authPromptModalType, promptType: "promptUserAndPass" },
+ { modalType: Ci.nsIPrompt.MODAL_TYPE_TAB, promptType: "promptUserAndPass" },
{ buttonNumClick: 0, loginInput: username, passwordInput: password }
);
@@ -106,13 +106,11 @@ const authUrl = `https://example.com/${DIRECTORY_PATH}authenticate.sjs`;
let normalWin;
let privateWin;
-let authPromptModalType;
// XXX: Note that tasks are currently run in sequence. Some tests may assume the state
// resulting from successful or unsuccessful logins in previous tasks
add_task(async function test_setup() {
- authPromptModalType = Services.prefs.getIntPref("prompts.modalType.httpAuth");
normalWin = await BrowserTestUtils.openNewBrowserWindow({ private: false });
privateWin = await BrowserTestUtils.openNewBrowserWindow({ private: true });
Services.logins.removeAllUserFacingLogins();
diff --git a/toolkit/components/passwordmgr/test/browser/browser_proxyAuth_prompt.js b/toolkit/components/passwordmgr/test/browser/browser_proxyAuth_prompt.js
index dfa78ff28e..6dfe7d4021 100644
--- a/toolkit/components/passwordmgr/test/browser/browser_proxyAuth_prompt.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_proxyAuth_prompt.js
@@ -60,10 +60,6 @@ function initProxy() {
}
add_setup(async function () {
- await SpecialPowers.pushPrefEnv({
- // This test relies on tab auth prompts.
- set: [["prompts.modalType.httpAuth", Services.prompt.MODAL_TYPE_TAB]],
- });
proxyChannel = await initProxy();
});
diff --git a/toolkit/components/passwordmgr/test/mochitest/pwmgr_common.js b/toolkit/components/passwordmgr/test/mochitest/pwmgr_common.js
index 1494c60bbd..1e02e6a368 100644
--- a/toolkit/components/passwordmgr/test/mochitest/pwmgr_common.js
+++ b/toolkit/components/passwordmgr/test/mochitest/pwmgr_common.js
@@ -25,11 +25,6 @@ const { LENGTH: GENERATED_PASSWORD_LENGTH, REGEX: GENERATED_PASSWORD_REGEX } =
const LOGIN_FIELD_UTILS = LoginTestUtils.loginField;
const TESTS_DIR = "/tests/toolkit/components/passwordmgr/test/";
-// Depending on pref state we either show auth prompts as windows or on tab level.
-let authPromptModalType = SpecialPowers.Services.prefs.getIntPref(
- "prompts.modalType.httpAuth"
-);
-
/**
* Recreate a DOM tree using the outerHTML to ensure that any event listeners
* and internal state for the elements are removed.
@@ -1051,6 +1046,23 @@ SimpleTest.registerCleanupFunction(() => {
});
});
+// This is a version of LoginHelper.loginToVanillaObject that is adapted to run
+// as content JS instead of chrome JS. This is needed to make it return a
+// content JS object because the structured cloning we use to send it over
+// JS IPC can't deal with a cross compartment wrapper.
+function loginToVanillaObject(login) {
+ let obj = {};
+ for (let i in SpecialPowers.do_QueryInterface(
+ login,
+ SpecialPowers.Ci.nsILoginMetaInfo
+ )) {
+ if (typeof login[i] !== "function") {
+ obj[i] = login[i];
+ }
+ }
+ return obj;
+}
+
/**
* Proxy for Services.logins (nsILoginManager).
* Only supports arguments which support structured clone plus {nsILoginInfo}
@@ -1067,7 +1079,7 @@ this.LoginManager = new Proxy(
SpecialPowers.call_Instanceof(val, SpecialPowers.Ci.nsILoginInfo)
) {
loginInfoIndices.push(index);
- return LoginHelper.loginToVanillaObject(val);
+ return loginToVanillaObject(val);
}
return val;
diff --git a/toolkit/components/passwordmgr/test/mochitest/test_formless_submit_form_removal.html b/toolkit/components/passwordmgr/test/mochitest/test_formless_submit_form_removal.html
index b2ff2d8845..83e2f1d3c6 100644
--- a/toolkit/components/passwordmgr/test/mochitest/test_formless_submit_form_removal.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_formless_submit_form_removal.html
@@ -161,7 +161,7 @@ const TESTCASES = [
},
/*
XXX - Bug 1698498 :
- This test case fails because when we call querySelector in LoginRecipes.jsm
+ This test case fails because when we call querySelector in LoginRecipes.sys.mjs
after the form is removed, querySelector can't find the element.
{
document: `<!-- recipe field override -->
diff --git a/toolkit/components/passwordmgr/test/mochitest/test_prompt_async.html b/toolkit/components/passwordmgr/test/mochitest/test_prompt_async.html
index 1edbf8cf18..84f8c4aafb 100644
--- a/toolkit/components/passwordmgr/test/mochitest/test_prompt_async.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_prompt_async.html
@@ -20,8 +20,8 @@
const EXAMPLE_ORG = "http://example.org/tests/toolkit/components/passwordmgr/test/mochitest/";
let mozproxyOrigin;
- // Let prompt_common know what kind of modal type is enabled for auth prompts.
- modalType = authPromptModalType;
+ // Let prompt_common know what kind of modal type is used for auth prompts.
+ modalType = Ci.nsIPrompt.MODAL_TYPE_TAB;
// These are magically defined on the window due to the iframe IDs
/* global iframe1, iframe2a, iframe2b */
diff --git a/toolkit/components/passwordmgr/test/mochitest/test_prompt_http.html b/toolkit/components/passwordmgr/test/mochitest/test_prompt_http.html
index f6f6e681dc..f008950c2b 100644
--- a/toolkit/components/passwordmgr/test/mochitest/test_prompt_http.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_prompt_http.html
@@ -19,8 +19,8 @@
<script class="testbody" type="text/javascript">
var iframe = document.getElementById("iframe");
-// Let prompt_common know what kind of modal type is enabled for auth prompts.
-modalType = authPromptModalType;
+// Let prompt_common know what kind of modal type is used for auth prompts.
+modalType = Ci.nsIPrompt.MODAL_TYPE_TAB;
const AUTHENTICATE_PATH = new URL("authenticate.sjs", window.location.href).pathname;
diff --git a/toolkit/components/passwordmgr/test/mochitest/test_prompt_noWindow.html b/toolkit/components/passwordmgr/test/mochitest/test_prompt_noWindow.html
index 19d05e47e5..0ece6f306c 100644
--- a/toolkit/components/passwordmgr/test/mochitest/test_prompt_noWindow.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_prompt_noWindow.html
@@ -17,8 +17,8 @@
<pre id="test">
<script class="testbody" type="text/javascript">
-// Let prompt_common know what kind of modal type is enabled for auth prompts.
-modalType = authPromptModalType;
+// Let prompt_common know what kind of modal type is used for auth prompts.
+modalType = Ci.nsIPrompt.MODAL_TYPE_TAB;
add_setup(async () => {
await setStoredLoginsAsync(
diff --git a/toolkit/components/passwordmgr/test/mochitest/test_prompt_promptAuth.html b/toolkit/components/passwordmgr/test/mochitest/test_prompt_promptAuth.html
index b82ea67faa..2ce85a61a9 100644
--- a/toolkit/components/passwordmgr/test/mochitest/test_prompt_promptAuth.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_prompt_promptAuth.html
@@ -27,8 +27,8 @@ const authinfo = {
realm: "",
};
-// Let prompt_common know what kind of modal type is enabled for auth prompts.
-modalType = authPromptModalType;
+// Let prompt_common know what kind of modal type is used for auth prompts.
+modalType = Ci.nsIPrompt.MODAL_TYPE_TAB;
const prompterParent = runInParent(() => {
const promptFac = Cc["@mozilla.org/passwordmanager/authpromptfactory;1"].
diff --git a/toolkit/components/passwordmgr/test/mochitest/test_prompt_promptAuth_proxy.html b/toolkit/components/passwordmgr/test/mochitest/test_prompt_promptAuth_proxy.html
index 75342cd4cb..e5e77d0bcc 100644
--- a/toolkit/components/passwordmgr/test/mochitest/test_prompt_promptAuth_proxy.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_prompt_promptAuth_proxy.html
@@ -31,8 +31,8 @@ let proxyAuthinfo = {
realm: "",
};
-// Let prompt_common know what kind of modal type is enabled for auth prompts.
-modalType = authPromptModalType;
+// Let prompt_common know what kind of modal type is used for auth prompts.
+modalType = Ci.nsIPrompt.MODAL_TYPE_TAB;
let chromeScript = runInParent(() => {
const promptFac = Cc[
diff --git a/toolkit/components/passwordmgr/test/mochitest/test_xhr.html b/toolkit/components/passwordmgr/test/mochitest/test_xhr.html
index 4461195359..b481099314 100644
--- a/toolkit/components/passwordmgr/test/mochitest/test_xhr.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_xhr.html
@@ -40,8 +40,8 @@ function xhrLoad(xmlDoc) {
return {username, password, authok};
}
-// Let prompt_common know what kind of modal type is enabled for auth prompts.
-modalType = authPromptModalType;
+// Let prompt_common know what kind of modal type is used for auth prompts.
+modalType = Ci.nsIPrompt.MODAL_TYPE_TAB;
let prompterParent = runInParent(() => {
const promptFac = Cc["@mozilla.org/passwordmanager/authpromptfactory;1"].
@@ -120,17 +120,6 @@ add_task(async function test2() {
defButton: "button0",
};
- // For window prompts check that the dialog is modal, chrome and dependent;
- // We can't just check window.opener because that'll be
- // a content window, which therefore isn't exposed (it'll lie and
- // be null).
- if (authPromptModalType === SpecialPowers.Services.prompt.MODAL_TYPE_WINDOW) {
- state.chrome = true;
- state.dialog = true;
- state.chromeDependent = true;
- state.isWindowModal = true;
- }
-
let action = {
buttonClick: "ok",
};
diff --git a/toolkit/components/passwordmgr/test/unit/test_PasswordRulesManager_generatePassword.js b/toolkit/components/passwordmgr/test/unit/test_PasswordRulesManager_generatePassword.js
index 88520769cf..43717f36c5 100644
--- a/toolkit/components/passwordmgr/test/unit/test_PasswordRulesManager_generatePassword.js
+++ b/toolkit/components/passwordmgr/test/unit/test_PasswordRulesManager_generatePassword.js
@@ -238,7 +238,7 @@ add_task(
const TEST_BASE_ORIGIN = "example.com";
const REQUIRED_ARBITRARY_CHARACTERS = "!#$@*()_+=";
// We use an extremely long password to ensure there are no invalid characters generated in the password.
- // This ensures we exhaust all of "allRequiredCharacters" in PasswordGenerator.jsm.
+ // This ensures we exhaust all of "allRequiredCharacters" in PasswordGenerator.sys.mjs.
// Otherwise, there's a small chance a "," may have been added to "allRequiredCharacters"
// which will generate an invalid password in this case.
const TEST_RULES = `required: [${REQUIRED_ARBITRARY_CHARACTERS}], upper, lower; maxlength: 255; minlength: 255;`;
diff --git a/toolkit/components/pdfjs/content/PdfJs.sys.mjs b/toolkit/components/pdfjs/content/PdfJs.sys.mjs
index fd6757737e..430a153ae2 100644
--- a/toolkit/components/pdfjs/content/PdfJs.sys.mjs
+++ b/toolkit/components/pdfjs/content/PdfJs.sys.mjs
@@ -242,7 +242,7 @@ export var PdfJs = {
},
// nsIObserver
- observe(aSubject, aTopic, aData) {
+ observe() {
this.checkIsDefault();
},
};
diff --git a/toolkit/components/pdfjs/content/PdfJsNetwork.sys.mjs b/toolkit/components/pdfjs/content/PdfJsNetwork.sys.mjs
index dd3ebc6bf5..a36f889539 100644
--- a/toolkit/components/pdfjs/content/PdfJsNetwork.sys.mjs
+++ b/toolkit/components/pdfjs/content/PdfJsNetwork.sys.mjs
@@ -33,7 +33,7 @@ export class NetworkManager {
this.getXhr =
args.getXhr ||
function NetworkManager_getXhr() {
- return new XMLHttpRequest();
+ return new XMLHttpRequest({ mozAnon: false });
};
this.currXhrId = 0;
@@ -79,7 +79,7 @@ export class NetworkManager {
xhr.responseType = "arraybuffer";
if (args.onError) {
- xhr.onerror = function (evt) {
+ xhr.onerror = function () {
args.onError(xhr.status);
};
}
@@ -109,7 +109,7 @@ export class NetworkManager {
}
}
- onStateChange(xhrId, evt) {
+ onStateChange(xhrId) {
var pendingRequest = this.pendingRequests[xhrId];
if (!pendingRequest) {
// Maybe abortRequest was called...
diff --git a/toolkit/components/pdfjs/content/PdfJsTelemetry.sys.mjs b/toolkit/components/pdfjs/content/PdfJsTelemetry.sys.mjs
index 16e4e85d58..4724f25c33 100644
--- a/toolkit/components/pdfjs/content/PdfJsTelemetry.sys.mjs
+++ b/toolkit/components/pdfjs/content/PdfJsTelemetry.sys.mjs
@@ -47,7 +47,7 @@ export class PdfJsTelemetry {
}
static onTimeToView(ms) {
- Glean.pdfjs.timeToView.accumulateSamples([ms]);
+ Glean.pdfjs.timeToView.accumulateSingleSample(ms);
}
static onEditing({ type, data }) {
@@ -99,9 +99,9 @@ export class PdfJsTelemetry {
Glean.pdfjsEditingHighlight.method[data.methodOfCreation].add(1);
Glean.pdfjsEditingHighlight.color[data.color].add(1);
if (data.type === "free_highlight") {
- Glean.pdfjsEditingHighlight.thickness.accumulateSamples([
- data.thickness,
- ]);
+ Glean.pdfjsEditingHighlight.thickness.accumulateSingleSample(
+ data.thickness
+ );
}
break;
case "color_changed":
@@ -109,9 +109,9 @@ export class PdfJsTelemetry {
Glean.pdfjsEditingHighlight.colorChanged.add(1);
break;
case "thickness_changed":
- Glean.pdfjsEditingHighlight.thickness.accumulateSamples([
- data.thickness,
- ]);
+ Glean.pdfjsEditingHighlight.thickness.accumulateSingleSample(
+ data.thickness
+ );
Glean.pdfjsEditingHighlight.thicknessChanged.add(1);
break;
case "deleted":
diff --git a/toolkit/components/pdfjs/content/PdfStreamConverter.sys.mjs b/toolkit/components/pdfjs/content/PdfStreamConverter.sys.mjs
index 2a56cbb935..ce9c6898e3 100644
--- a/toolkit/components/pdfjs/content/PdfStreamConverter.sys.mjs
+++ b/toolkit/components/pdfjs/content/PdfStreamConverter.sys.mjs
@@ -296,7 +296,7 @@ class ChromeActions {
}
}
- download(data, sendResponse) {
+ download(data) {
const { originalUrl, options } = data;
const blobUrl = data.blobUrl || originalUrl;
let { filename } = data;
@@ -361,7 +361,7 @@ class ChromeActions {
actor.sendAsyncMessage("PDFJS:Parent:getNimbus");
Services.obs.addObserver(
{
- observe(aSubject, aTopic, aData) {
+ observe(aSubject, aTopic) {
if (aTopic === "pdfjs-getNimbus") {
Services.obs.removeObserver(this, aTopic);
sendResponse(aSubject && JSON.stringify(aSubject.wrappedJSObject));
@@ -545,7 +545,7 @@ class RangedChromeActions extends ChromeActions {
}
};
var getXhr = function getXhr() {
- var xhr = new XMLHttpRequest();
+ var xhr = new XMLHttpRequest({ mozAnon: false });
xhr.addEventListener("readystatechange", xhr_onreadystatechange);
return xhr;
};
@@ -790,7 +790,7 @@ PdfStreamConverter.prototype = {
*/
// nsIStreamConverter::convert
- convert(aFromStream, aFromType, aToType, aCtxt) {
+ convert() {
throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
},
@@ -1063,7 +1063,7 @@ PdfStreamConverter.prototype = {
// request(aRequest) below so we don't overwrite the original channel and
// trigger an assertion.
var proxy = {
- onStartRequest(request) {
+ onStartRequest() {
listener.onStartRequest(aRequest);
},
onDataAvailable(request, inputStream, offset, count) {
diff --git a/toolkit/components/pdfjs/content/PdfjsParent.sys.mjs b/toolkit/components/pdfjs/content/PdfjsParent.sys.mjs
index a11d68ef02..32bb3d65b7 100644
--- a/toolkit/components/pdfjs/content/PdfjsParent.sys.mjs
+++ b/toolkit/components/pdfjs/content/PdfjsParent.sys.mjs
@@ -187,7 +187,7 @@ export class PdfjsParent extends JSWindowActorParent {
let newBrowser = aEvent.detail;
newBrowser.addEventListener(
"EndSwapDocShells",
- evt => {
+ () => {
this._hookupEventListeners(newBrowser);
},
{ once: true }
diff --git a/toolkit/components/pdfjs/content/build/pdf.mjs b/toolkit/components/pdfjs/content/build/pdf.mjs
index 4e51efdb78..dbba618862 100644
--- a/toolkit/components/pdfjs/content/build/pdf.mjs
+++ b/toolkit/components/pdfjs/content/build/pdf.mjs
@@ -70,7 +70,6 @@ __webpack_require__.d(__webpack_exports__, {
PasswordResponses: () => (/* reexport */ PasswordResponses),
PermissionFlag: () => (/* reexport */ PermissionFlag),
PixelsPerInch: () => (/* reexport */ PixelsPerInch),
- PromiseCapability: () => (/* reexport */ PromiseCapability),
RenderingCancelledException: () => (/* reexport */ RenderingCancelledException),
UnexpectedResponseException: () => (/* reexport */ UnexpectedResponseException),
Util: () => (/* reexport */ Util),
@@ -789,24 +788,6 @@ function getModificationDate(date = new Date()) {
const buffer = [date.getUTCFullYear().toString(), (date.getUTCMonth() + 1).toString().padStart(2, "0"), date.getUTCDate().toString().padStart(2, "0"), date.getUTCHours().toString().padStart(2, "0"), date.getUTCMinutes().toString().padStart(2, "0"), date.getUTCSeconds().toString().padStart(2, "0")];
return buffer.join("");
}
-class PromiseCapability {
- #settled = false;
- constructor() {
- this.promise = new Promise((resolve, reject) => {
- this.resolve = data => {
- this.#settled = true;
- resolve(data);
- };
- this.reject = reason => {
- this.#settled = true;
- reject(reason);
- };
- });
- }
- get settled() {
- return this.#settled;
- }
-}
let NormalizeRegex = null;
let NormalizationMap = null;
function normalizeUnicode(str) {
@@ -820,6 +801,17 @@ function getUuid() {
return crypto.randomUUID();
}
const AnnotationPrefix = "pdfjs_internal_id_";
+const FontRenderOps = {
+ BEZIER_CURVE_TO: 0,
+ MOVE_TO: 1,
+ LINE_TO: 2,
+ QUADRATIC_CURVE_TO: 3,
+ RESTORE: 4,
+ SAVE: 5,
+ SCALE: 6,
+ TRANSFORM: 7,
+ TRANSLATE: 8
+};
;// CONCATENATED MODULE: ./src/display/base_factory.js
@@ -1742,11 +1734,11 @@ class HighlightToolbar {
const button = document.createElement("button");
button.className = "highlightButton";
button.tabIndex = 0;
- button.setAttribute("data-l10n-id", `pdfjs-highlight-floating-button`);
+ button.setAttribute("data-l10n-id", `pdfjs-highlight-floating-button1`);
const span = document.createElement("span");
button.append(span);
span.className = "visuallyHidden";
- span.setAttribute("data-l10n-id", "pdfjs-editor-highlight-button-label");
+ span.setAttribute("data-l10n-id", "pdfjs-highlight-floating-button-label");
button.addEventListener("contextmenu", noContextMenu);
button.addEventListener("click", () => {
this.#uiManager.highlightSelection("floating_button");
@@ -3431,6 +3423,7 @@ class AnnotationEditor {
#editToolbar = null;
#focusedResizerName = "";
#hasBeenClicked = false;
+ #initialPosition = null;
#isEditing = false;
#isInEditMode = false;
#isResizerEnabledForKeyboard = false;
@@ -3656,12 +3649,14 @@ class AnnotationEditor {
this.#translate(this.parentDimensions, x, y);
}
translateInPage(x, y) {
+ this.#initialPosition ||= [this.x, this.y];
this.#translate(this.pageDimensions, x, y);
this.div.scrollIntoView({
block: "nearest"
});
}
drag(tx, ty) {
+ this.#initialPosition ||= [this.x, this.y];
const [parentWidth, parentHeight] = this.parentDimensions;
this.x += tx / parentWidth;
this.y += ty / parentHeight;
@@ -3688,6 +3683,9 @@ class AnnotationEditor {
block: "nearest"
});
}
+ get _hasBeenMoved() {
+ return !!this.#initialPosition && (this.#initialPosition[0] !== this.x || this.#initialPosition[1] !== this.y);
+ }
getBaseTranslation() {
const [parentWidth, parentHeight] = this.parentDimensions;
const {
@@ -4911,7 +4909,6 @@ class FontLoader {
}
class FontFaceObject {
constructor(translatedData, {
- isEvalSupported = true,
disableFontFace = false,
ignoreErrors = false,
inspectFont = null
@@ -4920,7 +4917,6 @@ class FontFaceObject {
for (const i in translatedData) {
this[i] = translatedData[i];
}
- this.isEvalSupported = isEvalSupported !== false;
this.disableFontFace = disableFontFace === true;
this.ignoreErrors = ignoreErrors === true;
this._inspectFont = inspectFont;
@@ -4975,22 +4971,72 @@ class FontFaceObject {
throw ex;
}
warn(`getPathGenerator - ignoring character: "${ex}".`);
+ }
+ if (!Array.isArray(cmds) || cmds.length === 0) {
return this.compiledGlyphs[character] = function (c, size) {};
}
- if (this.isEvalSupported && util_FeatureTest.isEvalSupported) {
- const jsBuf = [];
- for (const current of cmds) {
- const args = current.args !== undefined ? current.args.join(",") : "";
- jsBuf.push("c.", current.cmd, "(", args, ");\n");
+ const commands = [];
+ for (let i = 0, ii = cmds.length; i < ii;) {
+ switch (cmds[i++]) {
+ case FontRenderOps.BEZIER_CURVE_TO:
+ {
+ const [a, b, c, d, e, f] = cmds.slice(i, i + 6);
+ commands.push(ctx => ctx.bezierCurveTo(a, b, c, d, e, f));
+ i += 6;
+ }
+ break;
+ case FontRenderOps.MOVE_TO:
+ {
+ const [a, b] = cmds.slice(i, i + 2);
+ commands.push(ctx => ctx.moveTo(a, b));
+ i += 2;
+ }
+ break;
+ case FontRenderOps.LINE_TO:
+ {
+ const [a, b] = cmds.slice(i, i + 2);
+ commands.push(ctx => ctx.lineTo(a, b));
+ i += 2;
+ }
+ break;
+ case FontRenderOps.QUADRATIC_CURVE_TO:
+ {
+ const [a, b, c, d] = cmds.slice(i, i + 4);
+ commands.push(ctx => ctx.quadraticCurveTo(a, b, c, d));
+ i += 4;
+ }
+ break;
+ case FontRenderOps.RESTORE:
+ commands.push(ctx => ctx.restore());
+ break;
+ case FontRenderOps.SAVE:
+ commands.push(ctx => ctx.save());
+ break;
+ case FontRenderOps.SCALE:
+ assert(commands.length === 2, "Scale command is only valid at the third position.");
+ break;
+ case FontRenderOps.TRANSFORM:
+ {
+ const [a, b, c, d, e, f] = cmds.slice(i, i + 6);
+ commands.push(ctx => ctx.transform(a, b, c, d, e, f));
+ i += 6;
+ }
+ break;
+ case FontRenderOps.TRANSLATE:
+ {
+ const [a, b] = cmds.slice(i, i + 2);
+ commands.push(ctx => ctx.translate(a, b));
+ i += 2;
+ }
+ break;
}
- return this.compiledGlyphs[character] = new Function("c", "size", jsBuf.join(""));
}
- return this.compiledGlyphs[character] = function (c, size) {
- for (const current of cmds) {
- if (current.cmd === "scale") {
- current.args = [size, -size];
- }
- c[current.cmd].apply(c, current.args);
+ return this.compiledGlyphs[character] = function glyphDrawer(ctx, size) {
+ commands[0](ctx);
+ commands[1](ctx);
+ ctx.scale(size, -size);
+ for (let i = 2, ii = commands.length; i < ii; i++) {
+ commands[i](ctx);
}
};
}
@@ -7951,7 +7997,7 @@ class TextLayerRenderTask {
this._reader = null;
this._textDivProperties = textDivProperties || new WeakMap();
this._canceled = false;
- this._capability = new PromiseCapability();
+ this._capability = Promise.withResolvers();
this._layoutTextParams = {
prevFontSize: null,
prevFontFamily: null,
@@ -8019,7 +8065,11 @@ class TextLayerRenderTask {
}
}
_render() {
- const capability = new PromiseCapability();
+ const {
+ promise,
+ resolve,
+ reject
+ } = Promise.withResolvers();
let styleCache = Object.create(null);
if (this._isReadableStream) {
const pump = () => {
@@ -8028,13 +8078,13 @@ class TextLayerRenderTask {
done
}) => {
if (done) {
- capability.resolve();
+ resolve();
return;
}
Object.assign(styleCache, value.styles);
this._processItems(value.items, styleCache);
pump();
- }, capability.reject);
+ }, reject);
};
this._reader = this._textContentSource.getReader();
pump();
@@ -8044,11 +8094,11 @@ class TextLayerRenderTask {
styles
} = this._textContentSource;
this._processItems(items, styles);
- capability.resolve();
+ resolve();
} else {
throw new Error('No "textContentSource" parameter specified.');
}
- capability.promise.then(() => {
+ promise.then(() => {
styleCache = null;
render(this);
}, this._capability.reject);
@@ -8241,7 +8291,7 @@ class MessageHandler {
}
sendWithPromise(actionName, data, transfers) {
const callbackId = this.callbackId++;
- const capability = new PromiseCapability();
+ const capability = Promise.withResolvers();
this.callbackCapabilities[callbackId] = capability;
try {
this.comObj.postMessage({
@@ -8263,7 +8313,7 @@ class MessageHandler {
comObj = this.comObj;
return new ReadableStream({
start: controller => {
- const startCapability = new PromiseCapability();
+ const startCapability = Promise.withResolvers();
this.streamControllers[streamId] = {
controller,
startCall: startCapability,
@@ -8282,7 +8332,7 @@ class MessageHandler {
return startCapability.promise;
},
pull: controller => {
- const pullCapability = new PromiseCapability();
+ const pullCapability = Promise.withResolvers();
this.streamControllers[streamId].pullCall = pullCapability;
comObj.postMessage({
sourceName,
@@ -8295,7 +8345,7 @@ class MessageHandler {
},
cancel: reason => {
assert(reason instanceof Error, "cancel must have a valid reason");
- const cancelCapability = new PromiseCapability();
+ const cancelCapability = Promise.withResolvers();
this.streamControllers[streamId].cancelCall = cancelCapability;
this.streamControllers[streamId].isClosed = true;
comObj.postMessage({
@@ -8324,7 +8374,7 @@ class MessageHandler {
const lastDesiredSize = this.desiredSize;
this.desiredSize -= size;
if (lastDesiredSize > 0 && this.desiredSize <= 0) {
- this.sinkCapability = new PromiseCapability();
+ this.sinkCapability = Promise.withResolvers();
this.ready = this.sinkCapability.promise;
}
comObj.postMessage({
@@ -8362,7 +8412,7 @@ class MessageHandler {
reason: wrapReason(reason)
});
},
- sinkCapability: new PromiseCapability(),
+ sinkCapability: Promise.withResolvers(),
onPull: null,
onCancel: null,
isCancelled: false,
@@ -8975,7 +9025,7 @@ class PDFDataTransportStreamReader {
done: true
};
}
- const requestCapability = new PromiseCapability();
+ const requestCapability = Promise.withResolvers();
this._requests.push(requestCapability);
return requestCapability.promise;
}
@@ -9047,7 +9097,7 @@ class PDFDataTransportStreamRangeReader {
done: true
};
}
- const requestCapability = new PromiseCapability();
+ const requestCapability = Promise.withResolvers();
this._requests.push(requestCapability);
return requestCapability.promise;
}
@@ -9205,7 +9255,7 @@ function getDocument(src) {
}
const fetchDocParams = {
docId,
- apiVersion: "4.1.342",
+ apiVersion: "4.1.379",
data,
password,
disableAutoFetch,
@@ -9228,7 +9278,6 @@ function getDocument(src) {
};
const transportParams = {
ignoreErrors,
- isEvalSupported,
disableFontFace,
fontExtraProperties,
enableXfa,
@@ -9294,7 +9343,7 @@ function getDataProp(val) {
class PDFDocumentLoadingTask {
static #docId = 0;
constructor() {
- this._capability = new PromiseCapability();
+ this._capability = Promise.withResolvers();
this._transport = null;
this._worker = null;
this.docId = `d${PDFDocumentLoadingTask.#docId++}`;
@@ -9335,7 +9384,7 @@ class PDFDataRangeTransport {
this._progressListeners = [];
this._progressiveReadListeners = [];
this._progressiveDoneListeners = [];
- this._readyCapability = new PromiseCapability();
+ this._readyCapability = Promise.withResolvers();
}
addRangeListener(listener) {
this._rangeListeners.push(listener);
@@ -9588,7 +9637,7 @@ class PDFPageProxy {
}
const intentPrint = !!(renderingIntent & RenderingIntentFlag.PRINT);
if (!intentState.displayReadyCapability) {
- intentState.displayReadyCapability = new PromiseCapability();
+ intentState.displayReadyCapability = Promise.withResolvers();
intentState.operatorList = {
fnArray: [],
argsArray: [],
@@ -9934,7 +9983,7 @@ class PDFWorker {
this.name = name;
this.destroyed = false;
this.verbosity = verbosity;
- this._readyCapability = new PromiseCapability();
+ this._readyCapability = Promise.withResolvers();
this._port = null;
this._webWorker = null;
this._messageHandler = null;
@@ -10110,7 +10159,7 @@ class WorkerTransport {
this._networkStream = networkStream;
this._fullReader = null;
this._lastProgress = null;
- this.downloadInfoCapability = new PromiseCapability();
+ this.downloadInfoCapability = Promise.withResolvers();
this.setupMessageHandler();
}
#cacheSimpleMethod(name, data = null) {
@@ -10171,7 +10220,7 @@ class WorkerTransport {
return this.destroyCapability.promise;
}
this.destroyed = true;
- this.destroyCapability = new PromiseCapability();
+ this.destroyCapability = Promise.withResolvers();
this.#passwordCapability?.reject(new Error("Worker was destroyed during onPassword callback"));
const waitOn = [];
for (const page of this.#pageCache.values()) {
@@ -10239,7 +10288,7 @@ class WorkerTransport {
};
});
messageHandler.on("ReaderHeadersReady", data => {
- const headersCapability = new PromiseCapability();
+ const headersCapability = Promise.withResolvers();
const fullReader = this._fullReader;
fullReader.headersReady.then(() => {
if (!fullReader.isStreamingSupported || !fullReader.isRangeSupported) {
@@ -10325,7 +10374,7 @@ class WorkerTransport {
loadingTask._capability.reject(reason);
});
messageHandler.on("PasswordRequest", exception => {
- this.#passwordCapability = new PromiseCapability();
+ this.#passwordCapability = Promise.withResolvers();
if (loadingTask.onPassword) {
const updatePassword = password => {
if (password instanceof Error) {
@@ -10378,7 +10427,6 @@ class WorkerTransport {
}
const inspectFont = params.pdfBug && globalThis.FontInspector?.enabled ? (font, url) => globalThis.FontInspector.fontAdded(font, url) : null;
const font = new FontFaceObject(exportedData, {
- isEvalSupported: params.isEvalSupported,
disableFontFace: params.disableFontFace,
ignoreErrors: params.ignoreErrors,
inspectFont
@@ -10639,34 +10687,35 @@ class WorkerTransport {
});
}
}
+const INITIAL_DATA = Symbol("INITIAL_DATA");
class PDFObjects {
#objs = Object.create(null);
#ensureObj(objId) {
return this.#objs[objId] ||= {
- capability: new PromiseCapability(),
- data: null
+ ...Promise.withResolvers(),
+ data: INITIAL_DATA
};
}
get(objId, callback = null) {
if (callback) {
const obj = this.#ensureObj(objId);
- obj.capability.promise.then(() => callback(obj.data));
+ obj.promise.then(() => callback(obj.data));
return null;
}
const obj = this.#objs[objId];
- if (!obj?.capability.settled) {
+ if (!obj || obj.data === INITIAL_DATA) {
throw new Error(`Requesting object that isn't resolved yet ${objId}.`);
}
return obj.data;
}
has(objId) {
const obj = this.#objs[objId];
- return obj?.capability.settled ?? false;
+ return !!obj && obj.data !== INITIAL_DATA;
}
resolve(objId, data = null) {
const obj = this.#ensureObj(objId);
obj.data = data;
- obj.capability.resolve();
+ obj.resolve();
}
clear() {
for (const objId in this.#objs) {
@@ -10680,10 +10729,9 @@ class PDFObjects {
*[Symbol.iterator]() {
for (const objId in this.#objs) {
const {
- capability,
data
} = this.#objs[objId];
- if (!capability.settled) {
+ if (data === INITIAL_DATA) {
continue;
}
yield [objId, data];
@@ -10748,7 +10796,7 @@ class InternalRenderTask {
this.graphicsReady = false;
this._useRequestAnimationFrame = useRequestAnimationFrame === true && typeof window !== "undefined";
this.cancelled = false;
- this.capability = new PromiseCapability();
+ this.capability = Promise.withResolvers();
this.task = new RenderTask(this);
this._cancelBound = this.cancel.bind(this);
this._continueBound = this._continue.bind(this);
@@ -10849,8 +10897,8 @@ class InternalRenderTask {
}
}
}
-const version = "4.1.342";
-const build = "e384df6f1";
+const version = "4.1.379";
+const build = "017e49244";
;// CONCATENATED MODULE: ./src/shared/scripting_utils.js
function makeColorComp(n) {
@@ -13708,7 +13756,6 @@ class FreeTextEditor extends AnnotationEditor {
}
onceAdded() {
if (this.width) {
- this.#cheatInitialRect();
return;
}
this.enableEditMode();
@@ -14085,22 +14132,9 @@ class FreeTextEditor extends AnnotationEditor {
value,
fontSize,
color,
- rect,
pageIndex
} = this.#initialData;
- return serialized.value !== value || serialized.fontSize !== fontSize || serialized.rect.some((x, i) => Math.abs(x - rect[i]) >= 1) || serialized.color.some((c, i) => c !== color[i]) || serialized.pageIndex !== pageIndex;
- }
- #cheatInitialRect(delayed = false) {
- if (!this.annotationElementId) {
- return;
- }
- this.#setEditorDimensions();
- if (!delayed && (this.width === 0 || this.height === 0)) {
- setTimeout(() => this.#cheatInitialRect(true), 0);
- return;
- }
- const padding = FreeTextEditor._internalPadding * this.parentScale;
- this.#initialData.rect = this.getRect(padding, padding);
+ return this._hasBeenMoved || serialized.value !== value || serialized.fontSize !== fontSize || serialized.color.some((c, i) => c !== color[i]) || serialized.pageIndex !== pageIndex;
}
}
@@ -15315,10 +15349,8 @@ class HighlightEditor extends AnnotationEditor {
}
const div = super.render();
if (this.#text) {
- const mark = document.createElement("mark");
- div.append(mark);
- mark.append(document.createTextNode(this.#text));
- mark.className = "visuallyHidden";
+ div.setAttribute("aria-label", this.#text);
+ div.setAttribute("role", "mark");
}
if (this.#isFreeHighlight) {
div.classList.add("free");
@@ -17534,8 +17566,8 @@ class DrawLayer {
-const pdfjsVersion = "4.1.342";
-const pdfjsBuild = "e384df6f1";
+const pdfjsVersion = "4.1.379";
+const pdfjsBuild = "017e49244";
var __webpack_exports__AbortException = __webpack_exports__.AbortException;
var __webpack_exports__AnnotationEditorLayer = __webpack_exports__.AnnotationEditorLayer;
@@ -17561,7 +17593,6 @@ var __webpack_exports__PDFWorker = __webpack_exports__.PDFWorker;
var __webpack_exports__PasswordResponses = __webpack_exports__.PasswordResponses;
var __webpack_exports__PermissionFlag = __webpack_exports__.PermissionFlag;
var __webpack_exports__PixelsPerInch = __webpack_exports__.PixelsPerInch;
-var __webpack_exports__PromiseCapability = __webpack_exports__.PromiseCapability;
var __webpack_exports__RenderingCancelledException = __webpack_exports__.RenderingCancelledException;
var __webpack_exports__UnexpectedResponseException = __webpack_exports__.UnexpectedResponseException;
var __webpack_exports__Util = __webpack_exports__.Util;
@@ -17583,4 +17614,4 @@ var __webpack_exports__setLayerDimensions = __webpack_exports__.setLayerDimensio
var __webpack_exports__shadow = __webpack_exports__.shadow;
var __webpack_exports__updateTextLayer = __webpack_exports__.updateTextLayer;
var __webpack_exports__version = __webpack_exports__.version;
-export { __webpack_exports__AbortException as AbortException, __webpack_exports__AnnotationEditorLayer as AnnotationEditorLayer, __webpack_exports__AnnotationEditorParamsType as AnnotationEditorParamsType, __webpack_exports__AnnotationEditorType as AnnotationEditorType, __webpack_exports__AnnotationEditorUIManager as AnnotationEditorUIManager, __webpack_exports__AnnotationLayer as AnnotationLayer, __webpack_exports__AnnotationMode as AnnotationMode, __webpack_exports__CMapCompressionType as CMapCompressionType, __webpack_exports__ColorPicker as ColorPicker, __webpack_exports__DOMSVGFactory as DOMSVGFactory, __webpack_exports__DrawLayer as DrawLayer, __webpack_exports__FeatureTest as FeatureTest, __webpack_exports__GlobalWorkerOptions as GlobalWorkerOptions, __webpack_exports__ImageKind as ImageKind, __webpack_exports__InvalidPDFException as InvalidPDFException, __webpack_exports__MissingPDFException as MissingPDFException, __webpack_exports__OPS as OPS, __webpack_exports__Outliner as Outliner, __webpack_exports__PDFDataRangeTransport as PDFDataRangeTransport, __webpack_exports__PDFDateString as PDFDateString, __webpack_exports__PDFWorker as PDFWorker, __webpack_exports__PasswordResponses as PasswordResponses, __webpack_exports__PermissionFlag as PermissionFlag, __webpack_exports__PixelsPerInch as PixelsPerInch, __webpack_exports__PromiseCapability as PromiseCapability, __webpack_exports__RenderingCancelledException as RenderingCancelledException, __webpack_exports__UnexpectedResponseException as UnexpectedResponseException, __webpack_exports__Util as Util, __webpack_exports__VerbosityLevel as VerbosityLevel, __webpack_exports__XfaLayer as XfaLayer, __webpack_exports__build as build, __webpack_exports__createValidAbsoluteUrl as createValidAbsoluteUrl, __webpack_exports__fetchData as fetchData, __webpack_exports__getDocument as getDocument, __webpack_exports__getFilenameFromUrl as getFilenameFromUrl, __webpack_exports__getPdfFilenameFromUrl as getPdfFilenameFromUrl, __webpack_exports__getXfaPageViewport as getXfaPageViewport, __webpack_exports__isDataScheme as isDataScheme, __webpack_exports__isPdfFile as isPdfFile, __webpack_exports__noContextMenu as noContextMenu, __webpack_exports__normalizeUnicode as normalizeUnicode, __webpack_exports__renderTextLayer as renderTextLayer, __webpack_exports__setLayerDimensions as setLayerDimensions, __webpack_exports__shadow as shadow, __webpack_exports__updateTextLayer as updateTextLayer, __webpack_exports__version as version };
+export { __webpack_exports__AbortException as AbortException, __webpack_exports__AnnotationEditorLayer as AnnotationEditorLayer, __webpack_exports__AnnotationEditorParamsType as AnnotationEditorParamsType, __webpack_exports__AnnotationEditorType as AnnotationEditorType, __webpack_exports__AnnotationEditorUIManager as AnnotationEditorUIManager, __webpack_exports__AnnotationLayer as AnnotationLayer, __webpack_exports__AnnotationMode as AnnotationMode, __webpack_exports__CMapCompressionType as CMapCompressionType, __webpack_exports__ColorPicker as ColorPicker, __webpack_exports__DOMSVGFactory as DOMSVGFactory, __webpack_exports__DrawLayer as DrawLayer, __webpack_exports__FeatureTest as FeatureTest, __webpack_exports__GlobalWorkerOptions as GlobalWorkerOptions, __webpack_exports__ImageKind as ImageKind, __webpack_exports__InvalidPDFException as InvalidPDFException, __webpack_exports__MissingPDFException as MissingPDFException, __webpack_exports__OPS as OPS, __webpack_exports__Outliner as Outliner, __webpack_exports__PDFDataRangeTransport as PDFDataRangeTransport, __webpack_exports__PDFDateString as PDFDateString, __webpack_exports__PDFWorker as PDFWorker, __webpack_exports__PasswordResponses as PasswordResponses, __webpack_exports__PermissionFlag as PermissionFlag, __webpack_exports__PixelsPerInch as PixelsPerInch, __webpack_exports__RenderingCancelledException as RenderingCancelledException, __webpack_exports__UnexpectedResponseException as UnexpectedResponseException, __webpack_exports__Util as Util, __webpack_exports__VerbosityLevel as VerbosityLevel, __webpack_exports__XfaLayer as XfaLayer, __webpack_exports__build as build, __webpack_exports__createValidAbsoluteUrl as createValidAbsoluteUrl, __webpack_exports__fetchData as fetchData, __webpack_exports__getDocument as getDocument, __webpack_exports__getFilenameFromUrl as getFilenameFromUrl, __webpack_exports__getPdfFilenameFromUrl as getPdfFilenameFromUrl, __webpack_exports__getXfaPageViewport as getXfaPageViewport, __webpack_exports__isDataScheme as isDataScheme, __webpack_exports__isPdfFile as isPdfFile, __webpack_exports__noContextMenu as noContextMenu, __webpack_exports__normalizeUnicode as normalizeUnicode, __webpack_exports__renderTextLayer as renderTextLayer, __webpack_exports__setLayerDimensions as setLayerDimensions, __webpack_exports__shadow as shadow, __webpack_exports__updateTextLayer as updateTextLayer, __webpack_exports__version as version };
diff --git a/toolkit/components/pdfjs/content/build/pdf.scripting.mjs b/toolkit/components/pdfjs/content/build/pdf.scripting.mjs
index f590e83af3..8f4c2a7762 100644
--- a/toolkit/components/pdfjs/content/build/pdf.scripting.mjs
+++ b/toolkit/components/pdfjs/content/build/pdf.scripting.mjs
@@ -3957,8 +3957,8 @@ function initSandbox(params) {
;// CONCATENATED MODULE: ./src/pdf.scripting.js
-const pdfjsVersion = "4.1.342";
-const pdfjsBuild = "e384df6f1";
+const pdfjsVersion = "4.1.379";
+const pdfjsBuild = "017e49244";
globalThis.pdfjsScripting = {
initSandbox: initSandbox
};
diff --git a/toolkit/components/pdfjs/content/build/pdf.worker.mjs b/toolkit/components/pdfjs/content/build/pdf.worker.mjs
index e2cd54cf56..76770f6556 100644
--- a/toolkit/components/pdfjs/content/build/pdf.worker.mjs
+++ b/toolkit/components/pdfjs/content/build/pdf.worker.mjs
@@ -744,24 +744,6 @@ function getModificationDate(date = new Date()) {
const buffer = [date.getUTCFullYear().toString(), (date.getUTCMonth() + 1).toString().padStart(2, "0"), date.getUTCDate().toString().padStart(2, "0"), date.getUTCHours().toString().padStart(2, "0"), date.getUTCMinutes().toString().padStart(2, "0"), date.getUTCSeconds().toString().padStart(2, "0")];
return buffer.join("");
}
-class PromiseCapability {
- #settled = false;
- constructor() {
- this.promise = new Promise((resolve, reject) => {
- this.resolve = data => {
- this.#settled = true;
- resolve(data);
- };
- this.reject = reason => {
- this.#settled = true;
- reject(reason);
- };
- });
- }
- get settled() {
- return this.#settled;
- }
-}
let NormalizeRegex = null;
let NormalizationMap = null;
function normalizeUnicode(str) {
@@ -775,6 +757,17 @@ function getUuid() {
return crypto.randomUUID();
}
const AnnotationPrefix = "pdfjs_internal_id_";
+const FontRenderOps = {
+ BEZIER_CURVE_TO: 0,
+ MOVE_TO: 1,
+ LINE_TO: 2,
+ QUADRATIC_CURVE_TO: 3,
+ RESTORE: 4,
+ SAVE: 5,
+ SCALE: 6,
+ TRANSFORM: 7,
+ TRANSLATE: 8
+};
;// CONCATENATED MODULE: ./src/core/primitives.js
@@ -1789,7 +1782,7 @@ class ChunkedStreamManager {
this._promisesByRequest = new Map();
this.progressiveDataLength = 0;
this.aborted = false;
- this._loadedStreamCapability = new PromiseCapability();
+ this._loadedStreamCapability = Promise.withResolvers();
}
sendRequest(begin, end) {
const rangeReader = this.pdfNetworkStream.getRangeReader(begin, end);
@@ -1852,7 +1845,7 @@ class ChunkedStreamManager {
if (chunksNeeded.size === 0) {
return Promise.resolve();
}
- const capability = new PromiseCapability();
+ const capability = Promise.withResolvers();
this._promisesByRequest.set(requestId, capability);
const chunksToRequest = [];
for (const chunk of chunksNeeded) {
@@ -5075,6 +5068,18 @@ function decodeTextRegion(huffman, refinement, width, height, defaultPixelValue,
symbolHeight += rdh;
symbolBitmap = decodeRefinement(symbolWidth, symbolHeight, refinementTemplateIndex, symbolBitmap, (rdw >> 1) + rdx, (rdh >> 1) + rdy, false, refinementAt, decodingContext);
}
+ let increment = 0;
+ if (!transposed) {
+ if (referenceCorner > 1) {
+ currentS += symbolWidth - 1;
+ } else {
+ increment = symbolWidth - 1;
+ }
+ } else if (!(referenceCorner & 1)) {
+ currentS += symbolHeight - 1;
+ } else {
+ increment = symbolHeight - 1;
+ }
const offsetT = t - (referenceCorner & 1 ? 0 : symbolHeight - 1);
const offsetS = currentS - (referenceCorner & 2 ? symbolWidth - 1 : 0);
let s2, t2, symbolRow;
@@ -5101,7 +5106,6 @@ function decodeTextRegion(huffman, refinement, width, height, defaultPixelValue,
throw new Jbig2Error(`operator ${combinationOperator} is not supported`);
}
}
- currentS += symbolHeight - 1;
} else {
for (t2 = 0; t2 < symbolHeight; t2++) {
row = bitmap[offsetT + t2];
@@ -5124,14 +5128,13 @@ function decodeTextRegion(huffman, refinement, width, height, defaultPixelValue,
throw new Jbig2Error(`operator ${combinationOperator} is not supported`);
}
}
- currentS += symbolWidth - 1;
}
i++;
const deltaS = huffman ? huffmanTables.tableDeltaS.decode(huffmanInput) : decodeInteger(contextCache, "IADS", decoder);
if (deltaS === null) {
break;
}
- currentS += deltaS + dsOffset;
+ currentS += increment + deltaS + dsOffset;
} while (true);
}
return bitmap;
@@ -18702,22 +18705,13 @@ function lookupCmap(ranges, unicode) {
}
function compileGlyf(code, cmds, font) {
function moveTo(x, y) {
- cmds.push({
- cmd: "moveTo",
- args: [x, y]
- });
+ cmds.add(FontRenderOps.MOVE_TO, [x, y]);
}
function lineTo(x, y) {
- cmds.push({
- cmd: "lineTo",
- args: [x, y]
- });
+ cmds.add(FontRenderOps.LINE_TO, [x, y]);
}
function quadraticCurveTo(xa, ya, x, y) {
- cmds.push({
- cmd: "quadraticCurveTo",
- args: [xa, ya, x, y]
- });
+ cmds.add(FontRenderOps.QUADRATIC_CURVE_TO, [xa, ya, x, y]);
}
let i = 0;
const numberOfContours = getInt16(code, i);
@@ -18774,17 +18768,11 @@ function compileGlyf(code, cmds, font) {
}
const subglyph = font.glyphs[glyphIndex];
if (subglyph) {
- cmds.push({
- cmd: "save"
- }, {
- cmd: "transform",
- args: [scaleX, scale01, scale10, scaleY, x, y]
- });
+ cmds.add(FontRenderOps.SAVE);
+ cmds.add(FontRenderOps.TRANSFORM, [scaleX, scale01, scale10, scaleY, x, y]);
if (!(flags & 0x02)) {}
compileGlyf(subglyph, cmds, font);
- cmds.push({
- cmd: "restore"
- });
+ cmds.add(FontRenderOps.RESTORE);
}
} while (flags & 0x20);
} else {
@@ -18874,22 +18862,13 @@ function compileGlyf(code, cmds, font) {
}
function compileCharString(charStringCode, cmds, font, glyphId) {
function moveTo(x, y) {
- cmds.push({
- cmd: "moveTo",
- args: [x, y]
- });
+ cmds.add(FontRenderOps.MOVE_TO, [x, y]);
}
function lineTo(x, y) {
- cmds.push({
- cmd: "lineTo",
- args: [x, y]
- });
+ cmds.add(FontRenderOps.LINE_TO, [x, y]);
}
function bezierCurveTo(x1, y1, x2, y2, x, y) {
- cmds.push({
- cmd: "bezierCurveTo",
- args: [x1, y1, x2, y2, x, y]
- });
+ cmds.add(FontRenderOps.BEZIER_CURVE_TO, [x1, y1, x2, y2, x, y]);
}
const stack = [];
let x = 0,
@@ -19059,17 +19038,11 @@ function compileCharString(charStringCode, cmds, font, glyphId) {
const bchar = stack.pop();
y = stack.pop();
x = stack.pop();
- cmds.push({
- cmd: "save"
- }, {
- cmd: "translate",
- args: [x, y]
- });
+ cmds.add(FontRenderOps.SAVE);
+ cmds.add(FontRenderOps.TRANSLATE, [x, y]);
let cmap = lookupCmap(font.cmap, String.fromCharCode(font.glyphNameMap[StandardEncoding[achar]]));
compileCharString(font.glyphs[cmap.glyphId], cmds, font, cmap.glyphId);
- cmds.push({
- cmd: "restore"
- });
+ cmds.add(FontRenderOps.RESTORE);
cmap = lookupCmap(font.cmap, String.fromCharCode(font.glyphNameMap[StandardEncoding[bchar]]));
compileCharString(font.glyphs[cmap.glyphId], cmds, font, cmap.glyphId);
}
@@ -19236,6 +19209,22 @@ function compileCharString(charStringCode, cmds, font, glyphId) {
parse(charStringCode);
}
const NOOP = [];
+class Commands {
+ cmds = [];
+ add(cmd, args) {
+ if (args) {
+ if (args.some(arg => typeof arg !== "number")) {
+ warn(`Commands.add - "${cmd}" has at least one non-number arg: "${args}".`);
+ const newArgs = args.map(arg => typeof arg === "number" ? arg : 0);
+ this.cmds.push(cmd, ...newArgs);
+ } else {
+ this.cmds.push(cmd, ...args);
+ }
+ } else {
+ this.cmds.push(cmd);
+ }
+ }
+}
class CompiledFont {
constructor(fontMatrix) {
if (this.constructor === CompiledFont) {
@@ -19253,8 +19242,7 @@ class CompiledFont {
let fn = this.compiledGlyphs[glyphId];
if (!fn) {
try {
- fn = this.compileGlyph(this.glyphs[glyphId], glyphId);
- this.compiledGlyphs[glyphId] = fn;
+ fn = this.compiledGlyphs[glyphId] = this.compileGlyph(this.glyphs[glyphId], glyphId);
} catch (ex) {
this.compiledGlyphs[glyphId] = NOOP;
if (this.compiledCharCodeToGlyphId[charCode] === undefined) {
@@ -19282,20 +19270,13 @@ class CompiledFont {
warn("Invalid fd index for glyph index.");
}
}
- const cmds = [{
- cmd: "save"
- }, {
- cmd: "transform",
- args: fontMatrix.slice()
- }, {
- cmd: "scale",
- args: ["size", "-size"]
- }];
+ const cmds = new Commands();
+ cmds.add(FontRenderOps.SAVE);
+ cmds.add(FontRenderOps.TRANSFORM, fontMatrix.slice());
+ cmds.add(FontRenderOps.SCALE);
this.compileGlyphImpl(code, cmds, glyphId);
- cmds.push({
- cmd: "restore"
- });
- return cmds;
+ cmds.add(FontRenderOps.RESTORE);
+ return cmds.cmds;
}
compileGlyphImpl() {
unreachable("Children classes should implement this.");
@@ -25941,14 +25922,18 @@ class Font {
charCodeToGlyphId[mapping.charCode] = mapping.glyphId;
}
forcePostTable = true;
- } else {
+ } else if (cmapPlatformId === 3 && cmapEncodingId === 0) {
for (const mapping of cmapMappings) {
let charCode = mapping.charCode;
- if (cmapPlatformId === 3 && charCode >= 0xf000 && charCode <= 0xf0ff) {
+ if (charCode >= 0xf000 && charCode <= 0xf0ff) {
charCode &= 0xff;
}
charCodeToGlyphId[charCode] = mapping.glyphId;
}
+ } else {
+ for (const mapping of cmapMappings) {
+ charCodeToGlyphId[mapping.charCode] = mapping.glyphId;
+ }
}
if (properties.glyphNames && (baseEncoding.length || this.differences.length)) {
for (let i = 0; i < 256; ++i) {
@@ -27080,7 +27065,13 @@ class MeshShading extends BaseShading {
}
}
getIR() {
- return ["Mesh", this.shadingType, this.coords, this.colors, this.figures, this.bounds, this.bbox, this.background];
+ const {
+ bounds
+ } = this;
+ if (bounds[2] - bounds[0] === 0 || bounds[3] - bounds[1] === 0) {
+ throw new FormatError(`Invalid MeshShading bounds: [${bounds}].`);
+ }
+ return ["Mesh", this.shadingType, this.coords, this.colors, this.figures, bounds, this.bbox, this.background];
}
}
class DummyShading extends BaseShading {
@@ -31794,7 +31785,10 @@ class PartialEvaluator {
if (font.cacheKey && this.fontCache.has(font.cacheKey)) {
return this.fontCache.get(font.cacheKey);
}
- const fontCapability = new PromiseCapability();
+ const {
+ promise,
+ resolve
+ } = Promise.withResolvers();
let preEvaluatedFont;
try {
preEvaluatedFont = this.preEvaluateFont(font);
@@ -31831,14 +31825,14 @@ class PartialEvaluator {
}
assert(fontID?.startsWith("f"), 'The "fontID" must be (correctly) defined.');
if (fontRefIsRef) {
- this.fontCache.put(fontRef, fontCapability.promise);
+ this.fontCache.put(fontRef, promise);
} else {
font.cacheKey = `cacheKey_${fontID}`;
- this.fontCache.put(font.cacheKey, fontCapability.promise);
+ this.fontCache.put(font.cacheKey, promise);
}
font.loadedName = `${this.idFactory.getDocId()}_${fontID}`;
this.translateFont(preEvaluatedFont).then(translatedFont => {
- fontCapability.resolve(new TranslatedFont({
+ resolve(new TranslatedFont({
loadedName: font.loadedName,
font: translatedFont,
dict: font,
@@ -31846,14 +31840,14 @@ class PartialEvaluator {
}));
}).catch(reason => {
warn(`loadFont - translateFont failed: "${reason}".`);
- fontCapability.resolve(new TranslatedFont({
+ resolve(new TranslatedFont({
loadedName: font.loadedName,
font: new ErrorFont(reason instanceof Error ? reason.message : reason),
dict: font,
evaluatorOptions: this.options
}));
});
- return fontCapability.promise;
+ return promise;
}
buildPath(operatorList, fn, args, parsingText = false) {
const lastIndex = operatorList.length - 1;
@@ -31937,19 +31931,33 @@ class PartialEvaluator {
localShadingPatternCache
}) {
let id = localShadingPatternCache.get(shading);
- if (!id) {
- var shadingFill = Pattern.parseShading(shading, this.xref, resources, this._pdfFunctionFactory, localColorSpaceCache);
- const patternIR = shadingFill.getIR();
- id = `pattern_${this.idFactory.createObjId()}`;
- if (this.parsingType3Font) {
- id = `${this.idFactory.getDocId()}_type3_${id}`;
+ if (id) {
+ return id;
+ }
+ let patternIR;
+ try {
+ const shadingFill = Pattern.parseShading(shading, this.xref, resources, this._pdfFunctionFactory, localColorSpaceCache);
+ patternIR = shadingFill.getIR();
+ } catch (reason) {
+ if (reason instanceof AbortException) {
+ return null;
}
- localShadingPatternCache.set(shading, id);
- if (this.parsingType3Font) {
- this.handler.send("commonobj", [id, "Pattern", patternIR]);
- } else {
- this.handler.send("obj", [id, this.pageIndex, "Pattern", patternIR]);
+ if (this.options.ignoreErrors) {
+ warn(`parseShading - ignoring shading: "${reason}".`);
+ localShadingPatternCache.set(shading, null);
+ return null;
}
+ throw reason;
+ }
+ id = `pattern_${this.idFactory.createObjId()}`;
+ if (this.parsingType3Font) {
+ id = `${this.idFactory.getDocId()}_type3_${id}`;
+ }
+ localShadingPatternCache.set(shading, id);
+ if (this.parsingType3Font) {
+ this.handler.send("commonobj", [id, "Pattern", patternIR]);
+ } else {
+ this.handler.send("obj", [id, this.pageIndex, "Pattern", patternIR]);
}
return id;
}
@@ -31975,14 +31983,16 @@ class PartialEvaluator {
return this.handleTilingType(fn, color, resources, pattern, dict, operatorList, task, localTilingPatternCache);
} else if (typeNum === PatternType.SHADING) {
const shading = dict.get("Shading");
- const matrix = dict.getArray("Matrix");
const objId = this.parseShading({
shading,
resources,
localColorSpaceCache,
localShadingPatternCache
});
- operatorList.addOp(fn, ["Shading", objId, matrix]);
+ if (objId) {
+ const matrix = dict.getArray("Matrix");
+ operatorList.addOp(fn, ["Shading", objId, matrix]);
+ }
return undefined;
}
throw new FormatError(`Unknown PatternType: ${typeNum}`);
@@ -32393,6 +32403,9 @@ class PartialEvaluator {
localColorSpaceCache,
localShadingPatternCache
});
+ if (!patternId) {
+ continue;
+ }
args = [patternId];
fn = OPS.shadingFill;
break;
@@ -33998,6 +34011,10 @@ class PartialEvaluator {
systemFontInfo = getFontSubstitution(this.systemFontCache, this.idFactory, this.options.standardFontDataUrl, fontName.name, standardFontName);
}
}
+ let fontMatrix = dict.getArray("FontMatrix");
+ if (!Array.isArray(fontMatrix) || fontMatrix.length !== 6 || fontMatrix.some(x => typeof x !== "number")) {
+ fontMatrix = FONT_IDENTITY_MATRIX;
+ }
const properties = {
type,
name: fontName.name,
@@ -34010,7 +34027,7 @@ class PartialEvaluator {
loadedName: baseDict.loadedName,
composite,
fixedPitch: false,
- fontMatrix: dict.getArray("FontMatrix") || FONT_IDENTITY_MATRIX,
+ fontMatrix,
firstChar,
lastChar,
toUnicode,
@@ -35327,7 +35344,8 @@ function pickPlatformItem(dict) {
return null;
}
class FileSpec {
- constructor(root, xref) {
+ #contentAvailable = false;
+ constructor(root, xref, skipContent = false) {
if (!(root instanceof Dict)) {
return;
}
@@ -35340,10 +35358,12 @@ class FileSpec {
if (root.has("RF")) {
warn("Related file specifications are not supported");
}
- this.contentAvailable = true;
- if (!root.has("EF")) {
- this.contentAvailable = false;
- warn("Non-embedded file specifications are not supported");
+ if (!skipContent) {
+ if (root.has("EF")) {
+ this.#contentAvailable = true;
+ } else {
+ warn("Non-embedded file specifications are not supported");
+ }
}
}
get filename() {
@@ -35354,7 +35374,7 @@ class FileSpec {
return this._filename;
}
get content() {
- if (!this.contentAvailable) {
+ if (!this.#contentAvailable) {
return null;
}
if (!this.contentRef && this.root) {
@@ -38563,7 +38583,7 @@ class Catalog {
continue;
}
if (!outlineDict.has("Title")) {
- throw new FormatError("Invalid outline item encountered.");
+ warn("Invalid outline item encountered.");
}
const data = {
url: null,
@@ -38592,7 +38612,7 @@ class Catalog {
unsafeUrl: data.unsafeUrl,
newWindow: data.newWindow,
setOCGState: data.setOCGState,
- title: stringToPDFString(title),
+ title: typeof title === "string" ? stringToPDFString(title) : "",
color: rgbColor,
count: Number.isInteger(count) ? count : undefined,
bold: !!(flags & 2),
@@ -39598,7 +39618,11 @@ class Catalog {
case "GoToR":
const urlDict = action.get("F");
if (urlDict instanceof Dict) {
- url = urlDict.get("F") || null;
+ const fs = new FileSpec(urlDict, null, true);
+ const {
+ filename
+ } = fs.serializable;
+ url = filename;
} else if (typeof urlDict === "string") {
url = urlDict;
}
@@ -51360,7 +51384,7 @@ class AnnotationBorderStyle {
}
}
setDashArray(dashArray, forceStyle = false) {
- if (Array.isArray(dashArray) && dashArray.length > 0) {
+ if (Array.isArray(dashArray)) {
let isValid = true;
let allZeros = true;
for (const element of dashArray) {
@@ -51372,7 +51396,7 @@ class AnnotationBorderStyle {
allZeros = false;
}
}
- if (isValid && !allZeros) {
+ if (dashArray.length === 0 || isValid && !allZeros) {
this.dashArray = dashArray;
if (forceStyle) {
this.setStyle(Name.get("D"));
@@ -56282,7 +56306,7 @@ class MessageHandler {
}
sendWithPromise(actionName, data, transfers) {
const callbackId = this.callbackId++;
- const capability = new PromiseCapability();
+ const capability = Promise.withResolvers();
this.callbackCapabilities[callbackId] = capability;
try {
this.comObj.postMessage({
@@ -56304,7 +56328,7 @@ class MessageHandler {
comObj = this.comObj;
return new ReadableStream({
start: controller => {
- const startCapability = new PromiseCapability();
+ const startCapability = Promise.withResolvers();
this.streamControllers[streamId] = {
controller,
startCall: startCapability,
@@ -56323,7 +56347,7 @@ class MessageHandler {
return startCapability.promise;
},
pull: controller => {
- const pullCapability = new PromiseCapability();
+ const pullCapability = Promise.withResolvers();
this.streamControllers[streamId].pullCall = pullCapability;
comObj.postMessage({
sourceName,
@@ -56336,7 +56360,7 @@ class MessageHandler {
},
cancel: reason => {
assert(reason instanceof Error, "cancel must have a valid reason");
- const cancelCapability = new PromiseCapability();
+ const cancelCapability = Promise.withResolvers();
this.streamControllers[streamId].cancelCall = cancelCapability;
this.streamControllers[streamId].isClosed = true;
comObj.postMessage({
@@ -56365,7 +56389,7 @@ class MessageHandler {
const lastDesiredSize = this.desiredSize;
this.desiredSize -= size;
if (lastDesiredSize > 0 && this.desiredSize <= 0) {
- this.sinkCapability = new PromiseCapability();
+ this.sinkCapability = Promise.withResolvers();
this.ready = this.sinkCapability.promise;
}
comObj.postMessage({
@@ -56403,7 +56427,7 @@ class MessageHandler {
reason: wrapReason(reason)
});
},
- sinkCapability: new PromiseCapability(),
+ sinkCapability: Promise.withResolvers(),
onPull: null,
onCancel: null,
isCancelled: false,
@@ -56681,7 +56705,7 @@ class WorkerTask {
constructor(name) {
this.name = name;
this.terminated = false;
- this._capability = new PromiseCapability();
+ this._capability = Promise.withResolvers();
}
get finished() {
return this._capability.promise;
@@ -56725,7 +56749,7 @@ class WorkerMessageHandler {
docId,
apiVersion
} = docParams;
- const workerVersion = "4.1.342";
+ const workerVersion = "4.1.379";
if (apiVersion !== workerVersion) {
throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`);
}
@@ -56785,7 +56809,7 @@ class WorkerMessageHandler {
password,
rangeChunkSize
};
- const pdfManagerCapability = new PromiseCapability();
+ const pdfManagerCapability = Promise.withResolvers();
let newPdfManager;
if (data) {
try {
@@ -57287,8 +57311,8 @@ if (typeof window === "undefined" && !isNodeJS && typeof self !== "undefined" &&
;// CONCATENATED MODULE: ./src/pdf.worker.js
-const pdfjsVersion = "4.1.342";
-const pdfjsBuild = "e384df6f1";
+const pdfjsVersion = "4.1.379";
+const pdfjsBuild = "017e49244";
var __webpack_exports__WorkerMessageHandler = __webpack_exports__.WorkerMessageHandler;
export { __webpack_exports__WorkerMessageHandler as WorkerMessageHandler };
diff --git a/toolkit/components/pdfjs/content/web/viewer-geckoview.mjs b/toolkit/components/pdfjs/content/web/viewer-geckoview.mjs
index 866c4a405a..c309f8cf12 100644
--- a/toolkit/components/pdfjs/content/web/viewer-geckoview.mjs
+++ b/toolkit/components/pdfjs/content/web/viewer-geckoview.mjs
@@ -529,7 +529,6 @@ function toggleExpandedBtn(button, toggle, view = null) {
}
;// CONCATENATED MODULE: ./web/app_options.js
-const compatibilityParams = Object.create(null);
const OptionKind = {
BROWSER: 0x01,
VIEWER: 0x02,
@@ -772,11 +771,8 @@ class AppOptions {
constructor() {
throw new Error("Cannot initialize AppOptions.");
}
- static getCompat(name) {
- return compatibilityParams[name] ?? undefined;
- }
static get(name) {
- return userOptions[name] ?? compatibilityParams[name] ?? defaultOptions[name]?.value ?? undefined;
+ return userOptions[name] ?? defaultOptions[name]?.value ?? undefined;
}
static getAll(kind = null, defaultOnly = false) {
const options = Object.create(null);
@@ -785,7 +781,7 @@ class AppOptions {
if (kind && !(kind & defaultOption.kind)) {
continue;
}
- options[name] = defaultOnly ? defaultOption.value : userOptions[name] ?? compatibilityParams[name] ?? defaultOption.value;
+ options[name] = defaultOnly ? defaultOption.value : userOptions[name] ?? defaultOption.value;
}
return options;
}
@@ -1252,7 +1248,6 @@ const {
PDFWorker,
PermissionFlag,
PixelsPerInch,
- PromiseCapability,
RenderingCancelledException,
renderTextLayer,
setLayerDimensions,
@@ -1270,35 +1265,38 @@ const WaitOnType = {
EVENT: "event",
TIMEOUT: "timeout"
};
-function waitOnEventOrTimeout({
+async function waitOnEventOrTimeout({
target,
name,
delay = 0
}) {
- return new Promise(function (resolve, reject) {
- if (typeof target !== "object" || !(name && typeof name === "string") || !(Number.isInteger(delay) && delay >= 0)) {
- throw new Error("waitOnEventOrTimeout - invalid parameters.");
- }
- function handler(type) {
- if (target instanceof EventBus) {
- target._off(name, eventHandler);
- } else {
- target.removeEventListener(name, eventHandler);
- }
- if (timeout) {
- clearTimeout(timeout);
- }
- resolve(type);
- }
- const eventHandler = handler.bind(null, WaitOnType.EVENT);
+ if (typeof target !== "object" || !(name && typeof name === "string") || !(Number.isInteger(delay) && delay >= 0)) {
+ throw new Error("waitOnEventOrTimeout - invalid parameters.");
+ }
+ const {
+ promise,
+ resolve
+ } = Promise.withResolvers();
+ function handler(type) {
if (target instanceof EventBus) {
- target._on(name, eventHandler);
+ target._off(name, eventHandler);
} else {
- target.addEventListener(name, eventHandler);
+ target.removeEventListener(name, eventHandler);
}
- const timeoutHandler = handler.bind(null, WaitOnType.TIMEOUT);
- const timeout = setTimeout(timeoutHandler, delay);
- });
+ if (timeout) {
+ clearTimeout(timeout);
+ }
+ resolve(type);
+ }
+ const eventHandler = handler.bind(null, WaitOnType.EVENT);
+ if (target instanceof EventBus) {
+ target._on(name, eventHandler);
+ } else {
+ target.addEventListener(name, eventHandler);
+ }
+ const timeoutHandler = handler.bind(null, WaitOnType.TIMEOUT);
+ const timeout = setTimeout(timeoutHandler, delay);
+ return promise;
}
class EventBus {
#listeners = Object.create(null);
@@ -2200,10 +2198,8 @@ class PasswordPrompt {
this.dialog.addEventListener("close", this.#cancel.bind(this));
}
async open() {
- if (this.#activeCapability) {
- await this.#activeCapability.promise;
- }
- this.#activeCapability = new PromiseCapability();
+ await this.#activeCapability?.promise;
+ this.#activeCapability = Promise.withResolvers();
try {
await this.overlayManager.open(this.dialog);
} catch (ex) {
@@ -2326,7 +2322,6 @@ function getNormalizeWithNFKC() {
;// CONCATENATED MODULE: ./web/pdf_find_controller.js
-
const FindState = {
FOUND: 0,
NOT_FOUND: 1,
@@ -2664,7 +2659,7 @@ class PDFFindController {
this._dirtyMatch = false;
clearTimeout(this._findTimeout);
this._findTimeout = null;
- this._firstPageCapability = new PromiseCapability();
+ this._firstPageCapability = Promise.withResolvers();
}
get #query() {
const {
@@ -2827,14 +2822,17 @@ class PDFFindController {
if (this._extractTextPromises.length > 0) {
return;
}
- let promise = Promise.resolve();
+ let deferred = Promise.resolve();
const textOptions = {
disableNormalization: true
};
for (let i = 0, ii = this._linkService.pagesCount; i < ii; i++) {
- const extractTextCapability = new PromiseCapability();
- this._extractTextPromises[i] = extractTextCapability.promise;
- promise = promise.then(() => {
+ const {
+ promise,
+ resolve
+ } = Promise.withResolvers();
+ this._extractTextPromises[i] = promise;
+ deferred = deferred.then(() => {
return this._pdfDocument.getPage(i + 1).then(pdfPage => pdfPage.getTextContent(textOptions)).then(textContent => {
const strBuf = [];
for (const textItem of textContent.items) {
@@ -2844,13 +2842,13 @@ class PDFFindController {
}
}
[this._pageContents[i], this._pageDiffs[i], this._hasDiacritics[i]] = normalize(strBuf.join(""));
- extractTextCapability.resolve();
+ resolve();
}, reason => {
console.error(`Unable to get text content for page ${i + 1}`, reason);
this._pageContents[i] = "";
this._pageDiffs[i] = null;
this._hasDiacritics[i] = false;
- extractTextCapability.resolve();
+ resolve();
});
});
}
@@ -3982,7 +3980,7 @@ class PDFScriptingManager {
return;
}
await this.#willPrintCapability?.promise;
- this.#willPrintCapability = new PromiseCapability();
+ this.#willPrintCapability = Promise.withResolvers();
try {
await this.#scripting.dispatchEventInSandbox({
id: "doc",
@@ -4111,7 +4109,7 @@ class PDFScriptingManager {
const pdfDocument = this.#pdfDocument,
visitedPages = this._visitedPages;
if (initialize) {
- this.#closeCapability = new PromiseCapability();
+ this.#closeCapability = Promise.withResolvers();
}
if (!this.#closeCapability) {
return;
@@ -4161,7 +4159,7 @@ class PDFScriptingManager {
});
}
#initScripting() {
- this.#destroyCapability = new PromiseCapability();
+ this.#destroyCapability = Promise.withResolvers();
if (this.#scripting) {
throw new Error("#initScripting: Scripting already exists.");
}
@@ -5116,7 +5114,7 @@ class PDFPageView {
this.#textLayerMode = options.textLayerMode ?? TextLayerMode.ENABLE;
this.#annotationMode = options.annotationMode ?? AnnotationMode.ENABLE_FORMS;
this.imageResourcesPath = options.imageResourcesPath || "";
- this.maxCanvasPixels = options.maxCanvasPixels ?? (AppOptions.getCompat("maxCanvasPixels") || 2 ** 25);
+ this.maxCanvasPixels = options.maxCanvasPixels ?? AppOptions.get("maxCanvasPixels");
this.pageColors = options.pageColors || null;
this.eventBus = options.eventBus;
this.renderingQueue = options.renderingQueue;
@@ -5922,7 +5920,7 @@ class PDFViewer {
#scaleTimeoutId = null;
#textLayerMode = TextLayerMode.ENABLE;
constructor(options) {
- const viewerVersion = "4.1.342";
+ const viewerVersion = "4.1.379";
if (version !== viewerVersion) {
throw new Error(`The API version "${version}" does not match the Viewer version "${viewerVersion}".`);
}
@@ -5977,7 +5975,7 @@ class PDFViewer {
return new Set(this.#buffer);
}
get pageViewsReady() {
- return this._pagesCapability.settled && this._pages.every(pageView => pageView?.pdfPage);
+ return this._pages.every(pageView => pageView?.pdfPage);
}
get renderForms() {
return this.#annotationMode === AnnotationMode.ENABLE_FORMS;
@@ -6280,7 +6278,7 @@ class PDFViewer {
};
this.eventBus._on("pagerender", this._onBeforeDraw);
this._onAfterDraw = evt => {
- if (evt.cssTransform || this._onePageRenderedCapability.settled) {
+ if (evt.cssTransform) {
return;
}
this._onePageRenderedCapability.resolve({
@@ -6452,9 +6450,9 @@ class PDFViewer {
this._location = null;
this._pagesRotation = 0;
this._optionalContentConfigPromise = null;
- this._firstPageCapability = new PromiseCapability();
- this._onePageRenderedCapability = new PromiseCapability();
- this._pagesCapability = new PromiseCapability();
+ this._firstPageCapability = Promise.withResolvers();
+ this._onePageRenderedCapability = Promise.withResolvers();
+ this._pagesCapability = Promise.withResolvers();
this._scrollMode = ScrollMode.VERTICAL;
this._previousScrollMode = ScrollMode.UNKNOWN;
this._spreadMode = SpreadMode.NONE;
@@ -7475,7 +7473,10 @@ const ViewOnLoad = {
};
const PDFViewerApplication = {
initialBookmark: document.location.hash.substring(1),
- _initializedCapability: new PromiseCapability(),
+ _initializedCapability: {
+ ...Promise.withResolvers(),
+ settled: false
+ },
appConfig: null,
pdfDocument: null,
pdfLoadingTask: null,
@@ -7550,6 +7551,7 @@ const PDFViewerApplication = {
await this._initializeViewerComponents();
this.bindEvents();
this.bindWindowEvents();
+ this._initializedCapability.settled = true;
this._initializedCapability.resolve();
},
async _parseHashParams() {
@@ -8608,6 +8610,10 @@ const PDFViewerApplication = {
mediaQueryList.addEventListener("change", addWindowResolutionChange, {
once: true
});
+ _boundEvents.removeWindowResolutionChange ||= function () {
+ mediaQueryList.removeEventListener("change", addWindowResolutionChange);
+ _boundEvents.removeWindowResolutionChange = null;
+ };
}
addWindowResolutionChange();
_boundEvents.windowResize = () => {
@@ -9531,8 +9537,8 @@ function webViewerReportTelemetry({
-const pdfjsVersion = "4.1.342";
-const pdfjsBuild = "e384df6f1";
+const pdfjsVersion = "4.1.379";
+const pdfjsBuild = "017e49244";
const AppConstants = null;
window.PDFViewerApplication = PDFViewerApplication;
window.PDFViewerApplicationConstants = AppConstants;
diff --git a/toolkit/components/pdfjs/content/web/viewer.css b/toolkit/components/pdfjs/content/web/viewer.css
index 2999c89f3a..063fa1632b 100644
--- a/toolkit/components/pdfjs/content/web/viewer.css
+++ b/toolkit/components/pdfjs/content/web/viewer.css
@@ -3513,7 +3513,7 @@ a:is(.toolbarButton, .secondaryToolbarButton)[href="#"]{
transition-property:none;
.loadingInput:has(> &.loading)::after{
- display: block;
+ display:block;
visibility:visible;
transition-property:visibility;
@@ -3525,7 +3525,7 @@ a:is(.toolbarButton, .secondaryToolbarButton)[href="#"]{
&::after{
position:absolute;
visibility:hidden;
- display: none;
+ display:none;
top:calc(50% - 8px);
width:16px;
height:16px;
diff --git a/toolkit/components/pdfjs/content/web/viewer.mjs b/toolkit/components/pdfjs/content/web/viewer.mjs
index 778ce57e1a..864140b624 100644
--- a/toolkit/components/pdfjs/content/web/viewer.mjs
+++ b/toolkit/components/pdfjs/content/web/viewer.mjs
@@ -529,7 +529,6 @@ function toggleExpandedBtn(button, toggle, view = null) {
}
;// CONCATENATED MODULE: ./web/app_options.js
-const compatibilityParams = Object.create(null);
const OptionKind = {
BROWSER: 0x01,
VIEWER: 0x02,
@@ -772,11 +771,8 @@ class AppOptions {
constructor() {
throw new Error("Cannot initialize AppOptions.");
}
- static getCompat(name) {
- return compatibilityParams[name] ?? undefined;
- }
static get(name) {
- return userOptions[name] ?? compatibilityParams[name] ?? defaultOptions[name]?.value ?? undefined;
+ return userOptions[name] ?? defaultOptions[name]?.value ?? undefined;
}
static getAll(kind = null, defaultOnly = false) {
const options = Object.create(null);
@@ -785,7 +781,7 @@ class AppOptions {
if (kind && !(kind & defaultOption.kind)) {
continue;
}
- options[name] = defaultOnly ? defaultOption.value : userOptions[name] ?? compatibilityParams[name] ?? defaultOption.value;
+ options[name] = defaultOnly ? defaultOption.value : userOptions[name] ?? defaultOption.value;
}
return options;
}
@@ -1252,7 +1248,6 @@ const {
PDFWorker,
PermissionFlag,
PixelsPerInch,
- PromiseCapability,
RenderingCancelledException,
renderTextLayer,
setLayerDimensions,
@@ -1270,35 +1265,38 @@ const WaitOnType = {
EVENT: "event",
TIMEOUT: "timeout"
};
-function waitOnEventOrTimeout({
+async function waitOnEventOrTimeout({
target,
name,
delay = 0
}) {
- return new Promise(function (resolve, reject) {
- if (typeof target !== "object" || !(name && typeof name === "string") || !(Number.isInteger(delay) && delay >= 0)) {
- throw new Error("waitOnEventOrTimeout - invalid parameters.");
- }
- function handler(type) {
- if (target instanceof EventBus) {
- target._off(name, eventHandler);
- } else {
- target.removeEventListener(name, eventHandler);
- }
- if (timeout) {
- clearTimeout(timeout);
- }
- resolve(type);
- }
- const eventHandler = handler.bind(null, WaitOnType.EVENT);
+ if (typeof target !== "object" || !(name && typeof name === "string") || !(Number.isInteger(delay) && delay >= 0)) {
+ throw new Error("waitOnEventOrTimeout - invalid parameters.");
+ }
+ const {
+ promise,
+ resolve
+ } = Promise.withResolvers();
+ function handler(type) {
if (target instanceof EventBus) {
- target._on(name, eventHandler);
+ target._off(name, eventHandler);
} else {
- target.addEventListener(name, eventHandler);
+ target.removeEventListener(name, eventHandler);
}
- const timeoutHandler = handler.bind(null, WaitOnType.TIMEOUT);
- const timeout = setTimeout(timeoutHandler, delay);
- });
+ if (timeout) {
+ clearTimeout(timeout);
+ }
+ resolve(type);
+ }
+ const eventHandler = handler.bind(null, WaitOnType.EVENT);
+ if (target instanceof EventBus) {
+ target._on(name, eventHandler);
+ } else {
+ target.addEventListener(name, eventHandler);
+ }
+ const timeoutHandler = handler.bind(null, WaitOnType.TIMEOUT);
+ const timeout = setTimeout(timeoutHandler, delay);
+ return promise;
}
class EventBus {
#listeners = Object.create(null);
@@ -2480,10 +2478,8 @@ class PasswordPrompt {
this.dialog.addEventListener("close", this.#cancel.bind(this));
}
async open() {
- if (this.#activeCapability) {
- await this.#activeCapability.promise;
- }
- this.#activeCapability = new PromiseCapability();
+ await this.#activeCapability?.promise;
+ this.#activeCapability = Promise.withResolvers();
try {
await this.overlayManager.open(this.dialog);
} catch (ex) {
@@ -2642,7 +2638,7 @@ class PDFAttachmentViewer extends BaseTreeViewer {
super.reset();
this._attachments = null;
if (!keepRenderedCapability) {
- this._renderedCapability = new PromiseCapability();
+ this._renderedCapability = Promise.withResolvers();
}
this._pendingDispatchEvent = false;
}
@@ -3039,7 +3035,7 @@ class PDFDocumentProperties {
#reset() {
this.pdfDocument = null;
this.#fieldData = null;
- this._dataAvailableCapability = new PromiseCapability();
+ this._dataAvailableCapability = Promise.withResolvers();
this._currentPageNumber = 1;
this._pagesRotation = 0;
}
@@ -3214,7 +3210,6 @@ function getNormalizeWithNFKC() {
;// CONCATENATED MODULE: ./web/pdf_find_controller.js
-
const FindState = {
FOUND: 0,
NOT_FOUND: 1,
@@ -3552,7 +3547,7 @@ class PDFFindController {
this._dirtyMatch = false;
clearTimeout(this._findTimeout);
this._findTimeout = null;
- this._firstPageCapability = new PromiseCapability();
+ this._firstPageCapability = Promise.withResolvers();
}
get #query() {
const {
@@ -3715,14 +3710,17 @@ class PDFFindController {
if (this._extractTextPromises.length > 0) {
return;
}
- let promise = Promise.resolve();
+ let deferred = Promise.resolve();
const textOptions = {
disableNormalization: true
};
for (let i = 0, ii = this._linkService.pagesCount; i < ii; i++) {
- const extractTextCapability = new PromiseCapability();
- this._extractTextPromises[i] = extractTextCapability.promise;
- promise = promise.then(() => {
+ const {
+ promise,
+ resolve
+ } = Promise.withResolvers();
+ this._extractTextPromises[i] = promise;
+ deferred = deferred.then(() => {
return this._pdfDocument.getPage(i + 1).then(pdfPage => pdfPage.getTextContent(textOptions)).then(textContent => {
const strBuf = [];
for (const textItem of textContent.items) {
@@ -3732,13 +3730,13 @@ class PDFFindController {
}
}
[this._pageContents[i], this._pageDiffs[i], this._hasDiacritics[i]] = normalize(strBuf.join(""));
- extractTextCapability.resolve();
+ resolve();
}, reason => {
console.error(`Unable to get text content for page ${i + 1}`, reason);
this._pageContents[i] = "";
this._pageDiffs[i] = null;
this._hasDiacritics[i] = false;
- extractTextCapability.resolve();
+ resolve();
});
});
}
@@ -4722,7 +4720,6 @@ class PDFLayerViewer extends BaseTreeViewer {
;// CONCATENATED MODULE: ./web/pdf_outline_viewer.js
-
class PDFOutlineViewer extends BaseTreeViewer {
constructor(options) {
super(options);
@@ -4735,9 +4732,7 @@ class PDFOutlineViewer extends BaseTreeViewer {
});
this.eventBus._on("pagesloaded", evt => {
this._isPagesLoaded = !!evt.pagesCount;
- if (this._currentOutlineItemCapability && !this._currentOutlineItemCapability.settled) {
- this._currentOutlineItemCapability.resolve(this._isPagesLoaded);
- }
+ this._currentOutlineItemCapability?.resolve(this._isPagesLoaded);
});
this.eventBus._on("sidebarviewchanged", evt => {
this._sidebarView = evt.view;
@@ -4749,13 +4744,11 @@ class PDFOutlineViewer extends BaseTreeViewer {
this._pageNumberToDestHashCapability = null;
this._currentPageNumber = 1;
this._isPagesLoaded = null;
- if (this._currentOutlineItemCapability && !this._currentOutlineItemCapability.settled) {
- this._currentOutlineItemCapability.resolve(false);
- }
+ this._currentOutlineItemCapability?.resolve(false);
this._currentOutlineItemCapability = null;
}
_dispatchEvent(outlineCount) {
- this._currentOutlineItemCapability = new PromiseCapability();
+ this._currentOutlineItemCapability = Promise.withResolvers();
if (outlineCount === 0 || this._pdfDocument?.loadingParams.disableAutoFetch) {
this._currentOutlineItemCapability.resolve(false);
} else if (this._isPagesLoaded !== null) {
@@ -4937,7 +4930,7 @@ class PDFOutlineViewer extends BaseTreeViewer {
if (this._pageNumberToDestHashCapability) {
return this._pageNumberToDestHashCapability.promise;
}
- this._pageNumberToDestHashCapability = new PromiseCapability();
+ this._pageNumberToDestHashCapability = Promise.withResolvers();
const pageNumberToDestHash = new Map(),
pageNumberNesting = new Map();
const queue = [{
@@ -5752,7 +5745,7 @@ class PDFScriptingManager {
return;
}
await this.#willPrintCapability?.promise;
- this.#willPrintCapability = new PromiseCapability();
+ this.#willPrintCapability = Promise.withResolvers();
try {
await this.#scripting.dispatchEventInSandbox({
id: "doc",
@@ -5881,7 +5874,7 @@ class PDFScriptingManager {
const pdfDocument = this.#pdfDocument,
visitedPages = this._visitedPages;
if (initialize) {
- this.#closeCapability = new PromiseCapability();
+ this.#closeCapability = Promise.withResolvers();
}
if (!this.#closeCapability) {
return;
@@ -5931,7 +5924,7 @@ class PDFScriptingManager {
});
}
#initScripting() {
- this.#destroyCapability = new PromiseCapability();
+ this.#destroyCapability = Promise.withResolvers();
if (this.#scripting) {
throw new Error("#initScripting: Scripting already exists.");
}
@@ -6409,7 +6402,7 @@ class PDFThumbnailView {
}
this.resume = null;
}
- _getPageDrawContext(upscaleFactor = 1) {
+ #getPageDrawContext(upscaleFactor = 1) {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d", {
alpha: false
@@ -6424,11 +6417,11 @@ class PDFThumbnailView {
transform
};
}
- _convertCanvasToImage(canvas) {
+ #convertCanvasToImage(canvas) {
if (this.renderingState !== RenderingStates.FINISHED) {
- throw new Error("_convertCanvasToImage: Rendering has not finished.");
+ throw new Error("#convertCanvasToImage: Rendering has not finished.");
}
- const reducedCanvas = this._reduceImage(canvas);
+ const reducedCanvas = this.#reduceImage(canvas);
const image = document.createElement("img");
image.className = "thumbnailImage";
image.setAttribute("data-l10n-id", "pdfjs-thumb-page-canvas");
@@ -6448,7 +6441,7 @@ class PDFThumbnailView {
return;
}
this.renderingState = RenderingStates.FINISHED;
- this._convertCanvasToImage(canvas);
+ this.#convertCanvasToImage(canvas);
if (error) {
throw error;
}
@@ -6470,7 +6463,7 @@ class PDFThumbnailView {
ctx,
canvas,
transform
- } = this._getPageDrawContext(DRAW_UPSCALE_FACTOR);
+ } = this.#getPageDrawContext(DRAW_UPSCALE_FACTOR);
const drawViewport = this.viewport.clone({
scale: DRAW_UPSCALE_FACTOR * this.scale
});
@@ -6525,13 +6518,13 @@ class PDFThumbnailView {
return;
}
this.renderingState = RenderingStates.FINISHED;
- this._convertCanvasToImage(canvas);
+ this.#convertCanvasToImage(canvas);
}
- _reduceImage(img) {
+ #reduceImage(img) {
const {
ctx,
canvas
- } = this._getPageDrawContext();
+ } = this.#getPageDrawContext();
if (img.width <= 2 * canvas.width) {
ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height);
return canvas;
@@ -6585,16 +6578,16 @@ class PDFThumbnailViewer {
this.linkService = linkService;
this.renderingQueue = renderingQueue;
this.pageColors = pageColors || null;
- this.scroll = watchScroll(this.container, this._scrollUpdated.bind(this));
- this._resetView();
+ this.scroll = watchScroll(this.container, this.#scrollUpdated.bind(this));
+ this.#resetView();
}
- _scrollUpdated() {
+ #scrollUpdated() {
this.renderingQueue.renderHighestPriority();
}
getThumbnail(index) {
return this._thumbnails[index];
}
- _getVisibleThumbs() {
+ #getVisibleThumbs() {
return getVisibleElements({
scrollEl: this.container,
views: this._thumbnails
@@ -6618,7 +6611,7 @@ class PDFThumbnailViewer {
first,
last,
views
- } = this._getVisibleThumbs();
+ } = this.#getVisibleThumbs();
if (views.length > 0) {
let shouldScroll = false;
if (pageNumber <= first.id || pageNumber >= last.id) {
@@ -6672,7 +6665,7 @@ class PDFThumbnailViewer {
}
TempImageFactory.destroyCanvas();
}
- _resetView() {
+ #resetView() {
this._thumbnails = [];
this._currentPageNumber = 1;
this._pageLabels = null;
@@ -6681,8 +6674,8 @@ class PDFThumbnailViewer {
}
setDocument(pdfDocument) {
if (this.pdfDocument) {
- this._cancelRendering();
- this._resetView();
+ this.#cancelRendering();
+ this.#resetView();
}
this.pdfDocument = pdfDocument;
if (!pdfDocument) {
@@ -6717,7 +6710,7 @@ class PDFThumbnailViewer {
console.error("Unable to initialize thumbnail viewer", reason);
});
}
- _cancelRendering() {
+ #cancelRendering() {
for (const thumbnail of this._thumbnails) {
thumbnail.cancelRendering();
}
@@ -6762,7 +6755,7 @@ class PDFThumbnailViewer {
return this.scroll.down;
}
forceRendering() {
- const visibleThumbs = this._getVisibleThumbs();
+ const visibleThumbs = this.#getVisibleThumbs();
const scrollAhead = this.#getScrollAhead(visibleThumbs);
const thumbView = this.renderingQueue.getHighestPriority(visibleThumbs, this._thumbnails, scrollAhead);
if (thumbView) {
@@ -7694,7 +7687,7 @@ class PDFPageView {
this.#textLayerMode = options.textLayerMode ?? TextLayerMode.ENABLE;
this.#annotationMode = options.annotationMode ?? AnnotationMode.ENABLE_FORMS;
this.imageResourcesPath = options.imageResourcesPath || "";
- this.maxCanvasPixels = options.maxCanvasPixels ?? (AppOptions.getCompat("maxCanvasPixels") || 2 ** 25);
+ this.maxCanvasPixels = options.maxCanvasPixels ?? AppOptions.get("maxCanvasPixels");
this.pageColors = options.pageColors || null;
this.eventBus = options.eventBus;
this.renderingQueue = options.renderingQueue;
@@ -8500,7 +8493,7 @@ class PDFViewer {
#scaleTimeoutId = null;
#textLayerMode = TextLayerMode.ENABLE;
constructor(options) {
- const viewerVersion = "4.1.342";
+ const viewerVersion = "4.1.379";
if (version !== viewerVersion) {
throw new Error(`The API version "${version}" does not match the Viewer version "${viewerVersion}".`);
}
@@ -8555,7 +8548,7 @@ class PDFViewer {
return new Set(this.#buffer);
}
get pageViewsReady() {
- return this._pagesCapability.settled && this._pages.every(pageView => pageView?.pdfPage);
+ return this._pages.every(pageView => pageView?.pdfPage);
}
get renderForms() {
return this.#annotationMode === AnnotationMode.ENABLE_FORMS;
@@ -8858,7 +8851,7 @@ class PDFViewer {
};
this.eventBus._on("pagerender", this._onBeforeDraw);
this._onAfterDraw = evt => {
- if (evt.cssTransform || this._onePageRenderedCapability.settled) {
+ if (evt.cssTransform) {
return;
}
this._onePageRenderedCapability.resolve({
@@ -9030,9 +9023,9 @@ class PDFViewer {
this._location = null;
this._pagesRotation = 0;
this._optionalContentConfigPromise = null;
- this._firstPageCapability = new PromiseCapability();
- this._onePageRenderedCapability = new PromiseCapability();
- this._pagesCapability = new PromiseCapability();
+ this._firstPageCapability = Promise.withResolvers();
+ this._onePageRenderedCapability = Promise.withResolvers();
+ this._pagesCapability = Promise.withResolvers();
this._scrollMode = ScrollMode.VERTICAL;
this._previousScrollMode = ScrollMode.UNKNOWN;
this._spreadMode = SpreadMode.NONE;
@@ -10543,7 +10536,10 @@ const ViewOnLoad = {
};
const PDFViewerApplication = {
initialBookmark: document.location.hash.substring(1),
- _initializedCapability: new PromiseCapability(),
+ _initializedCapability: {
+ ...Promise.withResolvers(),
+ settled: false
+ },
appConfig: null,
pdfDocument: null,
pdfLoadingTask: null,
@@ -10617,6 +10613,7 @@ const PDFViewerApplication = {
await this._initializeViewerComponents();
this.bindEvents();
this.bindWindowEvents();
+ this._initializedCapability.settled = true;
this._initializedCapability.resolve();
},
async _parseHashParams() {
@@ -11708,6 +11705,10 @@ const PDFViewerApplication = {
mediaQueryList.addEventListener("change", addWindowResolutionChange, {
once: true
});
+ _boundEvents.removeWindowResolutionChange ||= function () {
+ mediaQueryList.removeEventListener("change", addWindowResolutionChange);
+ _boundEvents.removeWindowResolutionChange = null;
+ };
}
addWindowResolutionChange();
_boundEvents.windowResize = () => {
@@ -12631,8 +12632,8 @@ function webViewerReportTelemetry({
-const pdfjsVersion = "4.1.342";
-const pdfjsBuild = "e384df6f1";
+const pdfjsVersion = "4.1.379";
+const pdfjsBuild = "017e49244";
const AppConstants = null;
window.PDFViewerApplication = PDFViewerApplication;
window.PDFViewerApplicationConstants = AppConstants;
diff --git a/toolkit/components/pdfjs/moz.yaml b/toolkit/components/pdfjs/moz.yaml
index bd85f67f5a..0938b8d987 100644
--- a/toolkit/components/pdfjs/moz.yaml
+++ b/toolkit/components/pdfjs/moz.yaml
@@ -20,8 +20,8 @@ origin:
# Human-readable identifier for this version/release
# Generally "version NNN", "tag SSS", "bookmark SSS"
- release: e384df6f16b9ad1c55d1bc325bcae7e096ef3f9b (2024-03-27T19:54:30Z).
- revision: e384df6f16b9ad1c55d1bc325bcae7e096ef3f9b
+ release: a208d6bca72f8cb700f7bb506ea1bc3e76cbaec0 (2024-04-09T07:48:17Z).
+ revision: a208d6bca72f8cb700f7bb506ea1bc3e76cbaec0
# The package's license, where possible using the mnemonic from
# https://spdx.org/licenses/
diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_editing_contextmenu.js b/toolkit/components/pdfjs/test/browser_pdfjs_editing_contextmenu.js
index 779a3a6ad4..f13d03d643 100644
--- a/toolkit/components/pdfjs/test/browser_pdfjs_editing_contextmenu.js
+++ b/toolkit/components/pdfjs/test/browser_pdfjs_editing_contextmenu.js
@@ -130,7 +130,7 @@ function assertMenuitems(menuitems, expected) {
elmt =>
!elmt.id.includes("-sep-") &&
!elmt.hidden &&
- ["", "false"].includes(elmt.getAttribute("disabled"))
+ [null, "false"].includes(elmt.getAttribute("disabled"))
)
.map(elmt => elmt.id),
expected
diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_find.js b/toolkit/components/pdfjs/test/browser_pdfjs_find.js
index 24a3a50967..b3b3e11152 100644
--- a/toolkit/components/pdfjs/test/browser_pdfjs_find.js
+++ b/toolkit/components/pdfjs/test/browser_pdfjs_find.js
@@ -13,7 +13,7 @@ const OS_PDF_URL = TESTROOT + "file_pdfjs_object_stream.pdf";
const TEST_PDF_URL = TESTROOT + "file_pdfjs_test.pdf";
add_task(async function test_find_octet_stream_pdf() {
- await BrowserTestUtils.withNewTab(OS_PDF_URL, async browser => {
+ await BrowserTestUtils.withNewTab(OS_PDF_URL, async () => {
let findEls = ["cmd_find", "cmd_findAgain", "cmd_findPrevious"].map(id =>
document.getElementById(id)
);
diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_force_opening_files.js b/toolkit/components/pdfjs/test/browser_pdfjs_force_opening_files.js
index 0490a6eec3..f09581daa7 100644
--- a/toolkit/components/pdfjs/test/browser_pdfjs_force_opening_files.js
+++ b/toolkit/components/pdfjs/test/browser_pdfjs_force_opening_files.js
@@ -13,7 +13,7 @@ add_task(async function test_file_opening() {
// the default - because files from disk should always use pdfjs, unless
// it is forcibly disabled.
let openedWindow = false;
- let windowOpenedPromise = new Promise((resolve, reject) => {
+ let windowOpenedPromise = new Promise(resolve => {
addWindowListener(
"chrome://mozapps/content/downloads/unknownContentType.xhtml",
() => {
@@ -82,7 +82,7 @@ function addWindowListener(aURL, aCallback) {
aCallback();
}, domwindow);
},
- onCloseWindow(aXULWindow) {},
+ onCloseWindow() {},
};
Services.wm.addListener(listener);
listenerCleanup = () => Services.wm.removeListener(listener);
diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_fullscreen.js b/toolkit/components/pdfjs/test/browser_pdfjs_fullscreen.js
index d53e4cfa74..4add7a4715 100644
--- a/toolkit/components/pdfjs/test/browser_pdfjs_fullscreen.js
+++ b/toolkit/components/pdfjs/test/browser_pdfjs_fullscreen.js
@@ -8,7 +8,7 @@ function waitForFullScreenState(browser, state) {
return new Promise(resolve => {
let eventReceived = false;
- let observe = (subject, topic, data) => {
+ let observe = () => {
if (!eventReceived) {
return;
}
diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_navigation.js b/toolkit/components/pdfjs/test/browser_pdfjs_navigation.js
index 9851150e80..5fda1ac8b2 100644
--- a/toolkit/components/pdfjs/test/browser_pdfjs_navigation.js
+++ b/toolkit/components/pdfjs/test/browser_pdfjs_navigation.js
@@ -229,7 +229,7 @@ async function contentSetUp() {
return new Promise(resolve => {
document.addEventListener(
"pagerendered",
- function (e) {
+ function () {
document.querySelector("#viewer").click();
resolve();
},
diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_octet_stream.js b/toolkit/components/pdfjs/test/browser_pdfjs_octet_stream.js
index d2b4fe310f..8e81ddb2c5 100644
--- a/toolkit/components/pdfjs/test/browser_pdfjs_octet_stream.js
+++ b/toolkit/components/pdfjs/test/browser_pdfjs_octet_stream.js
@@ -88,7 +88,7 @@ add_task(async function test_octet_stream_in_frame() {
await BrowserTestUtils.withNewTab(
{ gBrowser, url: `data:text/html,<iframe src='${PDF_URL}'>` },
- async function (newTabBrowser) {
+ async function () {
// wait until downloadsPanel opens before continuing with test
info("Waiting for download panel to open");
await downloadsPanelPromise;
diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_savedialog.js b/toolkit/components/pdfjs/test/browser_pdfjs_savedialog.js
index 4f0c952ebb..e514eaf530 100644
--- a/toolkit/components/pdfjs/test/browser_pdfjs_savedialog.js
+++ b/toolkit/components/pdfjs/test/browser_pdfjs_savedialog.js
@@ -48,6 +48,6 @@ function addWindowListener(aURL, aCallback) {
aCallback();
}, domwindow);
},
- onCloseWindow(aXULWindow) {},
+ onCloseWindow() {},
});
}
diff --git a/toolkit/components/places/PlacesFrecencyRecalculator.sys.mjs b/toolkit/components/places/PlacesFrecencyRecalculator.sys.mjs
index 7c52c508ee..a1df8cac89 100644
--- a/toolkit/components/places/PlacesFrecencyRecalculator.sys.mjs
+++ b/toolkit/components/places/PlacesFrecencyRecalculator.sys.mjs
@@ -59,12 +59,31 @@ XPCOMUtils.defineLazyPreferenceGetter(
90
);
+// This pref stores whether recalculation should be faster.
+// It is set when we detect that a lot of changes happened recently, and it
+// will survive restarts. Once there's nothing left to recalculate, we unset
+// the pref and return to the normal recalculation rate.
+// Note this getter transforms the boolean pref value into an integer
+// acceleration rate.
+const PREF_ACCELERATE_RECALCULATION = "places.frecency.accelerateRecalculation";
+XPCOMUtils.defineLazyPreferenceGetter(
+ lazy,
+ "accelerationRate",
+ PREF_ACCELERATE_RECALCULATION,
+ false,
+ null,
+ accelerate => (accelerate ? 2 : 1)
+);
+
// Time between deferred task executions.
const DEFERRED_TASK_INTERVAL_MS = 2 * 60000;
// Maximum time to wait for an idle before the task is executed anyway.
const DEFERRED_TASK_MAX_IDLE_WAIT_MS = 5 * 60000;
// Number of entries to update at once.
const DEFAULT_CHUNK_SIZE = 50;
+// Threshold used to evaluate whether the number of Places events from the last
+// recalculation is high enough to deserve a recalculation rate increase.
+const ACCELERATION_EVENTS_THRESHOLD = 250;
export class PlacesFrecencyRecalculator {
classID = Components.ID("1141fd31-4c1a-48eb-8f1a-2f05fad94085");
@@ -104,11 +123,8 @@ export class PlacesFrecencyRecalculator {
return;
}
- this.#task = new lazy.DeferredTask(
- this.#taskFn.bind(this),
- DEFERRED_TASK_INTERVAL_MS,
- DEFERRED_TASK_MAX_IDLE_WAIT_MS
- );
+ this.#createOrUpdateTask();
+
lazy.AsyncShutdown.profileChangeTeardown.addBlocker(
"PlacesFrecencyRecalculator: shutdown",
() => this.#finalize()
@@ -133,6 +149,22 @@ export class PlacesFrecencyRecalculator {
this.maybeStartFrecencyRecalculation();
}
+ #createOrUpdateTask() {
+ let wasArmed = this.#task?.isArmed;
+ if (this.#task) {
+ this.#task.disarm();
+ this.#task.finalize().catch(console.error);
+ }
+ this.#task = new lazy.DeferredTask(
+ this.#taskFn.bind(this),
+ DEFERRED_TASK_INTERVAL_MS / lazy.accelerationRate,
+ DEFERRED_TASK_MAX_IDLE_WAIT_MS / lazy.accelerationRate
+ );
+ if (wasArmed) {
+ this.#task.arm();
+ }
+ }
+
async #taskFn() {
if (this.#task.isFinalized) {
return;
@@ -161,6 +193,40 @@ export class PlacesFrecencyRecalculator {
this.#task.finalize().catch(console.error);
}
+ #lastEventsCount = 0;
+
+ /**
+ * Evaluates whether recalculation speed should be increased, and eventually
+ * accelerates.
+ * @returns {boolean} whether the recalculation rate is increased.
+ */
+ maybeUpdateRecalculationSpeed() {
+ if (lazy.accelerationRate > 1) {
+ return true;
+ }
+ // We mostly care about additions to cover the common case of importing
+ // bookmarks or history. We may care about removals, but in most cases they
+ // reduce the number of entries to recalculate.
+ let eventsCount =
+ PlacesObservers.counts.get("page-visited") +
+ PlacesObservers.counts.get("bookmark-added");
+ let accelerate =
+ eventsCount - this.#lastEventsCount > ACCELERATION_EVENTS_THRESHOLD;
+ if (accelerate) {
+ Services.prefs.setBoolPref(PREF_ACCELERATE_RECALCULATION, true);
+ this.#createOrUpdateTask();
+ }
+ this.#lastEventsCount = eventsCount;
+ return accelerate;
+ }
+
+ #resetRecalculationSpeed() {
+ if (lazy.accelerationRate > 1) {
+ Services.prefs.clearUserPref(PREF_ACCELERATE_RECALCULATION);
+ this.#createOrUpdateTask();
+ }
+ }
+
/**
* Updates a chunk of outdated frecency values. If there's more frecency
* values to update at the end of the process, it may rearm the task.
@@ -169,6 +235,8 @@ export class PlacesFrecencyRecalculator {
* @resolves {boolean} Whether any entry was recalculated.
*/
async recalculateSomeFrecencies({ chunkSize = DEFAULT_CHUNK_SIZE } = {}) {
+ // In case of acceleration we don't bump up the chunkSize to avoid issues
+ // with slow disk systems.
lazy.logger.trace(
`Recalculate ${chunkSize >= 0 ? chunkSize : "infinite"} frecency values`
);
@@ -182,7 +250,7 @@ export class PlacesFrecencyRecalculator {
WHERE id IN (
SELECT id FROM moz_places
WHERE recalc_frecency = 1
- ORDER BY frecency DESC
+ ORDER BY frecency DESC, visit_count DESC
LIMIT ${chunkSize}
)
RETURNING id`
@@ -212,8 +280,10 @@ export class PlacesFrecencyRecalculator {
if (chunkSize > 0 && shouldRestartRecalculation) {
// There's more entries to recalculate, rearm the task.
+ this.maybeUpdateRecalculationSpeed();
this.#task.arm();
} else {
+ this.#resetRecalculationSpeed();
// There's nothing left to recalculate, wait for the next change.
lazy.PlacesUtils.history.shouldStartFrecencyRecalculation = false;
this.#task.disarm();
@@ -360,6 +430,7 @@ export class PlacesFrecencyRecalculator {
return;
case "frecency-recalculation-needed":
lazy.logger.trace("Frecency recalculation requested");
+ this.maybeUpdateRecalculationSpeed();
this.maybeStartFrecencyRecalculation();
return;
case "test-execute-taskFn":
diff --git a/toolkit/components/places/PlacesQuery.sys.mjs b/toolkit/components/places/PlacesQuery.sys.mjs
index 674e49b0ba..7ddf5ee988 100644
--- a/toolkit/components/places/PlacesQuery.sys.mjs
+++ b/toolkit/components/places/PlacesQuery.sys.mjs
@@ -58,7 +58,7 @@ export class PlacesQuery {
#historyListenerCallback = null;
/** @type {DeferredTask} */
#historyObserverTask = null;
- #searchInProgress = false;
+ searchInProgress = false;
/**
* Get a snapshot of history visits at this moment.
@@ -169,11 +169,11 @@ export class PlacesQuery {
GROUP BY url
ORDER BY ${orderBy}
LIMIT ${limit > 0 ? limit : -1}`;
- if (this.#searchInProgress) {
+ if (this.searchInProgress) {
db.interrupt();
}
try {
- this.#searchInProgress = true;
+ this.searchInProgress = true;
const rows = await db.executeCached(sql, {
query,
matchBehavior: Ci.mozIPlacesAutoComplete.MATCH_ANYWHERE_UNMODIFIED,
@@ -181,7 +181,7 @@ export class PlacesQuery {
});
return rows.map(row => this.formatRowAsVisit(row));
} finally {
- this.#searchInProgress = false;
+ this.searchInProgress = false;
}
}
diff --git a/toolkit/components/places/PlacesTransactions.sys.mjs b/toolkit/components/places/PlacesTransactions.sys.mjs
index bd0b7654a3..be922ec53f 100644
--- a/toolkit/components/places/PlacesTransactions.sys.mjs
+++ b/toolkit/components/places/PlacesTransactions.sys.mjs
@@ -31,11 +31,12 @@
* A note about GUIDs and item-ids
* -------------------------------
* There's an ongoing effort (see bug 1071511) to deprecate item-ids in Places
- * in favor of GUIDs. Both because new APIs (e.g. Bookmark.jsm) expose them to
- * the minimum necessary, and because GUIDs play much better with implementing
- * |redo|, this API doesn't support item-ids at all, and only accepts bookmark
- * GUIDs, both for input (e.g. for setting the parent folder for a new bookmark)
- * and for output (when the GUID for such a bookmark is propagated).
+ * in favor of GUIDs. Both because new APIs (e.g. Bookmarks.sys.mjs) expose them
+ * to the minimum necessary, and because GUIDs play much better with
+ * implementing |redo|, this API doesn't support item-ids at all, and only
+ * accepts bookmark GUIDs, both for input (e.g. for setting the parent folder
+ * for a new bookmark) and for output (when the GUID for such a bookmark is
+ * propagated).
*
* Constructing transactions
* -------------------------
diff --git a/toolkit/components/places/SQLFunctions.cpp b/toolkit/components/places/SQLFunctions.cpp
index 85c5cc8d17..de12b73583 100644
--- a/toolkit/components/places/SQLFunctions.cpp
+++ b/toolkit/components/places/SQLFunctions.cpp
@@ -1134,16 +1134,15 @@ GetQueryParamFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
RefPtr<nsVariant> result = new nsVariant();
if (!queryString.IsEmpty() && !paramName.IsEmpty()) {
- URLParams::Parse(
- queryString, true,
- [&paramName, &result](const nsAString& aName, const nsAString& aValue) {
- NS_ConvertUTF16toUTF8 name(aName);
- if (!paramName.Equals(name)) {
- return true;
- }
- result->SetAsAString(aValue);
- return false;
- });
+ URLParams::Parse(queryString, true,
+ [&paramName, &result](const nsACString& aName,
+ const nsACString& aValue) {
+ if (!paramName.Equals(aName)) {
+ return true;
+ }
+ result->SetAsACString(aValue);
+ return false;
+ });
}
result.forget(_result);
diff --git a/toolkit/components/places/SyncedBookmarksMirror.sys.mjs b/toolkit/components/places/SyncedBookmarksMirror.sys.mjs
index a09e1b7eb3..c68761a086 100644
--- a/toolkit/components/places/SyncedBookmarksMirror.sys.mjs
+++ b/toolkit/components/places/SyncedBookmarksMirror.sys.mjs
@@ -2060,11 +2060,7 @@ function validateURL(rawURL) {
if (typeof rawURL != "string" || rawURL.length > DB_URL_LENGTH_MAX) {
return null;
}
- let url = null;
- try {
- url = new URL(rawURL);
- } catch (ex) {}
- return url;
+ return URL.parse(rawURL);
}
function validateKeyword(rawKeyword) {
diff --git a/toolkit/components/places/bookmark_sync/src/store.rs b/toolkit/components/places/bookmark_sync/src/store.rs
index 6e8d8024b4..e5cf973e5d 100644
--- a/toolkit/components/places/bookmark_sync/src/store.rs
+++ b/toolkit/components/places/bookmark_sync/src/store.rs
@@ -1219,7 +1219,6 @@ trait Column<T> {
fn from_column(raw: T) -> Result<Self>
where
Self: Sized;
- fn into_column(self) -> T;
}
impl Column<i64> for Kind {
@@ -1233,16 +1232,6 @@ impl Column<i64> for Kind {
_ => return Err(Error::UnknownItemKind(raw)),
})
}
-
- fn into_column(self) -> i64 {
- match self {
- Kind::Bookmark => mozISyncedBookmarksMerger::KIND_BOOKMARK as i64,
- Kind::Query => mozISyncedBookmarksMerger::KIND_QUERY as i64,
- Kind::Folder => mozISyncedBookmarksMerger::KIND_FOLDER as i64,
- Kind::Livemark => mozISyncedBookmarksMerger::KIND_LIVEMARK as i64,
- Kind::Separator => mozISyncedBookmarksMerger::KIND_SEPARATOR as i64,
- }
- }
}
impl Column<i64> for Validity {
@@ -1254,14 +1243,6 @@ impl Column<i64> for Validity {
_ => return Err(Error::UnknownItemValidity(raw).into()),
})
}
-
- fn into_column(self) -> i64 {
- match self {
- Validity::Valid => mozISyncedBookmarksMerger::VALIDITY_VALID as i64,
- Validity::Reupload => mozISyncedBookmarksMerger::VALIDITY_REUPLOAD as i64,
- Validity::Replace => mozISyncedBookmarksMerger::VALIDITY_REPLACE as i64,
- }
- }
}
/// Formats an optional value so that it can be included in a SQL statement.
diff --git a/toolkit/components/places/mozIAsyncHistory.idl b/toolkit/components/places/mozIAsyncHistory.idl
index 88690b4798..27487ebc9b 100644
--- a/toolkit/components/places/mozIAsyncHistory.idl
+++ b/toolkit/components/places/mozIAsyncHistory.idl
@@ -107,8 +107,8 @@ interface mozIVisitInfoCallback : nsISupports
* handleResult and handleError, respectively, if/once
* results/errors occur.
*/
- readonly attribute bool ignoreResults;
- readonly attribute bool ignoreErrors;
+ readonly attribute boolean ignoreResults;
+ readonly attribute boolean ignoreErrors;
};
[scriptable, function, uuid(994092bf-936f-449b-8dd6-0941e024360d)]
diff --git a/toolkit/components/places/mozISyncedBookmarksMirror.idl b/toolkit/components/places/mozISyncedBookmarksMirror.idl
index 0ceaeff5ad..701b7a128d 100644
--- a/toolkit/components/places/mozISyncedBookmarksMirror.idl
+++ b/toolkit/components/places/mozISyncedBookmarksMirror.idl
@@ -34,7 +34,7 @@ interface mozISyncedBookmarksMirrorProgressListener : nsISupports {
// A callback called when the merge finishes.
[scriptable, uuid(d23fdfea-92c8-409d-a516-08ae395d578f)]
interface mozISyncedBookmarksMirrorCallback : nsISupports {
- void handleSuccess(in bool result);
+ void handleSuccess(in boolean result);
void handleError(in nsresult code, in AString message);
};
diff --git a/toolkit/components/places/tests/bookmarks/test_sync_fields.js b/toolkit/components/places/tests/bookmarks/test_sync_fields.js
index 0a6f53fb85..b7b2ab4a1a 100644
--- a/toolkit/components/places/tests/bookmarks/test_sync_fields.js
+++ b/toolkit/components/places/tests/bookmarks/test_sync_fields.js
@@ -330,7 +330,7 @@ async function findTagFolder(tag) {
return results.length ? results[0].getResultByName("guid") : null;
}
-// Exercises the new, async calls implemented in `Bookmarks.jsm`.
+// Exercises the new, async calls implemented in `Bookmarks.sys.mjs`.
class AsyncTestCases extends TestCases {
async createFolder(parentGuid, title, index) {
let item = await PlacesUtils.bookmarks.insert({
diff --git a/toolkit/components/places/tests/browser/browser_visituri.js b/toolkit/components/places/tests/browser/browser_visituri.js
index 529c010e63..6aafe084e7 100644
--- a/toolkit/components/places/tests/browser/browser_visituri.js
+++ b/toolkit/components/places/tests/browser/browser_visituri.js
@@ -139,7 +139,7 @@ add_task(async function test_userpass() {
);
// Open the target link as background.
- await ContentTask.spawn(gBrowser.selectedBrowser, null, async args => {
+ await ContentTask.spawn(gBrowser.selectedBrowser, null, async () => {
let link = content.document.getElementById("target-userpass");
EventUtils.synthesizeMouseAtCenter(
link,
diff --git a/toolkit/components/places/tests/browser/previews/browser_thumbnails.js b/toolkit/components/places/tests/browser/previews/browser_thumbnails.js
index 27f4fa3745..4fbbafccf9 100644
--- a/toolkit/components/places/tests/browser/previews/browser_thumbnails.js
+++ b/toolkit/components/places/tests/browser/previews/browser_thumbnails.js
@@ -2,7 +2,7 @@
* https://creativecommons.org/publicdomain/zero/1.0/ */
/**
- * Tests PlacesPreviews.jsm
+ * Tests PlacesPreviews.sys.mjs
*/
const { PlacesPreviews } = ChromeUtils.importESModule(
"resource://gre/modules/PlacesPreviews.sys.mjs"
diff --git a/toolkit/components/places/tests/history/test_hasVisits.js b/toolkit/components/places/tests/history/test_hasVisits.js
index 36fc9fd7be..5c7b56a8f3 100644
--- a/toolkit/components/places/tests/history/test_hasVisits.js
+++ b/toolkit/components/places/tests/history/test_hasVisits.js
@@ -1,7 +1,7 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
-// Tests for `History.hasVisits` as implemented in History.jsm
+// Tests for `History.hasVisits` as implemented in History.sys.mjs
"use strict";
diff --git a/toolkit/components/places/tests/history/test_insert.js b/toolkit/components/places/tests/history/test_insert.js
index a3a820ade9..ce83199516 100644
--- a/toolkit/components/places/tests/history/test_insert.js
+++ b/toolkit/components/places/tests/history/test_insert.js
@@ -1,7 +1,7 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
-// Tests for `History.insert` as implemented in History.jsm
+// Tests for `History.insert` as implemented in History.sys.mjs
"use strict";
diff --git a/toolkit/components/places/tests/history/test_insertMany.js b/toolkit/components/places/tests/history/test_insertMany.js
index 3d0774cbf2..4fe1481d85 100644
--- a/toolkit/components/places/tests/history/test_insertMany.js
+++ b/toolkit/components/places/tests/history/test_insertMany.js
@@ -1,7 +1,7 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
-// Tests for `History.insertMany` as implemented in History.jsm
+// Tests for `History.insertMany` as implemented in History.sys.mjs
"use strict";
diff --git a/toolkit/components/places/tests/history/test_remove.js b/toolkit/components/places/tests/history/test_remove.js
index 8c5e941fd0..c018a996f3 100644
--- a/toolkit/components/places/tests/history/test_remove.js
+++ b/toolkit/components/places/tests/history/test_remove.js
@@ -1,7 +1,7 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
-// Tests for `History.remove`, as implemented in History.jsm
+// Tests for `History.remove`, as implemented in History.sys.mjs
"use strict";
diff --git a/toolkit/components/places/tests/history/test_removeMany.js b/toolkit/components/places/tests/history/test_removeMany.js
index ff8c3a21ee..5c955af0e1 100644
--- a/toolkit/components/places/tests/history/test_removeMany.js
+++ b/toolkit/components/places/tests/history/test_removeMany.js
@@ -2,7 +2,7 @@
/* vim:set ts=2 sw=2 sts=2 et: */
// Tests for `History.remove` with removing many urls, as implemented in
-// History.jsm.
+// History.sys.mjs.
"use strict";
diff --git a/toolkit/components/places/tests/history/test_removeVisitsByFilter.js b/toolkit/components/places/tests/history/test_removeVisitsByFilter.js
index be01fcb901..1045245d89 100644
--- a/toolkit/components/places/tests/history/test_removeVisitsByFilter.js
+++ b/toolkit/components/places/tests/history/test_removeVisitsByFilter.js
@@ -1,7 +1,7 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
-// Tests for `History.removeVisitsByFilter`, as implemented in History.jsm
+// Tests for `History.removeVisitsByFilter`, as implemented in History.sys.mjs
"use strict";
diff --git a/toolkit/components/places/tests/history/test_update.js b/toolkit/components/places/tests/history/test_update.js
index d7beafd368..df5bcc7195 100644
--- a/toolkit/components/places/tests/history/test_update.js
+++ b/toolkit/components/places/tests/history/test_update.js
@@ -3,7 +3,7 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
-// Tests for `History.update` as implemented in History.jsm
+// Tests for `History.update` as implemented in History.sys.mjs
"use strict";
diff --git a/toolkit/components/places/tests/sync/test_bookmark_mirror_migration.js b/toolkit/components/places/tests/sync/test_bookmark_mirror_migration.js
index 86cf45eb0f..774ee9a1bb 100644
--- a/toolkit/components/places/tests/sync/test_bookmark_mirror_migration.js
+++ b/toolkit/components/places/tests/sync/test_bookmark_mirror_migration.js
@@ -1,7 +1,7 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
-// Keep in sync with `SyncedBookmarksMirror.jsm`.
+// Keep in sync with `SyncedBookmarksMirror.sys.mjs`.
const CURRENT_MIRROR_SCHEMA_VERSION = 9;
// The oldest schema version that we support. Any databases with schemas older
diff --git a/toolkit/components/places/tests/unit/test_PlacesObservers_counts.js b/toolkit/components/places/tests/unit/test_PlacesObservers_counts.js
new file mode 100644
index 0000000000..3eb7c2d2a9
--- /dev/null
+++ b/toolkit/components/places/tests/unit/test_PlacesObservers_counts.js
@@ -0,0 +1,58 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test PlacesObservers.counts.
+
+add_task(async function test_counts() {
+ const url = "http://example.com/title";
+ await PlacesUtils.history.insertMany([
+ {
+ title: "will change",
+ url,
+ visits: [{ transition: TRANSITION_LINK }],
+ },
+ {
+ title: "changed",
+ url,
+ referrer: url,
+ visits: [{ transition: TRANSITION_LINK }],
+ },
+ {
+ title: "another",
+ url: "http://example.com/another",
+ visits: [{ transition: TRANSITION_LINK }],
+ },
+ ]);
+ await PlacesUtils.bookmarks.insert({
+ url,
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ });
+ await PlacesUtils.history.clear();
+ await PlacesUtils.bookmarks.eraseEverything;
+
+ Assert.strictEqual(
+ PlacesObservers.counts.get("non-existing"),
+ undefined,
+ "Check non existing event returns undefined"
+ );
+ Assert.strictEqual(
+ PlacesObservers.counts.get("page-removed"),
+ 0,
+ "Check non fired event returns 0"
+ );
+ Assert.strictEqual(
+ PlacesObservers.counts.get("page-visited"),
+ 3,
+ "Check fired event `page-visited`"
+ );
+ Assert.strictEqual(
+ PlacesObservers.counts.get("history-cleared"),
+ 1,
+ "Check fired event `history-cleared`"
+ );
+ Assert.strictEqual(
+ PlacesObservers.counts.get("bookmark-added"),
+ 1,
+ "Check fired event `bookmark-added`"
+ );
+});
diff --git a/toolkit/components/places/tests/unit/test_frecency_observers.js b/toolkit/components/places/tests/unit/test_frecency_observers.js
index 44747b06f9..bc7a7ca099 100644
--- a/toolkit/components/places/tests/unit/test_frecency_observers.js
+++ b/toolkit/components/places/tests/unit/test_frecency_observers.js
@@ -37,7 +37,7 @@ add_task(async function test_nsNavHistory_UpdateFrecency() {
await promise;
});
-// History.jsm invalidateFrecencies()
+// History.sys.mjs invalidateFrecencies()
add_task(async function test_invalidateFrecencies() {
let url = Services.io.newURI("http://test-invalidateFrecencies.com/");
// Bookmarking the URI is enough to add it to moz_places, and importantly, it
@@ -54,7 +54,7 @@ add_task(async function test_invalidateFrecencies() {
await promise;
});
-// History.jsm clear() should not cause a frecency recalculation since pages
+// History.sys.mjs clear() should not cause a frecency recalculation since pages
// are removed.
add_task(async function test_clear() {
let received = [];
diff --git a/toolkit/components/places/tests/unit/test_frecency_recalculator.js b/toolkit/components/places/tests/unit/test_frecency_recalculator.js
index ed4501c56a..414882eaf5 100644
--- a/toolkit/components/places/tests/unit/test_frecency_recalculator.js
+++ b/toolkit/components/places/tests/unit/test_frecency_recalculator.js
@@ -176,3 +176,19 @@ add_task(async function test_chunk_time_telemetry() {
"Should still not have set shouldStartFrecencyRecalculation"
);
});
+
+add_task(async function test_acceleration() {
+ await PlacesTestUtils.addVisits(
+ new Array(300).fill("https://www.mozilla.org/")
+ );
+
+ Assert.ok(
+ PlacesFrecencyRecalculator.maybeUpdateRecalculationSpeed(),
+ "Recalculation accelerated"
+ );
+ await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
+ Assert.ok(
+ !PlacesFrecencyRecalculator.maybeUpdateRecalculationSpeed(),
+ "Recalculation back to normal rate"
+ );
+});
diff --git a/toolkit/components/places/tests/unit/test_origins_parsing.js b/toolkit/components/places/tests/unit/test_origins_parsing.js
index bdeabce271..8372e9fb66 100644
--- a/toolkit/components/places/tests/unit/test_origins_parsing.js
+++ b/toolkit/components/places/tests/unit/test_origins_parsing.js
@@ -71,7 +71,7 @@ add_task(async function parsing() {
// The history cannot be deleted at a URL with a user path.
} else {
expectedOrigins = expectedOrigins.filter(
- ([prefix, hostPort]) => !prefix.startsWith(uri.scheme + ":")
+ ([prefix]) => !prefix.startsWith(uri.scheme + ":")
);
}
await checkDB(expectedOrigins);
diff --git a/toolkit/components/places/tests/unit/xpcshell.toml b/toolkit/components/places/tests/unit/xpcshell.toml
index 8a56fcc370..62de0c0db7 100644
--- a/toolkit/components/places/tests/unit/xpcshell.toml
+++ b/toolkit/components/places/tests/unit/xpcshell.toml
@@ -179,6 +179,8 @@ prefs = ["places.frecency.pages.alternative.featureGate=true"]
["test_pageGuid_bookmarkGuid.js"]
+["test_PlacesObservers_counts.js"]
+
["test_placeURIs.js"]
["test_promiseBookmarksTree.js"]
diff --git a/toolkit/components/printing/content/print.css b/toolkit/components/printing/content/print.css
index c510c58bbc..fe84c93eb0 100644
--- a/toolkit/components/printing/content/print.css
+++ b/toolkit/components/printing/content/print.css
@@ -2,7 +2,12 @@
* 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/. */
-html, body {
+:root {
+ font: menu;
+}
+
+:root,
+body {
height: 100vh;
}
@@ -13,21 +18,17 @@ body {
overflow: hidden;
}
-body[loading] #print {
- visibility: hidden;
-}
-
*[hidden] {
display: none !important;
}
.section-block {
- margin: 16px;
-}
+ margin: var(--space-large);
-.section-block .block-label {
- display: block;
- margin-bottom: 8px;
+ .block-label {
+ display: block;
+ margin-bottom: var(--space-small);
+ }
}
.row {
@@ -35,24 +36,23 @@ body[loading] #print {
flex-direction: column;
width: 100%;
min-height: 1.8em;
- margin-block: 2px;
-}
+ margin-block: var(--space-xxsmall);
-.row.cols-2 {
- flex-direction: row;
- align-items: center;
-}
+ &.cols-2 {
+ flex-direction: row;
+ align-items: center;
-#scale .row.cols-2,
-#more-settings-options > .row.cols-2 {
- /* The margin-block of the checkboxes/radiobuttons
- already provide the needed vertical spacing. */
- margin-block: 0;
+ #scale &,
+ #more-settings-options > & {
+ /* The margin-block of the checkboxes/radiobuttons
+ already provide the needed vertical spacing. */
+ margin-block: 0;
+ }
+ }
}
hr {
- margin-inline: 8px;
- margin-block: 0;
+ margin: 0 var(--space-small);
}
.header-container {
@@ -61,236 +61,210 @@ hr {
justify-content: space-between;
align-items: center;
flex: 0 1 auto;
- padding: 8px 18px;
-}
-.header-container > h2 {
- margin: 0;
- font-size: 17px;
- line-height: 1;
+ padding: var(--space-small) var(--space-large);
+
+ > h2 {
+ margin: 0;
+ line-height: 1;
+ }
}
#sheet-count {
- font-size: 11px;
- padding: 3px 8px;
+ font: message-box;
+ padding-block: var(--space-xsmall);
margin: 0;
-}
-#sheet-count[loading],
-body[invalid] #sheet-count {
- visibility: hidden;
+ &[loading],
+ body[invalid] & {
+ visibility: hidden;
+ }
}
-form#print {
+#print {
display: flex;
flex: 1 1 auto;
flex-direction: column;
justify-content: flex-start;
overflow: hidden;
- font: menu;
+
+ body[loading] & {
+ visibility: hidden;
+ }
+}
+
+.body-container {
+ flex: 1 1 auto;
+ overflow: auto;
}
select {
margin: 0;
-}
+ width: 100%;
-select:not([size], [multiple])[iconic] {
- padding-inline-start: calc(8px + 16px + 4px); /* spacing before image + image width + spacing after image */
- background-size: auto 12px, 16px;
- background-position: right 19px center, left 8px center;
-}
+ &:not([size], [multiple])[iconic] {
+ padding-inline-start: calc(8px + 16px + 4px); /* spacing before image + image width + spacing after image */
+ background-size: auto 12px, 16px;
+ background-position: right 19px center, left 8px center;
-select:not([size], [multiple])[iconic]:dir(rtl) {
- background-position-x: left 19px, right 8px;
+ &:dir(rtl) {
+ background-position-x: left 19px, right 8px;
+ }
+ }
}
#printer-picker {
background-image: url("chrome://global/skin/icons/arrow-down-12.svg"), url("chrome://global/skin/icons/print.svg");
- width: 100%;
-}
-
-#printer-picker[output="pdf"] {
- background-image: url("chrome://global/skin/icons/arrow-down-12.svg"), url("chrome://global/skin/icons/page-portrait.svg");
-}
-input[type="number"],
-input[type="text"] {
- padding: 2px;
- padding-inline-start: 8px;
+ &[output="pdf"] {
+ background-image: url("chrome://global/skin/icons/arrow-down-12.svg"), url("chrome://global/skin/icons/page-portrait.svg");
+ }
}
-.toggle-group-label {
- padding: 4px 8px;
+input:is([type="number"], [type="text"]) {
+ padding: var(--space-xxsmall);
+ padding-inline-start: var(--space-small);
}
-.body-container {
- flex: 1 1 auto;
- overflow: auto;
+input[type="number"] {
+ box-sizing: content-box;
+ min-height: unset;
+ padding: 0;
+ padding-inline-start: var(--space-small);
+ margin: 0;
+ height: 1.5em;
+ width: 4em;
}
-.twisty > summary {
- list-style: none;
- display: flex;
- cursor: pointer;
- align-items: center;
+input:is([type="checkbox"], [type="radio"]):disabled + label,
+input[type="radio"]:disabled + span > label {
+ opacity: 0.5;
}
-#more-settings > summary > .twisty {
- background-image: url("chrome://global/skin/icons/arrow-down-12.svg");
- background-repeat: no-repeat;
- background-position: center;
- -moz-context-properties: fill;
- fill: currentColor;
- width: 12px;
- height: 12px;
+#custom-range {
+ margin: 0;
+ margin-top: var(--space-xsmall);
+ width: 100%;
}
-#more-settings > summary > .label {
- flex-grow: 1;
-}
+#percent-scale {
+ margin-inline-start: var(--space-xsmall);
-#more-settings[open] > summary > .twisty {
- background-image: url("chrome://global/skin/icons/arrow-up-12.svg");
+ &:disabled {
+ /* Let clicks on the disabled input select the radio button */
+ pointer-events: none;
+ }
}
-#open-dialog-link {
- display: flex;
- justify-content: space-between;
- align-items: center;
-}
+.toggle-group-label {
+ padding: var(--space-xsmall) var(--space-small);
-#open-dialog-link::after {
- display: block;
- flex-shrink: 0;
- content: "";
- background: url(chrome://global/skin/icons/open-in-new.svg) no-repeat center;
- background-size: 12px;
- -moz-context-properties: fill;
- fill: currentColor;
- width: 12px;
- height: 12px;
-}
+ #landscape + &::before {
+ content: url("chrome://global/skin/icons/page-landscape.svg");
+ }
-#open-dialog-link:dir(rtl)::after {
- scale: -1 1;
+ #portrait + &::before {
+ content: url("chrome://global/skin/icons/page-portrait.svg");
+ }
}
-.footer-container {
- flex: 0 1 auto;
-}
+#more-settings {
+ > summary {
+ list-style: none;
+ display: flex;
+ cursor: pointer;
+ align-items: center;
-#print-progress {
- background-image: image-set(
- url("chrome://global/skin/icons/loading.png"),
- url("chrome://global/skin/icons/loading@2x.png") 2x
- );
- background-repeat: no-repeat;
- background-size: 16px;
- background-position: left center;
- padding-inline-start: 20px;
-}
+ > .twisty {
+ background-image: url("chrome://global/skin/icons/arrow-down-12.svg");
+ background-repeat: no-repeat;
+ background-position: center;
+ -moz-context-properties: fill;
+ fill: currentColor;
+ width: 12px;
+ height: 12px;
+ }
-#print-progress:dir(rtl) {
- background-position-x: right;
-}
+ > .label {
+ flex-grow: 1;
+ }
+ }
-#custom-range {
- margin-top: 4px;
- margin-inline: 0;
+ &[open] > summary > .twisty {
+ background-image: url("chrome://global/skin/icons/arrow-up-12.svg");
+ }
}
.vertical-margins,
.horizontal-margins {
display: flex;
- gap: 20px;
- margin-block: 5px;
-}
+ gap: var(--space-large);
+ margin-block: var(--space-xsmall);
-.margin-pair {
- width: calc(50% - 10px); /* Half minus the gap */
-}
+ > .margin-pair {
+ width: calc(50% - (2 * var(--space-xsmall))); /* Half minus the gap */
-.margin-input {
- /* FIXME: Important is needed to override the photon-number styles below,
- * probably should be refactored */
- width: 100% !important;
- box-sizing: border-box;
-}
+ > .margin-descriptor {
+ display: block;
+ text-align: center;
+ margin-top: var(--space-xxsmall);
+ }
-.margin-descriptor {
- text-align: center;
- display: block;
- margin-top: 2px;
+ > .margin-input {
+ width: 100%;
+ box-sizing: border-box;
+ }
+ }
}
-.toggle-group #landscape + .toggle-group-label::before {
- content: url("chrome://global/skin/icons/page-landscape.svg");
-}
-.toggle-group #portrait + .toggle-group-label::before {
- content: url("chrome://global/skin/icons/page-portrait.svg");
-}
-
-.error-message {
- font-size: 12px;
- color: var(--text-color-error);
- margin-top: 4px;
-}
-
-#percent-scale {
- margin-inline-start: 4px;
+.horizontal-margins:dir(rtl) {
+ /* Swap between the left and right margin inputs to match their position
+ * in the form to their physical location they represent. */
+ flex-direction: row-reverse;
}
-#percent-scale:disabled {
- /* Let clicks on the disabled input select the radio button */
- pointer-events: none;
-}
+#open-dialog-link {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: var(--space-xsmall);
-input[type="number"].photon-number {
- padding: 0;
- padding-inline-start: 8px;
- margin: 0;
- height: 1.5em;
- width: 4em;
-}
+ &::after {
+ display: block;
+ flex-shrink: 0;
+ content: "";
+ background: url(chrome://global/skin/icons/open-in-new.svg) no-repeat center;
+ background-size: 12px;
+ -moz-context-properties: fill;
+ fill: currentColor;
+ width: 12px;
+ height: 12px;
+ }
-input[type="number"].photon-number::-moz-number-spin-box {
- height: 100%;
- max-height: 100%;
- border-inline-start: 1px solid var(--in-content-box-border-color);
- width: 1em;
- border-start-end-radius: 4px;
- border-end-end-radius: 4px;
+ &:dir(rtl)::after {
+ transform: scaleX(-1);
+ }
}
-input[type="number"].photon-number:hover::-moz-number-spin-box {
- border-color: var(--in-content-border-hover);
+.footer-container {
+ flex: 0 1 auto;
}
-input[type="number"].photon-number::-moz-number-spin-up,
-input[type="number"].photon-number::-moz-number-spin-down {
- border: 0;
- border-radius: 0;
- background-color: var(--in-content-button-background);
- background-image: url("chrome://global/skin/icons/arrow-down.svg");
+#print-progress {
+ background-image: image-set(
+ url("chrome://global/skin/icons/loading.png"),
+ url("chrome://global/skin/icons/loading@2x.png") 2x
+ );
background-repeat: no-repeat;
- background-size: 8px;
- background-position: center;
- -moz-context-properties: fill;
- fill: currentColor;
- appearance: none;
-}
-
-input[type="number"].photon-number::-moz-number-spin-up {
- background-image: url("chrome://global/skin/icons/arrow-up.svg");
-}
+ background-size: var(--size-item-small);
+ background-position: left center;
+ padding-inline-start: calc(var(--size-item-small) + var(--space-xsmall));
-input[type="number"].photon-number::-moz-number-spin-up:hover,
-input[type="number"].photon-number::-moz-number-spin-down:hover {
- background-color: var(--in-content-button-background-hover);
- color: var(--in-content-button-text-color-hover);
+ &:dir(rtl) {
+ background-position-x: right;
+ }
}
-input[type="checkbox"]:disabled + label,
-input[type="radio"]:disabled + label,
-input[type="radio"]:disabled + span > label {
- opacity: 0.5;
+.error-message {
+ color: var(--text-color-error);
+ margin-top: var(--space-xsmall);
}
diff --git a/toolkit/components/printing/content/print.html b/toolkit/components/printing/content/print.html
index 62d214608c..4e129c3f66 100644
--- a/toolkit/components/printing/content/print.html
+++ b/toolkit/components/printing/content/print.html
@@ -136,7 +136,6 @@
will filter this down to visible items only. -->
<input
id="percent-scale"
- class="photon-number"
is="setting-number"
min="10"
max="200"
@@ -166,7 +165,7 @@
data-setting-name="numCopies"
min="1"
max="10000"
- class="copy-count-input photon-number"
+ class="copy-count-input"
aria-errormessage="error-invalid-copies"
required
/>
@@ -189,7 +188,6 @@
is="margins-select"
id="margins-picker"
name="margin-type"
- class="row"
data-setting-name="margins"
>
<option value="default" data-l10n-id="printui-margins-default"></option>
@@ -206,7 +204,7 @@
<input
is="setting-number"
id="custom-margin-top"
- class="margin-input photon-number"
+ class="margin-input"
aria-describedby="margins-custom-margin-top-desc"
min="0"
step="0.01"
@@ -227,7 +225,7 @@
<input
is="setting-number"
id="custom-margin-bottom"
- class="margin-input photon-number"
+ class="margin-input"
aria-describedby="margins-custom-margin-bottom-desc"
min="0"
step="0.01"
@@ -250,7 +248,7 @@
<input
is="setting-number"
id="custom-margin-left"
- class="margin-input photon-number"
+ class="margin-input"
aria-describedby="margins-custom-margin-left-desc"
min="0"
step="0.01"
@@ -271,7 +269,7 @@
<input
is="setting-number"
id="custom-margin-right"
- class="margin-input photon-number"
+ class="margin-input"
aria-describedby="margins-custom-margin-right-desc"
min="0"
step="0.01"
@@ -322,14 +320,12 @@
class="block-label"
data-l10n-id="printui-destination-label"
></label>
- <div class="printer-picker-wrapper">
- <select
- is="destination-picker"
- id="printer-picker"
- data-setting-name="printerName"
- iconic
- ></select>
- </div>
+ <select
+ is="destination-picker"
+ id="printer-picker"
+ data-setting-name="printerName"
+ iconic
+ ></select>
</section>
<section id="settings">
<section id="copies" class="section-block">
@@ -357,15 +353,11 @@
<section id="pages" class="section-block">
<label
- for="page-range-input"
+ for="range-picker"
class="block-label"
data-l10n-id="printui-page-range-label"
></label>
- <div
- id="page-range-input"
- is="page-range-input"
- class="page-range-input row"
- ></div>
+ <div id="page-range-input" is="page-range-input"></div>
</section>
<section id="color-mode" class="section-block">
@@ -377,7 +369,6 @@
<select
is="color-mode-select"
id="color-mode-picker"
- class="row"
data-setting-name="printInColor"
>
<option
@@ -408,7 +399,6 @@
<select
is="paper-size-select"
id="paper-size-picker"
- class="row"
data-setting-name="paperId"
></select>
</section>
@@ -432,7 +422,6 @@
<select
is="setting-select"
id="pages-per-sheet-picker"
- class="row"
data-setting-name="numPagesPerSheet"
>
<option value="1">1</option>
@@ -445,11 +434,7 @@
</section>
<section id="margins" class="section-block">
- <div
- id="margins-select"
- is="margins-select"
- class="margins-select row"
- ></div>
+ <div id="margins-select" is="margins-select"></div>
</section>
<section id="two-sided-printing" class="section-block">
@@ -462,7 +447,6 @@
is="setting-select"
id="duplex-select"
name="duplex-type"
- class="row"
data-setting-name="printDuplex"
>
<option
diff --git a/toolkit/components/printing/content/print.js b/toolkit/components/printing/content/print.js
index 3398b28ace..1c3be1728b 100644
--- a/toolkit/components/printing/content/print.js
+++ b/toolkit/components/printing/content/print.js
@@ -46,7 +46,7 @@ var logger = (function () {
return _logger;
})();
-function serializeSettings(settings, logPrefix) {
+function serializeSettings(settings) {
let re = /^(k[A-Z]|resolution)/; // accessing settings.resolution throws an exception?
let types = new Set(["string", "boolean", "number", "undefined"]);
let nameValues = {};
@@ -80,7 +80,7 @@ function cancelDeferredTasks() {
document.addEventListener(
"DOMContentLoaded",
- e => {
+ () => {
window._initialized = PrintEventHandler.init().catch(e => console.error(e));
ourBrowser.setAttribute("flex", "0");
ourBrowser.setAttribute("constrainpopups", "false");
@@ -96,7 +96,7 @@ window.addEventListener("dialogclosing", () => {
window.addEventListener(
"unload",
- e => {
+ () => {
document.textContent = "";
},
{ once: true }
@@ -1608,7 +1608,7 @@ function PrintUIControlMixin(superClass) {
render() {}
- update(settings) {}
+ update() {}
dispatchSettingsChange(changedSettings) {
this.dispatchEvent(
@@ -1628,7 +1628,7 @@ function PrintUIControlMixin(superClass) {
);
}
- handleEvent(event) {}
+ handleEvent() {}
};
}
@@ -1863,7 +1863,7 @@ class PrintSettingCheckbox extends PrintUIControlMixin(HTMLInputElement) {
this.checked = settings[this.settingName];
}
- handleEvent(e) {
+ handleEvent() {
this.dispatchSettingsChange({
[this.settingName]: this.checked,
});
@@ -1884,7 +1884,7 @@ class PrintSettingRadio extends PrintUIControlMixin(HTMLInputElement) {
this.checked = settings[this.settingName] == this.value;
}
- handleEvent(e) {
+ handleEvent() {
this.dispatchSettingsChange({
[this.settingName]: this.value,
});
@@ -2020,7 +2020,7 @@ class CopiesInput extends PrintUIControlMixin(HTMLElement) {
this._copiesError.hidden = true;
}
- handleEvent(e) {
+ handleEvent() {
this._copiesError.hidden = this._copiesInput.checkValidity();
}
}
@@ -2679,7 +2679,7 @@ class TwistySummary extends PrintUIControlMixin(HTMLElement) {
this.updateSummary(shouldOpen);
}
- handleEvent(e) {
+ handleEvent() {
let willOpen = !this.isOpen;
Services.prefs.setBoolPref("print.more-settings.open", willOpen);
this.updateSummary(willOpen);
diff --git a/toolkit/components/printing/content/toggle-group.css b/toolkit/components/printing/content/toggle-group.css
index 94922eb963..4416bfa512 100644
--- a/toolkit/components/printing/content/toggle-group.css
+++ b/toolkit/components/printing/content/toggle-group.css
@@ -2,7 +2,6 @@
* 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/. */
-
/*
* A radiogroup styled to hide the radio buttons
* and present tab-like labels as buttons
@@ -10,6 +9,7 @@
.toggle-group {
display: inline-flex;
+ min-height: var(--button-min-height-small);
}
.toggle-group-label {
@@ -26,21 +26,21 @@
}
.toggle-group-label-iconic::before {
- width: 16px;
- height: 16px;
+ width: var(--size-item-small);
+ height: var(--size-item-small);
margin-inline-end: 5px;
-moz-context-properties: fill;
fill: currentColor;
}
.toggle-group-label:first-of-type {
- border-start-start-radius: 4px;
- border-end-start-radius: 4px;
+ border-start-start-radius: var(--border-radius-small);
+ border-end-start-radius: var(--border-radius-small);
}
.toggle-group-label:last-of-type {
- border-start-end-radius: 4px;
- border-end-end-radius: 4px;
+ border-start-end-radius: var(--border-radius-small);
+ border-end-end-radius: var(--border-radius-small);
}
.toggle-group-input:disabled + .toggle-group-label {
diff --git a/toolkit/components/printing/tests/browser_preview_navigation.js b/toolkit/components/printing/tests/browser_preview_navigation.js
index 53e54eddb0..23b3a9b7cc 100644
--- a/toolkit/components/printing/tests/browser_preview_navigation.js
+++ b/toolkit/components/printing/tests/browser_preview_navigation.js
@@ -23,7 +23,7 @@ async function waitForPageStatusUpdate(elem, expected, message) {
);
}
-async function waitUntilVisible(elem, visible = true) {
+async function waitUntilVisible(elem) {
await TestUtils.waitForCondition(
() =>
BrowserTestUtils.isVisible(elem) && getComputedStyle(elem).opacity == "1",
diff --git a/toolkit/components/printing/tests/file_window_print_reentrant.html b/toolkit/components/printing/tests/file_window_print_reentrant.html
index 7eebd8c2fe..2008ffaf27 100644
--- a/toolkit/components/printing/tests/file_window_print_reentrant.html
+++ b/toolkit/components/printing/tests/file_window_print_reentrant.html
@@ -3,7 +3,7 @@
<script>
let count = 0;
onload = function() {
- window.addEventListener('beforeprint', (event) => {
+ window.addEventListener('beforeprint', () => {
document.getElementById("before-print-count").innerText = ++count;
print()
});
diff --git a/toolkit/components/printing/tests/head.js b/toolkit/components/printing/tests/head.js
index 4c8c0b26b6..e2a83463a6 100644
--- a/toolkit/components/printing/tests/head.js
+++ b/toolkit/components/printing/tests/head.js
@@ -60,6 +60,9 @@ class PrintHelper {
}
static getTestPageUrl(pathName) {
+ if (pathName.startsWith("http://")) {
+ return pathName;
+ }
const testPath = getRootDirectory(gTestPath).replace(
"chrome://mochitests/content",
"http://example.com"
@@ -68,6 +71,9 @@ class PrintHelper {
}
static getTestPageUrlHTTPS(pathName) {
+ if (pathName.startsWith("https://")) {
+ return pathName;
+ }
const testPath = getRootDirectory(gTestPath).replace(
"chrome://mochitests/content",
"https://example.com"
@@ -232,11 +238,7 @@ class PrintHelper {
});
// Mock PrintEventHandler with our Promises.
- this.win.PrintEventHandler._showPrintDialog = (
- window,
- haveSelection,
- settings
- ) => {
+ this.win.PrintEventHandler._showPrintDialog = (window, haveSelection) => {
this.systemDialogOpenedWithSelection = haveSelection;
return showSystemDialogPromise;
};
diff --git a/toolkit/components/processsingleton/MainProcessSingleton.sys.mjs b/toolkit/components/processsingleton/MainProcessSingleton.sys.mjs
index a10493f79b..2c93bcdc65 100644
--- a/toolkit/components/processsingleton/MainProcessSingleton.sys.mjs
+++ b/toolkit/components/processsingleton/MainProcessSingleton.sys.mjs
@@ -10,7 +10,7 @@ MainProcessSingleton.prototype = {
"nsISupportsWeakReference",
]),
- observe(subject, topic, data) {
+ observe(subject, topic) {
switch (topic) {
case "app-startup": {
// Imported for side-effects.
diff --git a/toolkit/components/processtools/tests/browser/browser_test_powerMetrics.js b/toolkit/components/processtools/tests/browser/browser_test_powerMetrics.js
index 93833a5207..0ebaaa969e 100644
--- a/toolkit/components/processtools/tests/browser/browser_test_powerMetrics.js
+++ b/toolkit/components/processtools/tests/browser/browser_test_powerMetrics.js
@@ -87,16 +87,12 @@ add_task(async () => {
let initalCpuTime = await getChildCpuTime(pid);
let afterCpuTime;
do {
- await SpecialPowers.spawn(
- firstBrowser,
- [kBusyWaitForMs],
- async kBusyWaitForMs => {
- let startTime = Date.now();
- while (Date.now() - startTime < 10) {
- // Burn CPU time...
- }
+ await SpecialPowers.spawn(firstBrowser, [kBusyWaitForMs], async () => {
+ let startTime = Date.now();
+ while (Date.now() - startTime < 10) {
+ // Burn CPU time...
}
- );
+ });
afterCpuTime = await getChildCpuTime(pid);
} while (afterCpuTime - initalCpuTime < kBusyWaitForMs * kNS_PER_MS);
cpuTimeSpentOnBackgroundTab = Math.floor(
diff --git a/toolkit/components/processtools/tests/browser/browser_test_procinfo.js b/toolkit/components/processtools/tests/browser/browser_test_procinfo.js
index 07c7d5d2d0..5a301c664a 100644
--- a/toolkit/components/processtools/tests/browser/browser_test_procinfo.js
+++ b/toolkit/components/processtools/tests/browser/browser_test_procinfo.js
@@ -58,7 +58,7 @@ add_task(async function test_proc_info() {
await BrowserTestUtils.withNewTab(
{ gBrowser, url: DUMMY_URL },
- async function (browser) {
+ async function () {
// We test `SAMPLE_SIZE` times to increase a tad the chance of encountering race conditions.
for (let z = 0; z < SAMPLE_SIZE; z++) {
let parentProc = await ChromeUtils.requestProcInfo();
diff --git a/toolkit/components/promiseworker/tests/xpcshell/data/worker.js b/toolkit/components/promiseworker/tests/xpcshell/data/worker.js
index 94b6dd17ad..d9939bd163 100644
--- a/toolkit/components/promiseworker/tests/xpcshell/data/worker.js
+++ b/toolkit/components/promiseworker/tests/xpcshell/data/worker.js
@@ -43,7 +43,7 @@ var Agent = {
return args;
},
- throwError(msg, ...args) {
+ throwError(msg) {
throw new Error(msg);
},
};
diff --git a/toolkit/components/promiseworker/tests/xpcshell/data/worker.mjs b/toolkit/components/promiseworker/tests/xpcshell/data/worker.mjs
index 98a4b0ee5b..91b27ac8f1 100644
--- a/toolkit/components/promiseworker/tests/xpcshell/data/worker.mjs
+++ b/toolkit/components/promiseworker/tests/xpcshell/data/worker.mjs
@@ -30,7 +30,7 @@ var Agent = {
return args;
},
- throwError(msg, ...args) {
+ throwError(msg) {
throw new Error(msg);
},
};
diff --git a/toolkit/components/prompts/content/tabprompts.css b/toolkit/components/prompts/content/tabprompts.css
deleted file mode 100644
index 539e8792cc..0000000000
--- a/toolkit/components/prompts/content/tabprompts.css
+++ /dev/null
@@ -1,119 +0,0 @@
-/* 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/. */
-
-/* Tab Modal Prompt boxes */
-
-.tabModalBackground {
- justify-content: center;
- flex-direction: column;
-}
-
-.tabModalBackground,
-tabmodalprompt {
- width: 100%;
- height: 100%;
-}
-
-tabmodalprompt {
- --tabmodalprompt-padding: 20px;
- overflow: hidden;
- text-shadow: none; /* remove lightweight theme text-shadow */
-}
-
-tabmodalprompt:not([hidden]) {
- display: grid;
- grid-template-rows: 1fr [dialog-start] auto [dialog-end] 2fr;
- justify-items: center;
-}
-
-/*
- Adjustments for chrome level tab-prompts to make them
- overlap with the upper chrome UI and move them in
- front of content prompts.
-*/
-tabmodalprompt.tab-prompt {
- overflow: visible;
- z-index: 1;
- grid-template-rows: [dialog-start] auto [dialog-end] 1fr;
-}
-
-tabmodalprompt.tab-prompt .tabmodalprompt-mainContainer {
- margin-top: -5px;
-}
-
-.tabmodalprompt-mainContainer {
- min-width: 20em;
- min-height: 12em;
- max-width: calc(60% + calc(var(--tabmodalprompt-padding) * 2));
- -moz-user-focus: normal;
- grid-row: dialog;
-
- display: flex;
- flex-direction: column;
-}
-
-.tabmodalprompt-topContainer {
- flex-grow: 1;
- padding: var(--tabmodalprompt-padding);
- display: grid;
- grid-template-columns: auto 1fr;
- align-items: baseline;
- align-content: center; /* center content vertically */
- max-width: 100%;
- min-height: 0;
- max-height: 60vh;
- box-sizing: border-box;
-}
-
-.tabmodalprompt-topContainer > div:not(.tabmodalprompt-infoContainer, [hidden]) {
- display: contents;
-}
-
-.tabmodalprompt-infoContainer {
- grid-column: span 2;
-
- display: block;
- margin-block: auto;
- max-width: 100%;
- height: 100%;
- min-height: 0;
- justify-self: center; /* center text, but only when it fits in one line */
-}
-
-/* When all elements in the first column are hidden, prevent the second column
- from becoming the first one because it won't have the right fraction */
-.tabmodalprompt-topContainer > div > *:nth-child(2) {
- grid-column: 2;
-}
-
-.infoTitle {
- margin-bottom: 1em !important;
- font-weight: bold;
-}
-
-.infoBody {
- margin: 0 !important;
- -moz-user-focus: normal;
- user-select: text;
- cursor: text !important;
- white-space: pre-wrap;
- unicode-bidi: plaintext;
- outline: none; /* remove focus outline */
- overflow: auto;
- max-width: 100%;
- max-height: 100%;
-}
-
-tabmodalprompt label[value=""] {
- display: none;
-}
-
-.tabmodalprompt-buttonContainer {
- display: flex;
- padding: 12px var(--tabmodalprompt-padding) 15px;
-}
-
-.tabmodalprompt-buttonSpacer {
- flex-grow: 1;
-}
diff --git a/toolkit/components/prompts/content/tabprompts.sys.mjs b/toolkit/components/prompts/content/tabprompts.sys.mjs
deleted file mode 100644
index 7edb024fa8..0000000000
--- a/toolkit/components/prompts/content/tabprompts.sys.mjs
+++ /dev/null
@@ -1,298 +0,0 @@
-/* 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/. */
-
-import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
-
-export var TabModalPrompt = class {
- constructor(win) {
- this.win = win;
- let newPrompt = (this.element =
- win.document.createElement("tabmodalprompt"));
- win.MozXULElement.insertFTLIfNeeded("toolkit/global/tabprompts.ftl");
- newPrompt.setAttribute("role", "dialog");
- let randomIdSuffix = Math.random().toString(32).substring(2);
- newPrompt.setAttribute("aria-describedby", `infoBody-${randomIdSuffix}`);
- newPrompt.appendChild(
- win.MozXULElement.parseXULToFragment(
- `
- <div class="tabmodalprompt-mainContainer" xmlns="http://www.w3.org/1999/xhtml" xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
- <div class="tabmodalprompt-topContainer">
- <div class="tabmodalprompt-infoContainer">
- <div class="tabmodalprompt-infoTitle infoTitle" hidden="hidden"/>
- <div class="tabmodalprompt-infoBody infoBody" id="infoBody-${randomIdSuffix}" tabindex="-1"/>
- </div>
-
- <div class="tabmodalprompt-loginContainer" hidden="hidden">
- <xul:label class="tabmodalprompt-loginLabel" data-l10n-id="tabmodalprompt-username" control="loginTextbox-${randomIdSuffix}"/>
- <input class="tabmodalprompt-loginTextbox" id="loginTextbox-${randomIdSuffix}"/>
- </div>
-
- <div class="tabmodalprompt-password1Container" hidden="hidden">
- <xul:label class="tabmodalprompt-password1Label" data-l10n-id="tabmodalprompt-password" control="password1Textbox-${randomIdSuffix}"/>
- <input class="tabmodalprompt-password1Textbox" type="password" id="password1Textbox-${randomIdSuffix}"/>
- </div>
-
- <div class="tabmodalprompt-checkboxContainer" hidden="hidden">
- <div/>
- <xul:checkbox class="tabmodalprompt-checkbox"/>
- </div>
-
- <!-- content goes here -->
- </div>
- <div class="tabmodalprompt-buttonContainer">
- <xul:button class="tabmodalprompt-button3" hidden="true"/>
- <div class="tabmodalprompt-buttonSpacer"/>
- <xul:button class="tabmodalprompt-button0" data-l10n-id="tabmodalprompt-ok-button"/>
- <xul:button class="tabmodalprompt-button2" hidden="true"/>
- <xul:button class="tabmodalprompt-button1" data-l10n-id="tabmodalprompt-cancel-button"/>
- </div>
- </div>`
- )
- );
-
- this.ui = {
- prompt: this,
- promptContainer: this.element,
- mainContainer: newPrompt.querySelector(".tabmodalprompt-mainContainer"),
- loginContainer: newPrompt.querySelector(".tabmodalprompt-loginContainer"),
- loginTextbox: newPrompt.querySelector(".tabmodalprompt-loginTextbox"),
- loginLabel: newPrompt.querySelector(".tabmodalprompt-loginLabel"),
- password1Container: newPrompt.querySelector(
- ".tabmodalprompt-password1Container"
- ),
- password1Textbox: newPrompt.querySelector(
- ".tabmodalprompt-password1Textbox"
- ),
- password1Label: newPrompt.querySelector(".tabmodalprompt-password1Label"),
- infoContainer: newPrompt.querySelector(".tabmodalprompt-infoContainer"),
- infoBody: newPrompt.querySelector(".tabmodalprompt-infoBody"),
- infoTitle: newPrompt.querySelector(".tabmodalprompt-infoTitle"),
- infoIcon: null,
- rows: newPrompt.querySelector(".tabmodalprompt-topContainer"),
- checkbox: newPrompt.querySelector(".tabmodalprompt-checkbox"),
- checkboxContainer: newPrompt.querySelector(
- ".tabmodalprompt-checkboxContainer"
- ),
- button3: newPrompt.querySelector(".tabmodalprompt-button3"),
- button2: newPrompt.querySelector(".tabmodalprompt-button2"),
- button1: newPrompt.querySelector(".tabmodalprompt-button1"),
- button0: newPrompt.querySelector(".tabmodalprompt-button0"),
- // focusTarget (for BUTTON_DELAY_ENABLE) not yet supported
- };
-
- if (AppConstants.XP_UNIX) {
- // Reorder buttons on Linux
- let buttonContainer = newPrompt.querySelector(
- ".tabmodalprompt-buttonContainer"
- );
- buttonContainer.appendChild(this.ui.button3);
- buttonContainer.appendChild(this.ui.button2);
- buttonContainer.appendChild(
- newPrompt.querySelector(".tabmodalprompt-buttonSpacer")
- );
- buttonContainer.appendChild(this.ui.button1);
- buttonContainer.appendChild(this.ui.button0);
- }
-
- this.ui.button0.addEventListener(
- "command",
- this.onButtonClick.bind(this, 0)
- );
- this.ui.button1.addEventListener(
- "command",
- this.onButtonClick.bind(this, 1)
- );
- this.ui.button2.addEventListener(
- "command",
- this.onButtonClick.bind(this, 2)
- );
- this.ui.button3.addEventListener(
- "command",
- this.onButtonClick.bind(this, 3)
- );
- // Anonymous wrapper used here because |Dialog| doesn't exist until init() is called!
- this.ui.checkbox.addEventListener("command", () => {
- this.Dialog.onCheckbox();
- });
-
- /**
- * Based on dialog.xml handlers
- */
- this.element.addEventListener(
- "keypress",
- event => {
- switch (event.keyCode) {
- case KeyEvent.DOM_VK_RETURN:
- this.onKeyAction("default", event);
- break;
-
- case KeyEvent.DOM_VK_ESCAPE:
- this.onKeyAction("cancel", event);
- break;
-
- default:
- if (
- AppConstants.platform == "macosx" &&
- event.key == "." &&
- event.metaKey
- ) {
- this.onKeyAction("cancel", event);
- }
- break;
- }
- },
- { mozSystemGroup: true }
- );
-
- this.element.addEventListener(
- "focus",
- event => {
- let bnum = this.args.defaultButtonNum || 0;
- let defaultButton = this.ui["button" + bnum];
-
- if (AppConstants.platform == "macosx") {
- // On OS X, the default button always stays marked as such (until
- // the entire prompt blurs).
- defaultButton.setAttribute("default", "true");
- } else {
- // On other platforms, the default button is only marked as such
- // when no other button has focus. XUL buttons on not-OSX will
- // react to pressing enter as a command, so you can't trigger the
- // default without tabbing to it or something that isn't a button.
- let focusedDefault = event.originalTarget == defaultButton;
- let someButtonFocused =
- event.originalTarget.localName == "button" ||
- event.originalTarget.localName == "toolbarbutton";
- if (focusedDefault || !someButtonFocused) {
- defaultButton.setAttribute("default", "true");
- }
- }
- },
- true
- );
-
- this.element.addEventListener("blur", () => {
- // If focus shifted to somewhere else in the browser, don't make
- // the default button look active.
- let bnum = this.args.defaultButtonNum || 0;
- let button = this.ui["button" + bnum];
- button.removeAttribute("default");
- });
- }
-
- init(args, linkedTab, onCloseCallback) {
- this.args = args;
- this.linkedTab = linkedTab;
- this.onCloseCallback = onCloseCallback;
-
- if (args.enableDelay) {
- throw new Error(
- "BUTTON_DELAY_ENABLE not yet supported for tab-modal prompts"
- );
- }
-
- // Apply styling depending on modalType (content or tab prompt)
- if (args.modalType === Ci.nsIPrompt.MODAL_TYPE_TAB) {
- this.element.classList.add("tab-prompt");
- } else {
- this.element.classList.add("content-prompt");
- }
-
- // We need to remove the prompt when the tab or browser window is closed or
- // the page navigates, else we never unwind the event loop and that's sad times.
- // Remember to cleanup in shutdownPrompt()!
- this.win.addEventListener("resize", this);
- this.win.addEventListener("unload", this);
- if (linkedTab) {
- linkedTab.addEventListener("TabClose", this);
- }
- // Note:
- // nsPrompter.js or in e10s mode browser-parent.js call abortPrompt,
- // when the domWindow, for which the prompt was created, generates
- // a "pagehide" event.
-
- let { CommonDialog } = ChromeUtils.importESModule(
- "resource://gre/modules/CommonDialog.sys.mjs"
- );
- this.Dialog = new CommonDialog(args, this.ui);
- this.Dialog.onLoad(null);
-
- // For content prompts display the tabprompt title that shows the prompt origin when
- // the prompt origin is not the same as that of the top window.
- if (
- args.modalType == Ci.nsIPrompt.MODAL_TYPE_CONTENT &&
- args.showCallerOrigin
- ) {
- this.ui.infoTitle.removeAttribute("hidden");
- }
-
- // TODO: should unhide buttonSpacer on Windows when there are 4 buttons.
- // Better yet, just drop support for 4-button dialogs. (bug 609510)
- }
-
- shutdownPrompt() {
- // remove our event listeners
- try {
- this.win.removeEventListener("resize", this);
- this.win.removeEventListener("unload", this);
- if (this.linkedTab) {
- this.linkedTab.removeEventListener("TabClose", this);
- }
- } catch (e) {}
- // invoke callback
- this.onCloseCallback();
- this.win = null;
- this.ui = null;
- // Intentionally not cleaning up |this.element| here --
- // TabModalPromptBox.removePrompt() would need it and it might not
- // be called yet -- see browser_double_close_tabs.js.
- }
-
- abortPrompt() {
- // Called from other code when the page changes.
- this.Dialog.abortPrompt();
- this.shutdownPrompt();
- }
-
- handleEvent(aEvent) {
- switch (aEvent.type) {
- case "unload":
- case "TabClose":
- this.abortPrompt();
- break;
- }
- }
-
- onButtonClick(buttonNum) {
- // We want to do all the work her asynchronously off a Gecko
- // runnable, because of situations like the one described in
- // https://bugzilla.mozilla.org/show_bug.cgi?id=1167575#c35 : we
- // get here off processing of an OS event and will also process
- // one more Gecko runnable before we break out of the event loop
- // spin whoever posted the prompt is doing. If we do all our
- // work sync, we will exit modal state _before_ processing that
- // runnable, and if exiting moral state posts a runnable we will
- // incorrectly process that runnable before leaving our event
- // loop spin.
- Services.tm.dispatchToMainThread(() => {
- this.Dialog["onButton" + buttonNum]();
- this.shutdownPrompt();
- });
- }
-
- onKeyAction(action, event) {
- if (event.defaultPrevented) {
- return;
- }
-
- event.stopPropagation();
- if (action == "default") {
- let bnum = this.args.defaultButtonNum || 0;
- this.onButtonClick(bnum);
- } else {
- // action == "cancel"
- this.onButtonClick(1); // Cancel button
- }
- }
-};
diff --git a/toolkit/components/prompts/jar.mn b/toolkit/components/prompts/jar.mn
index 819af6dc1d..7a904c4363 100644
--- a/toolkit/components/prompts/jar.mn
+++ b/toolkit/components/prompts/jar.mn
@@ -9,5 +9,3 @@ toolkit.jar:
content/global/commonDialog.css (content/commonDialog.css)
content/global/selectDialog.js (content/selectDialog.js)
content/global/selectDialog.xhtml (content/selectDialog.xhtml)
- content/global/tabprompts.css (content/tabprompts.css)
- content/global/tabprompts.sys.mjs (content/tabprompts.sys.mjs)
diff --git a/toolkit/components/prompts/src/CommonDialog.sys.mjs b/toolkit/components/prompts/src/CommonDialog.sys.mjs
index a0812aa8ec..c788248442 100644
--- a/toolkit/components/prompts/src/CommonDialog.sys.mjs
+++ b/toolkit/components/prompts/src/CommonDialog.sys.mjs
@@ -30,11 +30,10 @@ CommonDialog.prototype = {
initialFocusResolver: null,
/**
- * @param [commonDialogEl] - Dialog element from commonDialog.xhtml,
- * null for TabModalPrompts.
+ * @param [commonDialogEl] - Dialog element from commonDialog.xhtml.
*/
- async onLoad(commonDialogEl = null) {
- let isEmbedded = !!commonDialogEl?.ownerGlobal.docShell.chromeEventHandler;
+ async onLoad(commonDialogEl) {
+ let isEmbedded = !!commonDialogEl.ownerGlobal.docShell.chromeEventHandler;
switch (this.args.promptType) {
case "alert":
@@ -106,36 +105,19 @@ CommonDialog.prototype = {
throw new Error("unknown dialog type");
}
- if (commonDialogEl) {
- commonDialogEl.setAttribute(
- "windowtype",
- "prompt:" + this.args.promptType
- );
- }
+ commonDialogEl.setAttribute("windowtype", "prompt:" + this.args.promptType);
// set the document title
let title = this.args.title;
let infoTitle = this.ui.infoTitle;
infoTitle.appendChild(infoTitle.ownerDocument.createTextNode(title));
- // Specific check to prevent showing the title on the old content prompts for macOS.
- // This should be removed when the old content prompts are removed.
- let contentSubDialogPromptEnabled = Services.prefs.getBoolPref(
- "prompts.contentPromptSubDialog"
- );
- let isOldContentPrompt =
- !contentSubDialogPromptEnabled &&
- this.args.modalType == Ci.nsIPrompt.MODAL_TYPE_CONTENT;
-
// After making these preventative checks, we can determine to show it if we're on
// macOS (where there is no titlebar) or if the prompt is a common dialog document
// and has been embedded (has a chromeEventHandler).
- infoTitle.hidden =
- isOldContentPrompt || !(AppConstants.platform === "macosx" || isEmbedded);
+ infoTitle.hidden = !(AppConstants.platform === "macosx" || isEmbedded);
- if (commonDialogEl) {
- commonDialogEl.ownerDocument.title = title;
- }
+ commonDialogEl.ownerDocument.title = title;
// Set button labels and visibility
//
@@ -193,7 +175,7 @@ CommonDialog.prototype = {
// set the icon
let icon = this.ui.infoIcon;
if (icon) {
- this.iconClass.forEach((el, idx, arr) => icon.classList.add(el));
+ this.iconClass.forEach(el => icon.classList.add(el));
}
// set default result to cancelled
@@ -202,15 +184,7 @@ CommonDialog.prototype = {
// Set the default button
let b = this.args.defaultButtonNum || 0;
- let button = this.ui["button" + b];
-
- if (commonDialogEl) {
- commonDialogEl.defaultButton = ["accept", "cancel", "extra1", "extra2"][
- b
- ];
- } else {
- button.setAttribute("default", "true");
- }
+ commonDialogEl.defaultButton = ["accept", "cancel", "extra1", "extra2"][b];
if (!isEmbedded && !this.ui.promptContainer?.hidden) {
// Set default focus and select textbox contents if applicable. If we're
@@ -229,7 +203,7 @@ CommonDialog.prototype = {
// Play a sound (unless we're showing a content prompt -- don't want those
// to feel like OS prompts).
try {
- if (commonDialogEl && this.soundID && !this.args.openedWithTabDialog) {
+ if (this.soundID && !this.args.openedWithTabDialog) {
Cc["@mozilla.org/sound;1"]
.getService(Ci.nsISound)
.playEventSound(this.soundID);
@@ -238,20 +212,12 @@ CommonDialog.prototype = {
console.error("Couldn't play common dialog event sound: ", e);
}
- if (commonDialogEl) {
- if (isEmbedded) {
- // If we delayed default focus above, wait for it to be ready before
- // sending the notification.
- await this.initialFocusPromise;
- }
- Services.obs.notifyObservers(this.ui.prompt, "common-dialog-loaded");
- } else {
- // ui.promptContainer is the <tabmodalprompt> element.
- Services.obs.notifyObservers(
- this.ui.promptContainer,
- "tabmodal-dialog-loaded"
- );
+ if (isEmbedded) {
+ // If we delayed default focus above, wait for it to be ready before
+ // sending the notification.
+ await this.initialFocusPromise;
}
+ Services.obs.notifyObservers(this.ui.prompt, "common-dialog-loaded");
},
setLabelForNode(aNode, aLabel) {
diff --git a/toolkit/components/prompts/src/Prompter.sys.mjs b/toolkit/components/prompts/src/Prompter.sys.mjs
index a0b5edca2f..2174ba10c6 100644
--- a/toolkit/components/prompts/src/Prompter.sys.mjs
+++ b/toolkit/components/prompts/src/Prompter.sys.mjs
@@ -1257,7 +1257,7 @@ class ModalPrompter {
}
async openInternalWindowPrompt(parentWindow, args) {
- if (!parentWindow?.gDialogBox || !ModalPrompter.windowPromptSubDialog) {
+ if (!parentWindow?.gDialogBox) {
this.openWindowPrompt(parentWindow, args);
return;
}
@@ -1720,15 +1720,7 @@ class ModalPrompter {
return result;
}
- asyncPromptAuth(
- channel,
- callback,
- context,
- level,
- authInfo,
- checkLabel,
- checkValue
- ) {
+ asyncPromptAuth() {
// Nothing calls this directly; netwerk ends up going through
// nsIPromptService::GetPrompt, which delegates to login manager.
// Login manger handles the async bits itself, and only calls out
@@ -1757,13 +1749,6 @@ XPCOMUtils.defineLazyPreferenceGetter(
MODAL_TYPE_WINDOW
);
-XPCOMUtils.defineLazyPreferenceGetter(
- ModalPrompter,
- "windowPromptSubDialog",
- "prompts.windowPromptSubDialog",
- false
-);
-
export function AuthPromptAdapterFactory() {}
AuthPromptAdapterFactory.prototype = {
classID: Components.ID("{6e134924-6c3a-4d86-81ac-69432dd971dc}"),
@@ -1786,7 +1771,7 @@ AuthPromptAdapter.prototype = {
/* ---------- nsIAuthPrompt2 ---------- */
- promptAuth(channel, level, authInfo, checkLabel, checkValue) {
+ promptAuth(channel, level, authInfo) {
let message = InternalPromptUtils.makeAuthMessage(
this.oldPrompter,
channel,
@@ -1833,15 +1818,7 @@ AuthPromptAdapter.prototype = {
return ok;
},
- asyncPromptAuth(
- channel,
- callback,
- context,
- level,
- authInfo,
- checkLabel,
- checkValue
- ) {
+ asyncPromptAuth() {
throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
},
};
diff --git a/toolkit/components/prompts/test/chromeScript.js b/toolkit/components/prompts/test/chromeScript.js
index 9a6bacc8a4..f873c5d04b 100644
--- a/toolkit/components/prompts/test/chromeScript.js
+++ b/toolkit/components/prompts/test/chromeScript.js
@@ -31,52 +31,6 @@ async function handlePromptWhenItAppears(action, modalType, isSelect) {
}
}
-function checkTabModal(prompt, browser) {
- let doc = browser.ownerDocument;
-
- let { bottom: toolboxBottom } = doc
- .getElementById("navigator-toolbox")
- .getBoundingClientRect();
-
- let { mainContainer } = prompt.ui;
-
- let { x, y } = mainContainer.getBoundingClientRect();
- ok(y > 0, "Container should have y > 0");
- // Inset by 1px since the corner point doesn't return the frame due to the
- // border-radius.
- is(
- doc.elementFromPoint(x + 1, y + 1).parentNode,
- mainContainer,
- "Check tabmodalprompt is visible"
- );
-
- info("Click to the left of the dialog over the content area");
- isnot(
- doc.elementFromPoint(x - 10, y + 50),
- browser,
- "Check clicks on the content area don't go to the browser"
- );
- is(
- doc.elementFromPoint(x - 10, y + 50),
- prompt.element,
- "Check clicks on the content area go to the prompt dialog background"
- );
-
- if (prompt.args.modalType == Ci.nsIPrompt.MODAL_TYPE_TAB) {
- ok(
- y <= toolboxBottom - 5,
- "Dialog should overlap the toolbox by at least 5px"
- );
- } else {
- ok(y >= toolboxBottom, "Dialog must not overlap with toolbox.");
- }
-
- ok(
- browser.hasAttribute("tabmodalPromptShowing"),
- "Check browser has @tabmodalPromptShowing"
- );
-}
-
async function handlePrompt(action, modalType, isSelect) {
let ui;
let browserWin = Services.wm.getMostRecentWindow("navigator:browser");
@@ -142,8 +96,7 @@ function getPromptState(ui) {
state.checkHidden = ui.checkboxContainer.hidden;
state.checkMsg = state.checkHidden ? "" : ui.checkbox.label;
state.checked = state.checkHidden ? false : ui.checkbox.checked;
- // TabModalPrompts don't have an infoIcon
- state.iconClass = ui.infoIcon ? ui.infoIcon.className : null;
+ state.iconClass = ui.infoIcon.className;
state.textValue = ui.loginTextbox.value;
state.passValue = ui.password1Textbox.value;
diff --git a/toolkit/components/prompts/test/prompt_common.js b/toolkit/components/prompts/test/prompt_common.js
index 8d583060da..25b0a4c438 100644
--- a/toolkit/components/prompts/test/prompt_common.js
+++ b/toolkit/components/prompts/test/prompt_common.js
@@ -17,10 +17,6 @@ function propBagToObject(bag) {
}
var modalType;
-var tabSubDialogsEnabled = SpecialPowers.Services.prefs.getBoolPref(
- "prompts.tabChromePromptSubDialog",
- false
-);
var isSelectDialog = false;
var isOSX = "nsILocalFileMac" in SpecialPowers.Ci;
var isE10S = SpecialPowers.Services.appinfo.processType == 2;
@@ -160,7 +156,7 @@ function onloadPromiseFor(id) {
return new Promise(resolve => {
iframe.addEventListener(
"load",
- function (e) {
+ function () {
resolve(true);
},
{ once: true }
@@ -227,7 +223,7 @@ function checkPromptState(promptState, expectedState) {
is(promptState.checked, expectedState.checked, "Checking checkbox checked");
if (
modalType === Ci.nsIPrompt.MODAL_TYPE_WINDOW ||
- (modalType === Ci.nsIPrompt.MODAL_TYPE_TAB && tabSubDialogsEnabled)
+ modalType === Ci.nsIPrompt.MODAL_TYPE_TAB
) {
is(
promptState.iconClass,
@@ -366,7 +362,7 @@ function PrompterProxy(chromeScript) {
return new Proxy(
{},
{
- get(target, prop, receiver) {
+ get(target, prop) {
return (...args) => {
// Array of indices of out/inout params to copy from the parent back to the caller.
let outParams = [];
diff --git a/toolkit/components/prompts/test/test_bug619644.html b/toolkit/components/prompts/test/test_bug619644.html
index 2b424c71a6..03650d6d6d 100644
--- a/toolkit/components/prompts/test/test_bug619644.html
+++ b/toolkit/components/prompts/test/test_bug619644.html
@@ -31,7 +31,7 @@ function inittest() {
SimpleTest.waitForExplicitFinish();
}
-function runtest(e) {
+function runtest() {
modalType = Ci.nsIPrompt.MODAL_TYPE_CONTENT;
window.removeEventListener("message", runtest);
diff --git a/toolkit/components/prompts/test/test_subresources_prompts.html b/toolkit/components/prompts/test/test_subresources_prompts.html
index b71ad0694e..134af95c6d 100644
--- a/toolkit/components/prompts/test/test_subresources_prompts.html
+++ b/toolkit/components/prompts/test/test_subresources_prompts.html
@@ -33,11 +33,6 @@ var iframe1Loaded = onloadPromiseFor("iframe_diff_origin");
var iframe2Loaded = onloadPromiseFor("iframe_same_origin");
var iframe_prompt = document.getElementById("iframe_prompt");
-// Depending on pref state we either show auth prompts as windows or on tab level.
-let authPromptModalType = SpecialPowers.Services.prefs.getIntPref(
- "prompts.modalType.httpAuth"
-);
-
add_task(async function runTest() {
modalType = Ci.nsIPrompt.MODAL_TYPE_CONTENT;
@@ -104,7 +99,7 @@ add_task(async function runTestAuth() {
// cross-origin subresources load
// Let prompt_common know what kind of modal type is enabled for auth prompts.
- modalType = authPromptModalType;
+ modalType = Ci.nsIPrompt.MODAL_TYPE_TAB;
let state, action;
diff --git a/toolkit/components/protobuf/regenerate_cpp_files.sh b/toolkit/components/protobuf/regenerate_cpp_files.sh
index 848e6f538e..84aaf25801 100755
--- a/toolkit/components/protobuf/regenerate_cpp_files.sh
+++ b/toolkit/components/protobuf/regenerate_cpp_files.sh
@@ -25,6 +25,7 @@ cd ../../.. # Top level.
regenerate devtools/shared/heapsnapshot/ CoreDump.proto
regenerate toolkit/components/reputationservice/chromium/chrome/common/safe_browsing/ csd.proto
regenerate toolkit/components/url-classifier/chromium/ safebrowsing.proto
+regenerate toolkit/components/cookiebanners/ cookieBanner.proto
command cp third_party/content_analysis_sdk/proto/content_analysis/sdk/analysis.proto toolkit/components/contentanalysis/content_analysis/sdk/analysis.proto
regenerate toolkit/components/contentanalysis/content_analysis/sdk/ analysis.proto
command cp third_party/rust/viaduct/src/fetch_msg_types.proto toolkit/components/viaduct/fetch_msg_types.proto
diff --git a/toolkit/components/reader/.eslintrc.js b/toolkit/components/reader/.eslintrc.js
index 4df95dd5f8..bf83f31ab1 100644
--- a/toolkit/components/reader/.eslintrc.js
+++ b/toolkit/components/reader/.eslintrc.js
@@ -8,6 +8,6 @@ module.exports = {
rules: {
"no-inner-declarations": "error",
"no-shadow": "error",
- "no-unused-vars": ["error", { vars: "all", args: "none" }],
+ "no-unused-vars": ["error", { vars: "all", argsIgnorePattern: "^_" }],
},
};
diff --git a/toolkit/components/reader/AboutReader.sys.mjs b/toolkit/components/reader/AboutReader.sys.mjs
index 6b1c28c228..75776b619f 100644
--- a/toolkit/components/reader/AboutReader.sys.mjs
+++ b/toolkit/components/reader/AboutReader.sys.mjs
@@ -8,6 +8,7 @@ import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
const lazy = {};
let gScrollPositions = new Map();
+let lastSelectedTheme = "auto";
ChromeUtils.defineESModuleGetters(lazy, {
AsyncPrefs: "resource://gre/modules/AsyncPrefs.sys.mjs",
@@ -26,10 +27,31 @@ ChromeUtils.defineLazyGetter(
);
const COLORSCHEME_L10N_IDS = {
- light: "about-reader-color-scheme-light",
- dark: "about-reader-color-scheme-dark",
- sepia: "about-reader-color-scheme-sepia",
- auto: "about-reader-color-scheme-auto",
+ auto: "about-reader-color-theme-auto",
+ light: "about-reader-color-theme-light",
+ dark: "about-reader-color-theme-dark",
+ sepia: "about-reader-color-theme-sepia",
+ contrast: "about-reader-color-theme-contrast",
+ gray: "about-reader-color-theme-gray",
+ custom: "about-reader-color-theme-custom",
+};
+
+const CUSTOM_THEME_COLOR_INPUTS = [
+ "foreground",
+ "background",
+ "unvisited-links",
+ "visited-links",
+ "selection-highlight",
+];
+
+const COLORS_MENU_TABS = ["fxtheme", "customtheme"];
+
+const DEFAULT_COLORS = {
+ background: "#FFFFFF",
+ foreground: "#14151A",
+ "unvisited-links": "#0060DF",
+ "visited-links": "#321C64",
+ "selection-highlight": "#FFFFCC",
};
Services.telemetry.setEventRecordingEnabled("readermode", true);
@@ -147,9 +169,27 @@ export var AboutReader = function (
// we're ready for any external setup, send a signal for that.
this._actor.sendAsyncMessage("Reader:OnSetup");
- let colorSchemeValues = JSON.parse(
+ // set up segmented tab controls for colors menu.
+ this._setupColorsTabs(
+ COLORS_MENU_TABS,
+ this._handleColorsTabClick.bind(this)
+ );
+
+ // fetch color scheme values from prefs.
+ let colorsMenuColorSchemeValues = JSON.parse(
Services.prefs.getCharPref("reader.color_scheme.values")
);
+ // remove contrast and gray options from regular menu.
+ let colorSchemeValues = [...colorsMenuColorSchemeValues];
+ colorSchemeValues.splice(colorSchemeValues.length - 2, 2);
+
+ let colorsMenuColorSchemeOptions = colorsMenuColorSchemeValues.map(value => ({
+ l10nId: COLORSCHEME_L10N_IDS[value],
+ groupName: "color-scheme",
+ value,
+ itemClass: value + "-button",
+ }));
+
let colorSchemeOptions = colorSchemeValues.map(value => ({
l10nId: COLORSCHEME_L10N_IDS[value],
groupName: "color-scheme",
@@ -158,12 +198,33 @@ export var AboutReader = function (
}));
let colorScheme = Services.prefs.getCharPref("reader.color_scheme");
- this._setupSegmentedButton(
- "color-scheme-buttons",
- colorSchemeOptions,
- colorScheme,
- this._setColorSchemePref.bind(this)
- );
+ if (Services.prefs.getBoolPref("reader.colors_menu.enabled", false)) {
+ doc.getElementById("regular-color-scheme").hidden = true;
+ doc.getElementById("custom-colors-color-scheme").hidden = false;
+ this._setupSegmentedButton(
+ "colors-menu-color-scheme-buttons",
+ colorsMenuColorSchemeOptions,
+ colorScheme,
+ this._setColorSchemePref.bind(this)
+ );
+ this._setupCustomColors(
+ CUSTOM_THEME_COLOR_INPUTS,
+ "custom-colors-selection",
+ "about-reader-custom-colors"
+ );
+ this._setupButton(
+ "custom-colors-reset-button",
+ this._resetCustomColors.bind(this)
+ );
+ } else {
+ this._setupSegmentedButton(
+ "color-scheme-buttons",
+ colorSchemeOptions,
+ colorScheme,
+ this._setColorSchemePref.bind(this)
+ );
+ }
+
this._setColorSchemePref(colorScheme);
let fontTypeOptions = [
@@ -738,19 +799,43 @@ AboutReader.prototype = {
this._colorScheme = "hcm";
}
+ if (this._colorScheme == "custom") {
+ const colorInputs = this._doc.querySelectorAll("color-input");
+ colorInputs.forEach(input => {
+ // Set document body styles to pref values.
+ let property = input.getAttribute("prop-name");
+ let pref = `reader.custom_colors.${property}`;
+ let customColor = Services.prefs.getStringPref(pref, "");
+ // If customColor is truthy, set the value from pref.
+ if (customColor) {
+ let cssProp = `--custom-theme-${property}`;
+ this._doc.body.style.setProperty(cssProp, customColor);
+ }
+ });
+ }
+
bodyClasses.add(this._colorScheme);
},
- // Pref values include "dark", "light", "sepia", and "auto"
- _setColorSchemePref(colorSchemePref) {
+ // Pref values include "auto", "dark", "light", "sepia",
+ // "gray", "contrast", and "custom"
+ _setColorSchemePref(colorSchemePref, fromInputEvent = false) {
+ // The input event for the last selected segmented button is fired
+ // upon loading a reader article in the same session. To prevent it
+ // from overwriting custom colors, we return false.
+ if (this._colorScheme == "custom" && fromInputEvent) {
+ lastSelectedTheme = colorSchemePref;
+ return false;
+ }
this._setColorScheme(colorSchemePref);
lazy.AsyncPrefs.set("reader.color_scheme", colorSchemePref);
+ return true;
},
_setFontType(newFontType) {
if (this._fontType === newFontType) {
- return;
+ return false;
}
let bodyClasses = this._doc.body.classList;
@@ -763,6 +848,8 @@ AboutReader.prototype = {
bodyClasses.add(this._fontType);
lazy.AsyncPrefs.set("reader.font_type", this._fontType);
+
+ return true;
},
async _loadArticle(docContentType = "document") {
@@ -1094,14 +1181,17 @@ AboutReader.prototype = {
label.removeAttribute("checked");
}
- aEvent.target.nextElementSibling.setAttribute("checked", "true");
- callback(option.value);
+ let setOption = callback(option.value, true);
+ if (setOption) {
+ aEvent.target.setAttribute("checked", "true");
+ aEvent.target.nextElementSibling.setAttribute("checked", "true");
+ }
},
true
);
if (option.value === initialValue) {
- radioButton.checked = true;
+ radioButton.setAttribute("checked", "true");
item.setAttribute("checked", "true");
}
}
@@ -1124,6 +1214,121 @@ AboutReader.prototype = {
);
},
+ _handleColorsTabClick(option) {
+ let doc = this._doc;
+ if (option == "customtheme") {
+ this._setColorSchemePref("custom");
+ lazy.AsyncPrefs.set("reader.color_scheme", "custom");
+
+ // Store the last selected preset theme button.
+ const colorSchemePresets = doc.querySelector(
+ ".colors-menu-color-scheme-buttons"
+ );
+ const labels = colorSchemePresets.querySelectorAll("label");
+ labels.forEach(label => {
+ if (label.hasAttribute("checked")) {
+ lastSelectedTheme = label.className.split("-")[0];
+ }
+ });
+ }
+ if (option == "fxtheme") {
+ this._setColorSchemePref(lastSelectedTheme);
+ lazy.AsyncPrefs.set("reader.color_scheme", lastSelectedTheme);
+ // set the last selected button to checked.
+ const colorSchemePresets = doc.querySelector(
+ ".colors-menu-color-scheme-buttons"
+ );
+ const labels = colorSchemePresets.querySelectorAll("label");
+ labels.forEach(label => {
+ if (label.className == `${lastSelectedTheme}-button`) {
+ label.setAttribute("checked", "true");
+ label.previousElementSibling.setAttribute("checked", "true");
+ }
+ });
+ }
+ },
+
+ _setupColorsTabs(options, callback) {
+ let doc = this._doc;
+ let colorScheme = Services.prefs.getCharPref("reader.color_scheme");
+ for (let option of options) {
+ let tabButton = doc.getElementById(`tabs-deck-button-${option}`);
+ // Open custom theme tab if color scheme is set to custom.
+ if (option == "customtheme" && colorScheme == "custom") {
+ tabButton.click();
+ }
+ tabButton.addEventListener(
+ "click",
+ function (aEvent) {
+ if (!aEvent.isTrusted) {
+ return;
+ }
+
+ callback(option);
+ },
+ true
+ );
+ }
+ },
+
+ _setupColorInput(prop) {
+ let doc = this._doc;
+ let input = doc.createElement("color-input");
+ input.setAttribute("prop-name", prop);
+ let labelL10nId = `about-reader-custom-colors-${prop}`;
+ input.setAttribute("data-l10n-id", labelL10nId);
+
+ let pref = `reader.custom_colors.${prop}`;
+ let customColor = Services.prefs.getStringPref(pref, "");
+ // Set the swatch color from prefs if one has been set.
+ if (customColor) {
+ input.setAttribute("color", customColor);
+ } else {
+ let defaultColor = DEFAULT_COLORS[prop];
+ input.setAttribute("color", defaultColor);
+ }
+
+ // Attach event listener to update the pref and page colors on input.
+ input.addEventListener("color-picked", e => {
+ const cssPropToUpdate = `--custom-theme-${prop}`;
+ this._doc.body.style.setProperty(cssPropToUpdate, e.detail);
+
+ const prefToUpdate = `reader.custom_colors.${prop}`;
+ lazy.AsyncPrefs.set(prefToUpdate, e.detail);
+ });
+
+ return input;
+ },
+
+ _setupCustomColors(options, id) {
+ let doc = this._doc;
+ const list = doc.getElementsByClassName(id)[0];
+
+ for (let option of options) {
+ let listItem = doc.createElement("li");
+ let colorInput = this._setupColorInput(option);
+ listItem.appendChild(colorInput);
+ list.appendChild(listItem);
+ }
+ },
+
+ _resetCustomColors() {
+ // Need to reset prefs, page colors, and color inputs.
+ const colorInputs = this._doc.querySelectorAll("color-input");
+ colorInputs.forEach(input => {
+ let property = input.getAttribute("prop-name");
+ let pref = `reader.custom_colors.${property}`;
+ lazy.AsyncPrefs.set(pref, "");
+
+ // Set css props to empty strings so they use fallback value.
+ let cssProp = `--custom-theme-${property}`;
+ this._doc.body.style.setProperty(cssProp, "");
+
+ let defaultColor = DEFAULT_COLORS[property];
+ input.setAttribute("color", defaultColor);
+ });
+ },
+
_toggleDropdownClicked(event) {
let dropdown = event.target.closest(".dropdown");
@@ -1143,7 +1348,7 @@ AboutReader.prototype = {
/*
* If the ReaderView banner font-dropdown is closed, open it.
*/
- _openDropdown(dropdown, window) {
+ _openDropdown(dropdown) {
if (dropdown.classList.contains("open")) {
return;
}
diff --git a/toolkit/components/reader/ReaderMode.sys.mjs b/toolkit/components/reader/ReaderMode.sys.mjs
index 4b754d88b0..c2561bfb23 100644
--- a/toolkit/components/reader/ReaderMode.sys.mjs
+++ b/toolkit/components/reader/ReaderMode.sys.mjs
@@ -273,11 +273,11 @@ export var ReaderMode = {
"READER_MODE_DOWNLOAD_RESULT"
);
return new Promise((resolve, reject) => {
- let xhr = new XMLHttpRequest();
+ let xhr = new XMLHttpRequest({ mozAnon: false });
xhr.open("GET", url, true);
xhr.onerror = evt => reject(evt.error);
xhr.responseType = docContentType === "text/plain" ? "text" : "document";
- xhr.onload = evt => {
+ xhr.onload = () => {
if (xhr.status !== 200) {
reject("Reader mode XHR failed with status: " + xhr.status);
histogram.add(DOWNLOAD_ERROR_XHR);
diff --git a/toolkit/components/reader/color-input.css b/toolkit/components/reader/color-input.css
new file mode 100644
index 0000000000..93e269dd16
--- /dev/null
+++ b/toolkit/components/reader/color-input.css
@@ -0,0 +1,47 @@
+/* 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/. */
+
+.color-input-container {
+ display: flex;
+ position: relative;
+ justify-content: flex-start;
+ align-items: center;
+ gap: var(--space-small);
+ min-height: 46px;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ padding: 0 var(--space-small);
+ border-radius: var(--border-radius-small);
+}
+
+.color-input-container:hover {
+ background-color: var(--toolbar-button-background-hover);
+}
+
+#color-swatch:focus-visible {
+ outline: none;
+}
+
+.color-input-container:focus-within {
+ outline: 2px solid var(--primary-color);
+ outline-offset: var(--focus-outline-offset);
+}
+
+.icon-container {
+ display: flex;
+ margin-inline: auto var(--space-xsmall);
+}
+
+#color-swatch {
+ appearance: none;
+ width: 34px;
+ height: 34px;
+ background-color: transparent;
+ border: none;
+ cursor: pointer;
+}
+
+#color-swatch::-moz-color-swatch {
+ border-radius: var(--border-radius-circle);
+ border: 1px solid rgba(0, 0, 0, 0.25);
+}
diff --git a/toolkit/components/reader/color-input.mjs b/toolkit/components/reader/color-input.mjs
new file mode 100644
index 0000000000..1c460dac3e
--- /dev/null
+++ b/toolkit/components/reader/color-input.mjs
@@ -0,0 +1,69 @@
+/* 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/. */
+
+import { html } from "chrome://global/content/vendor/lit.all.mjs";
+import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
+
+/**
+ * @tagname color-input
+ * @property {string} color - The initial color value as a hex code.
+ * @property {string} propName - The property that the color input sets.
+ * @property {string} l10nId - l10nId for label text.
+ */
+export default class ColorInput extends MozLitElement {
+ static properties = {
+ color: { type: String },
+ propName: { type: String, attribute: "prop-name" },
+ l10nId: { type: String, attribute: "data-l10n-id" },
+ };
+
+ static queries = {
+ inputEl: "#color-swatch",
+ };
+
+ handleColorInput(event) {
+ this.color = event.target.value;
+ this.dispatchEvent(
+ new CustomEvent("color-picked", {
+ detail: this.color,
+ })
+ );
+ }
+
+ /* Function to launch color picker when the user clicks anywhere in the container. */
+ handleClick(event) {
+ // If the user directly clicks the color swatch, no need to propagate click.
+ if (event.target.matches("input")) {
+ return;
+ }
+ this.inputEl.click();
+ }
+
+ render() {
+ return html`
+ <link
+ rel="stylesheet"
+ href="chrome://global/content/reader/color-input.css"
+ />
+ <div class="color-input-container" @click="${this.handleClick}">
+ <input
+ type="color"
+ name="${this.propName}"
+ .value="${this.color}"
+ id="color-swatch"
+ @input="${this.handleColorInput}"
+ />
+ <label for="color-swatch" data-l10n-id=${this.l10nId}></label>
+ <div class="icon-container">
+ <img
+ class="icon"
+ role="presentation"
+ src="chrome://global/skin/icons/edit-outline.svg"
+ />
+ </div>
+ </div>
+ `;
+ }
+}
+customElements.define("color-input", ColorInput);
diff --git a/toolkit/components/reader/color-input.stories.mjs b/toolkit/components/reader/color-input.stories.mjs
new file mode 100644
index 0000000000..cec85a8d98
--- /dev/null
+++ b/toolkit/components/reader/color-input.stories.mjs
@@ -0,0 +1,32 @@
+/* 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/. */
+
+import { html } from "../../content/widgets/vendor/lit.all.mjs";
+// eslint-disable-next-line import/no-unassigned-import
+import "chrome://global/content/reader/color-input.mjs";
+
+export default {
+ title: "Domain-specific UI Widgets/Reader View/Color Input",
+ component: "color-input",
+ argTypes: {},
+ parameters: {
+ status: "stable",
+ fluent: `moz-color-input-label = Background`,
+ },
+};
+
+const Template = ({ color, propName, labelL10nId }) => html`
+ <color-input
+ color=${color}
+ data-l10n-id=${labelL10nId}
+ prop-name=${propName}
+ ></color-input>
+`;
+
+export const Default = Template.bind({});
+Default.args = {
+ propName: "background",
+ color: "#7293C9",
+ labelL10nId: "moz-color-input-label",
+};
diff --git a/toolkit/components/reader/content/aboutReader.html b/toolkit/components/reader/content/aboutReader.html
index db93e9f80b..d1c9164d42 100644
--- a/toolkit/components/reader/content/aboutReader.html
+++ b/toolkit/components/reader/content/aboutReader.html
@@ -14,11 +14,23 @@
<meta name="viewport" content="width=device-width; user-scalable=0" />
<link
rel="stylesheet"
+ href="chrome://global/skin/design-system/tokens-brand.css"
+ />
+ <link
+ rel="stylesheet"
href="chrome://global/skin/aboutReader.css"
type="text/css"
/>
<link rel="localization" href="toolkit/about/aboutReader.ftl" />
<link rel="localization" href="toolkit/branding/brandings.ftl" />
+ <script
+ type="module"
+ src="chrome://global/content/reader/color-input.mjs"
+ ></script>
+ <script
+ type="module"
+ src="chrome://global/content/elements/named-deck.js"
+ ></script>
</head>
<body>
@@ -53,7 +65,6 @@
</button>
</li>
<li class="dropdown-popup">
- <div class="dropdown-arrow"></div>
<div class="font-type-buttons radiorow"></div>
<div class="font-size-buttons buttonrow">
<button
@@ -88,7 +99,63 @@
data-l10n-id="about-reader-toolbar-lineheightplus"
></button>
</div>
- <div class="color-scheme-buttons radiorow"></div>
+ <div
+ class="color-scheme-buttons radiorow"
+ id="regular-color-scheme"
+ hidden="false"
+ ></div>
+ </li>
+ </ul>
+ <ul
+ class="dropdown colors-dropdown"
+ id="custom-colors-color-scheme"
+ hidden="true"
+ >
+ <li>
+ <button
+ class="dropdown-toggle toolbar-button colors-button"
+ aria-labelledby="toolbar-color-controls"
+ data-telemetry-id="reader-color-controls"
+ >
+ <span
+ class="hover-label"
+ id="toolbar-color-controls"
+ data-l10n-id="about-reader-toolbar-color-controls"
+ ></span>
+ </button>
+ </li>
+ <li class="dropdown-popup" id="color-controls">
+ <h2
+ data-l10n-id="about-reader-colors-menu-header"
+ id="about-reader-colors-menu-header"
+ ></h2>
+ <button-group aria-labelledby="about-reader-colors-menu-header">
+ <button
+ is="named-deck-button"
+ deck="tabs-deck"
+ name="fxtheme"
+ data-l10n-id="about-reader-fxtheme-tab"
+ ></button>
+ <button
+ is="named-deck-button"
+ deck="tabs-deck"
+ name="customtheme"
+ data-l10n-id="about-reader-customtheme-tab"
+ ></button>
+ </button-group>
+ <named-deck id="tabs-deck" is-tabbed>
+ <div
+ name="fxtheme"
+ class="colors-menu-color-scheme-buttons radiorow"
+ ></div>
+ <div name="customtheme">
+ <ul class="custom-colors-selection"></ul>
+ <button
+ class="custom-colors-reset-button"
+ data-l10n-id="about-reader-custom-colors-reset-button"
+ ></button>
+ </div>
+ </named-deck>
</li>
</ul>
</div>
diff --git a/toolkit/components/reader/jar.mn b/toolkit/components/reader/jar.mn
index 4b72136192..587dd45cf8 100644
--- a/toolkit/components/reader/jar.mn
+++ b/toolkit/components/reader/jar.mn
@@ -4,3 +4,5 @@
toolkit.jar:
content/global/reader/aboutReader.html (content/aboutReader.html)
+ content/global/reader/color-input.css (color-input.css)
+ content/global/reader/color-input.mjs (color-input.mjs)
diff --git a/toolkit/components/reader/moz.build b/toolkit/components/reader/moz.build
index e57a0fe705..dba7a5a2ef 100644
--- a/toolkit/components/reader/moz.build
+++ b/toolkit/components/reader/moz.build
@@ -22,7 +22,9 @@ EXTRA_JS_MODULES.reader = [
"ReaderWorker.sys.mjs",
]
-BROWSER_CHROME_MANIFESTS += ["test/browser.toml"]
+BROWSER_CHROME_MANIFESTS += ["tests/browser/browser.toml"]
+
+MOCHITEST_CHROME_MANIFESTS += ["tests/chrome/chrome.toml"]
with Files("**"):
BUG_COMPONENT = ("Toolkit", "Reader Mode")
diff --git a/toolkit/components/reader/test/browser_readerMode_colorSchemePref.js b/toolkit/components/reader/test/browser_readerMode_colorSchemePref.js
deleted file mode 100644
index 0bee6eaf05..0000000000
--- a/toolkit/components/reader/test/browser_readerMode_colorSchemePref.js
+++ /dev/null
@@ -1,67 +0,0 @@
-/* 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/. */
-
-"use strict";
-
-const TEST_PATH = getRootDirectory(gTestPath).replace(
- "chrome://mochitests/content",
- "http://example.com"
-);
-
-async function testColorScheme(aPref, aExpectation) {
- // Set the browser content theme to light or dark.
- Services.prefs.setIntPref("browser.theme.content-theme", aPref);
-
- // Reader Mode Color Scheme Preference must be manually set by the user, will
- // default to "auto" initially.
- Services.prefs.setCharPref("reader.color_scheme", aExpectation);
-
- let aBodyExpectation = aExpectation;
- if (aBodyExpectation === "auto") {
- aBodyExpectation = aPref === 1 ? "light" : "dark";
- }
-
- // Open a browser tab, enter reader mode, and test if we have the valid
- // reader mode color scheme preference pre-selected.
- await BrowserTestUtils.withNewTab(
- TEST_PATH + "readerModeArticle.html",
- async function (browser) {
- let pageShownPromise = BrowserTestUtils.waitForContentEvent(
- browser,
- "AboutReaderContentReady"
- );
-
- let readerButton = document.getElementById("reader-mode-button");
- readerButton.click();
- await pageShownPromise;
-
- let colorScheme = Services.prefs.getCharPref("reader.color_scheme");
-
- Assert.equal(colorScheme, aExpectation);
-
- await SpecialPowers.spawn(browser, [aBodyExpectation], expectation => {
- let bodyClass = content.document.body.className;
- ok(
- bodyClass.includes(expectation),
- "The body of the test document has the correct color scheme."
- );
- });
- }
- );
-}
-
-/**
- * Test that opening reader mode maintains the correct color scheme preference
- * until the user manually sets a different color scheme.
- */
-add_task(async function () {
- await testColorScheme(0, "auto");
- await testColorScheme(1, "auto");
- await testColorScheme(0, "light");
- await testColorScheme(1, "light");
- await testColorScheme(0, "dark");
- await testColorScheme(1, "dark");
- await testColorScheme(0, "sepia");
- await testColorScheme(1, "sepia");
-});
diff --git a/toolkit/components/reader/test/browser_readerMode_menu.js b/toolkit/components/reader/test/browser_readerMode_menu.js
deleted file mode 100644
index 304fefa5d0..0000000000
--- a/toolkit/components/reader/test/browser_readerMode_menu.js
+++ /dev/null
@@ -1,69 +0,0 @@
-/* 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/. */
-
-"use strict";
-
-const TEST_PATH = getRootDirectory(gTestPath).replace(
- "chrome://mochitests/content",
- "http://example.com"
-);
-
-/**
- * Test that the reader mode correctly calculates and displays the
- * estimated reading time for a short article
- */
-add_task(async function () {
- await BrowserTestUtils.withNewTab(
- TEST_PATH + "readerModeArticleShort.html",
- async function (browser) {
- let pageShownPromise = BrowserTestUtils.waitForContentEvent(
- browser,
- "AboutReaderContentReady"
- );
- let readerButton = document.getElementById("reader-mode-button");
- readerButton.click();
- await pageShownPromise;
- await SpecialPowers.spawn(browser, [], async function () {
- function dispatchMouseEvent(win, target, eventName) {
- let mouseEvent = new win.MouseEvent(eventName, {
- view: win,
- bubbles: true,
- cancelable: true,
- composed: true,
- });
- target.dispatchEvent(mouseEvent);
- }
-
- function simulateClick(target) {
- dispatchMouseEvent(win, target, "mousedown");
- dispatchMouseEvent(win, target, "mouseup");
- dispatchMouseEvent(win, target, "click");
- }
-
- let doc = content.document;
- let win = content.window;
- let styleButton = doc.querySelector(".style-button");
-
- let styleDropdown = doc.querySelector(".style-dropdown");
- ok(!styleDropdown.classList.contains("open"), "dropdown is closed");
-
- simulateClick(styleButton);
- ok(styleDropdown.classList.contains("open"), "dropdown is open");
-
- // simulate clicking on the article title to close the dropdown
- let title = doc.querySelector("h1");
- simulateClick(title);
- ok(!styleDropdown.classList.contains("open"), "dropdown is closed");
-
- // reopen the dropdown
- simulateClick(styleButton);
- ok(styleDropdown.classList.contains("open"), "dropdown is open");
-
- // now click on the button again to close it
- simulateClick(styleButton);
- ok(!styleDropdown.classList.contains("open"), "dropdown is closed");
- });
- }
- );
-});
diff --git a/toolkit/components/reader/test/browser.toml b/toolkit/components/reader/tests/browser/browser.toml
index 9382e6d60f..9382e6d60f 100644
--- a/toolkit/components/reader/test/browser.toml
+++ b/toolkit/components/reader/tests/browser/browser.toml
diff --git a/toolkit/components/reader/test/browser_bug1124271_readerModePinnedTab.js b/toolkit/components/reader/tests/browser/browser_bug1124271_readerModePinnedTab.js
index 346d503675..346d503675 100644
--- a/toolkit/components/reader/test/browser_bug1124271_readerModePinnedTab.js
+++ b/toolkit/components/reader/tests/browser/browser_bug1124271_readerModePinnedTab.js
diff --git a/toolkit/components/reader/test/browser_bug1453818_samesite_cookie.js b/toolkit/components/reader/tests/browser/browser_bug1453818_samesite_cookie.js
index 1fbfdeabfb..1fbfdeabfb 100644
--- a/toolkit/components/reader/test/browser_bug1453818_samesite_cookie.js
+++ b/toolkit/components/reader/tests/browser/browser_bug1453818_samesite_cookie.js
diff --git a/toolkit/components/reader/test/browser_bug1780350_readerModeSaveScroll.js b/toolkit/components/reader/tests/browser/browser_bug1780350_readerModeSaveScroll.js
index 76add5511e..76add5511e 100644
--- a/toolkit/components/reader/test/browser_bug1780350_readerModeSaveScroll.js
+++ b/toolkit/components/reader/tests/browser/browser_bug1780350_readerModeSaveScroll.js
diff --git a/toolkit/components/reader/test/browser_drag_url_readerMode.js b/toolkit/components/reader/tests/browser/browser_drag_url_readerMode.js
index 2dae1872c3..2dae1872c3 100644
--- a/toolkit/components/reader/test/browser_drag_url_readerMode.js
+++ b/toolkit/components/reader/tests/browser/browser_drag_url_readerMode.js
diff --git a/toolkit/components/reader/test/browser_localfile_readerMode.js b/toolkit/components/reader/tests/browser/browser_localfile_readerMode.js
index 118e4bb23f..118e4bb23f 100644
--- a/toolkit/components/reader/test/browser_localfile_readerMode.js
+++ b/toolkit/components/reader/tests/browser/browser_localfile_readerMode.js
diff --git a/toolkit/components/reader/test/browser_readerMode.js b/toolkit/components/reader/tests/browser/browser_readerMode.js
index a38e0a6de6..9093aa7fc1 100644
--- a/toolkit/components/reader/test/browser_readerMode.js
+++ b/toolkit/components/reader/tests/browser/browser_readerMode.js
@@ -23,7 +23,7 @@ ChromeUtils.defineESModuleGetters(this, {
add_task(async function test_reader_button() {
registerCleanupFunction(function () {
// Reset test prefs.
- TEST_PREFS.forEach(([name, value]) => {
+ TEST_PREFS.forEach(([name]) => {
Services.prefs.clearUserPref(name);
});
while (gBrowser.tabs.length > 1) {
diff --git a/toolkit/components/reader/test/browser_readerMode_bc_reuse.js b/toolkit/components/reader/tests/browser/browser_readerMode_bc_reuse.js
index 9ac0e367ca..9ac0e367ca 100644
--- a/toolkit/components/reader/test/browser_readerMode_bc_reuse.js
+++ b/toolkit/components/reader/tests/browser/browser_readerMode_bc_reuse.js
diff --git a/toolkit/components/reader/test/browser_readerMode_cached.js b/toolkit/components/reader/tests/browser/browser_readerMode_cached.js
index 7f36a15dbb..ca9dd4a447 100644
--- a/toolkit/components/reader/test/browser_readerMode_cached.js
+++ b/toolkit/components/reader/tests/browser/browser_readerMode_cached.js
@@ -7,7 +7,7 @@
// contained within it, so if the article gets reloaded instead of using
// the cached version, it would have a different value in it.
const URL =
- "http://mochi.test:8888/browser/toolkit/components/reader/test/readerModeRandom.sjs";
+ "http://mochi.test:8888/browser/toolkit/components/reader/tests/browser/readerModeRandom.sjs";
add_task(async function () {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, URL);
diff --git a/toolkit/components/reader/tests/browser/browser_readerMode_colorSchemePref.js b/toolkit/components/reader/tests/browser/browser_readerMode_colorSchemePref.js
new file mode 100644
index 0000000000..73d3f160b8
--- /dev/null
+++ b/toolkit/components/reader/tests/browser/browser_readerMode_colorSchemePref.js
@@ -0,0 +1,121 @@
+/* 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/. */
+
+"use strict";
+
+const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "http://example.com"
+);
+
+async function testColorScheme(aPref, aExpectation) {
+ // Set the browser content theme to light or dark.
+ Services.prefs.setIntPref("browser.theme.content-theme", aPref);
+
+ // Reader Mode Color Scheme Preference must be manually set by the user, will
+ // default to "auto" initially.
+ Services.prefs.setCharPref("reader.color_scheme", aExpectation);
+
+ let aBodyExpectation = aExpectation;
+ if (aBodyExpectation === "auto") {
+ aBodyExpectation = aPref === 1 ? "light" : "dark";
+ }
+
+ // Open a browser tab, enter reader mode, and test if we have the valid
+ // reader mode color scheme preference pre-selected.
+ await BrowserTestUtils.withNewTab(
+ TEST_PATH + "readerModeArticle.html",
+ async function (browser) {
+ let pageShownPromise = BrowserTestUtils.waitForContentEvent(
+ browser,
+ "AboutReaderContentReady"
+ );
+
+ let readerButton = document.getElementById("reader-mode-button");
+ readerButton.click();
+ await pageShownPromise;
+
+ let colorScheme = Services.prefs.getCharPref("reader.color_scheme");
+
+ Assert.equal(colorScheme, aExpectation);
+
+ await SpecialPowers.spawn(browser, [aBodyExpectation], expectation => {
+ let bodyClass = content.document.body.className;
+ ok(
+ bodyClass.includes(expectation),
+ "The body of the test document has the correct color scheme."
+ );
+ });
+ }
+ );
+}
+
+/**
+ * Test that opening reader mode maintains the correct color scheme preference
+ * until the user manually sets a different color scheme.
+ */
+add_task(async function () {
+ await testColorScheme(0, "auto");
+ await testColorScheme(1, "auto");
+ await testColorScheme(0, "light");
+ await testColorScheme(1, "light");
+ await testColorScheme(0, "dark");
+ await testColorScheme(1, "dark");
+ await testColorScheme(0, "sepia");
+ await testColorScheme(1, "sepia");
+});
+
+async function testCustomColors(aPref, color) {
+ // Set the theme selection to custom.
+ Services.prefs.setBoolPref("reader.colors_menu.enabled", true);
+ Services.prefs.setCharPref("reader.color_scheme", "custom");
+
+ // Set the custom pref to the color value.
+ Services.prefs.setCharPref(`reader.custom_colors.${aPref}`, color);
+
+ // Open a browser tab, enter reader mode, and test if the page colors
+ // reflect the pref selection.
+ await BrowserTestUtils.withNewTab(
+ TEST_PATH + "readerModeArticle.html",
+ async function (browser) {
+ let pageShownPromise = BrowserTestUtils.waitForContentEvent(
+ browser,
+ "AboutReaderContentReady"
+ );
+
+ let readerButton = document.getElementById("reader-mode-button");
+ readerButton.click();
+ await pageShownPromise;
+
+ let colorScheme = Services.prefs.getCharPref("reader.color_scheme");
+ Assert.equal(colorScheme, "custom");
+ let prefValue = Services.prefs.getStringPref(
+ `reader.custom_colors.${aPref}`
+ );
+ let cssProp = `--custom-theme-${aPref}`;
+
+ await SpecialPowers.spawn(
+ browser,
+ [prefValue, cssProp],
+ (customColor, prop) => {
+ let style = content.window.getComputedStyle(content.document.body);
+ let actualColor = style.getPropertyValue(prop);
+ Assert.equal(customColor, actualColor);
+ }
+ );
+ }
+ );
+}
+
+/**
+ * Test that the custom color scheme selection updates the document colors correctly.
+ */
+add_task(async function () {
+ await testCustomColors("foreground", "#ffffff");
+ await testCustomColors("background", "#000000");
+ await testCustomColors("unvisited-links", "#ffffff");
+ await testCustomColors("visited-links", "#ffffff");
+ await testCustomColors("visited-links", "#ffffff");
+ await testCustomColors("selection-highlight", "#ffffff");
+});
diff --git a/toolkit/components/reader/test/browser_readerMode_hidden_nodes.js b/toolkit/components/reader/tests/browser/browser_readerMode_hidden_nodes.js
index d8e432a164..a6d058dfb1 100644
--- a/toolkit/components/reader/test/browser_readerMode_hidden_nodes.js
+++ b/toolkit/components/reader/tests/browser/browser_readerMode_hidden_nodes.js
@@ -18,7 +18,7 @@ var readerButton = document.getElementById("reader-mode-button");
add_task(async function test_reader_button() {
registerCleanupFunction(function () {
// Reset test prefs.
- TEST_PREFS.forEach(([name, value]) => {
+ TEST_PREFS.forEach(([name]) => {
Services.prefs.clearUserPref(name);
});
while (gBrowser.tabs.length > 1) {
diff --git a/toolkit/components/reader/tests/browser/browser_readerMode_menu.js b/toolkit/components/reader/tests/browser/browser_readerMode_menu.js
new file mode 100644
index 0000000000..2c08faf4f9
--- /dev/null
+++ b/toolkit/components/reader/tests/browser/browser_readerMode_menu.js
@@ -0,0 +1,75 @@
+/* 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/. */
+
+"use strict";
+
+const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "http://example.com"
+);
+
+/**
+ * Test that the reader mode correctly calculates and displays the
+ * estimated reading time for a short article
+ */
+add_task(async function () {
+ await BrowserTestUtils.withNewTab(
+ TEST_PATH + "readerModeArticleShort.html",
+ async function (browser) {
+ let pageShownPromise = BrowserTestUtils.waitForContentEvent(
+ browser,
+ "AboutReaderContentReady"
+ );
+ let readerButton = document.getElementById("reader-mode-button");
+ readerButton.click();
+ await pageShownPromise;
+ await SpecialPowers.spawn(browser, [], async function () {
+ function dispatchMouseEvent(win, target, eventName) {
+ let mouseEvent = new win.MouseEvent(eventName, {
+ view: win,
+ bubbles: true,
+ cancelable: true,
+ composed: true,
+ });
+ target.dispatchEvent(mouseEvent);
+ }
+
+ function simulateClick(target) {
+ dispatchMouseEvent(win, target, "mousedown");
+ dispatchMouseEvent(win, target, "mouseup");
+ dispatchMouseEvent(win, target, "click");
+ }
+
+ async function testOpenCloseDropdown(target) {
+ let button = doc.querySelector(`.${target}-button`);
+
+ let dropdown = doc.querySelector(`.${target}-dropdown`);
+ ok(!dropdown.classList.contains("open"), "dropdown is closed");
+
+ simulateClick(button);
+ ok(dropdown.classList.contains("open"), "dropdown is open");
+
+ // simulate clicking on the article title to close the dropdown
+ let title = doc.querySelector(".reader-title");
+ simulateClick(title);
+ ok(!dropdown.classList.contains("open"), "dropdown is closed");
+
+ // reopen the dropdown
+ simulateClick(button);
+ ok(dropdown.classList.contains("open"), "dropdown is open");
+
+ // now click on the button again to close it
+ simulateClick(button);
+ ok(!dropdown.classList.contains("open"), "dropdown is closed");
+ }
+
+ let doc = content.document;
+ let win = content.window;
+
+ testOpenCloseDropdown("style");
+ testOpenCloseDropdown("colors");
+ });
+ }
+ );
+});
diff --git a/toolkit/components/reader/test/browser_readerMode_pocket.js b/toolkit/components/reader/tests/browser/browser_readerMode_pocket.js
index 43426383ff..43426383ff 100644
--- a/toolkit/components/reader/test/browser_readerMode_pocket.js
+++ b/toolkit/components/reader/tests/browser/browser_readerMode_pocket.js
diff --git a/toolkit/components/reader/test/browser_readerMode_readingTime.js b/toolkit/components/reader/tests/browser/browser_readerMode_readingTime.js
index 91631b6234..91631b6234 100644
--- a/toolkit/components/reader/test/browser_readerMode_readingTime.js
+++ b/toolkit/components/reader/tests/browser/browser_readerMode_readingTime.js
diff --git a/toolkit/components/reader/test/browser_readerMode_refresh.js b/toolkit/components/reader/tests/browser/browser_readerMode_refresh.js
index 00b4557f70..00b4557f70 100644
--- a/toolkit/components/reader/test/browser_readerMode_refresh.js
+++ b/toolkit/components/reader/tests/browser/browser_readerMode_refresh.js
diff --git a/toolkit/components/reader/test/browser_readerMode_remoteType.js b/toolkit/components/reader/tests/browser/browser_readerMode_remoteType.js
index c2510667c8..c2510667c8 100644
--- a/toolkit/components/reader/test/browser_readerMode_remoteType.js
+++ b/toolkit/components/reader/tests/browser/browser_readerMode_remoteType.js
diff --git a/toolkit/components/reader/test/browser_readerMode_samesite_cookie_redirect.js b/toolkit/components/reader/tests/browser/browser_readerMode_samesite_cookie_redirect.js
index 22703b9a81..22703b9a81 100644
--- a/toolkit/components/reader/test/browser_readerMode_samesite_cookie_redirect.js
+++ b/toolkit/components/reader/tests/browser/browser_readerMode_samesite_cookie_redirect.js
diff --git a/toolkit/components/reader/test/browser_readerMode_with_anchor.js b/toolkit/components/reader/tests/browser/browser_readerMode_with_anchor.js
index 229daaed9d..229daaed9d 100644
--- a/toolkit/components/reader/test/browser_readerMode_with_anchor.js
+++ b/toolkit/components/reader/tests/browser/browser_readerMode_with_anchor.js
diff --git a/toolkit/components/reader/test/getCookies.sjs b/toolkit/components/reader/tests/browser/getCookies.sjs
index 02e29fd877..02e29fd877 100644
--- a/toolkit/components/reader/test/getCookies.sjs
+++ b/toolkit/components/reader/tests/browser/getCookies.sjs
diff --git a/toolkit/components/reader/test/head.js b/toolkit/components/reader/tests/browser/head.js
index 5f9baf8fcd..5f9baf8fcd 100644
--- a/toolkit/components/reader/test/head.js
+++ b/toolkit/components/reader/tests/browser/head.js
diff --git a/toolkit/components/reader/test/linkToGetCookies.html b/toolkit/components/reader/tests/browser/linkToGetCookies.html
index 341046a21d..a05d32af4e 100644
--- a/toolkit/components/reader/test/linkToGetCookies.html
+++ b/toolkit/components/reader/tests/browser/linkToGetCookies.html
@@ -7,7 +7,7 @@
<article>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a semper sed, adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. Curabitur dapibus enim sit amet elit pharetra tincidunt feugiat nisl imperdiet. Ut convallis libero in urna ultrices accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at malesuada arcu rhoncus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In rutrum accumsan ultricies. Mauris vitae nisi at sem facilisis semper ac in est.</p>
- <p><a href="http://example.com/browser/toolkit/components/reader/test/getCookies.sjs" id="link">Cross-origin link to getCookies.html</a></p>
+ <p><a href="http://example.com/browser/toolkit/components/reader/tests/browser/getCookies.sjs" id="link">Cross-origin link to getCookies.html</a></p>
</article>
</body>
</html>
diff --git a/toolkit/components/reader/test/readerModeArticle.html b/toolkit/components/reader/tests/browser/readerModeArticle.html
index a0f1c64da0..a0f1c64da0 100644
--- a/toolkit/components/reader/test/readerModeArticle.html
+++ b/toolkit/components/reader/tests/browser/readerModeArticle.html
diff --git a/toolkit/components/reader/test/readerModeArticleContainsLink.html b/toolkit/components/reader/tests/browser/readerModeArticleContainsLink.html
index 871349adcd..be2d7d6469 100644
--- a/toolkit/components/reader/test/readerModeArticleContainsLink.html
+++ b/toolkit/components/reader/tests/browser/readerModeArticleContainsLink.html
@@ -12,7 +12,7 @@
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a semper sed, adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. Curabitur dapibus enim sit amet elit pharetra tincidunt feugiat nisl imperdiet. Ut convallis libero in urna ultrices accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at malesuada arcu rhoncus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In rutrum accumsan ultricies. Mauris vitae nisi at sem facilisis semper ac in est.</p>
<p>Vivamus fermentum semper porta. Nunc diam velit, adipiscing ut tristique vitae, sagittis vel odio. Maecenas convallis ullamcorper ultricies. Curabitur ornare, ligula semper consectetur sagittis, nisi diam iaculis velit, id fringilla sem nunc vel mi. Nam dictum, odio nec pretium volutpat, arcu ante placerat erat, non tristique elit urna et turpis. Quisque mi metus, ornare sit amet fermentum et, tincidunt et orci. Fusce eget orci a orci congue vestibulum. Ut dolor diam, elementum et vestibulum eu, porttitor vel elit. Curabitur venenatis pulvinar tellus gravida ornare. Sed et erat faucibus nunc euismod ultricies ut id justo. Nullam cursus suscipit nisi, et ultrices justo sodales nec. Fusce venenatis facilisis lectus ac semper. Aliquam at massa ipsum. Quisque bibendum purus convallis nulla ultrices ultricies. Nullam aliquam, mi eu aliquam tincidunt, purus velit laoreet tortor, viverra pretium nisi quam vitae mi. Fusce vel volutpat elit. Nam sagittis nisi dui.</p>
<p>Vivamus fermentum semper porta. Nunc diam velit, adipiscing ut tristique vitae, sagittis vel odio. Maecenas convallis ullamcorper ultricies. Curabitur ornare, ligula semper consectetur sagittis, nisi diam iaculis velit, id fringilla sem nunc vel mi. Nam dictum, odio nec pretium volutpat, arcu ante placerat erat, non tristique elit urna et turpis. Quisque mi metus, ornare sit amet fermentum et, tincidunt et orci. Fusce eget orci a orci congue vestibulum. Ut dolor diam, elementum et vestibulum eu, porttitor vel elit. Curabitur venenatis pulvinar tellus gravida ornare. Sed et erat faucibus nunc euismod ultricies ut id justo. Nullam cursus suscipit nisi, et ultrices justo sodales nec. Fusce venenatis facilisis lectus ac semper. Aliquam at massa ipsum. Quisque bibendum purus convallis nulla ultrices ultricies. Nullam aliquam, mi eu aliquam tincidunt, purus velit laoreet tortor, viverra pretium nisi quam vitae mi. Fusce vel volutpat elit. Nam sagittis nisi dui.</p>
-<p><a href="http://example.com/browser/toolkit/components/reader/test/readerModeArticle.html" id="link">Link to another page.</a></p>
+<p><a href="http://example.com/browser/toolkit/components/reader/tests/browser/readerModeArticle.html" id="link">Link to another page.</a></p>
<p>Vivamus fermentum semper porta. Nunc diam velit, adipiscing ut tristique vitae, sagittis vel odio. Maecenas convallis ullamcorper ultricies. Curabitur ornare, ligula semper consectetur sagittis, nisi diam iaculis velit, id fringilla sem nunc vel mi. Nam dictum, odio nec pretium volutpat, arcu ante placerat erat, non tristique elit urna et turpis. Quisque mi metus, ornare sit amet fermentum et, tincidunt et orci. Fusce eget orci a orci congue vestibulum. Ut dolor diam, elementum et vestibulum eu, porttitor vel elit. Curabitur venenatis pulvinar tellus gravida ornare. Sed et erat faucibus nunc euismod ultricies ut id justo. Nullam cursus suscipit nisi, et ultrices justo sodales nec. Fusce venenatis facilisis lectus ac semper. Aliquam at massa ipsum. Quisque bibendum purus convallis nulla ultrices ultricies. Nullam aliquam, mi eu aliquam tincidunt, purus velit laoreet tortor, viverra pretium nisi quam vitae mi. Fusce vel volutpat elit. Nam sagittis nisi dui.</p>
<p>Vivamus fermentum semper porta. Nunc diam velit, adipiscing ut tristique vitae, sagittis vel odio. Maecenas convallis ullamcorper ultricies. Curabitur ornare, ligula semper consectetur sagittis, nisi diam iaculis velit, id fringilla sem nunc vel mi. Nam dictum, odio nec pretium volutpat, arcu ante placerat erat, non tristique elit urna et turpis. Quisque mi metus, ornare sit amet fermentum et, tincidunt et orci. Fusce eget orci a orci congue vestibulum. Ut dolor diam, elementum et vestibulum eu, porttitor vel elit. Curabitur venenatis pulvinar tellus gravida ornare. Sed et erat faucibus nunc euismod ultricies ut id justo. Nullam cursus suscipit nisi, et ultrices justo sodales nec. Fusce venenatis facilisis lectus ac semper. Aliquam at massa ipsum. Quisque bibendum purus convallis nulla ultrices ultricies. Nullam aliquam, mi eu aliquam tincidunt, purus velit laoreet tortor, viverra pretium nisi quam vitae mi. Fusce vel volutpat elit. Nam sagittis nisi dui.</p>
</div>
diff --git a/toolkit/components/reader/test/readerModeArticleHiddenNodes.html b/toolkit/components/reader/tests/browser/readerModeArticleHiddenNodes.html
index 92441b7978..92441b7978 100644
--- a/toolkit/components/reader/test/readerModeArticleHiddenNodes.html
+++ b/toolkit/components/reader/tests/browser/readerModeArticleHiddenNodes.html
diff --git a/toolkit/components/reader/test/readerModeArticleMedium.html b/toolkit/components/reader/tests/browser/readerModeArticleMedium.html
index 70b172cf63..70b172cf63 100644
--- a/toolkit/components/reader/test/readerModeArticleMedium.html
+++ b/toolkit/components/reader/tests/browser/readerModeArticleMedium.html
diff --git a/toolkit/components/reader/test/readerModeArticleShort.html b/toolkit/components/reader/tests/browser/readerModeArticleShort.html
index 692471f27f..692471f27f 100644
--- a/toolkit/components/reader/test/readerModeArticleShort.html
+++ b/toolkit/components/reader/tests/browser/readerModeArticleShort.html
diff --git a/toolkit/components/reader/test/readerModeArticleTextPlain.txt b/toolkit/components/reader/tests/browser/readerModeArticleTextPlain.txt
index c5b7861b73..c5b7861b73 100644
--- a/toolkit/components/reader/test/readerModeArticleTextPlain.txt
+++ b/toolkit/components/reader/tests/browser/readerModeArticleTextPlain.txt
diff --git a/toolkit/components/reader/test/readerModeNonArticle.html b/toolkit/components/reader/tests/browser/readerModeNonArticle.html
index e216af3c1f..e216af3c1f 100644
--- a/toolkit/components/reader/test/readerModeNonArticle.html
+++ b/toolkit/components/reader/tests/browser/readerModeNonArticle.html
diff --git a/toolkit/components/reader/test/readerModeRandom.sjs b/toolkit/components/reader/tests/browser/readerModeRandom.sjs
index f6bb15c06a..f6bb15c06a 100644
--- a/toolkit/components/reader/test/readerModeRandom.sjs
+++ b/toolkit/components/reader/tests/browser/readerModeRandom.sjs
diff --git a/toolkit/components/reader/test/setSameSiteCookie.html b/toolkit/components/reader/tests/browser/setSameSiteCookie.html
index 67bb714922..67bb714922 100644
--- a/toolkit/components/reader/test/setSameSiteCookie.html
+++ b/toolkit/components/reader/tests/browser/setSameSiteCookie.html
diff --git a/toolkit/components/reader/test/setSameSiteCookie.html^headers^ b/toolkit/components/reader/tests/browser/setSameSiteCookie.html^headers^
index c0229c93b6..c0229c93b6 100644
--- a/toolkit/components/reader/test/setSameSiteCookie.html^headers^
+++ b/toolkit/components/reader/tests/browser/setSameSiteCookie.html^headers^
diff --git a/toolkit/components/reader/tests/chrome/chrome.toml b/toolkit/components/reader/tests/chrome/chrome.toml
new file mode 100644
index 0000000000..e416c49181
--- /dev/null
+++ b/toolkit/components/reader/tests/chrome/chrome.toml
@@ -0,0 +1,3 @@
+[DEFAULT]
+
+["test_color_input.html"]
diff --git a/toolkit/components/reader/tests/chrome/test_color_input.html b/toolkit/components/reader/tests/chrome/test_color_input.html
new file mode 100644
index 0000000000..4cd8cde77b
--- /dev/null
+++ b/toolkit/components/reader/tests/chrome/test_color_input.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>ColorInput Tests</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="module" src="chrome://global/content/reader/color-input.mjs"></script>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: block">
+ <color-input color="#ffffff" prop-name="test-prop" data-l10n-id="color-input-test-label"></color-input>
+</div>
+<pre id="test">
+ <script class="testbody" type="application/javascript">
+ add_task(async function testColorInput() {
+ const colorInput = document.querySelector("color-input");
+ ok(colorInput, "color input element is rendered");
+
+ let input = colorInput.shadowRoot.querySelector("input");
+ is(input.value, "#ffffff", "color input has the correct initial value");
+ });
+ add_task(async function testColorInputEvents() {
+ const colorInput = document.querySelector("color-input");
+ let input = colorInput.shadowRoot.querySelector("input");
+
+ const pickedColor = new Promise((resolve) => {
+ colorInput.addEventListener("color-picked", (event) => resolve(event.detail), { once: true });
+ });
+ input.value = "#0000ff";
+ input.dispatchEvent(new Event("input"));
+ let color = await pickedColor;
+ is(color, "#0000ff", "color-picked event dispatches on input");
+ });
+ </script>
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/remotebrowserutils/tests/browser/browser_documentChannel.js b/toolkit/components/remotebrowserutils/tests/browser/browser_documentChannel.js
index f32216010b..87bf94a59b 100644
--- a/toolkit/components/remotebrowserutils/tests/browser/browser_documentChannel.js
+++ b/toolkit/components/remotebrowserutils/tests/browser/browser_documentChannel.js
@@ -37,14 +37,14 @@ const EXTENSION_DATA = {
async background() {
browser.test.log("background script running");
browser.webRequest.onAuthRequired.addListener(
- async details => {
+ async () => {
browser.test.log("webRequest onAuthRequired");
// A blocking request that returns a promise exercises a codepath that
// sets the notificationCallbacks on the channel to a JS object that we
// can't do directly QueryObject on with expected results.
// This triggered a crash which was fixed in bug 1528188.
- return new Promise((resolve, reject) => {
+ return new Promise(resolve => {
setTimeout(resolve, 0);
});
},
diff --git a/toolkit/components/remotebrowserutils/tests/browser/browser_httpCrossOriginOpenerPolicy.js b/toolkit/components/remotebrowserutils/tests/browser/browser_httpCrossOriginOpenerPolicy.js
index 5f3eade823..7ef8204246 100644
--- a/toolkit/components/remotebrowserutils/tests/browser/browser_httpCrossOriginOpenerPolicy.js
+++ b/toolkit/components/remotebrowserutils/tests/browser/browser_httpCrossOriginOpenerPolicy.js
@@ -106,7 +106,7 @@ function waitForDownloadWindow() {
var domwindow = aXULWindow.docShell.domWindow;
domwindow.addEventListener("load", downloadOnLoad, true);
},
- onCloseWindow: aXULWindow => {},
+ onCloseWindow: () => {},
};
Services.wm.addListener(listener);
diff --git a/toolkit/components/remotebrowserutils/tests/browser/browser_oopProcessSwap.js b/toolkit/components/remotebrowserutils/tests/browser/browser_oopProcessSwap.js
index 3286227d37..a30a919507 100644
--- a/toolkit/components/remotebrowserutils/tests/browser/browser_oopProcessSwap.js
+++ b/toolkit/components/remotebrowserutils/tests/browser/browser_oopProcessSwap.js
@@ -63,14 +63,11 @@ add_task(async function oopProcessSwap() {
);
is(browser.browsingContext.children.length, 1);
-
- if (Services.prefs.getBoolPref("fission.preserve_browsing_contexts")) {
- is(
- frameId,
- oopinfo.browsingContextId,
- `BrowsingContext should not have changed (${frameId} != ${oopinfo.browsingContextId})`
- );
- }
+ is(
+ frameId,
+ oopinfo.browsingContextId,
+ `BrowsingContext should not have changed (${frameId} != ${oopinfo.browsingContextId})`
+ );
is(oopinfo.location, WEB, "correct location");
}
);
@@ -146,13 +143,11 @@ add_task(async function oopOriginProcessSwap() {
);
is(browser.browsingContext.children.length, 1);
- if (Services.prefs.getBoolPref("fission.preserve_browsing_contexts")) {
- is(
- frameId,
- oopinfo.browsingContextId,
- `BrowsingContext should not have changed (${frameId} != ${oopinfo.browsingContextId})`
- );
- }
+ is(
+ frameId,
+ oopinfo.browsingContextId,
+ `BrowsingContext should not have changed (${frameId} != ${oopinfo.browsingContextId})`
+ );
is(oopinfo.location, ORG_POSTMSG, "correct location");
}
);
diff --git a/toolkit/components/reportbrokensite/ReportBrokenSiteChild.sys.mjs b/toolkit/components/reportbrokensite/ReportBrokenSiteChild.sys.mjs
index 291b1defc8..5f8ccaec3e 100644
--- a/toolkit/components/reportbrokensite/ReportBrokenSiteChild.sys.mjs
+++ b/toolkit/components/reportbrokensite/ReportBrokenSiteChild.sys.mjs
@@ -236,7 +236,7 @@ export class ReportBrokenSiteChild extends JSWindowActorChild {
});
}
- async #getConsoleLogs(docShell) {
+ async #getConsoleLogs() {
return this.#getLoggedMessages()
.flat()
.sort((a, b) => a.timeStamp - b.timeStamp)
diff --git a/toolkit/components/reputationservice/nsIApplicationReputation.idl b/toolkit/components/reputationservice/nsIApplicationReputation.idl
index ea7f92038e..50a923ad2d 100644
--- a/toolkit/components/reputationservice/nsIApplicationReputation.idl
+++ b/toolkit/components/reputationservice/nsIApplicationReputation.idl
@@ -59,7 +59,7 @@ interface nsIApplicationReputationService : nsISupports {
* @param aFilename
* The filename to check.
*/
- bool isBinary(in AUTF8String aFilename);
+ boolean isBinary(in AUTF8String aFilename);
/**
* Check if a file with this name should be treated as an executable,
@@ -70,7 +70,7 @@ interface nsIApplicationReputationService : nsISupports {
* @param aFilename
* The filename to check.
*/
- bool isExecutable(in AUTF8String aFilename);
+ boolean isExecutable(in AUTF8String aFilename);
};
/**
@@ -141,7 +141,7 @@ interface nsIApplicationReputationCallback : nsISupports {
* This may be set to a value different than "VERDICT_SAFE" even if
* aShouldBlock is false, so you should always check aShouldBlock.
*/
- void onComplete(in bool aShouldBlock,
+ void onComplete(in boolean aShouldBlock,
in nsresult aStatus,
in unsigned long aVerdict);
};
diff --git a/toolkit/components/reputationservice/test/unit/test_app_rep.js b/toolkit/components/reputationservice/test/unit/test_app_rep.js
index 9c381a7beb..d28b2100ef 100644
--- a/toolkit/components/reputationservice/test/unit/test_app_rep.js
+++ b/toolkit/components/reputationservice/test/unit/test_app_rep.js
@@ -115,7 +115,7 @@ add_task(async function test_setup() {
gHttpServ = new HttpServer();
gHttpServ.registerDirectory("/", do_get_cwd());
- gHttpServ.registerPathHandler("/download", function (request, response) {
+ gHttpServ.registerPathHandler("/download", function (request) {
if (gExpectedRemote) {
let body = NetUtil.readInputStreamToString(
request.bodyInputStream,
diff --git a/toolkit/components/reputationservice/test/unit/test_app_rep_maclinux.js b/toolkit/components/reputationservice/test/unit/test_app_rep_maclinux.js
index a89197e8ec..6274f8f75d 100644
--- a/toolkit/components/reputationservice/test/unit/test_app_rep_maclinux.js
+++ b/toolkit/components/reputationservice/test/unit/test_app_rep_maclinux.js
@@ -106,7 +106,7 @@ add_task(function test_setup() {
return blob;
}
- gHttpServer.registerPathHandler("/throw", function (request, response) {
+ gHttpServer.registerPathHandler("/throw", function () {
do_throw("We shouldn't be getting here");
});
diff --git a/toolkit/components/reputationservice/test/unit/test_app_rep_windows.js b/toolkit/components/reputationservice/test/unit/test_app_rep_windows.js
index 597810859f..7ab164cd2a 100644
--- a/toolkit/components/reputationservice/test/unit/test_app_rep_windows.js
+++ b/toolkit/components/reputationservice/test/unit/test_app_rep_windows.js
@@ -212,7 +212,7 @@ add_task(async function test_setup() {
return blob;
}
- gHttpServer.registerPathHandler("/throw", function (request, response) {
+ gHttpServer.registerPathHandler("/throw", function () {
do_throw("We shouldn't be getting here");
});
diff --git a/toolkit/components/resistfingerprinting/FingerprintingWebCompatService.sys.mjs b/toolkit/components/resistfingerprinting/FingerprintingWebCompatService.sys.mjs
index b6d5bed59c..56f6435e47 100644
--- a/toolkit/components/resistfingerprinting/FingerprintingWebCompatService.sys.mjs
+++ b/toolkit/components/resistfingerprinting/FingerprintingWebCompatService.sys.mjs
@@ -1,7 +1,7 @@
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
/* 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/. */
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
const lazy = {};
diff --git a/toolkit/components/resistfingerprinting/KeyCodeConsensus_En_US.h b/toolkit/components/resistfingerprinting/KeyCodeConsensus_En_US.h
index c8fafcf22e..6558bcdc29 100644
--- a/toolkit/components/resistfingerprinting/KeyCodeConsensus_En_US.h
+++ b/toolkit/components/resistfingerprinting/KeyCodeConsensus_En_US.h
@@ -1,7 +1,7 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
/**
* This file contains the spoofed keycodes of en-US for fingerprinting
diff --git a/toolkit/components/resistfingerprinting/RFPHelper.sys.mjs b/toolkit/components/resistfingerprinting/RFPHelper.sys.mjs
index 223c0259b7..c586873a74 100644
--- a/toolkit/components/resistfingerprinting/RFPHelper.sys.mjs
+++ b/toolkit/components/resistfingerprinting/RFPHelper.sys.mjs
@@ -1,7 +1,7 @@
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
/* 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/. */
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
@@ -43,6 +43,8 @@ class _RFPHelper {
this._initialized = true;
// Add unconditional observers
+ Services.obs.addObserver(this, "user-characteristics-populating-data");
+ Services.obs.addObserver(this, "user-characteristics-populating-data-done");
Services.prefs.addObserver(kPrefResistFingerprinting, this);
Services.prefs.addObserver(kPrefLetterboxing, this);
XPCOMUtils.defineLazyPreferenceGetter(
@@ -80,6 +82,12 @@ class _RFPHelper {
observe(subject, topic, data) {
switch (topic) {
+ case "user-characteristics-populating-data":
+ this._registerUserCharacteristicsActor();
+ break;
+ case "user-characteristics-populating-data-done":
+ this._unregisterUserCharacteristicsActor();
+ break;
case "nsPref:changed":
this._handlePrefChanged(data);
break;
@@ -97,6 +105,28 @@ class _RFPHelper {
}
}
+ _registerUserCharacteristicsActor() {
+ log("_registerUserCharacteristicsActor()");
+ ChromeUtils.registerWindowActor("UserCharacteristics", {
+ parent: {
+ esModuleURI: "resource://gre/actors/UserCharacteristicsParent.sys.mjs",
+ },
+ child: {
+ esModuleURI: "resource://gre/actors/UserCharacteristicsChild.sys.mjs",
+ events: {
+ UserCharacteristicsDataDone: { wantUntrusted: true },
+ },
+ },
+ matches: ["about:fingerprinting"],
+ remoteTypes: ["privilegedabout"],
+ });
+ }
+
+ _unregisterUserCharacteristicsActor() {
+ log("_unregisterUserCharacteristicsActor()");
+ ChromeUtils.unregisterWindowActor("UserCharacteristics");
+ }
+
handleEvent(aMessage) {
switch (aMessage.type) {
case "TabOpen": {
@@ -186,7 +216,7 @@ class _RFPHelper {
);
}
- _handleHttpOnModifyRequest(subject, data) {
+ _handleHttpOnModifyRequest(subject) {
// If we are loading an HTTP page from content, show the
// "request English language web pages?" prompt.
let httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);
@@ -283,16 +313,16 @@ class _RFPHelper {
_handleLetterboxingPrefChanged() {
if (Services.prefs.getBoolPref(kPrefLetterboxing, false)) {
Services.ww.registerNotification(this);
- this._registerActor();
+ this._registerLetterboxingActor();
this._attachAllWindows();
} else {
- this._unregisterActor();
+ this._unregisterLetterboxingActor();
this._detachAllWindows();
Services.ww.unregisterNotification(this);
}
}
- _registerActor() {
+ _registerLetterboxingActor() {
ChromeUtils.registerWindowActor("RFPHelper", {
parent: {
esModuleURI: "resource:///actors/RFPHelperParent.sys.mjs",
@@ -307,7 +337,7 @@ class _RFPHelper {
});
}
- _unregisterActor() {
+ _unregisterLetterboxingActor() {
ChromeUtils.unregisterWindowActor("RFPHelper");
}
diff --git a/toolkit/components/resistfingerprinting/RFPTargetIPCUtils.h b/toolkit/components/resistfingerprinting/RFPTargetIPCUtils.h
index 198332e3f6..414aa94905 100644
--- a/toolkit/components/resistfingerprinting/RFPTargetIPCUtils.h
+++ b/toolkit/components/resistfingerprinting/RFPTargetIPCUtils.h
@@ -2,7 +2,7 @@
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
#ifndef __RFPTargetIPCUtils_h__
#define __RFPTargetIPCUtils_h__
diff --git a/toolkit/components/resistfingerprinting/RFPTargets.inc b/toolkit/components/resistfingerprinting/RFPTargets.inc
index d2327ffbfe..9bc9dcbb8e 100644
--- a/toolkit/components/resistfingerprinting/RFPTargets.inc
+++ b/toolkit/components/resistfingerprinting/RFPTargets.inc
@@ -1,7 +1,7 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
// Names should not be re-used.
diff --git a/toolkit/components/resistfingerprinting/RelativeTimeline.cpp b/toolkit/components/resistfingerprinting/RelativeTimeline.cpp
index dd2d304adb..84fdeb93f8 100644
--- a/toolkit/components/resistfingerprinting/RelativeTimeline.cpp
+++ b/toolkit/components/resistfingerprinting/RelativeTimeline.cpp
@@ -1,7 +1,7 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#include "nsCOMPtr.h"
#include "nsIRandomGenerator.h"
diff --git a/toolkit/components/resistfingerprinting/RelativeTimeline.h b/toolkit/components/resistfingerprinting/RelativeTimeline.h
index bf23de904d..c8ad1c7aad 100644
--- a/toolkit/components/resistfingerprinting/RelativeTimeline.h
+++ b/toolkit/components/resistfingerprinting/RelativeTimeline.h
@@ -1,7 +1,7 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#ifndef __RelativeTimeline_h__
#define __RelativeTimeline_h__
diff --git a/toolkit/components/resistfingerprinting/UserCharacteristicsPageService.sys.mjs b/toolkit/components/resistfingerprinting/UserCharacteristicsPageService.sys.mjs
new file mode 100644
index 0000000000..d24a2eae33
--- /dev/null
+++ b/toolkit/components/resistfingerprinting/UserCharacteristicsPageService.sys.mjs
@@ -0,0 +1,209 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ HiddenFrame: "resource://gre/modules/HiddenFrame.sys.mjs",
+});
+
+ChromeUtils.defineLazyGetter(lazy, "console", () => {
+ return console.createInstance({
+ prefix: "UserCharacteristicsPage",
+ maxLogLevelPref: "toolkit.telemetry.user_characteristics_ping.logLevel",
+ });
+});
+
+const BACKGROUND_WIDTH = 1024;
+const BACKGROUND_HEIGHT = 768;
+
+/**
+ * A manager for hidden browsers. Responsible for creating and destroying a
+ * hidden frame to hold them.
+ * All of this is copied from PageDataService.sys.mjs
+ */
+class HiddenBrowserManager {
+ /**
+ * The hidden frame if one has been created.
+ *
+ * @type {HiddenFrame | null}
+ */
+ #frame = null;
+ /**
+ * The number of hidden browser elements currently in use.
+ *
+ * @type {number}
+ */
+ #browsers = 0;
+
+ /**
+ * Creates and returns a new hidden browser.
+ *
+ * @returns {Browser}
+ */
+ async #acquireBrowser() {
+ this.#browsers++;
+ if (!this.#frame) {
+ this.#frame = new lazy.HiddenFrame();
+ }
+
+ let frame = await this.#frame.get();
+ let doc = frame.document;
+ let browser = doc.createXULElement("browser");
+ browser.setAttribute("remote", "true");
+ browser.setAttribute("type", "content");
+ browser.setAttribute(
+ "style",
+ `
+ width: ${BACKGROUND_WIDTH}px;
+ min-width: ${BACKGROUND_WIDTH}px;
+ height: ${BACKGROUND_HEIGHT}px;
+ min-height: ${BACKGROUND_HEIGHT}px;
+ `
+ );
+ browser.setAttribute("maychangeremoteness", "true");
+ doc.documentElement.appendChild(browser);
+
+ return browser;
+ }
+
+ /**
+ * Releases the given hidden browser.
+ *
+ * @param {Browser} browser
+ * The hidden browser element.
+ */
+ #releaseBrowser(browser) {
+ browser.remove();
+
+ this.#browsers--;
+ if (this.#browsers == 0) {
+ this.#frame.destroy();
+ this.#frame = null;
+ }
+ }
+
+ /**
+ * Calls a callback function with a new hidden browser.
+ * This function will return whatever the callback function returns.
+ *
+ * @param {Callback} callback
+ * The callback function will be called with the browser element and may
+ * be asynchronous.
+ * @returns {T}
+ */
+ async withHiddenBrowser(callback) {
+ let browser = await this.#acquireBrowser();
+ try {
+ return await callback(browser);
+ } finally {
+ this.#releaseBrowser(browser);
+ }
+ }
+}
+
+export class UserCharacteristicsPageService {
+ classId = Components.ID("{ce3e9659-e311-49fb-b18b-7f27c6659b23}");
+ QueryInterface = ChromeUtils.generateQI([
+ "nsIUserCharacteristicsPageService",
+ ]);
+
+ _initialized = false;
+ _isParentProcess = false;
+
+ /**
+ * A manager for hidden browsers.
+ *
+ * @type {HiddenBrowserManager}
+ */
+ _browserManager = new HiddenBrowserManager();
+
+ /**
+ * A map of hidden browsers to a resolve function that should be passed the
+ * actor that was created for the browser.
+ *
+ * @type {WeakMap<Browser, function(PageDataParent): void>}
+ */
+ _backgroundBrowsers = new WeakMap();
+
+ constructor() {
+ lazy.console.debug("Init");
+
+ if (
+ Services.appinfo.processType !== Services.appinfo.PROCESS_TYPE_DEFAULT
+ ) {
+ throw new Error(
+ "Shouldn't init UserCharacteristicsPage in content processes."
+ );
+ }
+
+ // Return if we have initiated.
+ if (this._initialized) {
+ lazy.console.warn("preventing re-initilization...");
+ return;
+ }
+ this._initialized = true;
+ }
+
+ shutdown() {}
+
+ createContentPage() {
+ lazy.console.debug("called createContentPage");
+ return this._browserManager.withHiddenBrowser(async browser => {
+ lazy.console.debug(`In withHiddenBrowser`);
+ try {
+ let { promise, resolve } = Promise.withResolvers();
+ this._backgroundBrowsers.set(browser, resolve);
+
+ let principal = Services.scriptSecurityManager.getSystemPrincipal();
+ let loadURIOptions = {
+ triggeringPrincipal: principal,
+ };
+
+ let userCharacteristicsPageURI = Services.io.newURI(
+ "about:fingerprinting"
+ );
+
+ browser.loadURI(userCharacteristicsPageURI, loadURIOptions);
+
+ let data = await promise;
+ if (data.debug) {
+ lazy.console.debug(`Debugging Output:`);
+ for (let line of data.debug) {
+ lazy.console.debug(line);
+ }
+ lazy.console.debug(`(debugging output done)`);
+ }
+ lazy.console.debug(`Data:`, data.output);
+
+ lazy.console.debug("Populating Glean metrics...");
+ Glean.characteristics.timezone.set(data.output.foo);
+
+ lazy.console.debug("Unregistering actor");
+ Services.obs.notifyObservers(
+ null,
+ "user-characteristics-populating-data-done"
+ );
+ } finally {
+ this._backgroundBrowsers.delete(browser);
+ }
+ });
+ }
+
+ async pageLoaded(browsingContext, data) {
+ lazy.console.debug(
+ `pageLoaded browsingContext=${browsingContext} data=${data}`
+ );
+
+ let browser = browsingContext.embedderElement;
+
+ let backgroundResolve = this._backgroundBrowsers.get(browser);
+ if (backgroundResolve) {
+ backgroundResolve(data);
+ return;
+ }
+ throw new Error(`No backround resolve for ${browser} found`);
+ }
+}
diff --git a/toolkit/components/resistfingerprinting/components.conf b/toolkit/components/resistfingerprinting/components.conf
index aae29becf4..d7a5f71578 100644
--- a/toolkit/components/resistfingerprinting/components.conf
+++ b/toolkit/components/resistfingerprinting/components.conf
@@ -2,7 +2,7 @@
# vim: set filetype=python:
# 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/.
+# file, You can obtain one at https://mozilla.org/MPL/2.0/.
Classes = [
{
@@ -34,4 +34,11 @@ Classes = [
'constructor': 'FingerprintingWebCompatService',
'processes': ProcessSelector.MAIN_PROCESS_ONLY,
},
+ {
+ 'cid': '{ce3e9659-e311-49fb-b18b-7f27c6659b23}',
+ 'contract_ids': ['@mozilla.org/user-characteristics-page;1'],
+ 'esModule': 'resource://gre/modules/UserCharacteristicsPageService.sys.mjs',
+ 'constructor': 'UserCharacteristicsPageService',
+ 'processes': ProcessSelector.MAIN_PROCESS_ONLY,
+ },
]
diff --git a/toolkit/components/resistfingerprinting/content/usercharacteristics.html b/toolkit/components/resistfingerprinting/content/usercharacteristics.html
new file mode 100644
index 0000000000..50071bb21e
--- /dev/null
+++ b/toolkit/components/resistfingerprinting/content/usercharacteristics.html
@@ -0,0 +1,18 @@
+<!-- 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 https://mozilla.org/MPL/2.0/. -->
+
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <meta
+ http-equiv="Content-Security-Policy"
+ content="default-src 'none'; object-src 'none'; script-src chrome:"
+ />
+ <title>about:fingerprinting</title>
+ </head>
+ <body>
+ <script src="chrome://global/content/usercharacteristics/usercharacteristics.js"></script>
+ </body>
+</html>
diff --git a/toolkit/components/resistfingerprinting/content/usercharacteristics.js b/toolkit/components/resistfingerprinting/content/usercharacteristics.js
new file mode 100644
index 0000000000..1f10ba9cbb
--- /dev/null
+++ b/toolkit/components/resistfingerprinting/content/usercharacteristics.js
@@ -0,0 +1,41 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+var debugMsgs = [];
+function debug(...args) {
+ let msg = "";
+ if (!args.length) {
+ debugMsgs.push("");
+ return;
+ }
+
+ let stringify = o => {
+ if (typeof o == "string") {
+ return o;
+ }
+ return JSON.stringify(o);
+ };
+
+ let stringifiedArgs = args.map(stringify);
+ msg += stringifiedArgs.join(" ");
+ debugMsgs.push(msg);
+}
+
+debug("Debug Line");
+debug("Another debug line, with", { an: "object" });
+
+// The first time we put a real value in here, please update browser_usercharacteristics.js
+let output = {
+ foo: "Hello World",
+};
+
+document.dispatchEvent(
+ new CustomEvent("UserCharacteristicsDataDone", {
+ bubbles: true,
+ detail: {
+ debug: debugMsgs,
+ output,
+ },
+ })
+);
diff --git a/toolkit/components/resistfingerprinting/jar.mn b/toolkit/components/resistfingerprinting/jar.mn
new file mode 100644
index 0000000000..657797b6d0
--- /dev/null
+++ b/toolkit/components/resistfingerprinting/jar.mn
@@ -0,0 +1,7 @@
+# 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 https://mozilla.org/MPL/2.0/.
+
+toolkit.jar:
+ content/global/usercharacteristics/usercharacteristics.html (content/usercharacteristics.html)
+ content/global/usercharacteristics/usercharacteristics.js (content/usercharacteristics.js)
diff --git a/toolkit/components/resistfingerprinting/metrics.yaml b/toolkit/components/resistfingerprinting/metrics.yaml
index 3c706d20fa..51b251508c 100644
--- a/toolkit/components/resistfingerprinting/metrics.yaml
+++ b/toolkit/components/resistfingerprinting/metrics.yaml
@@ -1,6 +1,6 @@
# 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/.
+# file, You can obtain one at https://mozilla.org/MPL/2.0/.
# Adding a new metric? We have docs for that!
# https://firefox-source-docs.mozilla.org/toolkit/components/glean/user/new_definitions_file.html
@@ -337,6 +337,23 @@ characteristics:
data_sensitivity:
- interaction
+ system_locale:
+ type: string
+ description: >
+ The locale used by the host OS for localization.
+ lifetime: application
+ send_in_pings:
+ - user-characteristics
+ notification_emails:
+ - tom@mozilla.com
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1881744
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1881744#c7
+ expires: never
+ data_sensitivity:
+ - technical
+
target_frame_rate:
type: quantity
unit: int
@@ -409,3 +426,105 @@ characteristics:
expires: never
data_sensitivity:
- interaction
+
+ prefs_privacy_donottrackheader_enabled:
+ type: boolean
+ description: >
+ Sending "do not track" HTTP header
+ lifetime: application
+ send_in_pings:
+ - user-characteristics
+ notification_emails:
+ - tom@mozilla.com
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1884693
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1884693#c5
+ expires: never
+ data_sensitivity:
+ - interaction
+
+ prefs_privacy_globalprivacycontrol_enabled:
+ type: boolean
+ description: >
+ Sending "global privacy control" HTTP header
+ lifetime: application
+ send_in_pings:
+ - user-characteristics
+ notification_emails:
+ - tom@mozilla.com
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1884693
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1884693#c5
+ expires: never
+ data_sensitivity:
+ - interaction
+
+ prefs_general_autoscroll: # general.autoScroll
+ type: boolean
+ description: >
+ Use autoscrolling
+ lifetime: application
+ send_in_pings:
+ - user-characteristics
+ notification_emails:
+ - tom@mozilla.com
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1884693
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1884693#c5
+ expires: never
+ data_sensitivity:
+ - interaction
+
+ prefs_general_smoothscroll: # general.smoothScroll
+ type: boolean
+ description: >
+ Use smooth scrolling
+ lifetime: application
+ send_in_pings:
+ - user-characteristics
+ notification_emails:
+ - tom@mozilla.com
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1884693
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1884693#c5
+ expires: never
+ data_sensitivity:
+ - interaction
+
+ prefs_overlay_scrollbars: # widget.gtk.overlay-scrollbars.enabled
+ type: boolean
+ description: >
+ Use overlay scrollbars (or otherwise "Always show scrollbars")
+ lifetime: application
+ send_in_pings:
+ - user-characteristics
+ notification_emails:
+ - tom@mozilla.com
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1884693
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1884693#c5
+ expires: never
+ data_sensitivity:
+ - interaction
+
+ prefs_block_popups: # dom.disable_open_during_load
+ type: boolean
+ description: >
+ Block pop-up windows (The dom.disable_open_during_load pref)
+ lifetime: application
+ send_in_pings:
+ - user-characteristics
+ notification_emails:
+ - tom@mozilla.com
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1884693
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1884693#c5
+ expires: never
+ data_sensitivity:
+ - interaction
diff --git a/toolkit/components/resistfingerprinting/moz.build b/toolkit/components/resistfingerprinting/moz.build
index 9b4e9cc8ed..4b71617b9b 100644
--- a/toolkit/components/resistfingerprinting/moz.build
+++ b/toolkit/components/resistfingerprinting/moz.build
@@ -2,13 +2,15 @@
# vim: set filetype=python:
# 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/.
+# file, You can obtain one at https://mozilla.org/MPL/2.0/.
with Files("**"):
BUG_COMPONENT = ("Core", "Privacy: Anti-Tracking")
TEST_DIRS += ["tests"]
+JAR_MANIFESTS += ["jar.mn"]
+
UNIFIED_SOURCES += [
"nsRFPService.cpp",
"RelativeTimeline.cpp",
@@ -39,6 +41,7 @@ EXPORTS.mozilla.gtest += ["nsUserCharacteristics.h"]
EXTRA_JS_MODULES += [
"FingerprintingWebCompatService.sys.mjs",
"RFPHelper.sys.mjs",
+ "UserCharacteristicsPageService.sys.mjs",
]
XPIDL_MODULE = "toolkit_resistfingerprinting"
@@ -50,6 +53,7 @@ XPCOM_MANIFESTS += [
XPIDL_SOURCES += [
"nsIFingerprintingWebCompatService.idl",
"nsIRFPService.idl",
+ "nsIUserCharacteristicsPageService.idl",
]
include("/ipc/chromium/chromium-config.mozbuild")
diff --git a/toolkit/components/resistfingerprinting/nsIFingerprintingWebCompatService.idl b/toolkit/components/resistfingerprinting/nsIFingerprintingWebCompatService.idl
index 01b1379e44..e8c7195a0e 100644
--- a/toolkit/components/resistfingerprinting/nsIFingerprintingWebCompatService.idl
+++ b/toolkit/components/resistfingerprinting/nsIFingerprintingWebCompatService.idl
@@ -1,6 +1,6 @@
/* 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/. */
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
diff --git a/toolkit/components/resistfingerprinting/nsIRFPService.idl b/toolkit/components/resistfingerprinting/nsIRFPService.idl
index b81868d202..8c83cc7e6e 100644
--- a/toolkit/components/resistfingerprinting/nsIRFPService.idl
+++ b/toolkit/components/resistfingerprinting/nsIRFPService.idl
@@ -1,6 +1,6 @@
/* 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/. */
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
#include "nsIFingerprintingWebCompatService.idl"
diff --git a/toolkit/components/resistfingerprinting/nsIUserCharacteristicsPageService.idl b/toolkit/components/resistfingerprinting/nsIUserCharacteristicsPageService.idl
new file mode 100644
index 0000000000..ce58c1a14a
--- /dev/null
+++ b/toolkit/components/resistfingerprinting/nsIUserCharacteristicsPageService.idl
@@ -0,0 +1,23 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+webidl BrowsingContext;
+
+[scriptable, uuid(ce3e9659-e311-49fb-b18b-7f27c6659b23)]
+interface nsIUserCharacteristicsPageService : nsISupports {
+
+ /*
+ * Create the UserCharacteristics about: page as a HiddenFrame
+ * and begin the data collection.
+ */
+ Promise createContentPage();
+
+ /*
+ * Called when the UserCharacteristics about: page has been loaded
+ * and supplied data back to the actor, which is passed as `data`
+ */
+ void pageLoaded(in BrowsingContext browsingContext, in jsval data);
+};
diff --git a/toolkit/components/resistfingerprinting/nsRFPService.cpp b/toolkit/components/resistfingerprinting/nsRFPService.cpp
index 643cc2cb7a..f39deb3283 100644
--- a/toolkit/components/resistfingerprinting/nsRFPService.cpp
+++ b/toolkit/components/resistfingerprinting/nsRFPService.cpp
@@ -1,7 +1,7 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#include "nsRFPService.h"
@@ -19,6 +19,7 @@
#include "MainThreadUtils.h"
#include "ScopedNSSTypes.h"
+#include "mozilla/AntiTrackingUtils.h"
#include "mozilla/ArrayIterator.h"
#include "mozilla/Assertions.h"
#include "mozilla/Atomics.h"
@@ -109,6 +110,8 @@ static mozilla::LazyLogModule gFingerprinterDetection("FingerprinterDetection");
#define LAST_PB_SESSION_EXITED_TOPIC "last-pb-context-exited"
#define IDLE_TOPIC "browser-idle-startup-tasks-finished"
#define GFX_FEATURES "gfx-features-ready"
+#define USER_CHARACTERISTICS_TEST_REQUEST \
+ "user-characteristics-testing-please-populate-data"
static constexpr uint32_t kVideoFramesPerSec = 30;
static constexpr uint32_t kVideoDroppedRatio = 5;
@@ -190,6 +193,9 @@ nsresult nsRFPService::Init() {
rv = obs->AddObserver(this, GFX_FEATURES, false);
NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = obs->AddObserver(this, USER_CHARACTERISTICS_TEST_REQUEST, false);
+ NS_ENSURE_SUCCESS(rv, rv);
}
Preferences::RegisterCallbacks(nsRFPService::PrefChanged, gCallbackPrefs,
@@ -289,6 +295,7 @@ void nsRFPService::StartShutdown() {
obs->RemoveObserver(this, OBSERVER_TOPIC_IDLE_DAILY);
obs->RemoveObserver(this, IDLE_TOPIC);
obs->RemoveObserver(this, GFX_FEATURES);
+ obs->RemoveObserver(this, USER_CHARACTERISTICS_TEST_REQUEST);
}
}
@@ -350,6 +357,12 @@ nsRFPService::Observe(nsISupports* aObject, const char* aTopic,
}
}
+ if (!strcmp(USER_CHARACTERISTICS_TEST_REQUEST, aTopic) &&
+ xpc::IsInAutomation()) {
+ nsUserCharacteristics::PopulateDataAndEventuallySubmit(
+ /* aUpdatePref = */ false, /* aTesting = */ true);
+ }
+
if (!strcmp(OBSERVER_TOPIC_IDLE_DAILY, aTopic)) {
if (StaticPrefs::
privacy_resistFingerprinting_randomization_daily_reset_enabled()) {
@@ -1267,7 +1280,10 @@ Maybe<nsTArray<uint8_t>> nsRFPService::GenerateKey(nsIChannel* aChannel) {
// Set the partitionKey using the top level URI to ensure that the key is
// specific to the top level site.
- attrs.SetPartitionKey(topLevelURI);
+ bool foreignByAncestorContext =
+ AntiTrackingUtils::IsThirdPartyChannel(aChannel) &&
+ loadInfo->GetIsThirdPartyContextToTopWindow();
+ attrs.SetPartitionKey(topLevelURI, foreignByAncestorContext);
nsAutoCString oaSuffix;
attrs.CreateSuffix(oaSuffix);
@@ -1337,8 +1353,14 @@ nsRFPService::CleanRandomKeyByPrincipal(nsIPrincipal* aPrincipal) {
OriginAttributes attrs = aPrincipal->OriginAttributesRef();
nsCOMPtr<nsIURI> uri = aPrincipal->GetURI();
- attrs.SetPartitionKey(uri);
+ attrs.SetPartitionKey(uri, false);
+ ClearBrowsingSessionKey(attrs);
+
+ // We must also include the cross-site embeds of this principal that end up
+ // re-embedded back into the same principal's top level, otherwise state will
+ // persist for this target
+ attrs.SetPartitionKey(uri, true);
ClearBrowsingSessionKey(attrs);
return NS_OK;
}
@@ -1354,14 +1376,21 @@ nsRFPService::CleanRandomKeyByDomain(const nsACString& aDomain) {
// Use the originAttributes to get the partitionKey.
OriginAttributes attrs;
- attrs.SetPartitionKey(httpURI);
+ attrs.SetPartitionKey(httpURI, false);
// Create a originAttributesPattern and set the http partitionKey to the
// pattern.
OriginAttributesPattern pattern;
pattern.mPartitionKey.Reset();
pattern.mPartitionKey.Construct(attrs.mPartitionKey);
+ ClearBrowsingSessionKey(pattern);
+ // We must also include the cross-site embeds of this principal that end up
+ // re-embedded back into the same principal's top level, otherwise state will
+ // persist for this target
+ attrs.SetPartitionKey(httpURI, true);
+ pattern.mPartitionKey.Reset();
+ pattern.mPartitionKey.Construct(attrs.mPartitionKey);
ClearBrowsingSessionKey(pattern);
// Get https URI from the domain.
@@ -1370,10 +1399,17 @@ nsRFPService::CleanRandomKeyByDomain(const nsACString& aDomain) {
NS_ENSURE_SUCCESS(rv, rv);
// Use the originAttributes to get the partitionKey and set to the pattern.
- attrs.SetPartitionKey(httpsURI);
+ attrs.SetPartitionKey(httpsURI, false);
pattern.mPartitionKey.Reset();
pattern.mPartitionKey.Construct(attrs.mPartitionKey);
+ ClearBrowsingSessionKey(pattern);
+ // We must also include the cross-site embeds of this principal that end up
+ // re-embedded back into the same principal's top level, otherwise state will
+ // persist for this target
+ attrs.SetPartitionKey(httpsURI, true);
+ pattern.mPartitionKey.Reset();
+ pattern.mPartitionKey.Construct(attrs.mPartitionKey);
ClearBrowsingSessionKey(pattern);
return NS_OK;
}
@@ -1395,7 +1431,7 @@ nsRFPService::CleanRandomKeyByHost(const nsACString& aHost,
// Use the originAttributes to get the partitionKey.
OriginAttributes attrs;
- attrs.SetPartitionKey(httpURI);
+ attrs.SetPartitionKey(httpURI, false);
// Set the partitionKey to the pattern.
pattern.mPartitionKey.Reset();
@@ -1403,16 +1439,31 @@ nsRFPService::CleanRandomKeyByHost(const nsACString& aHost,
ClearBrowsingSessionKey(pattern);
+ // We must also include the cross-site embeds of this principal that end up
+ // re-embedded back into the same principal's top level, otherwise state will
+ // persist for this target
+ attrs.SetPartitionKey(httpURI, true);
+ pattern.mPartitionKey.Reset();
+ pattern.mPartitionKey.Construct(attrs.mPartitionKey);
+ ClearBrowsingSessionKey(pattern);
+
// Get https URI from the host.
nsCOMPtr<nsIURI> httpsURI;
rv = NS_NewURI(getter_AddRefs(httpsURI), "https://"_ns + aHost);
NS_ENSURE_SUCCESS(rv, rv);
// Use the originAttributes to get the partitionKey and set to the pattern.
- attrs.SetPartitionKey(httpsURI);
+ attrs.SetPartitionKey(httpsURI, false);
pattern.mPartitionKey.Reset();
pattern.mPartitionKey.Construct(attrs.mPartitionKey);
+ ClearBrowsingSessionKey(pattern);
+ // We must also include the cross-site embeds of this principal that end up
+ // re-embedded back into the same principal's top level, otherwise state will
+ // persist for this target
+ attrs.SetPartitionKey(httpsURI, true);
+ pattern.mPartitionKey.Reset();
+ pattern.mPartitionKey.Construct(attrs.mPartitionKey);
ClearBrowsingSessionKey(pattern);
return NS_OK;
}
@@ -2011,19 +2062,63 @@ Maybe<RFPTarget> nsRFPService::GetOverriddenFingerprintingSettingsForChannel(
}
// The channel is for the first-party load.
- if (!loadInfo->GetIsThirdPartyContextToTopWindow()) {
+ if (!AntiTrackingUtils::IsThirdPartyChannel(aChannel)) {
return GetOverriddenFingerprintingSettingsForURI(uri, nullptr);
}
// The channel is for the third-party load. We get the first-party URI from
// the top-level window global parent.
- RefPtr<dom::WindowGlobalParent> topWGP =
- bc->Top()->Canonical()->GetCurrentWindowGlobal();
+ RefPtr<dom::CanonicalBrowsingContext> topBC = bc->Top()->Canonical();
+ RefPtr<dom::WindowGlobalParent> topWGP = topBC->GetCurrentWindowGlobal();
if (NS_WARN_IF(!topWGP)) {
return Nothing();
}
+ nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
+ DebugOnly<nsresult> rv =
+ loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ MOZ_ASSERT(cookieJarSettings);
+
+ uint64_t topWindowContextIdFromCJS =
+ net::CookieJarSettings::Cast(cookieJarSettings)
+ ->GetTopLevelWindowContextId();
+
+ // The top-level window could be navigated away when we get the fingerprinting
+ // override here. For example, the beacon requests. In this case, the
+ // top-level windowContext id won't match the inner window id of the top-level
+ // windowGlobalParent. So, we cannot rely on the URI from the top-level
+ // windowGlobalParent because it could be different from the one that creates
+ // the channel. Instead, we fallback to use the partitionKey in the
+ // cookieJarSettings to get the top-level URI.
+ if (topWGP->InnerWindowId() != topWindowContextIdFromCJS) {
+ nsAutoString partitionKey;
+ rv = cookieJarSettings->GetPartitionKey(partitionKey);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+ // Bail out early if the partitionKey is empty.
+ if (partitionKey.IsEmpty()) {
+ return Nothing();
+ }
+
+ nsAutoString scheme;
+ nsAutoString domain;
+ int32_t unused;
+ bool unused2;
+ if (!OriginAttributes::ParsePartitionKey(partitionKey, scheme, domain,
+ unused, unused2)) {
+ MOZ_ASSERT(false);
+ return Nothing();
+ }
+
+ nsCOMPtr<nsIURI> topURI;
+ rv = NS_NewURI(getter_AddRefs(topURI), scheme + u"://"_ns + domain);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+ return GetOverriddenFingerprintingSettingsForURI(topURI, uri);
+ }
+
nsCOMPtr<nsIPrincipal> topPrincipal = topWGP->DocumentPrincipal();
if (NS_WARN_IF(!topPrincipal)) {
return Nothing();
@@ -2049,19 +2144,20 @@ Maybe<RFPTarget> nsRFPService::GetOverriddenFingerprintingSettingsForChannel(
#ifdef DEBUG
// Verify if the top URI matches the partitionKey of the channel.
- nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
- Unused << loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));
-
nsAutoString partitionKey;
cookieJarSettings->GetPartitionKey(partitionKey);
OriginAttributes attrs;
- attrs.SetPartitionKey(topURI);
+ attrs.SetPartitionKey(topURI, false);
+
+ OriginAttributes attrsForeignByAncestor;
+ attrsForeignByAncestor.SetPartitionKey(topURI, true);
// The partitionKey of the channel could haven't been set here if the loading
// channel is top-level.
MOZ_ASSERT_IF(!partitionKey.IsEmpty(),
- attrs.mPartitionKey.Equals(partitionKey));
+ attrs.mPartitionKey.Equals(partitionKey) ||
+ attrsForeignByAncestor.mPartitionKey.Equals(partitionKey));
#endif
return GetOverriddenFingerprintingSettingsForURI(topURI, uri);
diff --git a/toolkit/components/resistfingerprinting/nsRFPService.h b/toolkit/components/resistfingerprinting/nsRFPService.h
index 9e0ae9b6af..d45f4172c9 100644
--- a/toolkit/components/resistfingerprinting/nsRFPService.h
+++ b/toolkit/components/resistfingerprinting/nsRFPService.h
@@ -1,7 +1,7 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#ifndef __nsRFPService_h__
#define __nsRFPService_h__
diff --git a/toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp b/toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp
index 9bce616f81..86de7d61a7 100644
--- a/toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp
+++ b/toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp
@@ -1,25 +1,34 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#include "nsUserCharacteristics.h"
#include "nsID.h"
#include "nsIUUIDGenerator.h"
+#include "nsIUserCharacteristicsPageService.h"
#include "nsServiceManagerUtils.h"
#include "mozilla/Logging.h"
#include "mozilla/glean/GleanPings.h"
#include "mozilla/glean/GleanMetrics.h"
+#include "jsapi.h"
+#include "mozilla/Components.h"
+#include "mozilla/dom/Promise-inl.h"
+
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/StaticPrefs_general.h"
#include "mozilla/StaticPrefs_media.h"
+#include "mozilla/StaticPrefs_widget.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/PreferenceSheet.h"
#include "mozilla/RelativeLuminanceUtils.h"
#include "mozilla/ServoStyleConsts.h"
#include "mozilla/dom/ScreenBinding.h"
+#include "mozilla/intl/OSPreferences.h"
#include "mozilla/intl/TimeZone.h"
#include "mozilla/widget/ScreenManager.h"
@@ -33,7 +42,9 @@
# include "nsMacUtilsImpl.h"
#endif
-static mozilla::LazyLogModule gUserCharacteristicsLog("UserCharacteristics");
+using namespace mozilla;
+
+static LazyLogModule gUserCharacteristicsLog("UserCharacteristics");
// ==================================================================
namespace testing {
@@ -41,9 +52,9 @@ extern "C" {
int MaxTouchPoints() {
#if defined(XP_WIN)
- return mozilla::widget::WinUtils::GetMaxTouchPoints();
+ return widget::WinUtils::GetMaxTouchPoints();
#elif defined(MOZ_WIDGET_ANDROID)
- return mozilla::java::GeckoAppShell::GetMaxTouchPoints();
+ return java::GeckoAppShell::GetMaxTouchPoints();
#else
return 0;
#endif
@@ -53,84 +64,113 @@ int MaxTouchPoints() {
}; // namespace testing
// ==================================================================
+// ==================================================================
+already_AddRefed<mozilla::dom::Promise> ContentPageStuff() {
+ nsCOMPtr<nsIUserCharacteristicsPageService> ucp =
+ do_GetService("@mozilla.org/user-characteristics-page;1");
+ MOZ_ASSERT(ucp);
+
+ RefPtr<mozilla::dom::Promise> promise;
+ nsresult rv = ucp->CreateContentPage(getter_AddRefs(promise));
+ if (NS_FAILED(rv)) {
+ MOZ_LOG(gUserCharacteristicsLog, mozilla::LogLevel::Error,
+ ("Could not create Content Page"));
+ return nullptr;
+ }
+ MOZ_LOG(gUserCharacteristicsLog, mozilla::LogLevel::Debug,
+ ("Created Content Page"));
+
+ return promise.forget();
+}
+
void PopulateCSSProperties() {
- mozilla::glean::characteristics::video_dynamic_range.Set(
- mozilla::LookAndFeel::GetInt(
- mozilla::LookAndFeel::IntID::VideoDynamicRange));
- mozilla::glean::characteristics::prefers_reduced_transparency.Set(
- mozilla::LookAndFeel::GetInt(
- mozilla::LookAndFeel::IntID::PrefersReducedTransparency));
- mozilla::glean::characteristics::prefers_reduced_motion.Set(
- mozilla::LookAndFeel::GetInt(
- mozilla::LookAndFeel::IntID::PrefersReducedMotion));
- mozilla::glean::characteristics::inverted_colors.Set(
- mozilla::LookAndFeel::GetInt(
- mozilla::LookAndFeel::IntID::InvertedColors));
- mozilla::glean::characteristics::color_scheme.Set(
- (int)mozilla::PreferenceSheet::ContentPrefs().mColorScheme);
-
- mozilla::StylePrefersContrast prefersContrast = [] {
+ glean::characteristics::prefers_reduced_transparency.Set(
+ LookAndFeel::GetInt(LookAndFeel::IntID::PrefersReducedTransparency));
+ glean::characteristics::prefers_reduced_motion.Set(
+ LookAndFeel::GetInt(LookAndFeel::IntID::PrefersReducedMotion));
+ glean::characteristics::inverted_colors.Set(
+ LookAndFeel::GetInt(LookAndFeel::IntID::InvertedColors));
+ glean::characteristics::color_scheme.Set(
+ (int)PreferenceSheet::ContentPrefs().mColorScheme);
+
+ StylePrefersContrast prefersContrast = [] {
// Replicates Gecko_MediaFeatures_PrefersContrast but without a Document
- if (!mozilla::PreferenceSheet::ContentPrefs().mUseAccessibilityTheme &&
- mozilla::PreferenceSheet::ContentPrefs().mUseDocumentColors) {
- return mozilla::StylePrefersContrast::NoPreference;
+ if (!PreferenceSheet::ContentPrefs().mUseAccessibilityTheme &&
+ PreferenceSheet::ContentPrefs().mUseDocumentColors) {
+ return StylePrefersContrast::NoPreference;
}
- const auto& colors = mozilla::PreferenceSheet::ContentPrefs().ColorsFor(
- mozilla::ColorScheme::Light);
- float ratio = mozilla::RelativeLuminanceUtils::ContrastRatio(
+ const auto& colors =
+ PreferenceSheet::ContentPrefs().ColorsFor(ColorScheme::Light);
+ float ratio = RelativeLuminanceUtils::ContrastRatio(
colors.mDefaultBackground, colors.mDefault);
// https://www.w3.org/TR/WCAG21/#contrast-minimum
if (ratio < 4.5f) {
- return mozilla::StylePrefersContrast::Less;
+ return StylePrefersContrast::Less;
}
// https://www.w3.org/TR/WCAG21/#contrast-enhanced
if (ratio >= 7.0f) {
- return mozilla::StylePrefersContrast::More;
+ return StylePrefersContrast::More;
}
- return mozilla::StylePrefersContrast::Custom;
+ return StylePrefersContrast::Custom;
}();
- mozilla::glean::characteristics::prefers_contrast.Set((int)prefersContrast);
+ glean::characteristics::prefers_contrast.Set((int)prefersContrast);
}
void PopulateScreenProperties() {
- auto& screenManager = mozilla::widget::ScreenManager::GetSingleton();
- RefPtr<mozilla::widget::Screen> screen = screenManager.GetPrimaryScreen();
+ auto& screenManager = widget::ScreenManager::GetSingleton();
+ RefPtr<widget::Screen> screen = screenManager.GetPrimaryScreen();
MOZ_ASSERT(screen);
- mozilla::dom::ScreenColorGamut colorGamut;
+ dom::ScreenColorGamut colorGamut;
screen->GetColorGamut(&colorGamut);
- mozilla::glean::characteristics::color_gamut.Set((int)colorGamut);
+ glean::characteristics::color_gamut.Set((int)colorGamut);
int32_t colorDepth;
screen->GetColorDepth(&colorDepth);
- mozilla::glean::characteristics::color_depth.Set(colorDepth);
+ glean::characteristics::color_depth.Set(colorDepth);
+
+ glean::characteristics::color_gamut.Set((int)colorGamut);
+ glean::characteristics::color_depth.Set(colorDepth);
+ const LayoutDeviceIntRect rect = screen->GetRect();
+ glean::characteristics::screen_height.Set(rect.Height());
+ glean::characteristics::screen_width.Set(rect.Width());
- mozilla::glean::characteristics::color_gamut.Set((int)colorGamut);
- mozilla::glean::characteristics::color_depth.Set(colorDepth);
- const mozilla::LayoutDeviceIntRect rect = screen->GetRect();
- mozilla::glean::characteristics::screen_height.Set(rect.Height());
- mozilla::glean::characteristics::screen_width.Set(rect.Width());
+ glean::characteristics::video_dynamic_range.Set(screen->GetIsHDR());
}
void PopulateMissingFonts() {
nsCString aMissingFonts;
gfxPlatformFontList::PlatformFontList()->GetMissingFonts(aMissingFonts);
- mozilla::glean::characteristics::missing_fonts.Set(aMissingFonts);
+ glean::characteristics::missing_fonts.Set(aMissingFonts);
}
void PopulatePrefs() {
nsAutoCString acceptLang;
- mozilla::Preferences::GetLocalizedCString("intl.accept_languages",
- acceptLang);
- mozilla::glean::characteristics::prefs_intl_accept_languages.Set(acceptLang);
+ Preferences::GetLocalizedCString("intl.accept_languages", acceptLang);
+ glean::characteristics::prefs_intl_accept_languages.Set(acceptLang);
+
+ glean::characteristics::prefs_media_eme_enabled.Set(
+ StaticPrefs::media_eme_enabled());
+
+ glean::characteristics::prefs_zoom_text_only.Set(
+ !Preferences::GetBool("browser.zoom.full"));
+
+ glean::characteristics::prefs_privacy_donottrackheader_enabled.Set(
+ StaticPrefs::privacy_donottrackheader_enabled());
+ glean::characteristics::prefs_privacy_globalprivacycontrol_enabled.Set(
+ StaticPrefs::privacy_globalprivacycontrol_enabled());
- mozilla::glean::characteristics::prefs_media_eme_enabled.Set(
- mozilla::StaticPrefs::media_eme_enabled());
+ glean::characteristics::prefs_general_autoscroll.Set(
+ Preferences::GetBool("general.autoScroll"));
+ glean::characteristics::prefs_general_smoothscroll.Set(
+ StaticPrefs::general_smoothScroll());
+ glean::characteristics::prefs_overlay_scrollbars.Set(
+ StaticPrefs::widget_gtk_overlay_scrollbars_enabled());
- mozilla::glean::characteristics::prefs_zoom_text_only.Set(
- !mozilla::Preferences::GetBool("browser.zoom.full"));
+ glean::characteristics::prefs_block_popups.Set(
+ StaticPrefs::dom_disable_open_during_load());
}
// ==================================================================
@@ -138,12 +178,16 @@ void PopulatePrefs() {
// metric is set, this variable should be incremented. It'll be a lot. It's
// okay. We're going to need it to know (including during development) what is
// the source of the data we are looking at.
-const int kSubmissionSchema = 0;
+const int kSubmissionSchema = 1;
+
+const auto* const kLastVersionPref =
+ "toolkit.telemetry.user_characteristics_ping.last_version_sent";
+const auto* const kCurrentVersionPref =
+ "toolkit.telemetry.user_characteristics_ping.current_version";
/* static */
void nsUserCharacteristics::MaybeSubmitPing() {
- MOZ_LOG(gUserCharacteristicsLog, mozilla::LogLevel::Debug,
- ("In MaybeSubmitPing()"));
+ MOZ_LOG(gUserCharacteristicsLog, LogLevel::Debug, ("In MaybeSubmitPing()"));
MOZ_ASSERT(XRE_IsParentProcess());
/**
@@ -161,14 +205,8 @@ void nsUserCharacteristics::MaybeSubmitPing() {
* Sent = Current Version.
*
*/
- const auto* const kLastVersionPref =
- "toolkit.telemetry.user_characteristics_ping.last_version_sent";
- const auto* const kCurrentVersionPref =
- "toolkit.telemetry.user_characteristics_ping.current_version";
-
- auto lastSubmissionVersion =
- mozilla::Preferences::GetInt(kLastVersionPref, 0);
- auto currentVersion = mozilla::Preferences::GetInt(kCurrentVersionPref, 0);
+ auto lastSubmissionVersion = Preferences::GetInt(kLastVersionPref, 0);
+ auto currentVersion = Preferences::GetInt(kCurrentVersionPref, 0);
MOZ_ASSERT(currentVersion == -1 || lastSubmissionVersion <= currentVersion,
"lastSubmissionVersion is somehow greater than currentVersion "
@@ -176,46 +214,40 @@ void nsUserCharacteristics::MaybeSubmitPing() {
if (lastSubmissionVersion < 0) {
// This is a way for users to opt out of this ping specifically.
- MOZ_LOG(gUserCharacteristicsLog, mozilla::LogLevel::Debug,
+ MOZ_LOG(gUserCharacteristicsLog, LogLevel::Debug,
("Returning, User Opt-out"));
return;
}
if (currentVersion == 0) {
// Do nothing. We do not want any pings.
- MOZ_LOG(gUserCharacteristicsLog, mozilla::LogLevel::Debug,
+ MOZ_LOG(gUserCharacteristicsLog, LogLevel::Debug,
("Returning, currentVersion == 0"));
return;
}
if (currentVersion == -1) {
// currentVersion = -1 is a development value to force a ping submission
- MOZ_LOG(gUserCharacteristicsLog, mozilla::LogLevel::Debug,
+ MOZ_LOG(gUserCharacteristicsLog, LogLevel::Debug,
("Force-Submitting Ping"));
- if (NS_SUCCEEDED(PopulateData())) {
- SubmitPing();
- }
+ PopulateDataAndEventuallySubmit(false);
return;
}
if (lastSubmissionVersion > currentVersion) {
// This is an unexpected scneario that indicates something is wrong. We
// asserted against it (in debug, above) We will try to sanity-correct
// ourselves by setting it to the current version.
- mozilla::Preferences::SetInt(kLastVersionPref, currentVersion);
- MOZ_LOG(gUserCharacteristicsLog, mozilla::LogLevel::Warning,
+ Preferences::SetInt(kLastVersionPref, currentVersion);
+ MOZ_LOG(gUserCharacteristicsLog, LogLevel::Warning,
("Returning, lastSubmissionVersion > currentVersion"));
return;
}
if (lastSubmissionVersion == currentVersion) {
// We are okay, we've already submitted the most recent ping
- MOZ_LOG(gUserCharacteristicsLog, mozilla::LogLevel::Warning,
+ MOZ_LOG(gUserCharacteristicsLog, LogLevel::Warning,
("Returning, lastSubmissionVersion == currentVersion"));
return;
}
if (lastSubmissionVersion < currentVersion) {
- if (NS_SUCCEEDED(PopulateData())) {
- if (NS_SUCCEEDED(SubmitPing())) {
- mozilla::Preferences::SetInt(kLastVersionPref, currentVersion);
- }
- }
+ PopulateDataAndEventuallySubmit(false);
} else {
MOZ_ASSERT_UNREACHABLE("Should never reach here");
}
@@ -225,73 +257,128 @@ const auto* const kUUIDPref =
"toolkit.telemetry.user_characteristics_ping.uuid";
/* static */
-nsresult nsUserCharacteristics::PopulateData(bool aTesting /* = false */) {
- MOZ_LOG(gUserCharacteristicsLog, mozilla::LogLevel::Warning,
- ("Populating Data"));
+void nsUserCharacteristics::PopulateDataAndEventuallySubmit(
+ bool aUpdatePref /* = true */, bool aTesting /* = false */
+) {
+ MOZ_LOG(gUserCharacteristicsLog, LogLevel::Warning, ("Populating Data"));
MOZ_ASSERT(XRE_IsParentProcess());
- mozilla::glean::characteristics::submission_schema.Set(kSubmissionSchema);
+
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (!obs) {
+ return;
+ }
+
+ // This notification tells us to register the actor
+ obs->NotifyObservers(nullptr, "user-characteristics-populating-data",
+ nullptr);
+
+ glean::characteristics::submission_schema.Set(kSubmissionSchema);
nsAutoCString uuidString;
- nsresult rv = mozilla::Preferences::GetCString(kUUIDPref, uuidString);
+ nsresult rv = Preferences::GetCString(kUUIDPref, uuidString);
if (NS_FAILED(rv) || uuidString.Length() == 0) {
nsCOMPtr<nsIUUIDGenerator> uuidgen =
do_GetService("@mozilla.org/uuid-generator;1", &rv);
- NS_ENSURE_SUCCESS(rv, rv);
+ if (NS_FAILED(rv)) {
+ return;
+ }
nsIDToCString id(nsID::GenerateUUID());
uuidString = id.get();
- mozilla::Preferences::SetCString(kUUIDPref, uuidString);
+ Preferences::SetCString(kUUIDPref, uuidString);
}
- mozilla::glean::characteristics::client_identifier.Set(uuidString);
- mozilla::glean::characteristics::max_touch_points.Set(
- testing::MaxTouchPoints());
+ glean::characteristics::client_identifier.Set(uuidString);
+
+ glean::characteristics::max_touch_points.Set(testing::MaxTouchPoints());
+
+ // ------------------------------------------------------------------------
- if (aTesting) {
+ if (!aTesting) {
// Many of the later peices of data do not work in a gtest
- // so just populate something, and return
- return NS_OK;
- }
+ // so skip populating them
+
+ // ------------------------------------------------------------------------
- PopulateMissingFonts();
- PopulateCSSProperties();
- PopulateScreenProperties();
- PopulatePrefs();
+ PopulateMissingFonts();
+ PopulateCSSProperties();
+ PopulateScreenProperties();
+ PopulatePrefs();
- mozilla::glean::characteristics::target_frame_rate.Set(
- gfxPlatform::TargetFrameRate());
+ glean::characteristics::target_frame_rate.Set(
+ gfxPlatform::TargetFrameRate());
- int32_t processorCount = 0;
+ int32_t processorCount = 0;
#if defined(XP_MACOSX)
- if (nsMacUtilsImpl::IsTCSMAvailable()) {
- // On failure, zero is returned from GetPhysicalCPUCount()
- // and we fallback to PR_GetNumberOfProcessors below.
- processorCount = nsMacUtilsImpl::GetPhysicalCPUCount();
- }
+ if (nsMacUtilsImpl::IsTCSMAvailable()) {
+ // On failure, zero is returned from GetPhysicalCPUCount()
+ // and we fallback to PR_GetNumberOfProcessors below.
+ processorCount = nsMacUtilsImpl::GetPhysicalCPUCount();
+ }
#endif
- if (processorCount == 0) {
- processorCount = PR_GetNumberOfProcessors();
+ if (processorCount == 0) {
+ processorCount = PR_GetNumberOfProcessors();
+ }
+ glean::characteristics::processor_count.Set(processorCount);
+
+ AutoTArray<char16_t, 128> tzBuffer;
+ auto result = intl::TimeZone::GetDefaultTimeZone(tzBuffer);
+ if (result.isOk()) {
+ NS_ConvertUTF16toUTF8 timeZone(
+ nsDependentString(tzBuffer.Elements(), tzBuffer.Length()));
+ glean::characteristics::timezone.Set(timeZone);
+ } else {
+ glean::characteristics::timezone.Set("<error>"_ns);
+ }
+
+ nsAutoCString locale;
+ intl::OSPreferences::GetInstance()->GetSystemLocale(locale);
+ glean::characteristics::system_locale.Set(locale);
}
- mozilla::glean::characteristics::processor_count.Set(processorCount);
-
- AutoTArray<char16_t, 128> tzBuffer;
- auto result = mozilla::intl::TimeZone::GetDefaultTimeZone(tzBuffer);
- if (result.isOk()) {
- NS_ConvertUTF16toUTF8 timeZone(
- nsDependentString(tzBuffer.Elements(), tzBuffer.Length()));
- mozilla::glean::characteristics::timezone.Set(timeZone);
+
+ // When this promise resolves, everything succeeded and we can submit.
+ RefPtr<mozilla::dom::Promise> promise = ContentPageStuff();
+
+ // ------------------------------------------------------------------------
+
+ auto fulfillSteps = [aUpdatePref, aTesting](
+ JSContext* aCx, JS::Handle<JS::Value> aPromiseResult,
+ mozilla::ErrorResult& aRv) {
+ MOZ_LOG(gUserCharacteristicsLog, mozilla::LogLevel::Debug,
+ ("ContentPageStuff Promise Resolved"));
+
+ if (!aTesting) {
+ nsUserCharacteristics::SubmitPing();
+ }
+
+ if (aUpdatePref) {
+ MOZ_LOG(gUserCharacteristicsLog, mozilla::LogLevel::Debug,
+ ("Updating preference"));
+ auto current_version =
+ mozilla::Preferences::GetInt(kCurrentVersionPref, 0);
+ mozilla::Preferences::SetInt(kLastVersionPref, current_version);
+ }
+ };
+
+ // Something failed in the Content Page...
+ auto rejectSteps = [](JSContext* aCx, JS::Handle<JS::Value> aReason,
+ mozilla::ErrorResult& aRv) {
+ MOZ_LOG(gUserCharacteristicsLog, mozilla::LogLevel::Error,
+ ("ContentPageStuff Promise Rejected"));
+ };
+
+ if (promise) {
+ promise->AddCallbacksWithCycleCollectedArgs(std::move(fulfillSteps),
+ std::move(rejectSteps));
} else {
- mozilla::glean::characteristics::timezone.Set("<error>"_ns);
+ MOZ_LOG(gUserCharacteristicsLog, mozilla::LogLevel::Error,
+ ("Did not get a Promise back from ContentPageStuff"));
}
-
- return NS_OK;
}
/* static */
-nsresult nsUserCharacteristics::SubmitPing() {
+void nsUserCharacteristics::SubmitPing() {
MOZ_LOG(gUserCharacteristicsLog, mozilla::LogLevel::Warning,
("Submitting Ping"));
- mozilla::glean_pings::UserCharacteristics.Submit();
-
- return NS_OK;
+ glean_pings::UserCharacteristics.Submit();
}
diff --git a/toolkit/components/resistfingerprinting/nsUserCharacteristics.h b/toolkit/components/resistfingerprinting/nsUserCharacteristics.h
index a52bc9aea7..7d78dcd965 100644
--- a/toolkit/components/resistfingerprinting/nsUserCharacteristics.h
+++ b/toolkit/components/resistfingerprinting/nsUserCharacteristics.h
@@ -1,7 +1,7 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#ifndef __nsUserCharacteristics_h__
#define __nsUserCharacteristics_h__
@@ -12,9 +12,15 @@ class nsUserCharacteristics {
public:
static void MaybeSubmitPing();
- // Public For testing
- static nsresult PopulateData(bool aTesting = false);
- static nsresult SubmitPing();
+ /*
+ * These APIs are public only for testing using the gtest
+ * When PopulateDataAndEventuallySubmit is called with aTesting = true
+ * it will not submit the data, and SubmitPing must be called explicitly.
+ * This is perfect because that's what we want for the gtest.
+ */
+ static void PopulateDataAndEventuallySubmit(bool aUpdatePref = true,
+ bool aTesting = false);
+ static void SubmitPing();
};
namespace testing {
diff --git a/toolkit/components/resistfingerprinting/pings.yaml b/toolkit/components/resistfingerprinting/pings.yaml
index 46a4b2da19..ddbd48d1d1 100644
--- a/toolkit/components/resistfingerprinting/pings.yaml
+++ b/toolkit/components/resistfingerprinting/pings.yaml
@@ -1,6 +1,6 @@
# 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/.
+# file, You can obtain one at https://mozilla.org/MPL/2.0/.
---
$schema: moz://mozilla.org/schemas/glean/pings/2-0-0
diff --git a/toolkit/components/resistfingerprinting/tests/browser/browser.toml b/toolkit/components/resistfingerprinting/tests/browser/browser.toml
index 213d6d4287..527e39ad89 100644
--- a/toolkit/components/resistfingerprinting/tests/browser/browser.toml
+++ b/toolkit/components/resistfingerprinting/tests/browser/browser.toml
@@ -31,3 +31,5 @@ support-files = [
support-files = ["file_pdf.pdf"]
["browser_serviceWorker_fingerprinting_webcompat.js"]
+
+["browser_usercharacteristics.js"]
diff --git a/toolkit/components/resistfingerprinting/tests/browser/browser_canvas_fingerprinter_telemetry.js b/toolkit/components/resistfingerprinting/tests/browser/browser_canvas_fingerprinter_telemetry.js
index d2a33a4347..1098d91138 100644
--- a/toolkit/components/resistfingerprinting/tests/browser/browser_canvas_fingerprinter_telemetry.js
+++ b/toolkit/components/resistfingerprinting/tests/browser/browser_canvas_fingerprinter_telemetry.js
@@ -1,7 +1,7 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
"use strict";
diff --git a/toolkit/components/resistfingerprinting/tests/browser/browser_canvas_randomization.js b/toolkit/components/resistfingerprinting/tests/browser/browser_canvas_randomization.js
index 8b4be78d53..e526f7fff1 100644
--- a/toolkit/components/resistfingerprinting/tests/browser/browser_canvas_randomization.js
+++ b/toolkit/components/resistfingerprinting/tests/browser/browser_canvas_randomization.js
@@ -1,6 +1,6 @@
/* 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/. */
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
/**
* Bug 1816189 - Testing canvas randomization on canvas data extraction.
diff --git a/toolkit/components/resistfingerprinting/tests/browser/browser_canvas_randomization_worker.js b/toolkit/components/resistfingerprinting/tests/browser/browser_canvas_randomization_worker.js
index dd7c292fa4..723d7382f8 100644
--- a/toolkit/components/resistfingerprinting/tests/browser/browser_canvas_randomization_worker.js
+++ b/toolkit/components/resistfingerprinting/tests/browser/browser_canvas_randomization_worker.js
@@ -1,6 +1,6 @@
/* 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/. */
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
const emptyPage =
getRootDirectory(gTestPath).replace(
diff --git a/toolkit/components/resistfingerprinting/tests/browser/browser_fingerprintingRemoteOverrides.js b/toolkit/components/resistfingerprinting/tests/browser/browser_fingerprintingRemoteOverrides.js
index 29c7c9170b..ac4722fd84 100644
--- a/toolkit/components/resistfingerprinting/tests/browser/browser_fingerprintingRemoteOverrides.js
+++ b/toolkit/components/resistfingerprinting/tests/browser/browser_fingerprintingRemoteOverrides.js
@@ -1,6 +1,6 @@
/* 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/. */
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
"use strict";
@@ -20,6 +20,18 @@ const TARGET_CanvasRandomization = 0x000000100;
const TARGET_WindowOuterSize = 0x002000000;
const TARGET_Gamepad = 0x00800000;
+const TEST_PAGE =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "empty.html";
+
+const TEST_ANOTHER_PAGE =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.net"
+ ) + "empty.html";
+
// A helper function to filter high 32 bits.
function extractLow32Bits(value) {
return value & 0xffffffff;
@@ -404,3 +416,49 @@ add_task(async function test_pref_override_remote_settings() {
db.clear();
});
+
+// Bug 1873682 - Verify that a third-party beacon request won't hit the
+// assertion in nsRFPService::GetOverriddenFingerprintingSettingsForChannel().
+add_task(async function test_beacon_request() {
+ // Open an empty page.
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE);
+
+ await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [TEST_ANOTHER_PAGE],
+ async url => {
+ // Create a third-party iframe
+ let ifr = content.document.createElement("iframe");
+
+ await new content.Promise(resolve => {
+ ifr.onload = resolve;
+ content.document.body.appendChild(ifr);
+ ifr.src = url;
+ });
+
+ await SpecialPowers.spawn(ifr, [url], url => {
+ // Sending the beacon request right before the tab navigates away.
+ content.addEventListener("unload", _ => {
+ let value = ["text"];
+ let blob = new Blob(value, {
+ type: "application/x-www-form-urlencoded",
+ });
+ content.navigator.sendBeacon(url, blob);
+ });
+ });
+
+ // Navigate the tab to another page.
+ content.location = url;
+ }
+ );
+
+ await BrowserTestUtils.browserLoaded(
+ tab.linkedBrowser,
+ false,
+ TEST_ANOTHER_PAGE
+ );
+
+ ok(true, "Successfully navigates away.");
+
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/toolkit/components/resistfingerprinting/tests/browser/browser_fingerprintingWebCompat.js b/toolkit/components/resistfingerprinting/tests/browser/browser_fingerprintingWebCompat.js
index 1a882fc63d..62335b2fe7 100644
--- a/toolkit/components/resistfingerprinting/tests/browser/browser_fingerprintingWebCompat.js
+++ b/toolkit/components/resistfingerprinting/tests/browser/browser_fingerprintingWebCompat.js
@@ -1,6 +1,6 @@
/* 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/. */
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
"use strict";
diff --git a/toolkit/components/resistfingerprinting/tests/browser/browser_font_fingerprinter_telemetry.js b/toolkit/components/resistfingerprinting/tests/browser/browser_font_fingerprinter_telemetry.js
index 2231197eed..0cccf0c5f3 100644
--- a/toolkit/components/resistfingerprinting/tests/browser/browser_font_fingerprinter_telemetry.js
+++ b/toolkit/components/resistfingerprinting/tests/browser/browser_font_fingerprinter_telemetry.js
@@ -1,7 +1,7 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
"use strict";
diff --git a/toolkit/components/resistfingerprinting/tests/browser/browser_fpiServiceWorkers_fingerprinting.js b/toolkit/components/resistfingerprinting/tests/browser/browser_fpiServiceWorkers_fingerprinting.js
index 64279ae442..aaa40a7928 100644
--- a/toolkit/components/resistfingerprinting/tests/browser/browser_fpiServiceWorkers_fingerprinting.js
+++ b/toolkit/components/resistfingerprinting/tests/browser/browser_fpiServiceWorkers_fingerprinting.js
@@ -68,7 +68,7 @@ runTestInFirstAndThirdPartyContexts(
},
async _ => {
await new Promise(resolve => {
- Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value =>
+ Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, () =>
resolve()
);
});
diff --git a/toolkit/components/resistfingerprinting/tests/browser/browser_serviceWorker_fingerprinting_webcompat.js b/toolkit/components/resistfingerprinting/tests/browser/browser_serviceWorker_fingerprinting_webcompat.js
index a9bab38e61..eb1ab8d795 100644
--- a/toolkit/components/resistfingerprinting/tests/browser/browser_serviceWorker_fingerprinting_webcompat.js
+++ b/toolkit/components/resistfingerprinting/tests/browser/browser_serviceWorker_fingerprinting_webcompat.js
@@ -59,7 +59,7 @@ runTestInFirstAndThirdPartyContexts(
async _ => {
await new Promise(resolve => {
- Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value =>
+ Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, () =>
resolve()
);
});
@@ -142,7 +142,7 @@ runTestInFirstAndThirdPartyContexts(
async _ => {
await new Promise(resolve => {
- Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value =>
+ Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, () =>
resolve()
);
});
diff --git a/toolkit/components/resistfingerprinting/tests/browser/browser_usercharacteristics.js b/toolkit/components/resistfingerprinting/tests/browser/browser_usercharacteristics.js
new file mode 100644
index 0000000000..9896948311
--- /dev/null
+++ b/toolkit/components/resistfingerprinting/tests/browser/browser_usercharacteristics.js
@@ -0,0 +1,51 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const emptyPage =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "empty.html";
+
+function promiseObserverNotification() {
+ return TestUtils.topicObserved(
+ "user-characteristics-populating-data-done",
+ _ => {
+ var submitted = false;
+ GleanPings.userCharacteristics.testBeforeNextSubmit(_ => {
+ submitted = true;
+
+ // Did we assign a value we got out of about:fingerprinting?
+ // For now, we are sticking the test value in a random telemetry
+ // metric, but once we have a real metric, we'll update this
+ Assert.equal(
+ "Hello World",
+ Glean.characteristics.timezone.testGetValue()
+ );
+ });
+ GleanPings.userCharacteristics.submit();
+
+ return submitted;
+ }
+ );
+}
+
+add_task(async function run_test() {
+ info("Starting test...");
+
+ await BrowserTestUtils.withNewTab(
+ { gBrowser, url: emptyPage },
+ async function tabTask(_) {
+ let promise = promiseObserverNotification();
+
+ Services.obs.notifyObservers(
+ null,
+ "user-characteristics-testing-please-populate-data"
+ );
+
+ let submitted = await promise;
+ Assert.ok(submitted);
+ }
+ );
+});
diff --git a/toolkit/components/resistfingerprinting/tests/browser/head.js b/toolkit/components/resistfingerprinting/tests/browser/head.js
index 9d8ec19956..e2fddaecc6 100644
--- a/toolkit/components/resistfingerprinting/tests/browser/head.js
+++ b/toolkit/components/resistfingerprinting/tests/browser/head.js
@@ -54,7 +54,7 @@ function countDifferencesInArrayBuffers(buffer1, buffer2) {
function promiseObserver(topic) {
return new Promise(resolve => {
- let obs = (aSubject, aTopic, aData) => {
+ let obs = (aSubject, aTopic) => {
Services.obs.removeObserver(obs, aTopic);
resolve(aSubject);
};
diff --git a/toolkit/components/resistfingerprinting/tests/gtest/test_reduceprecision.cpp b/toolkit/components/resistfingerprinting/tests/gtest/test_reduceprecision.cpp
index 5d8b598ff0..a36b819d8f 100644
--- a/toolkit/components/resistfingerprinting/tests/gtest/test_reduceprecision.cpp
+++ b/toolkit/components/resistfingerprinting/tests/gtest/test_reduceprecision.cpp
@@ -2,7 +2,7 @@
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
* 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/. */
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#include <math.h>
diff --git a/toolkit/components/resistfingerprinting/tests/gtest/test_usercharping.cpp b/toolkit/components/resistfingerprinting/tests/gtest/test_usercharping.cpp
index dd1cbe7d46..2eefa8e7fe 100644
--- a/toolkit/components/resistfingerprinting/tests/gtest/test_usercharping.cpp
+++ b/toolkit/components/resistfingerprinting/tests/gtest/test_usercharping.cpp
@@ -2,7 +2,7 @@
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
* 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/. */
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#include "gtest/gtest.h"
#include "mozilla/gtest/nsUserCharacteristics.h"
@@ -35,7 +35,8 @@ TEST(ResistFingerprinting, UserCharacteristics_Simple)
TEST(ResistFingerprinting, UserCharacteristics_Complex)
{
- nsUserCharacteristics::PopulateData(true);
+ nsUserCharacteristics::PopulateDataAndEventuallySubmit(
+ /* aUpdatePref = */ false, /* aTesting = */ true);
bool submitted = false;
mozilla::glean_pings::UserCharacteristics.TestBeforeNextSubmit(
@@ -102,7 +103,8 @@ TEST(ResistFingerprinting, UserCharacteristics_ClearPref)
.value()
.get());
});
- nsUserCharacteristics::PopulateData(true);
+ nsUserCharacteristics::PopulateDataAndEventuallySubmit(
+ /* aUpdatePref = */ false, /* aTesting = */ true);
nsUserCharacteristics::SubmitPing();
auto original_value =
@@ -135,7 +137,8 @@ TEST(ResistFingerprinting, UserCharacteristics_ClearPref)
Preferences::GetCString(kUUIDPref, uuidValue);
ASSERT_STRNE("", uuidValue.get());
});
- nsUserCharacteristics::PopulateData(true);
+ nsUserCharacteristics::PopulateDataAndEventuallySubmit(
+ /* aUpdatePref = */ false, /* aTesting = */ true);
nsUserCharacteristics::SubmitPing();
Preferences::SetBool("datareporting.healthreport.uploadEnabled",
diff --git a/toolkit/components/satchel/FillHelpers.sys.mjs b/toolkit/components/satchel/FillHelpers.sys.mjs
index 88a248adba..fd335f271e 100644
--- a/toolkit/components/satchel/FillHelpers.sys.mjs
+++ b/toolkit/components/satchel/FillHelpers.sys.mjs
@@ -39,3 +39,51 @@ export function showConfirmation(
const anchor = browser.ownerDocument.getElementById(anchorId);
anchor.ownerGlobal.ConfirmationHint.show(anchor, messageId, {});
}
+
+let fillRequestId = 0;
+
+/**
+ * Send a message encoded in the comment from an autocomplete item
+ * to the parent.
+ *
+ * @param {string} actorName name of the actor to send to
+ * @param {object} autocompleteInput current nsIAutoCompleteInput
+ * @param {string} comment serialized JSON comment containing fillMessageName and
+ * fillMessageData to send to the actor
+ */
+export async function sendFillRequestToParent(
+ actorName,
+ autocompleteInput,
+ comment
+) {
+ if (!comment) {
+ return;
+ }
+
+ const { fillMessageName, fillMessageData } = JSON.parse(comment);
+ if (!fillMessageName) {
+ return;
+ }
+
+ fillRequestId++;
+ const currentFillRequestId = fillRequestId;
+ const actor =
+ autocompleteInput.focusedInput.ownerGlobal?.windowGlobalChild?.getActor(
+ actorName
+ );
+ const value = await actor.sendQuery(fillMessageName, fillMessageData ?? {});
+
+ // skip fill if another fill operation started during await
+ if (currentFillRequestId != fillRequestId) {
+ return;
+ }
+
+ if (typeof value !== "string") {
+ return;
+ }
+
+ // If the parent returned a string to fill, we must do it here because
+ // nsAutoCompleteController.cpp already finished it's work before we finished await.
+ autocompleteInput.textValue = value;
+ autocompleteInput.selectTextRange(value.length, value.length);
+}
diff --git a/toolkit/components/satchel/FormAutoComplete.sys.mjs b/toolkit/components/satchel/FormAutoComplete.sys.mjs
deleted file mode 100644
index 1cae8b07c1..0000000000
--- a/toolkit/components/satchel/FormAutoComplete.sys.mjs
+++ /dev/null
@@ -1,693 +0,0 @@
-/* vim: set ts=4 sts=4 sw=4 et tw=80: */
-/* 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/. */
-
-import { GenericAutocompleteItem } from "resource://gre/modules/FillHelpers.sys.mjs";
-
-const lazy = {};
-
-ChromeUtils.defineESModuleGetters(lazy, {
- FormScenarios: "resource://gre/modules/FormScenarios.sys.mjs",
-});
-
-const formFillController = Cc[
- "@mozilla.org/satchel/form-fill-controller;1"
-].getService(Ci.nsIFormFillController);
-
-function isAutocompleteDisabled(aField) {
- if (!aField) {
- return false;
- }
-
- if (aField.autocomplete !== "") {
- return aField.autocomplete === "off";
- }
-
- return aField.form?.autocomplete === "off";
-}
-
-/**
- * An abstraction to talk with the FormHistory database over
- * the message layer. FormHistoryClient will take care of
- * figuring out the most appropriate message manager to use,
- * and what things to send.
- *
- * It is assumed that nsFormAutoComplete will only ever use
- * one instance at a time, and will not attempt to perform more
- * than one search request with the same instance at a time.
- * However, nsFormAutoComplete might call remove() any number of
- * times with the same instance of the client.
- *
- * @param {object} clientInfo
- * Info required to build the FormHistoryClient
- * @param {Node} clientInfo.formField
- * A DOM node that we're requesting form history for.
- * @param {string} clientInfo.inputName
- * The name of the input to do the FormHistory look-up with.
- * If this is searchbar-history, then formField needs to be null,
- * otherwise constructing will throw.
- */
-export class FormHistoryClient {
- constructor({ formField, inputName }) {
- if (formField) {
- if (inputName == this.SEARCHBAR_ID) {
- throw new Error(
- "FormHistoryClient constructed with both a formField and an inputName. " +
- "This is not supported, and only empty results will be returned."
- );
- }
- const window = formField.ownerGlobal;
- this.windowGlobal = window.windowGlobalChild;
- }
-
- this.inputName = inputName;
- this.id = FormHistoryClient.nextRequestID++;
- }
-
- static nextRequestID = 1;
- SEARCHBAR_ID = "searchbar-history";
- cancelled = false;
- inputName = "";
-
- getActor() {
- return this.windowGlobal?.getActor("FormHistory");
- }
-
- /**
- * Query FormHistory for some results.
- *
- * @param {string} searchString
- * The string to search FormHistory for. See
- * FormHistory.getAutoCompleteResults.
- * @param {object} params
- * An Object with search properties. See
- * FormHistory.getAutoCompleteResults.
- * @param {string} scenarioName
- * Optional autocompletion scenario name.
- * @param {Function} callback
- * A callback function that will take a single
- * argument (the found entries).
- */
- requestAutoCompleteResults(searchString, params, scenarioName, callback) {
- this.cancelled = false;
-
- // Use the actor if possible, otherwise for the searchbar,
- // use the more roundabout per-process message manager which has
- // no sendQuery method.
- const actor = this.getActor();
- if (actor) {
- actor
- .sendQuery("FormHistory:AutoCompleteSearchAsync", {
- searchString,
- params,
- scenarioName,
- })
- .then(
- results => this.handleAutoCompleteResults(results, callback),
- () => this.cancel()
- );
- } else {
- this.callback = callback;
- Services.cpmm.addMessageListener(
- "FormHistory:AutoCompleteSearchResults",
- this
- );
- Services.cpmm.sendAsyncMessage("FormHistory:AutoCompleteSearchAsync", {
- id: this.id,
- searchString,
- params,
- scenarioName,
- });
- }
- }
-
- handleAutoCompleteResults(results, callback) {
- if (this.cancelled) {
- return;
- }
-
- if (!callback) {
- console.error("FormHistoryClient received response with no callback");
- return;
- }
-
- callback(results);
- this.cancel();
- }
-
- /**
- * Cancel an in-flight results request. This ensures that the
- * callback that requestAutoCompleteResults was passed is never
- * called from this FormHistoryClient.
- */
- cancel() {
- if (this.callback) {
- Services.cpmm.removeMessageListener(
- "FormHistory:AutoCompleteSearchResults",
- this
- );
- this.callback = null;
- }
- this.cancelled = true;
- }
-
- /**
- * Remove an item from FormHistory.
- *
- * @param {string} value
- *
- * The value to remove for this particular
- * field.
- *
- * @param {string} guid
- *
- * The guid for the item being removed.
- */
- remove(value, guid) {
- const actor = this.getActor() || Services.cpmm;
- actor.sendAsyncMessage("FormHistory:RemoveEntry", {
- inputName: this.inputName,
- value,
- guid,
- });
- }
-
- receiveMessage(msg) {
- const { id, results } = msg.data;
- if (id == this.id) {
- this.handleAutoCompleteResults(results, this.callback);
- }
- }
-}
-
-/**
- * This autocomplete result combines 3 arrays of entries, fixedEntries and
- * externalEntries.
- * Entries are Form History entries, they can be removed.
- * Fixed entries are "appended" to entries, they are used for datalist items,
- * search suggestions and extra items from integrations.
- * External entries are meant for integrations, like Firefox Relay.
- * Internally entries and fixed entries are kept separated so we can
- * reuse and filter them.
- *
- * @implements {nsIAutoCompleteResult}
- */
-export class FormAutoCompleteResult {
- constructor(client, entries, fieldName, searchString) {
- this.client = client;
- this.entries = entries;
- this.fieldName = fieldName;
- this.searchString = searchString;
- }
-
- QueryInterface = ChromeUtils.generateQI([
- "nsIAutoCompleteResult",
- "nsISupportsWeakReference",
- ]);
-
- // private
- client = null;
- entries = null;
- fieldName = null;
- #fixedEntries = [];
- externalEntries = [];
-
- set fixedEntries(value) {
- this.#fixedEntries = value;
- this.removeDuplicateHistoryEntries();
- }
-
- canSearchIncrementally(searchString) {
- const prevSearchString = this.searchString.trim();
- return (
- prevSearchString.length > 1 &&
- searchString.includes(prevSearchString.toLowerCase())
- );
- }
-
- incrementalSearch(searchString) {
- this.searchString = searchString;
- searchString = searchString.trim().toLowerCase();
- this.#fixedEntries = this.#fixedEntries.filter(item =>
- item.label.toLowerCase().includes(searchString)
- );
-
- const searchTokens = searchString.split(/\s+/);
- // We have a list of results for a shorter search string, so just
- // filter them further based on the new search string and add to a new array.
- let filteredEntries = [];
- for (const entry of this.entries) {
- // Remove results that do not contain the token
- // XXX bug 394604 -- .toLowerCase can be wrong for some intl chars
- if (searchTokens.some(tok => !entry.textLowerCase.includes(tok))) {
- continue;
- }
- this.#calculateScore(entry, searchString, searchTokens);
- filteredEntries.push(entry);
- }
- filteredEntries.sort((a, b) => b.totalScore - a.totalScore);
- this.entries = filteredEntries;
- this.removeDuplicateHistoryEntries();
- }
-
- /*
- * #calculateScore
- *
- * entry -- an nsIAutoCompleteResult entry
- * aSearchString -- current value of the input (lowercase)
- * searchTokens -- array of tokens of the search string
- *
- * Returns: an int
- */
- #calculateScore(entry, aSearchString, searchTokens) {
- let boundaryCalc = 0;
- // for each word, calculate word boundary weights
- for (const token of searchTokens) {
- if (entry.textLowerCase.startsWith(token)) {
- boundaryCalc++;
- }
- if (entry.textLowerCase.includes(" " + token)) {
- boundaryCalc++;
- }
- }
- boundaryCalc = boundaryCalc * this._boundaryWeight;
- // now add more weight if we have a traditional prefix match and
- // multiply boundary bonuses by boundary weight
- if (entry.textLowerCase.startsWith(aSearchString)) {
- boundaryCalc += this._prefixWeight;
- }
- entry.totalScore = Math.round(entry.frecency * Math.max(1, boundaryCalc));
- }
-
- /**
- * Remove items from history list that are already present in fixed list.
- * We do this rather than the opposite ( i.e. remove items from fixed list)
- * to reflect the order that is specified in the fixed list.
- */
- removeDuplicateHistoryEntries() {
- this.entries = this.entries.filter(entry =>
- this.#fixedEntries.every(
- fixed => entry.text != (fixed.label || fixed.value)
- )
- );
- }
-
- getAt(index) {
- for (const group of [
- this.entries,
- this.#fixedEntries,
- this.externalEntries,
- ]) {
- if (index < group.length) {
- return group[index];
- }
- index -= group.length;
- }
-
- throw Components.Exception(
- "Index out of range.",
- Cr.NS_ERROR_ILLEGAL_VALUE
- );
- }
-
- // Allow autoCompleteSearch to get at the JS object so it can
- // modify some readonly properties for internal use.
- get wrappedJSObject() {
- return this;
- }
-
- // Interfaces from idl...
- searchString = "";
- errorDescription = "";
-
- get defaultIndex() {
- return this.matchCount ? 0 : -1;
- }
-
- get searchResult() {
- return this.matchCount
- ? Ci.nsIAutoCompleteResult.RESULT_SUCCESS
- : Ci.nsIAutoCompleteResult.RESULT_NOMATCH;
- }
-
- get matchCount() {
- return (
- this.entries.length +
- this.#fixedEntries.length +
- this.externalEntries.length
- );
- }
-
- getValueAt(index) {
- const item = this.getAt(index);
- return item.text || item.value;
- }
-
- getLabelAt(index) {
- const item = this.getAt(index);
- return item.text || item.label || item.value;
- }
-
- getCommentAt(index) {
- return this.getAt(index).comment ?? "";
- }
-
- getStyleAt(index) {
- const itemStyle = this.getAt(index).style;
- if (itemStyle) {
- return itemStyle;
- }
-
- if (index >= 0) {
- if (index < this.entries.length) {
- return "fromhistory";
- }
-
- if (index > 0 && index == this.entries.length) {
- return "datalist-first";
- }
- }
- return "";
- }
-
- getImageAt(_index) {
- return "";
- }
-
- getFinalCompleteValueAt(index) {
- return this.getValueAt(index);
- }
-
- isRemovableAt(index) {
- return this.#isFormHistoryEntry(index) || this.getAt(index).removable;
- }
-
- removeValueAt(index) {
- if (this.#isFormHistoryEntry(index)) {
- const [removedEntry] = this.entries.splice(index, 1);
- this.client.remove(removedEntry.text, removedEntry.guid);
- }
- }
-
- #isFormHistoryEntry(index) {
- return index >= 0 && index < this.entries.length;
- }
-}
-
-export class FormAutoComplete {
- constructor() {
- // Preferences. Add observer so we get notified of changes.
- this._prefBranch = Services.prefs.getBranch("browser.formfill.");
- this._prefBranch.addObserver("", this.observer, true);
- this.observer._self = this;
-
- this._debug = this._prefBranch.getBoolPref("debug");
- this._enabled = this._prefBranch.getBoolPref("enable");
- Services.obs.addObserver(this, "autocomplete-will-enter-text");
- }
-
- classID = Components.ID("{c11c21b2-71c9-4f87-a0f8-5e13f50495fd}");
- QueryInterface = ChromeUtils.generateQI([
- "nsIFormAutoComplete",
- "nsISupportsWeakReference",
- ]);
-
- // Only one query via FormHistoryClient is performed at a time, and the
- // most recent FormHistoryClient which will be stored in _pendingClient
- // while the query is being performed. It will be cleared when the query
- // finishes, is cancelled, or an error occurs. If a new query occurs while
- // one is already pending, the existing one is cancelled.
- #pendingClient = null;
-
- fillRequestId = 0;
-
- observer = {
- _self: null,
-
- QueryInterface: ChromeUtils.generateQI([
- "nsIObserver",
- "nsISupportsWeakReference",
- ]),
-
- observe(_subject, topic, data) {
- const self = this._self;
-
- if (topic == "nsPref:changed") {
- const prefName = data;
- self.log(`got change to ${prefName} preference`);
-
- switch (prefName) {
- case "debug":
- self._debug = self._prefBranch.getBoolPref(prefName);
- break;
- case "enable":
- self._enabled = self._prefBranch.getBoolPref(prefName);
- break;
- }
- }
- },
- };
-
- // AutoCompleteE10S needs to be able to call autoCompleteSearchAsync without
- // going through IDL in order to pass a mock DOM object field.
- get wrappedJSObject() {
- return this;
- }
-
- /*
- * log
- *
- * Internal function for logging debug messages to the Error Console
- * window
- */
- log(message) {
- if (!this._debug) {
- return;
- }
- dump("FormAutoComplete: " + message + "\n");
- Services.console.logStringMessage("FormAutoComplete: " + message);
- }
-
- /*
- * autoCompleteSearchAsync
- *
- * aInputName -- |name| or |id| attribute value from the form input being
- * autocompleted
- * aUntrimmedSearchString -- current value of the input
- * aField -- HTMLInputElement being autocompleted (may be null if from chrome)
- * aPreviousResult -- previous search result, if any.
- * aAddDataList -- add results from list=datalist for aField.
- * aListener -- nsIFormAutoCompleteObserver that listens for the nsIAutoCompleteResult
- * that may be returned asynchronously.
- */
- autoCompleteSearchAsync(
- aInputName,
- aUntrimmedSearchString,
- aField,
- aPreviousResult,
- aAddDataList,
- aListener
- ) {
- // Guard against void DOM strings filtering into this code.
- if (typeof aInputName === "object") {
- aInputName = "";
- }
- if (typeof aUntrimmedSearchString === "object") {
- aUntrimmedSearchString = "";
- }
-
- const client = new FormHistoryClient({
- formField: aField,
- inputName: aInputName,
- });
-
- function reportSearchResult(result) {
- aListener?.onSearchCompletion(result);
- }
-
- // If we have datalist results, they become our "empty" result.
- const result = new FormAutoCompleteResult(
- client,
- [],
- aInputName,
- aUntrimmedSearchString
- );
-
- if (aAddDataList) {
- result.fixedEntries = this.getDataListSuggestions(aField);
- }
-
- if (!this._enabled) {
- reportSearchResult(result);
- return;
- }
-
- // Don't allow form inputs (aField != null) to get results from
- // search bar history.
- if (aInputName == "searchbar-history" && aField) {
- this.log(`autoCompleteSearch for input name "${aInputName}" is denied`);
- reportSearchResult(result);
- return;
- }
-
- if (isAutocompleteDisabled(aField)) {
- this.log("autoCompleteSearch not allowed due to autcomplete=off");
- reportSearchResult(result);
- return;
- }
-
- const searchString = aUntrimmedSearchString.trim().toLowerCase();
- const prevResult = aPreviousResult?.wrappedJSObject;
- if (prevResult?.canSearchIncrementally(searchString)) {
- this.log("Using previous autocomplete result");
- prevResult.incrementalSearch(aUntrimmedSearchString);
- reportSearchResult(prevResult);
- } else {
- this.log("Creating new autocomplete search result.");
- this.getAutoCompleteValues(
- client,
- aInputName,
- searchString,
- lazy.FormScenarios.detect({ input: aField }).signUpForm
- ? "SignUpFormScenario"
- : "",
- ({ formHistoryEntries, externalEntries }) => {
- formHistoryEntries ??= [];
- externalEntries ??= [];
-
- if (aField?.maxLength > -1) {
- result.entries = formHistoryEntries.filter(
- el => el.text.length <= aField.maxLength
- );
- } else {
- result.entries = formHistoryEntries;
- }
-
- result.externalEntries.push(
- ...externalEntries.map(
- entry =>
- new GenericAutocompleteItem(
- entry.image,
- entry.title,
- entry.subtitle,
- entry.fillMessageName,
- entry.fillMessageData
- )
- )
- );
-
- result.removeDuplicateHistoryEntries();
- reportSearchResult(result);
- }
- );
- }
- }
-
- getDataListSuggestions(aField) {
- const items = [];
-
- if (!aField?.list) {
- return items;
- }
-
- const upperFieldValue = aField.value.toUpperCase();
-
- for (const option of aField.list.options) {
- const label = option.label || option.text || option.value || "";
-
- if (!label.toUpperCase().includes(upperFieldValue)) {
- continue;
- }
-
- items.push({
- label,
- value: option.value,
- });
- }
-
- return items;
- }
-
- stopAutoCompleteSearch() {
- if (this.#pendingClient) {
- this.#pendingClient.cancel();
- this.#pendingClient = null;
- }
- }
-
- /*
- * Get the values for an autocomplete list given a search string.
- *
- * client - a FormHistoryClient instance to perform the search with
- * fieldname - fieldname field within form history (the form input name)
- * searchString - string to search for
- * scenarioName - Optional autocompletion scenario name.
- * callback - called when the values are available. Passed an array of objects,
- * containing properties for each result. The callback is only called
- * when successful.
- */
- getAutoCompleteValues(
- client,
- fieldname,
- searchString,
- scenarioName,
- callback
- ) {
- this.stopAutoCompleteSearch();
- client.requestAutoCompleteResults(
- searchString,
- { fieldname },
- scenarioName,
- entries => {
- this.#pendingClient = null;
- callback(entries);
- }
- );
- this.#pendingClient = client;
- }
-
- async observe(subject, topic, data) {
- switch (topic) {
- case "autocomplete-will-enter-text": {
- await this.sendFillRequestToFormHistoryParent(subject, data);
- break;
- }
- }
- }
-
- async sendFillRequestToFormHistoryParent(input, comment) {
- if (!comment) {
- return;
- }
-
- if (!input || input != formFillController.controller?.input) {
- return;
- }
-
- const { fillMessageName, fillMessageData } = JSON.parse(comment ?? "{}");
- if (!fillMessageName) {
- return;
- }
-
- this.fillRequestId++;
- const fillRequestId = this.fillRequestId;
- const actor =
- input.focusedInput.ownerGlobal.windowGlobalChild.getActor("FormHistory");
- const value = await actor.sendQuery(fillMessageName, fillMessageData ?? {});
-
- // skip fill if another fill operation started during await
- if (fillRequestId != this.fillRequestId) {
- return;
- }
-
- if (typeof value !== "string") {
- return;
- }
-
- // If FormHistoryParent returned a string to fill, we must do it here because
- // nsAutoCompleteController.cpp already finished it's work before we finished await.
- input.textValue = value;
- input.selectTextRange(value.length, value.length);
- }
-}
diff --git a/toolkit/components/satchel/FormHandlerChild.sys.mjs b/toolkit/components/satchel/FormHandlerChild.sys.mjs
index 6b1af3dbc3..526066e46e 100644
--- a/toolkit/components/satchel/FormHandlerChild.sys.mjs
+++ b/toolkit/components/satchel/FormHandlerChild.sys.mjs
@@ -44,13 +44,13 @@ export class FormHandlerChild extends JSWindowActorChild {
}
// handle form-removal-after-fetch
- processFormRemovalAfterFetch(params) {}
+ processFormRemovalAfterFetch(_params) {}
// handle iframe-pagehide
- processIframePagehide(params) {}
+ processIframePagehide(_params) {}
// handle page-navigation
- processPageNavigation(params) {}
+ processPageNavigation(_params) {}
/**
* Dispatch the CustomEvent form-submission-detected also transfer
diff --git a/toolkit/components/satchel/FormHistoryAutoComplete.sys.mjs b/toolkit/components/satchel/FormHistoryAutoComplete.sys.mjs
new file mode 100644
index 0000000000..2799d2e955
--- /dev/null
+++ b/toolkit/components/satchel/FormHistoryAutoComplete.sys.mjs
@@ -0,0 +1,660 @@
+/* vim: set ts=4 sts=4 sw=4 et tw=80: */
+/* 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/. */
+
+import {
+ GenericAutocompleteItem,
+ sendFillRequestToParent,
+} from "resource://gre/modules/FillHelpers.sys.mjs";
+
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ FormScenarios: "resource://gre/modules/FormScenarios.sys.mjs",
+});
+
+const formFillController = Cc[
+ "@mozilla.org/satchel/form-fill-controller;1"
+].getService(Ci.nsIFormFillController);
+
+function isAutocompleteDisabled(aField) {
+ if (!aField) {
+ return false;
+ }
+
+ if (aField.autocomplete !== "") {
+ return aField.autocomplete === "off";
+ }
+
+ return aField.form?.autocomplete === "off";
+}
+
+/**
+ * An abstraction to talk with the FormHistory database over
+ * the message layer. FormHistoryClient will take care of
+ * figuring out the most appropriate message manager to use,
+ * and what things to send.
+ *
+ * It is assumed that FormHistoryAutoComplete will only ever use
+ * one instance at a time, and will not attempt to perform more
+ * than one search request with the same instance at a time.
+ * However, FormHistoryAutoComplete might call remove() any number of
+ * times with the same instance of the client.
+ *
+ * @param {object} clientInfo
+ * Info required to build the FormHistoryClient
+ * @param {Node} clientInfo.formField
+ * A DOM node that we're requesting form history for.
+ * @param {string} clientInfo.inputName
+ * The name of the input to do the FormHistory look-up with.
+ * If this is searchbar-history, then formField needs to be null,
+ * otherwise constructing will throw.
+ */
+export class FormHistoryClient {
+ constructor({ formField, inputName }) {
+ if (formField) {
+ if (inputName == this.SEARCHBAR_ID) {
+ throw new Error(
+ "FormHistoryClient constructed with both a formField and an inputName. " +
+ "This is not supported, and only empty results will be returned."
+ );
+ }
+ const window = formField.ownerGlobal;
+ this.windowGlobal = window.windowGlobalChild;
+ }
+
+ this.inputName = inputName;
+ this.id = FormHistoryClient.nextRequestID++;
+ }
+
+ static nextRequestID = 1;
+ SEARCHBAR_ID = "searchbar-history";
+ cancelled = false;
+ inputName = "";
+
+ getActor() {
+ return this.windowGlobal?.getActor("FormHistory");
+ }
+
+ /**
+ * Query FormHistory for some results.
+ *
+ * @param {string} searchString
+ * The string to search FormHistory for. See
+ * FormHistory.getAutoCompleteResults.
+ * @param {object} params
+ * An Object with search properties. See
+ * FormHistory.getAutoCompleteResults.
+ * @param {string} scenarioName
+ * Optional autocompletion scenario name.
+ * @param {Function} callback
+ * A callback function that will take a single
+ * argument (the found entries).
+ */
+ requestAutoCompleteResults(searchString, params, scenarioName, callback) {
+ this.cancelled = false;
+
+ // Use the actor if possible, otherwise for the searchbar,
+ // use the more roundabout per-process message manager which has
+ // no sendQuery method.
+ const actor = this.getActor();
+ if (actor) {
+ actor
+ .sendQuery("FormHistory:AutoCompleteSearchAsync", {
+ searchString,
+ params,
+ scenarioName,
+ })
+ .then(
+ results => this.handleAutoCompleteResults(results, callback),
+ () => this.cancel()
+ );
+ } else {
+ this.callback = callback;
+ Services.cpmm.addMessageListener(
+ "FormHistory:AutoCompleteSearchResults",
+ this
+ );
+ Services.cpmm.sendAsyncMessage("FormHistory:AutoCompleteSearchAsync", {
+ id: this.id,
+ searchString,
+ params,
+ scenarioName,
+ });
+ }
+ }
+
+ handleAutoCompleteResults(results, callback) {
+ if (this.cancelled) {
+ return;
+ }
+
+ if (!callback) {
+ console.error("FormHistoryClient received response with no callback");
+ return;
+ }
+
+ callback(results);
+ this.cancel();
+ }
+
+ /**
+ * Cancel an in-flight results request. This ensures that the
+ * callback that requestAutoCompleteResults was passed is never
+ * called from this FormHistoryClient.
+ */
+ cancel() {
+ if (this.callback) {
+ Services.cpmm.removeMessageListener(
+ "FormHistory:AutoCompleteSearchResults",
+ this
+ );
+ this.callback = null;
+ }
+ this.cancelled = true;
+ }
+
+ /**
+ * Remove an item from FormHistory.
+ *
+ * @param {string} value
+ *
+ * The value to remove for this particular
+ * field.
+ *
+ * @param {string} guid
+ *
+ * The guid for the item being removed.
+ */
+ remove(value, guid) {
+ const actor = this.getActor() || Services.cpmm;
+ actor.sendAsyncMessage("FormHistory:RemoveEntry", {
+ inputName: this.inputName,
+ value,
+ guid,
+ });
+ }
+
+ receiveMessage(msg) {
+ const { id, results } = msg.data;
+ if (id == this.id) {
+ this.handleAutoCompleteResults(results, this.callback);
+ }
+ }
+}
+
+/**
+ * This autocomplete result combines 3 arrays of entries, fixedEntries and
+ * externalEntries.
+ * Entries are Form History entries, they can be removed.
+ * Fixed entries are "appended" to entries, they are used for datalist items,
+ * search suggestions and extra items from integrations.
+ * External entries are meant for integrations, like Firefox Relay.
+ * Internally entries and fixed entries are kept separated so we can
+ * reuse and filter them.
+ *
+ * @implements {nsIAutoCompleteResult}
+ */
+export class FormHistoryAutoCompleteResult {
+ constructor(client, entries, fieldName, searchString) {
+ this.client = client;
+ this.entries = entries;
+ this.fieldName = fieldName;
+ this.searchString = searchString;
+ }
+
+ QueryInterface = ChromeUtils.generateQI([
+ "nsIAutoCompleteResult",
+ "nsISupportsWeakReference",
+ ]);
+
+ // private
+ client = null;
+ entries = null;
+ fieldName = null;
+ #fixedEntries = [];
+ externalEntries = [];
+
+ set fixedEntries(value) {
+ this.#fixedEntries = value;
+ this.removeDuplicateHistoryEntries();
+ }
+
+ canSearchIncrementally(searchString) {
+ const prevSearchString = this.searchString.trim();
+ return (
+ prevSearchString.length > 1 &&
+ searchString.includes(prevSearchString.toLowerCase())
+ );
+ }
+
+ incrementalSearch(searchString) {
+ this.searchString = searchString;
+ searchString = searchString.trim().toLowerCase();
+ this.#fixedEntries = this.#fixedEntries.filter(item =>
+ item.label.toLowerCase().includes(searchString)
+ );
+
+ const searchTokens = searchString.split(/\s+/);
+ // We have a list of results for a shorter search string, so just
+ // filter them further based on the new search string and add to a new array.
+ let filteredEntries = [];
+ for (const entry of this.entries) {
+ // Remove results that do not contain the token
+ // XXX bug 394604 -- .toLowerCase can be wrong for some intl chars
+ if (searchTokens.some(tok => !entry.textLowerCase.includes(tok))) {
+ continue;
+ }
+ this.#calculateScore(entry, searchString, searchTokens);
+ filteredEntries.push(entry);
+ }
+ filteredEntries.sort((a, b) => b.totalScore - a.totalScore);
+ this.entries = filteredEntries;
+ this.removeDuplicateHistoryEntries();
+ }
+
+ /*
+ * #calculateScore
+ *
+ * entry -- an nsIAutoCompleteResult entry
+ * aSearchString -- current value of the input (lowercase)
+ * searchTokens -- array of tokens of the search string
+ *
+ * Returns: an int
+ */
+ #calculateScore(entry, aSearchString, searchTokens) {
+ let boundaryCalc = 0;
+ // for each word, calculate word boundary weights
+ for (const token of searchTokens) {
+ if (entry.textLowerCase.startsWith(token)) {
+ boundaryCalc++;
+ }
+ if (entry.textLowerCase.includes(" " + token)) {
+ boundaryCalc++;
+ }
+ }
+ boundaryCalc = boundaryCalc * this._boundaryWeight;
+ // now add more weight if we have a traditional prefix match and
+ // multiply boundary bonuses by boundary weight
+ if (entry.textLowerCase.startsWith(aSearchString)) {
+ boundaryCalc += this._prefixWeight;
+ }
+ entry.totalScore = Math.round(entry.frecency * Math.max(1, boundaryCalc));
+ }
+
+ /**
+ * Remove items from history list that are already present in fixed list.
+ * We do this rather than the opposite ( i.e. remove items from fixed list)
+ * to reflect the order that is specified in the fixed list.
+ */
+ removeDuplicateHistoryEntries() {
+ this.entries = this.entries.filter(entry =>
+ this.#fixedEntries.every(
+ fixed => entry.text != (fixed.label || fixed.value)
+ )
+ );
+ }
+
+ getAt(index) {
+ for (const group of [
+ this.entries,
+ this.#fixedEntries,
+ this.externalEntries,
+ ]) {
+ if (index < group.length) {
+ return group[index];
+ }
+ index -= group.length;
+ }
+
+ throw Components.Exception(
+ "Index out of range.",
+ Cr.NS_ERROR_ILLEGAL_VALUE
+ );
+ }
+
+ // Allow autoCompleteSearch to get at the JS object so it can
+ // modify some readonly properties for internal use.
+ get wrappedJSObject() {
+ return this;
+ }
+
+ // Interfaces from idl...
+ searchString = "";
+ errorDescription = "";
+
+ get defaultIndex() {
+ return this.matchCount ? 0 : -1;
+ }
+
+ get searchResult() {
+ return this.matchCount
+ ? Ci.nsIAutoCompleteResult.RESULT_SUCCESS
+ : Ci.nsIAutoCompleteResult.RESULT_NOMATCH;
+ }
+
+ get matchCount() {
+ return (
+ this.entries.length +
+ this.#fixedEntries.length +
+ this.externalEntries.length
+ );
+ }
+
+ getValueAt(index) {
+ const item = this.getAt(index);
+ return item.text || item.value;
+ }
+
+ getLabelAt(index) {
+ const item = this.getAt(index);
+ return item.text || item.label || item.value;
+ }
+
+ getCommentAt(index) {
+ return this.getAt(index).comment ?? "";
+ }
+
+ getStyleAt(index) {
+ const itemStyle = this.getAt(index).style;
+ if (itemStyle) {
+ return itemStyle;
+ }
+
+ if (index >= 0) {
+ if (index < this.entries.length) {
+ return "fromhistory";
+ }
+
+ if (index > 0 && index == this.entries.length) {
+ return "datalist-first";
+ }
+ }
+ return "";
+ }
+
+ getImageAt(_index) {
+ return "";
+ }
+
+ getFinalCompleteValueAt(index) {
+ return this.getValueAt(index);
+ }
+
+ isRemovableAt(index) {
+ return this.#isFormHistoryEntry(index) || this.getAt(index).removable;
+ }
+
+ removeValueAt(index) {
+ if (this.#isFormHistoryEntry(index)) {
+ const [removedEntry] = this.entries.splice(index, 1);
+ this.client.remove(removedEntry.text, removedEntry.guid);
+ }
+ }
+
+ #isFormHistoryEntry(index) {
+ return index >= 0 && index < this.entries.length;
+ }
+}
+
+export class FormHistoryAutoComplete {
+ constructor() {
+ // Preferences. Add observer so we get notified of changes.
+ this._prefBranch = Services.prefs.getBranch("browser.formfill.");
+ this._prefBranch.addObserver("", this.observer, true);
+ this.observer._self = this;
+
+ this._debug = this._prefBranch.getBoolPref("debug");
+ this._enabled = this._prefBranch.getBoolPref("enable");
+ Services.obs.addObserver(this, "autocomplete-will-enter-text");
+ }
+
+ classID = Components.ID("{23530265-31d1-4ee9-864c-c081975fb7bc}");
+ QueryInterface = ChromeUtils.generateQI([
+ "nsIFormHistoryAutoComplete",
+ "nsISupportsWeakReference",
+ ]);
+
+ // Only one query via FormHistoryClient is performed at a time, and the
+ // most recent FormHistoryClient which will be stored in _pendingClient
+ // while the query is being performed. It will be cleared when the query
+ // finishes, is cancelled, or an error occurs. If a new query occurs while
+ // one is already pending, the existing one is cancelled.
+ #pendingClient = null;
+
+ observer = {
+ _self: null,
+
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIObserver",
+ "nsISupportsWeakReference",
+ ]),
+
+ observe(_subject, topic, data) {
+ const self = this._self;
+
+ if (topic == "nsPref:changed") {
+ const prefName = data;
+ self.log(`got change to ${prefName} preference`);
+
+ switch (prefName) {
+ case "debug":
+ self._debug = self._prefBranch.getBoolPref(prefName);
+ break;
+ case "enable":
+ self._enabled = self._prefBranch.getBoolPref(prefName);
+ break;
+ }
+ }
+ },
+ };
+
+ // AutoCompleteE10S needs to be able to call autoCompleteSearchAsync without
+ // going through IDL in order to pass a mock DOM object field.
+ get wrappedJSObject() {
+ return this;
+ }
+
+ /*
+ * log
+ *
+ * Internal function for logging debug messages to the Error Console
+ * window
+ */
+ log(message) {
+ if (!this._debug) {
+ return;
+ }
+ Services.console.logStringMessage("FormHistoryAutoComplete: " + message);
+ }
+
+ /*
+ * autoCompleteSearchAsync
+ *
+ * aInputName -- |name| or |id| attribute value from the form input being
+ * autocompleted
+ * aUntrimmedSearchString -- current value of the input
+ * aField -- HTMLInputElement being autocompleted (may be null if from chrome)
+ * aPreviousResult -- previous search result, if any.
+ * aAddDataList -- add results from list=datalist for aField.
+ * aListener -- nsIFormHistoryAutoCompleteObserver that listens for the nsIAutoCompleteResult
+ * that may be returned asynchronously.
+ */
+ autoCompleteSearchAsync(
+ aInputName,
+ aUntrimmedSearchString,
+ aField,
+ aPreviousResult,
+ aAddDataList,
+ aListener
+ ) {
+ // Guard against void DOM strings filtering into this code.
+ if (typeof aInputName === "object") {
+ aInputName = "";
+ }
+ if (typeof aUntrimmedSearchString === "object") {
+ aUntrimmedSearchString = "";
+ }
+
+ const client = new FormHistoryClient({
+ formField: aField,
+ inputName: aInputName,
+ });
+
+ function reportSearchResult(result) {
+ aListener?.onSearchCompletion(result);
+ }
+
+ // If we have datalist results, they become our "empty" result.
+ const result = new FormHistoryAutoCompleteResult(
+ client,
+ [],
+ aInputName,
+ aUntrimmedSearchString
+ );
+
+ if (aAddDataList) {
+ result.fixedEntries = this.getDataListSuggestions(aField);
+ }
+
+ if (!this._enabled) {
+ reportSearchResult(result);
+ return;
+ }
+
+ // Don't allow form inputs (aField != null) to get results from
+ // search bar history.
+ if (aInputName == "searchbar-history" && aField) {
+ this.log(`autoCompleteSearch for input name "${aInputName}" is denied`);
+ reportSearchResult(result);
+ return;
+ }
+
+ if (isAutocompleteDisabled(aField)) {
+ this.log("autoCompleteSearch not allowed due to autcomplete=off");
+ reportSearchResult(result);
+ return;
+ }
+
+ const searchString = aUntrimmedSearchString.trim().toLowerCase();
+ const prevResult = aPreviousResult?.wrappedJSObject;
+ if (prevResult?.canSearchIncrementally(searchString)) {
+ this.log("Using previous autocomplete result");
+ prevResult.incrementalSearch(aUntrimmedSearchString);
+ reportSearchResult(prevResult);
+ } else {
+ this.log("Creating new autocomplete search result.");
+ this.getAutoCompleteValues(
+ client,
+ aInputName,
+ searchString,
+ lazy.FormScenarios.detect({ input: aField }).signUpForm
+ ? "SignUpFormScenario"
+ : "",
+ ({ formHistoryEntries, externalEntries }) => {
+ formHistoryEntries ??= [];
+ externalEntries ??= [];
+
+ if (aField?.maxLength > -1) {
+ result.entries = formHistoryEntries.filter(
+ el => el.text.length <= aField.maxLength
+ );
+ } else {
+ result.entries = formHistoryEntries;
+ }
+
+ result.externalEntries.push(
+ ...externalEntries.map(
+ entry =>
+ new GenericAutocompleteItem(
+ entry.image,
+ entry.title,
+ entry.subtitle,
+ entry.fillMessageName,
+ entry.fillMessageData
+ )
+ )
+ );
+
+ result.removeDuplicateHistoryEntries();
+ reportSearchResult(result);
+ }
+ );
+ }
+ }
+
+ getDataListSuggestions(aField) {
+ const items = [];
+
+ if (!aField?.list) {
+ return items;
+ }
+
+ const upperFieldValue = aField.value.toUpperCase();
+
+ for (const option of aField.list.options) {
+ const label = option.label || option.text || option.value || "";
+
+ if (!label.toUpperCase().includes(upperFieldValue)) {
+ continue;
+ }
+
+ items.push({
+ label,
+ value: option.value,
+ });
+ }
+
+ return items;
+ }
+
+ stopAutoCompleteSearch() {
+ if (this.#pendingClient) {
+ this.#pendingClient.cancel();
+ this.#pendingClient = null;
+ }
+ }
+
+ /*
+ * Get the values for an autocomplete list given a search string.
+ *
+ * client - a FormHistoryClient instance to perform the search with
+ * fieldname - fieldname field within form history (the form input name)
+ * searchString - string to search for
+ * scenarioName - Optional autocompletion scenario name.
+ * callback - called when the values are available. Passed an array of objects,
+ * containing properties for each result. The callback is only called
+ * when successful.
+ */
+ getAutoCompleteValues(
+ client,
+ fieldname,
+ searchString,
+ scenarioName,
+ callback
+ ) {
+ this.stopAutoCompleteSearch();
+ client.requestAutoCompleteResults(
+ searchString,
+ { fieldname },
+ scenarioName,
+ entries => {
+ this.#pendingClient = null;
+ callback(entries);
+ }
+ );
+ this.#pendingClient = client;
+ }
+
+ async observe(subject, topic, data) {
+ switch (topic) {
+ case "autocomplete-will-enter-text": {
+ if (subject && subject == formFillController.controller?.input) {
+ await sendFillRequestToParent("FormHistory", subject, data);
+ }
+ break;
+ }
+ }
+ }
+}
diff --git a/toolkit/components/satchel/components.conf b/toolkit/components/satchel/components.conf
index d843b869d6..d5a670efd9 100644
--- a/toolkit/components/satchel/components.conf
+++ b/toolkit/components/satchel/components.conf
@@ -18,10 +18,10 @@ Classes = [
},
{
- 'cid': '{c11c21b2-71c9-4f87-a0f8-5e13f50495fd}',
- 'contract_ids': ['@mozilla.org/satchel/form-autocomplete;1'],
- 'esModule': 'resource://gre/modules/FormAutoComplete.sys.mjs',
- 'constructor': 'FormAutoComplete',
+ 'cid': '{23530265-31d1-4ee9-864c-c081975fb7bc}',
+ 'contract_ids': ['@mozilla.org/satchel/form-history-autocomplete;1'],
+ 'esModule': 'resource://gre/modules/FormHistoryAutoComplete.sys.mjs',
+ 'constructor': 'FormHistoryAutoComplete',
},
{
'cid': '{3a0012eb-007f-4bb8-aa81-a07385f77a25}',
diff --git a/toolkit/components/satchel/jar.mn b/toolkit/components/satchel/jar.mn
new file mode 100644
index 0000000000..a3f250f2e8
--- /dev/null
+++ b/toolkit/components/satchel/jar.mn
@@ -0,0 +1,10 @@
+# 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/.
+
+toolkit.jar:
+ content/global/megalist/megalist.css (megalist/content/megalist.css)
+ content/global/megalist/megalist.html (megalist/content/megalist.html)
+ content/global/megalist/MegalistView.mjs (megalist/content/MegalistView.mjs)
+ content/global/megalist/search-input.mjs (megalist/content/search-input.mjs)
+ content/global/megalist/VirtualizedList.mjs (megalist/content/VirtualizedList.mjs)
diff --git a/toolkit/components/satchel/megalist/MegalistChild.sys.mjs b/toolkit/components/satchel/megalist/MegalistChild.sys.mjs
new file mode 100644
index 0000000000..cd17798c95
--- /dev/null
+++ b/toolkit/components/satchel/megalist/MegalistChild.sys.mjs
@@ -0,0 +1,17 @@
+/* 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/. */
+
+export class MegalistChild extends JSWindowActorChild {
+ receiveMessage(message) {
+ // Forward message to the View
+ const win = this.document.defaultView;
+ const ev = new win.CustomEvent("MessageFromViewModel", {
+ detail: message,
+ });
+ win.dispatchEvent(ev);
+ }
+
+ // Prevent TypeError: Property 'handleEvent' is not callable.
+ handleEvent() {}
+}
diff --git a/toolkit/components/satchel/megalist/MegalistParent.sys.mjs b/toolkit/components/satchel/megalist/MegalistParent.sys.mjs
new file mode 100644
index 0000000000..de04af7ea6
--- /dev/null
+++ b/toolkit/components/satchel/megalist/MegalistParent.sys.mjs
@@ -0,0 +1,27 @@
+/* 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/. */
+
+import { MegalistViewModel } from "resource://gre/modules/megalist/MegalistViewModel.sys.mjs";
+
+/**
+ * MegalistParent integrates MegalistViewModel into Parent/Child model.
+ */
+export class MegalistParent extends JSWindowActorParent {
+ #viewModel;
+
+ actorCreated() {
+ this.#viewModel = new MegalistViewModel((...args) =>
+ this.sendAsyncMessage(...args)
+ );
+ }
+
+ didDestroy() {
+ this.#viewModel.willDestroy();
+ this.#viewModel = null;
+ }
+
+ receiveMessage(message) {
+ return this.#viewModel?.handleViewMessage(message);
+ }
+}
diff --git a/toolkit/components/satchel/megalist/MegalistViewModel.sys.mjs b/toolkit/components/satchel/megalist/MegalistViewModel.sys.mjs
new file mode 100644
index 0000000000..f11a8a3198
--- /dev/null
+++ b/toolkit/components/satchel/megalist/MegalistViewModel.sys.mjs
@@ -0,0 +1,291 @@
+/* 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/. */
+
+import { DefaultAggregator } from "resource://gre/modules/megalist/aggregator/DefaultAggregator.sys.mjs";
+
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ OSKeyStore: "resource://gre/modules/OSKeyStore.sys.mjs",
+});
+
+/**
+ * View Model for Megalist.
+ *
+ * Responsible for filtering, grouping, moving selection, editing.
+ * Refers to the same MegalistAggregator in the parent process to access data.
+ * Paired to exactly one MegalistView in the child process to present to the user.
+ * Receives user commands from MegalistView.
+ *
+ * There can be multiple snapshots of the same line displayed in different contexts.
+ *
+ * snapshotId - an id for a snapshot of a line used between View Model and View.
+ */
+export class MegalistViewModel {
+ /**
+ *
+ * View Model prepares snapshots in the parent process to be displayed
+ * by the View in the child process. View gets the firstSnapshotId + length of the
+ * list. Making it a very short message for each time we filter or refresh data.
+ *
+ * View requests line data by providing snapshotId = firstSnapshotId + index.
+ *
+ */
+ #firstSnapshotId = 0;
+ #snapshots = [];
+ #selectedIndex = 0;
+ #searchText = "";
+ #messageToView;
+ static #aggregator = new DefaultAggregator();
+
+ constructor(messageToView) {
+ this.#messageToView = messageToView;
+ MegalistViewModel.#aggregator.attachViewModel(this);
+ }
+
+ willDestroy() {
+ MegalistViewModel.#aggregator.detachViewModel(this);
+ }
+
+ refreshAllLinesOnScreen() {
+ this.#rebuildSnapshots();
+ }
+
+ refreshSingleLineOnScreen(line) {
+ if (this.#searchText) {
+ // Data is filtered, which may require rebuilding the whole list
+ //@sg check if current filter would affected by this line
+ //@sg throttle refresh operation
+ this.#rebuildSnapshots();
+ } else {
+ const snapshotIndex = this.#snapshots.indexOf(line);
+ if (snapshotIndex >= 0) {
+ const snapshotId = snapshotIndex + this.#firstSnapshotId;
+ this.#sendSnapshotToView(snapshotId, line);
+ }
+ }
+ }
+
+ /**
+ *
+ * Send snapshot of necessary line data across parent-child boundary.
+ *
+ * @param {number} snapshotId
+ * @param {object} snapshotData
+ */
+ async #sendSnapshotToView(snapshotId, snapshotData) {
+ if (!snapshotData) {
+ return;
+ }
+
+ // Only usable set of fields is sent over to the View.
+ // Line object may contain other data used by the Data Source.
+ const snapshot = {
+ label: snapshotData.label,
+ value: await snapshotData.value,
+ };
+ if ("template" in snapshotData) {
+ snapshot.template = snapshotData.template;
+ }
+ if ("start" in snapshotData) {
+ snapshot.start = snapshotData.start;
+ }
+ if ("end" in snapshotData) {
+ snapshot.end = snapshotData.end;
+ }
+ if ("commands" in snapshotData) {
+ snapshot.commands = snapshotData.commands;
+ }
+ if ("valueIcon" in snapshotData) {
+ snapshot.valueIcon = snapshotData.valueIcon;
+ }
+ if ("href" in snapshotData) {
+ snapshot.href = snapshotData.href;
+ }
+ if (snapshotData.stickers) {
+ for (const sticker of snapshotData.stickers) {
+ snapshot.stickers ??= [];
+ snapshot.stickers.push(sticker);
+ }
+ }
+
+ this.#messageToView("Snapshot", { snapshotId, snapshot });
+ }
+
+ receiveRequestSnapshot({ snapshotId }) {
+ const snapshotIndex = snapshotId - this.#firstSnapshotId;
+ const snapshot = this.#snapshots[snapshotIndex];
+ if (!snapshot) {
+ // Ignore request for unknown line index or outdated list
+ return;
+ }
+
+ if (snapshot.lineIsReady()) {
+ this.#sendSnapshotToView(snapshotId, snapshot);
+ }
+ }
+
+ handleViewMessage({ name, data }) {
+ const handlerName = `receive${name}`;
+ if (!(handlerName in this)) {
+ throw new Error(`Received unknown message "${name}"`);
+ }
+ return this[handlerName](data);
+ }
+
+ receiveRefresh() {
+ this.#rebuildSnapshots();
+ }
+
+ #rebuildSnapshots() {
+ // Remember current selection to attempt to restore it later
+ const prevSelected = this.#snapshots[this.#selectedIndex];
+
+ // Rebuild snapshots
+ this.#firstSnapshotId += this.#snapshots.length;
+ this.#snapshots = Array.from(
+ MegalistViewModel.#aggregator.enumerateLines(this.#searchText)
+ );
+
+ // Update snapshots on screen
+ this.#messageToView("ShowSnapshots", {
+ firstSnapshotId: this.#firstSnapshotId,
+ count: this.#snapshots.length,
+ });
+
+ // Restore selection
+ const usedToBeSelectedNewIndex = this.#snapshots.findIndex(
+ snapshot => snapshot == prevSelected
+ );
+ if (usedToBeSelectedNewIndex >= 0) {
+ this.#selectSnapshotByIndex(usedToBeSelectedNewIndex);
+ } else {
+ // Make sure selection is within visible lines
+ this.#selectSnapshotByIndex(
+ Math.min(this.#selectedIndex, this.#snapshots.length - 1)
+ );
+ }
+ }
+
+ receiveUpdateFilter({ searchText } = { searchText: "" }) {
+ if (this.#searchText != searchText) {
+ this.#searchText = searchText;
+ this.#messageToView("MegalistUpdateFilter", { searchText });
+ this.#rebuildSnapshots();
+ }
+ }
+
+ async receiveCommand({ commandId, snapshotId, value } = {}) {
+ const index = snapshotId
+ ? snapshotId - this.#firstSnapshotId
+ : this.#selectedIndex;
+ const snapshot = this.#snapshots[index];
+ if (snapshot) {
+ commandId = commandId ?? snapshot.commands[0]?.id;
+ const mustVerify = snapshot.commands.find(c => c.id == commandId)?.verify;
+ if (!mustVerify || (await this.#verifyUser())) {
+ // TODO:Enter the prompt message and pref for #verifyUser()
+ await snapshot[`execute${commandId}`]?.(value);
+ }
+ }
+ }
+
+ receiveSelectSnapshot({ snapshotId }) {
+ const index = snapshotId - this.#firstSnapshotId;
+ if (index >= 0) {
+ this.#selectSnapshotByIndex(index);
+ }
+ }
+
+ receiveSelectNextSnapshot() {
+ this.#selectSnapshotByIndex(this.#selectedIndex + 1);
+ }
+
+ receiveSelectPreviousSnapshot() {
+ this.#selectSnapshotByIndex(this.#selectedIndex - 1);
+ }
+
+ receiveSelectNextGroup() {
+ let i = this.#selectedIndex + 1;
+ while (i < this.#snapshots.length - 1 && !this.#snapshots[i].start) {
+ i += 1;
+ }
+ this.#selectSnapshotByIndex(i);
+ }
+
+ receiveSelectPreviousGroup() {
+ let i = this.#selectedIndex - 1;
+ while (i >= 0 && !this.#snapshots[i].start) {
+ i -= 1;
+ }
+ this.#selectSnapshotByIndex(i);
+ }
+
+ #selectSnapshotByIndex(index) {
+ if (index >= 0 && index < this.#snapshots.length) {
+ this.#selectedIndex = index;
+ const selectedIndex = this.#selectedIndex;
+ this.#messageToView("UpdateSelection", { selectedIndex });
+ }
+ }
+
+ async #verifyUser(promptMessage, prefName) {
+ if (!this.getOSAuthEnabled(prefName)) {
+ promptMessage = false;
+ }
+ let result = await lazy.OSKeyStore.ensureLoggedIn(promptMessage);
+ return result.authenticated;
+ }
+
+ /**
+ * Get the decrypted value for a string pref.
+ *
+ * @param {string} prefName -> The pref whose value is needed.
+ * @param {string} safeDefaultValue -> Value to be returned incase the pref is not yet set.
+ * @returns {string}
+ */
+ #getSecurePref(prefName, safeDefaultValue) {
+ try {
+ let encryptedValue = Services.prefs.getStringPref(prefName, "");
+ return this._crypto.decrypt(encryptedValue);
+ } catch {
+ return safeDefaultValue;
+ }
+ }
+
+ /**
+ * Set the pref to the encrypted form of the value.
+ *
+ * @param {string} prefName -> The pref whose value is to be set.
+ * @param {string} value -> The value to be set in its encryoted form.
+ */
+ #setSecurePref(prefName, value) {
+ let encryptedValue = this._crypto.encrypt(value);
+ Services.prefs.setStringPref(prefName, encryptedValue);
+ }
+
+ /**
+ * Get whether the OSAuth is enabled or not.
+ *
+ * @param {string} prefName -> The name of the pref (creditcards or addresses)
+ * @returns {boolean}
+ */
+ getOSAuthEnabled(prefName) {
+ return this.#getSecurePref(prefName, "") !== "opt out";
+ }
+
+ /**
+ * Set whether the OSAuth is enabled or not.
+ *
+ * @param {string} prefName -> The pref to encrypt.
+ * @param {boolean} enable -> Whether the pref is to be enabled.
+ */
+ setOSAuthEnabled(prefName, enable) {
+ if (enable) {
+ Services.prefs.clearUserPref(prefName);
+ } else {
+ this.#setSecurePref(prefName, "opt out");
+ }
+ }
+}
diff --git a/toolkit/components/satchel/megalist/aggregator/Aggregator.sys.mjs b/toolkit/components/satchel/megalist/aggregator/Aggregator.sys.mjs
new file mode 100644
index 0000000000..e101fadd16
--- /dev/null
+++ b/toolkit/components/satchel/megalist/aggregator/Aggregator.sys.mjs
@@ -0,0 +1,78 @@
+/* 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/. */
+
+/**
+ * Connects multiple Data Sources with multiple View Models.
+ * Aggregator owns Data Sources.
+ * Aggregator weakly refers to View Models.
+ */
+export class Aggregator {
+ #sources = [];
+ #attachedViewModels = [];
+
+ attachViewModel(viewModel) {
+ // Weak reference the View Model so we do not keep it in memory forever
+ this.#attachedViewModels.push(new WeakRef(viewModel));
+ }
+
+ detachViewModel(viewModel) {
+ for (let i = this.#attachedViewModels.length - 1; i >= 0; i--) {
+ const knownViewModel = this.#attachedViewModels[i].deref();
+ if (viewModel == knownViewModel || !knownViewModel) {
+ this.#attachedViewModels.splice(i, 1);
+ }
+ }
+ }
+
+ /**
+ * Run action on each of the alive attached view models.
+ * Remove dead consumers.
+ *
+ * @param {Function} action to perform on each alive consumer
+ */
+ forEachViewModel(action) {
+ for (let i = this.#attachedViewModels.length - 1; i >= 0; i--) {
+ const viewModel = this.#attachedViewModels[i].deref();
+ if (viewModel) {
+ action(viewModel);
+ } else {
+ this.#attachedViewModels.splice(i, 1);
+ }
+ }
+ }
+
+ *enumerateLines(searchText) {
+ for (let source of this.#sources) {
+ yield* source.enumerateLines(searchText);
+ }
+ }
+
+ /**
+ *
+ * @param {Function} createSourceFn (aggregatorApi) used to create Data Source.
+ * aggregatorApi is the way for Data Source to push data
+ * to the Aggregator.
+ */
+ addSource(createSourceFn) {
+ const api = this.#apiForDataSource();
+ const source = createSourceFn(api);
+ this.#sources.push(source);
+ }
+
+ /**
+ * Exposes interface for a datasource to communicate with Aggregator.
+ */
+ #apiForDataSource() {
+ const aggregator = this;
+ return {
+ refreshSingleLineOnScreen(line) {
+ aggregator.forEachViewModel(vm => vm.refreshSingleLineOnScreen(line));
+ },
+
+ refreshAllLinesOnScreen() {
+ aggregator.forEachViewModel(vm => vm.refreshAllLinesOnScreen());
+ },
+ };
+ }
+}
diff --git a/toolkit/components/satchel/megalist/aggregator/DefaultAggregator.sys.mjs b/toolkit/components/satchel/megalist/aggregator/DefaultAggregator.sys.mjs
new file mode 100644
index 0000000000..cf3a78a6a4
--- /dev/null
+++ b/toolkit/components/satchel/megalist/aggregator/DefaultAggregator.sys.mjs
@@ -0,0 +1,17 @@
+/* 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/. */
+
+import { Aggregator } from "resource://gre/modules/megalist/aggregator/Aggregator.sys.mjs";
+import { AddressesDataSource } from "resource://gre/modules/megalist/aggregator/datasources/AddressesDataSource.sys.mjs";
+import { BankCardDataSource } from "resource://gre/modules/megalist/aggregator/datasources/BankCardDataSource.sys.mjs";
+import { LoginDataSource } from "resource://gre/modules/megalist/aggregator/datasources/LoginDataSource.sys.mjs";
+
+export class DefaultAggregator extends Aggregator {
+ constructor() {
+ super();
+ this.addSource(aggregatorApi => new AddressesDataSource(aggregatorApi));
+ this.addSource(aggregatorApi => new BankCardDataSource(aggregatorApi));
+ this.addSource(aggregatorApi => new LoginDataSource(aggregatorApi));
+ }
+}
diff --git a/toolkit/components/satchel/megalist/aggregator/datasources/AddressesDataSource.sys.mjs b/toolkit/components/satchel/megalist/aggregator/datasources/AddressesDataSource.sys.mjs
new file mode 100644
index 0000000000..f00df0b40b
--- /dev/null
+++ b/toolkit/components/satchel/megalist/aggregator/datasources/AddressesDataSource.sys.mjs
@@ -0,0 +1,258 @@
+/* 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/. */
+
+import { DataSourceBase } from "resource://gre/modules/megalist/aggregator/datasources/DataSourceBase.sys.mjs";
+import { formAutofillStorage } from "resource://autofill/FormAutofillStorage.sys.mjs";
+
+async function updateAddress(address, field, value) {
+ try {
+ const newAddress = {
+ ...address,
+ [field]: value ?? "",
+ };
+
+ formAutofillStorage.INTERNAL_FIELDS.forEach(
+ name => delete newAddress[name]
+ );
+ formAutofillStorage.addresses.VALID_COMPUTED_FIELDS.forEach(
+ name => delete newAddress[name]
+ );
+
+ if (address.guid) {
+ await formAutofillStorage.addresses.update(address.guid, newAddress);
+ } else {
+ await formAutofillStorage.addresses.add(newAddress);
+ }
+ } catch (error) {
+ //todo
+ console.error("failed to modify address", error);
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Data source for Addresses.
+ *
+ */
+
+export class AddressesDataSource extends DataSourceBase {
+ #namePrototype;
+ #organizationPrototype;
+ #streetAddressPrototype;
+ #addressLevelOnePrototype;
+ #addressLevelTwoPrototype;
+ #addressLevelThreePrototype;
+ #postalCodePrototype;
+ #countryPrototype;
+ #phonePrototype;
+ #emailPrototype;
+
+ #addressesDisabledMessage;
+ #enabled;
+ #header;
+
+ constructor(...args) {
+ super(...args);
+ this.formatMessages(
+ "addresses-section-label",
+ "address-name-label",
+ "address-phone-label",
+ "address-email-label",
+ "command-copy",
+ "addresses-disabled",
+ "command-delete",
+ "command-edit",
+ "addresses-command-create"
+ ).then(
+ ([
+ headerLabel,
+ nameLabel,
+ phoneLabel,
+ emailLabel,
+ copyLabel,
+ addressesDisabled,
+ deleteLabel,
+ editLabel,
+ createLabel,
+ ]) => {
+ const copyCommand = { id: "Copy", label: copyLabel };
+ const editCommand = { id: "Edit", label: editLabel };
+ const deleteCommand = { id: "Delete", label: deleteLabel };
+ this.#addressesDisabledMessage = addressesDisabled;
+ this.#header = this.createHeaderLine(headerLabel);
+ this.#header.commands.push({ id: "Create", label: createLabel });
+
+ let self = this;
+
+ function prototypeLine(label, key, options = {}) {
+ return self.prototypeDataLine({
+ label: { value: label },
+ value: {
+ get() {
+ return this.editingValue ?? this.record[key];
+ },
+ },
+ commands: {
+ value: [copyCommand, editCommand, "-", deleteCommand],
+ },
+ executeEdit: {
+ value() {
+ this.editingValue = this.record[key] ?? "";
+ this.refreshOnScreen();
+ },
+ },
+ executeSave: {
+ async value(value) {
+ if (await updateAddress(this.record, key, value)) {
+ this.executeCancel();
+ }
+ },
+ },
+ ...options,
+ });
+ }
+
+ this.#namePrototype = prototypeLine(nameLabel, "name", {
+ start: { value: true },
+ });
+ this.#organizationPrototype = prototypeLine(
+ "Organization",
+ "organization"
+ );
+ this.#streetAddressPrototype = prototypeLine(
+ "Street Address",
+ "street-address"
+ );
+ this.#addressLevelThreePrototype = prototypeLine(
+ "Neighbourhood",
+ "address-level3"
+ );
+ this.#addressLevelTwoPrototype = prototypeLine(
+ "City",
+ "address-level2"
+ );
+ this.#addressLevelOnePrototype = prototypeLine(
+ "Province",
+ "address-level1"
+ );
+ this.#postalCodePrototype = prototypeLine("Postal Code", "postal-code");
+ this.#countryPrototype = prototypeLine("Country", "country");
+ this.#phonePrototype = prototypeLine(phoneLabel, "tel");
+ this.#emailPrototype = prototypeLine(emailLabel, "email", {
+ end: { value: true },
+ });
+
+ Services.obs.addObserver(this, "formautofill-storage-changed");
+ Services.prefs.addObserver(
+ "extensions.formautofill.addresses.enabled",
+ this
+ );
+ this.#reloadDataSource();
+ }
+ );
+ }
+
+ async #reloadDataSource() {
+ this.#enabled = Services.prefs.getBoolPref(
+ "extensions.formautofill.addresses.enabled"
+ );
+ if (!this.#enabled) {
+ this.#reloadEmptyDataSource();
+ return;
+ }
+
+ await formAutofillStorage.initialize();
+ const addresses = await formAutofillStorage.addresses.getAll();
+ this.beforeReloadingDataSource();
+ addresses.forEach(address => {
+ const lineId = `${address.name}:${address.tel}`;
+
+ this.addOrUpdateLine(address, lineId + "0", this.#namePrototype);
+ this.addOrUpdateLine(address, lineId + "1", this.#organizationPrototype);
+ this.addOrUpdateLine(address, lineId + "2", this.#streetAddressPrototype);
+ this.addOrUpdateLine(
+ address,
+ lineId + "3",
+ this.#addressLevelThreePrototype
+ );
+ this.addOrUpdateLine(
+ address,
+ lineId + "4",
+ this.#addressLevelTwoPrototype
+ );
+ this.addOrUpdateLine(
+ address,
+ lineId + "5",
+ this.#addressLevelOnePrototype
+ );
+ this.addOrUpdateLine(address, lineId + "6", this.#postalCodePrototype);
+ this.addOrUpdateLine(address, lineId + "7", this.#countryPrototype);
+ this.addOrUpdateLine(address, lineId + "8", this.#phonePrototype);
+ this.addOrUpdateLine(address, lineId + "9", this.#emailPrototype);
+ });
+ this.afterReloadingDataSource();
+ }
+
+ /**
+ * Enumerate all the lines provided by this data source.
+ *
+ * @param {string} searchText used to filter data
+ */
+ *enumerateLines(searchText) {
+ if (this.#enabled === undefined) {
+ // Async Fluent API makes it possible to have data source waiting
+ // for the localized strings, which can be detected by undefined in #enabled.
+ return;
+ }
+
+ yield this.#header;
+ if (this.#header.collapsed || !this.#enabled) {
+ return;
+ }
+
+ const stats = { total: 0, count: 0 };
+ searchText = searchText.toUpperCase();
+ yield* this.enumerateLinesForMatchingRecords(searchText, stats, address =>
+ [
+ "name",
+ "organization",
+ "street-address",
+ "address-level3",
+ "address-level2",
+ "address-level1",
+ "postal-code",
+ "country",
+ "tel",
+ "email",
+ ].some(key => address[key]?.toUpperCase().includes(searchText))
+ );
+
+ this.formatMessages({
+ id:
+ stats.count == stats.total
+ ? "addresses-count"
+ : "addresses-filtered-count",
+ args: stats,
+ }).then(([headerLabel]) => {
+ this.#header.value = headerLabel;
+ });
+ }
+
+ #reloadEmptyDataSource() {
+ this.lines.length = 0;
+ this.#header.value = this.#addressesDisabledMessage;
+ this.refreshAllLinesOnScreen();
+ }
+
+ observe(_subj, topic, message) {
+ if (
+ topic == "formautofill-storage-changed" ||
+ message == "extensions.formautofill.addresses.enabled"
+ ) {
+ this.#reloadDataSource();
+ }
+ }
+}
diff --git a/toolkit/components/satchel/megalist/aggregator/datasources/BankCardDataSource.sys.mjs b/toolkit/components/satchel/megalist/aggregator/datasources/BankCardDataSource.sys.mjs
new file mode 100644
index 0000000000..06266a7979
--- /dev/null
+++ b/toolkit/components/satchel/megalist/aggregator/datasources/BankCardDataSource.sys.mjs
@@ -0,0 +1,339 @@
+/* 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/. */
+
+import { DataSourceBase } from "resource://gre/modules/megalist/aggregator/datasources/DataSourceBase.sys.mjs";
+import { CreditCardRecord } from "resource://gre/modules/shared/CreditCardRecord.sys.mjs";
+import { formAutofillStorage } from "resource://autofill/FormAutofillStorage.sys.mjs";
+import { OSKeyStore } from "resource://gre/modules/OSKeyStore.sys.mjs";
+
+async function decryptCard(card) {
+ if (card["cc-number-encrypted"] && !card["cc-number-decrypted"]) {
+ try {
+ card["cc-number-decrypted"] = await OSKeyStore.decrypt(
+ card["cc-number-encrypted"],
+ false
+ );
+ card["cc-number"] = card["cc-number-decrypted"];
+ } catch (e) {
+ console.error(e);
+ }
+ }
+}
+
+async function updateCard(card, field, value) {
+ try {
+ await decryptCard(card);
+ const newCard = {
+ ...card,
+ [field]: value ?? "",
+ };
+ formAutofillStorage.INTERNAL_FIELDS.forEach(name => delete newCard[name]);
+ formAutofillStorage.creditCards.VALID_COMPUTED_FIELDS.forEach(
+ name => delete newCard[name]
+ );
+ delete newCard["cc-number-decrypted"];
+ CreditCardRecord.normalizeFields(newCard);
+
+ if (card.guid) {
+ await formAutofillStorage.creditCards.update(card.guid, newCard);
+ } else {
+ await formAutofillStorage.creditCards.add(newCard);
+ }
+ } catch (error) {
+ //todo
+ console.error("failed to modify credit card", error);
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Data source for Bank Cards.
+ *
+ * Each card is represented by 3 lines: card number, expiration date and holder name.
+ *
+ * Protypes are used to reduce memory need because for different records
+ * similar lines will differ in values only.
+ */
+export class BankCardDataSource extends DataSourceBase {
+ #cardNumberPrototype;
+ #expirationPrototype;
+ #holderNamePrototype;
+ #cardsDisabledMessage;
+ #enabled;
+ #header;
+
+ constructor(...args) {
+ super(...args);
+ // Wait for Fluent to provide strings before loading data
+ this.formatMessages(
+ "payments-section-label",
+ "card-number-label",
+ "card-expiration-label",
+ "card-holder-label",
+ "command-copy",
+ "command-reveal",
+ "command-conceal",
+ "payments-disabled",
+ "command-delete",
+ "command-edit",
+ "payments-command-create"
+ ).then(
+ ([
+ headerLabel,
+ numberLabel,
+ expirationLabel,
+ holderLabel,
+ copyCommandLabel,
+ revealCommandLabel,
+ concealCommandLabel,
+ cardsDisabled,
+ deleteCommandLabel,
+ editCommandLabel,
+ cardsCreateCommandLabel,
+ ]) => {
+ const copyCommand = { id: "Copy", label: copyCommandLabel };
+ const editCommand = {
+ id: "Edit",
+ label: editCommandLabel,
+ verify: true,
+ };
+ const deleteCommand = {
+ id: "Delete",
+ label: deleteCommandLabel,
+ verify: true,
+ };
+ this.#cardsDisabledMessage = cardsDisabled;
+ this.#header = this.createHeaderLine(headerLabel);
+ this.#header.commands.push({
+ id: "Create",
+ label: cardsCreateCommandLabel,
+ });
+ this.#cardNumberPrototype = this.prototypeDataLine({
+ label: { value: numberLabel },
+ concealed: { value: true, writable: true },
+ start: { value: true },
+ value: {
+ async get() {
+ if (this.editingValue !== undefined) {
+ return this.editingValue;
+ }
+
+ if (this.concealed) {
+ return (
+ "••••••••" +
+ this.record["cc-number"].replaceAll("*", "").substr(-4)
+ );
+ }
+
+ await decryptCard(this.record);
+ return this.record["cc-number-decrypted"];
+ },
+ },
+ valueIcon: {
+ get() {
+ const typeToImage = {
+ amex: "third-party/cc-logo-amex.png",
+ cartebancaire: "third-party/cc-logo-cartebancaire.png",
+ diners: "third-party/cc-logo-diners.svg",
+ discover: "third-party/cc-logo-discover.png",
+ jcb: "third-party/cc-logo-jcb.svg",
+ mastercard: "third-party/cc-logo-mastercard.svg",
+ mir: "third-party/cc-logo-mir.svg",
+ unionpay: "third-party/cc-logo-unionpay.svg",
+ visa: "third-party/cc-logo-visa.svg",
+ };
+ return (
+ "chrome://formautofill/content/" +
+ (typeToImage[this.record["cc-type"]] ??
+ "icon-credit-card-generic.svg")
+ );
+ },
+ },
+ commands: {
+ get() {
+ const commands = [
+ { id: "Conceal", label: concealCommandLabel },
+ { ...copyCommand, verify: true },
+ editCommand,
+ "-",
+ deleteCommand,
+ ];
+ if (this.concealed) {
+ commands[0] = {
+ id: "Reveal",
+ label: revealCommandLabel,
+ verify: true,
+ };
+ }
+ return commands;
+ },
+ },
+ executeReveal: {
+ value() {
+ this.concealed = false;
+ this.refreshOnScreen();
+ },
+ },
+ executeConceal: {
+ value() {
+ this.concealed = true;
+ this.refreshOnScreen();
+ },
+ },
+ executeCopy: {
+ async value() {
+ await decryptCard(this.record);
+ this.copyToClipboard(this.record["cc-number-decrypted"]);
+ },
+ },
+ executeEdit: {
+ async value() {
+ await decryptCard(this.record);
+ this.editingValue = this.record["cc-number-decrypted"] ?? "";
+ this.refreshOnScreen();
+ },
+ },
+ executeSave: {
+ async value(value) {
+ if (updateCard(this.record, "cc-number", value)) {
+ this.executeCancel();
+ }
+ },
+ },
+ });
+ this.#expirationPrototype = this.prototypeDataLine({
+ label: { value: expirationLabel },
+ value: {
+ get() {
+ return `${this.record["cc-exp-month"]}/${this.record["cc-exp-year"]}`;
+ },
+ },
+ commands: {
+ value: [copyCommand, editCommand, "-", deleteCommand],
+ },
+ });
+ this.#holderNamePrototype = this.prototypeDataLine({
+ label: { value: holderLabel },
+ end: { value: true },
+ value: {
+ get() {
+ return this.editingValue ?? this.record["cc-name"];
+ },
+ },
+ commands: {
+ value: [copyCommand, editCommand, "-", deleteCommand],
+ },
+ executeEdit: {
+ value() {
+ this.editingValue = this.record["cc-name"] ?? "";
+ this.refreshOnScreen();
+ },
+ },
+ executeSave: {
+ async value(value) {
+ if (updateCard(this.record, "cc-name", value)) {
+ this.executeCancel();
+ }
+ },
+ },
+ });
+
+ Services.obs.addObserver(this, "formautofill-storage-changed");
+ Services.prefs.addObserver(
+ "extensions.formautofill.creditCards.enabled",
+ this
+ );
+ this.#reloadDataSource();
+ }
+ );
+ }
+
+ /**
+ * Enumerate all the lines provided by this data source.
+ *
+ * @param {string} searchText used to filter data
+ */
+ *enumerateLines(searchText) {
+ if (this.#enabled === undefined) {
+ // Async Fluent API makes it possible to have data source waiting
+ // for the localized strings, which can be detected by undefined in #enabled.
+ return;
+ }
+
+ yield this.#header;
+ if (this.#header.collapsed || !this.#enabled) {
+ return;
+ }
+
+ const stats = { count: 0, total: 0 };
+ searchText = searchText.toUpperCase();
+ yield* this.enumerateLinesForMatchingRecords(
+ searchText,
+ stats,
+ card =>
+ (card["cc-number-decrypted"] || card["cc-number"])
+ .toUpperCase()
+ .includes(searchText) ||
+ `${card["cc-exp-month"]}/${card["cc-exp-year"]}`
+ .toUpperCase()
+ .includes(searchText) ||
+ card["cc-name"].toUpperCase().includes(searchText)
+ );
+
+ this.formatMessages({
+ id:
+ stats.count == stats.total
+ ? "payments-count"
+ : "payments-filtered-count",
+ args: stats,
+ }).then(([headerLabel]) => {
+ this.#header.value = headerLabel;
+ });
+ }
+
+ /**
+ * Sync lines array with the actual data source.
+ * This function reads all cards from the storage, adds or updates lines and
+ * removes lines for the removed cards.
+ */
+ async #reloadDataSource() {
+ this.#enabled = Services.prefs.getBoolPref(
+ "extensions.formautofill.creditCards.enabled"
+ );
+ if (!this.#enabled) {
+ this.#reloadEmptyDataSource();
+ return;
+ }
+
+ await formAutofillStorage.initialize();
+ const cards = await formAutofillStorage.creditCards.getAll();
+ this.beforeReloadingDataSource();
+ cards.forEach(card => {
+ const lineId = `${card["cc-name"]}:${card.guid}`;
+
+ this.addOrUpdateLine(card, lineId + "0", this.#cardNumberPrototype);
+ this.addOrUpdateLine(card, lineId + "1", this.#expirationPrototype);
+ this.addOrUpdateLine(card, lineId + "2", this.#holderNamePrototype);
+ });
+ this.afterReloadingDataSource();
+ }
+
+ #reloadEmptyDataSource() {
+ this.lines.length = 0;
+ //todo: user can enable credit cards by activating header line
+ this.#header.value = this.#cardsDisabledMessage;
+ this.refreshAllLinesOnScreen();
+ }
+
+ observe(_subj, topic, message) {
+ if (
+ topic == "formautofill-storage-changed" ||
+ message == "extensions.formautofill.creditCards.enabled"
+ ) {
+ this.#reloadDataSource();
+ }
+ }
+}
diff --git a/toolkit/components/satchel/megalist/aggregator/datasources/DataSourceBase.sys.mjs b/toolkit/components/satchel/megalist/aggregator/datasources/DataSourceBase.sys.mjs
new file mode 100644
index 0000000000..49be733aef
--- /dev/null
+++ b/toolkit/components/satchel/megalist/aggregator/datasources/DataSourceBase.sys.mjs
@@ -0,0 +1,291 @@
+/* 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/. */
+
+import { BinarySearch } from "resource://gre/modules/BinarySearch.sys.mjs";
+import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
+
+const lazy = {};
+
+XPCOMUtils.defineLazyServiceGetter(
+ lazy,
+ "ClipboardHelper",
+ "@mozilla.org/widget/clipboardhelper;1",
+ "nsIClipboardHelper"
+);
+
+/**
+ * Create a function to format messages.
+ *
+ * @param {...any} ftlFiles to be used for formatting messages
+ * @returns {Function} a function that can be used to format messsages
+ */
+function createFormatMessages(...ftlFiles) {
+ const strings = new Localization(ftlFiles);
+
+ return async (...ids) => {
+ for (const i in ids) {
+ if (typeof ids[i] == "string") {
+ ids[i] = { id: ids[i] };
+ }
+ }
+
+ const messages = await strings.formatMessages(ids);
+ return messages.map(message => {
+ if (message.attributes) {
+ return message.attributes.reduce(
+ (result, { name, value }) => ({ ...result, [name]: value }),
+ {}
+ );
+ }
+ return message.value;
+ });
+ };
+}
+
+/**
+ * Base datasource class
+ */
+export class DataSourceBase {
+ #aggregatorApi;
+
+ constructor(aggregatorApi) {
+ this.#aggregatorApi = aggregatorApi;
+ }
+
+ // proxy consumer api functions to datasource interface
+
+ refreshSingleLineOnScreen(line) {
+ this.#aggregatorApi.refreshSingleLineOnScreen(line);
+ }
+
+ refreshAllLinesOnScreen() {
+ this.#aggregatorApi.refreshAllLinesOnScreen();
+ }
+
+ formatMessages = createFormatMessages("preview/megalist.ftl");
+
+ /**
+ * Prototype for the each line.
+ * See this link for details:
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties#props
+ */
+ #linePrototype = {
+ /**
+ * Reference to the Data Source that owns this line.
+ */
+ source: this,
+
+ /**
+ * Each line has a reference to the actual data record.
+ */
+ record: { writable: true },
+
+ /**
+ * Is line ready to be displayed?
+ * Used by the View Model.
+ *
+ * @returns {boolean} true if line can be sent to the view.
+ * false if line is not ready to be displayed. In this case
+ * data source will start pulling value from the underlying
+ * storage and will push data to screen when it's ready.
+ */
+ lineIsReady() {
+ return true;
+ },
+
+ copyToClipboard(text) {
+ lazy.ClipboardHelper.copyString(text, lazy.ClipboardHelper.Sensitive);
+ },
+
+ openLinkInTab(url) {
+ const { BrowserWindowTracker } = ChromeUtils.importESModule(
+ "resource:///modules/BrowserWindowTracker.sys.mjs"
+ );
+ const browser = BrowserWindowTracker.getTopWindow().gBrowser;
+ browser.addWebTab(url, { inBackground: false });
+ },
+
+ /**
+ * Simple version of Copy command. Line still needs to add "Copy" command.
+ * Override if copied value != displayed value.
+ */
+ executeCopy() {
+ this.copyToClipboard(this.value);
+ },
+
+ executeOpen() {
+ this.openLinkInTab(this.href);
+ },
+
+ executeEditInProgress(value) {
+ this.editingValue = value;
+ this.refreshOnScreen();
+ },
+
+ executeCancel() {
+ delete this.editingValue;
+ this.refreshOnScreen();
+ },
+
+ get template() {
+ return "editingValue" in this ? "editingLineTemplate" : undefined;
+ },
+
+ refreshOnScreen() {
+ this.source.refreshSingleLineOnScreen(this);
+ },
+ };
+
+ /**
+ * Creates collapsible section header line.
+ *
+ * @param {string} label for the section
+ * @returns {object} section header line
+ */
+ createHeaderLine(label) {
+ const toggleCommand = { id: "Toggle", label: "" };
+ const result = {
+ label,
+ value: "",
+ collapsed: false,
+ start: true,
+ end: true,
+ source: this,
+
+ /**
+ * Use different templates depending on the collapsed state.
+ */
+ get template() {
+ return this.collapsed
+ ? "collapsedSectionTemplate"
+ : "expandedSectionTemplate";
+ },
+
+ lineIsReady: () => true,
+
+ commands: [toggleCommand],
+
+ executeToggle() {
+ this.collapsed = !this.collapsed;
+ this.source.refreshAllLinesOnScreen();
+ },
+ };
+
+ this.formatMessages("command-toggle").then(([toggleLabel]) => {
+ toggleCommand.label = toggleLabel;
+ });
+
+ return result;
+ }
+
+ /**
+ * Create a prototype to be used for data lines,
+ * provides common set of features like Copy command.
+ *
+ * @param {object} properties to customize data line
+ * @returns {object} data line prototype
+ */
+ prototypeDataLine(properties) {
+ return Object.create(this.#linePrototype, properties);
+ }
+
+ lines = [];
+ #collator = new Intl.Collator();
+ #linesToForget;
+
+ /**
+ * Code to run before reloading data source.
+ * It will start tracking which lines are no longer at the source so
+ * afterReloadingDataSource() can remove them.
+ */
+ beforeReloadingDataSource() {
+ this.#linesToForget = new Set(this.lines);
+ }
+
+ /**
+ * Code to run after reloading data source.
+ * It will forget lines that are no longer at the source and refresh screen.
+ */
+ afterReloadingDataSource() {
+ if (this.#linesToForget.size) {
+ for (let i = this.lines.length; i >= 0; i--) {
+ if (this.#linesToForget.has(this.lines[i])) {
+ this.lines.splice(i, 1);
+ }
+ }
+ }
+
+ this.#linesToForget = null;
+ this.refreshAllLinesOnScreen();
+ }
+
+ /**
+ * Add or update line associated with the record.
+ *
+ * @param {object} record with which line is associated
+ * @param {*} id sortable line id
+ * @param {*} fieldPrototype to be used when creating a line.
+ */
+ addOrUpdateLine(record, id, fieldPrototype) {
+ let [found, index] = BinarySearch.search(
+ (target, value) => this.#collator.compare(target, value.id),
+ this.lines,
+ id
+ );
+
+ if (found) {
+ this.#linesToForget.delete(this.lines[index]);
+ } else {
+ const line = Object.create(fieldPrototype, { id: { value: id } });
+ this.lines.splice(index, 0, line);
+ }
+ this.lines[index].record = record;
+ return this.lines[index];
+ }
+
+ *enumerateLinesForMatchingRecords(searchText, stats, match) {
+ stats.total = 0;
+ stats.count = 0;
+
+ if (searchText) {
+ let i = 0;
+ while (i < this.lines.length) {
+ const currentRecord = this.lines[i].record;
+ stats.total += 1;
+
+ if (match(currentRecord)) {
+ // Record matches, yield all it's lines
+ while (
+ i < this.lines.length &&
+ currentRecord == this.lines[i].record
+ ) {
+ yield this.lines[i];
+ i += 1;
+ }
+ stats.count += 1;
+ } else {
+ // Record does not match, skip until the next one
+ while (
+ i < this.lines.length &&
+ currentRecord == this.lines[i].record
+ ) {
+ i += 1;
+ }
+ }
+ }
+ } else {
+ // No search text is provided - send all lines out, count records
+ let currentRecord;
+ for (const line of this.lines) {
+ yield line;
+
+ if (line.record != currentRecord) {
+ stats.total += 1;
+ currentRecord = line.record;
+ }
+ }
+ stats.count = stats.total;
+ }
+ }
+}
diff --git a/toolkit/components/satchel/megalist/aggregator/datasources/LoginDataSource.sys.mjs b/toolkit/components/satchel/megalist/aggregator/datasources/LoginDataSource.sys.mjs
new file mode 100644
index 0000000000..324bc4d141
--- /dev/null
+++ b/toolkit/components/satchel/megalist/aggregator/datasources/LoginDataSource.sys.mjs
@@ -0,0 +1,472 @@
+/* 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/. */
+
+import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
+import { LoginHelper } from "resource://gre/modules/LoginHelper.sys.mjs";
+import { DataSourceBase } from "resource://gre/modules/megalist/aggregator/datasources/DataSourceBase.sys.mjs";
+import { LoginCSVImport } from "resource://gre/modules/LoginCSVImport.sys.mjs";
+
+const lazy = {};
+ChromeUtils.defineESModuleGetters(lazy, {
+ LoginBreaches: "resource:///modules/LoginBreaches.sys.mjs",
+});
+
+XPCOMUtils.defineLazyPreferenceGetter(
+ lazy,
+ "BREACH_ALERTS_ENABLED",
+ "signon.management.page.breach-alerts.enabled",
+ false
+);
+
+XPCOMUtils.defineLazyPreferenceGetter(
+ lazy,
+ "VULNERABLE_PASSWORDS_ENABLED",
+ "signon.management.page.vulnerable-passwords.enabled",
+ false
+);
+
+/**
+ * Data source for Logins.
+ *
+ * Each login is represented by 3 lines: origin, username and password.
+ *
+ * Protypes are used to reduce memory need because for different records
+ * similar lines will differ in values only.
+ */
+export class LoginDataSource extends DataSourceBase {
+ #originPrototype;
+ #usernamePrototype;
+ #passwordPrototype;
+ #loginsDisabledMessage;
+ #enabled;
+ #header;
+
+ constructor(...args) {
+ super(...args);
+ // Wait for Fluent to provide strings before loading data
+ this.formatMessages(
+ "passwords-section-label",
+ "passwords-origin-label",
+ "passwords-username-label",
+ "passwords-password-label",
+ "command-open",
+ "command-copy",
+ "command-reveal",
+ "command-conceal",
+ "passwords-disabled",
+ "command-delete",
+ "command-edit",
+ "passwords-command-create",
+ "passwords-command-import",
+ "passwords-command-export",
+ "passwords-command-remove-all",
+ "passwords-command-settings",
+ "passwords-command-help",
+ "passwords-import-file-picker-title",
+ "passwords-import-file-picker-import-button",
+ "passwords-import-file-picker-csv-filter-title",
+ "passwords-import-file-picker-tsv-filter-title"
+ ).then(
+ ([
+ headerLabel,
+ originLabel,
+ usernameLabel,
+ passwordLabel,
+ openCommandLabel,
+ copyCommandLabel,
+ revealCommandLabel,
+ concealCommandLabel,
+ passwordsDisabled,
+ deleteCommandLabel,
+ editCommandLabel,
+ passwordsCreateCommandLabel,
+ passwordsImportCommandLabel,
+ passwordsExportCommandLabel,
+ passwordsRemoveAllCommandLabel,
+ passwordsSettingsCommandLabel,
+ passwordsHelpCommandLabel,
+ passwordsImportFilePickerTitle,
+ passwordsImportFilePickerImportButton,
+ passwordsImportFilePickerCsvFilterTitle,
+ passwordsImportFilePickerTsvFilterTitle,
+ ]) => {
+ const copyCommand = { id: "Copy", label: copyCommandLabel };
+ const editCommand = { id: "Edit", label: editCommandLabel };
+ const deleteCommand = { id: "Delete", label: deleteCommandLabel };
+ this.breachedSticker = { type: "warning", label: "BREACH" };
+ this.vulnerableSticker = { type: "risk", label: "🤮 Vulnerable" };
+ this.#loginsDisabledMessage = passwordsDisabled;
+ this.#header = this.createHeaderLine(headerLabel);
+ this.#header.commands.push(
+ { id: "Create", label: passwordsCreateCommandLabel },
+ { id: "Import", label: passwordsImportCommandLabel },
+ { id: "Export", label: passwordsExportCommandLabel },
+ { id: "RemoveAll", label: passwordsRemoveAllCommandLabel },
+ { id: "Settings", label: passwordsSettingsCommandLabel },
+ { id: "Help", label: passwordsHelpCommandLabel }
+ );
+ this.#header.executeImport = async () => {
+ await this.#importFromFile(
+ passwordsImportFilePickerTitle,
+ passwordsImportFilePickerImportButton,
+ passwordsImportFilePickerCsvFilterTitle,
+ passwordsImportFilePickerTsvFilterTitle
+ );
+ };
+ this.#header.executeSettings = () => {
+ this.#openPreferences();
+ };
+ this.#header.executeHelp = () => {
+ this.#getHelp();
+ };
+
+ this.#originPrototype = this.prototypeDataLine({
+ label: { value: originLabel },
+ start: { value: true },
+ value: {
+ get() {
+ return this.record.displayOrigin;
+ },
+ },
+ valueIcon: {
+ get() {
+ return `page-icon:${this.record.origin}`;
+ },
+ },
+ href: {
+ get() {
+ return this.record.origin;
+ },
+ },
+ commands: {
+ value: [
+ { id: "Open", label: openCommandLabel },
+ copyCommand,
+ "-",
+ deleteCommand,
+ ],
+ },
+ executeCopy: {
+ value() {
+ this.copyToClipboard(this.record.origin);
+ },
+ },
+ });
+ this.#usernamePrototype = this.prototypeDataLine({
+ label: { value: usernameLabel },
+ value: {
+ get() {
+ return this.editingValue ?? this.record.username;
+ },
+ },
+ commands: { value: [copyCommand, editCommand, "-", deleteCommand] },
+ executeEdit: {
+ value() {
+ this.editingValue = this.record.username ?? "";
+ this.refreshOnScreen();
+ },
+ },
+ executeSave: {
+ value(value) {
+ try {
+ const modifiedLogin = this.record.clone();
+ modifiedLogin.username = value;
+ Services.logins.modifyLogin(this.record, modifiedLogin);
+ } catch (error) {
+ //todo
+ console.error("failed to modify login", error);
+ }
+ this.executeCancel();
+ },
+ },
+ });
+ this.#passwordPrototype = this.prototypeDataLine({
+ label: { value: passwordLabel },
+ concealed: { value: true, writable: true },
+ end: { value: true },
+ value: {
+ get() {
+ return (
+ this.editingValue ??
+ (this.concealed ? "••••••••" : this.record.password)
+ );
+ },
+ },
+ commands: {
+ get() {
+ const commands = [
+ { id: "Conceal", label: concealCommandLabel },
+ {
+ id: "Copy",
+ label: copyCommandLabel,
+ verify: true,
+ },
+ editCommand,
+ "-",
+ deleteCommand,
+ ];
+ if (this.concealed) {
+ commands[0] = {
+ id: "Reveal",
+ label: revealCommandLabel,
+ verify: true,
+ };
+ }
+ return commands;
+ },
+ },
+ executeReveal: {
+ value() {
+ this.concealed = false;
+ this.refreshOnScreen();
+ },
+ },
+ executeConceal: {
+ value() {
+ this.concealed = true;
+ this.refreshOnScreen();
+ },
+ },
+ executeCopy: {
+ value() {
+ this.copyToClipboard(this.record.password);
+ },
+ },
+ executeEdit: {
+ value() {
+ this.editingValue = this.record.password ?? "";
+ this.refreshOnScreen();
+ },
+ },
+ executeSave: {
+ value(value) {
+ try {
+ const modifiedLogin = this.record.clone();
+ modifiedLogin.password = value;
+ Services.logins.modifyLogin(this.record, modifiedLogin);
+ } catch (error) {
+ //todo
+ console.error("failed to modify login", error);
+ }
+ this.executeCancel();
+ },
+ },
+ });
+
+ Services.obs.addObserver(this, "passwordmgr-storage-changed");
+ Services.prefs.addObserver("signon.rememberSignons", this);
+ Services.prefs.addObserver(
+ "signon.management.page.breach-alerts.enabled",
+ this
+ );
+ Services.prefs.addObserver(
+ "signon.management.page.vulnerable-passwords.enabled",
+ this
+ );
+ this.#reloadDataSource();
+ }
+ );
+ }
+
+ async #importFromFile(title, buttonLabel, csvTitle, tsvTitle) {
+ const { BrowserWindowTracker } = ChromeUtils.importESModule(
+ "resource:///modules/BrowserWindowTracker.sys.mjs"
+ );
+ const browser = BrowserWindowTracker.getTopWindow().gBrowser;
+ let { result, path } = await this.openFilePickerDialog(
+ title,
+ buttonLabel,
+ [
+ {
+ title: csvTitle,
+ extensionPattern: "*.csv",
+ },
+ {
+ title: tsvTitle,
+ extensionPattern: "*.tsv",
+ },
+ ],
+ browser.ownerGlobal
+ );
+
+ if (result != Ci.nsIFilePicker.returnCancel) {
+ let summary;
+ try {
+ summary = await LoginCSVImport.importFromCSV(path);
+ } catch (e) {
+ // TODO: Display error for import
+ }
+ if (summary) {
+ // TODO: Display successful import summary
+ }
+ }
+ }
+
+ async openFilePickerDialog(title, okButtonLabel, appendFilters, ownerGlobal) {
+ return new Promise(resolve => {
+ let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+ fp.init(ownerGlobal, title, Ci.nsIFilePicker.modeOpen);
+ for (const appendFilter of appendFilters) {
+ fp.appendFilter(appendFilter.title, appendFilter.extensionPattern);
+ }
+ fp.appendFilters(Ci.nsIFilePicker.filterAll);
+ fp.okButtonLabel = okButtonLabel;
+ fp.open(async result => {
+ resolve({ result, path: fp.file.path });
+ });
+ });
+ }
+
+ #openPreferences() {
+ const { BrowserWindowTracker } = ChromeUtils.importESModule(
+ "resource:///modules/BrowserWindowTracker.sys.mjs"
+ );
+ const browser = BrowserWindowTracker.getTopWindow().gBrowser;
+ browser.ownerGlobal.openPreferences("privacy-logins");
+ }
+
+ #getHelp() {
+ const { BrowserWindowTracker } = ChromeUtils.importESModule(
+ "resource:///modules/BrowserWindowTracker.sys.mjs"
+ );
+ const browser = BrowserWindowTracker.getTopWindow().gBrowser;
+ const SUPPORT_URL =
+ Services.urlFormatter.formatURLPref("app.support.baseURL") +
+ "password-manager-remember-delete-edit-logins";
+ browser.ownerGlobal.openWebLinkIn(SUPPORT_URL, "tab", {
+ relatedToCurrent: true,
+ });
+ }
+
+ /**
+ * Enumerate all the lines provided by this data source.
+ *
+ * @param {string} searchText used to filter data
+ */
+ *enumerateLines(searchText) {
+ if (this.#enabled === undefined) {
+ // Async Fluent API makes it possible to have data source waiting
+ // for the localized strings, which can be detected by undefined in #enabled.
+ return;
+ }
+
+ yield this.#header;
+ if (this.#header.collapsed || !this.#enabled) {
+ return;
+ }
+
+ const stats = { count: 0, total: 0 };
+ searchText = searchText.toUpperCase();
+ yield* this.enumerateLinesForMatchingRecords(
+ searchText,
+ stats,
+ login =>
+ login.displayOrigin.toUpperCase().includes(searchText) ||
+ login.username.toUpperCase().includes(searchText) ||
+ login.password.toUpperCase().includes(searchText)
+ );
+
+ this.formatMessages({
+ id:
+ stats.count == stats.total
+ ? "passwords-count"
+ : "passwords-filtered-count",
+ args: stats,
+ }).then(([headerLabel]) => {
+ this.#header.value = headerLabel;
+ });
+ }
+
+ /**
+ * Sync lines array with the actual data source.
+ * This function reads all logins from the storage, adds or updates lines and
+ * removes lines for the removed logins.
+ */
+ async #reloadDataSource() {
+ this.#enabled = Services.prefs.getBoolPref("signon.rememberSignons");
+ if (!this.#enabled) {
+ this.#reloadEmptyDataSource();
+ return;
+ }
+
+ const logins = await LoginHelper.getAllUserFacingLogins();
+ this.beforeReloadingDataSource();
+
+ const breachesMap = lazy.BREACH_ALERTS_ENABLED
+ ? await lazy.LoginBreaches.getPotentialBreachesByLoginGUID(logins)
+ : new Map();
+
+ logins.forEach(login => {
+ // Similar domains will be grouped together
+ // www. will have least effect on the sorting
+ const parts = login.displayOrigin.split(".");
+
+ // Exclude TLD domain
+ //todo support eTLD and use public suffix here https://publicsuffix.org
+ if (parts.length > 1) {
+ parts.length -= 1;
+ }
+ const domain = parts.reverse().join(".");
+ const lineId = `${domain}:${login.username}:${login.guid}`;
+
+ let originLine = this.addOrUpdateLine(
+ login,
+ lineId + "0",
+ this.#originPrototype
+ );
+ this.addOrUpdateLine(login, lineId + "1", this.#usernamePrototype);
+ let passwordLine = this.addOrUpdateLine(
+ login,
+ lineId + "2",
+ this.#passwordPrototype
+ );
+
+ let breachIndex =
+ originLine.stickers?.findIndex(s => s === this.breachedSticker) ?? -1;
+ let breach = breachesMap.get(login.guid);
+ if (breach && breachIndex < 0) {
+ originLine.stickers ??= [];
+ originLine.stickers.push(this.breachedSticker);
+ } else if (!breach && breachIndex >= 0) {
+ originLine.stickers.splice(breachIndex, 1);
+ }
+
+ const vulnerable = lazy.VULNERABLE_PASSWORDS_ENABLED
+ ? lazy.LoginBreaches.getPotentiallyVulnerablePasswordsByLoginGUID([
+ login,
+ ]).size
+ : 0;
+
+ let vulnerableIndex =
+ passwordLine.stickers?.findIndex(s => s === this.vulnerableSticker) ??
+ -1;
+ if (vulnerable && vulnerableIndex < 0) {
+ passwordLine.stickers ??= [];
+ passwordLine.stickers.push(this.vulnerableSticker);
+ } else if (!vulnerable && vulnerableIndex >= 0) {
+ passwordLine.stickers.splice(vulnerableIndex, 1);
+ }
+ });
+
+ this.afterReloadingDataSource();
+ }
+
+ #reloadEmptyDataSource() {
+ this.lines.length = 0;
+ //todo: user can enable passwords by activating Passwords header line
+ this.#header.value = this.#loginsDisabledMessage;
+ this.refreshAllLinesOnScreen();
+ }
+
+ observe(_subj, topic, message) {
+ if (
+ topic == "passwordmgr-storage-changed" ||
+ message == "signon.rememberSignons" ||
+ message == "signon.management.page.breach-alerts.enabled" ||
+ message == "signon.management.page.vulnerable-passwords.enabled"
+ ) {
+ this.#reloadDataSource();
+ }
+ }
+}
diff --git a/toolkit/components/satchel/megalist/aggregator/moz.build b/toolkit/components/satchel/megalist/aggregator/moz.build
new file mode 100644
index 0000000000..f244ade794
--- /dev/null
+++ b/toolkit/components/satchel/megalist/aggregator/moz.build
@@ -0,0 +1,17 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXTRA_JS_MODULES["megalist/aggregator"] += [
+ "Aggregator.sys.mjs",
+ "DefaultAggregator.sys.mjs",
+]
+
+EXTRA_JS_MODULES["megalist/aggregator/datasources"] += [
+ "datasources/AddressesDataSource.sys.mjs",
+ "datasources/BankCardDataSource.sys.mjs",
+ "datasources/DataSourceBase.sys.mjs",
+ "datasources/LoginDataSource.sys.mjs",
+]
diff --git a/toolkit/components/satchel/megalist/content/MegalistView.mjs b/toolkit/components/satchel/megalist/content/MegalistView.mjs
new file mode 100644
index 0000000000..44a0198692
--- /dev/null
+++ b/toolkit/components/satchel/megalist/content/MegalistView.mjs
@@ -0,0 +1,477 @@
+/* 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/. */
+
+import { html } from "chrome://global/content/vendor/lit.all.mjs";
+import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
+
+// eslint-disable-next-line import/no-unassigned-import
+import "chrome://global/content/megalist/VirtualizedList.mjs";
+
+// eslint-disable-next-line import/no-unassigned-import
+import "chrome://global/content/megalist/search-input.mjs";
+
+/**
+ * Map with limit on how many entries it can have.
+ * When over limit entries are added, oldest one are removed.
+ */
+class MostRecentMap {
+ constructor(maxSize) {
+ this.#maxSize = maxSize;
+ }
+
+ get(id) {
+ const data = this.#map.get(id);
+ if (data) {
+ this.#keepAlive(id, data);
+ }
+ return data;
+ }
+
+ has(id) {
+ this.#map.has(id);
+ }
+
+ set(id, data) {
+ this.#keepAlive(id, data);
+ this.#enforceLimits();
+ }
+
+ clear() {
+ this.#map.clear();
+ }
+
+ #maxSize;
+ #map = new Map();
+
+ #keepAlive(id, data) {
+ // Re-insert data to the map so it will be less likely to be evicted
+ this.#map.delete(id);
+ this.#map.set(id, data);
+ }
+
+ #enforceLimits() {
+ // Maps preserve order in which data was inserted,
+ // we use that fact to remove oldest data from it.
+ while (this.#map.size > this.#maxSize) {
+ this.#map.delete(this.#map.keys().next().value);
+ }
+ }
+}
+
+/**
+ * MegalistView presents data pushed to it by the MegalistViewModel and
+ * notify MegalistViewModel of user commands.
+ */
+export class MegalistView extends MozLitElement {
+ static keyToMessage = {
+ ArrowUp: "SelectPreviousSnapshot",
+ ArrowDown: "SelectNextSnapshot",
+ PageUp: "SelectPreviousGroup",
+ PageDown: "SelectNextGroup",
+ Escape: "UpdateFilter",
+ };
+ static LINE_HEIGHT = 64;
+
+ constructor() {
+ super();
+ this.selectedIndex = 0;
+ this.searchText = "";
+
+ window.addEventListener("MessageFromViewModel", ev =>
+ this.#onMessageFromViewModel(ev)
+ );
+ }
+
+ static get properties() {
+ return {
+ listLength: { type: Number },
+ selectedIndex: { type: Number },
+ searchText: { type: String },
+ };
+ }
+
+ /**
+ * View shows list of snapshots of lines stored in the View Model.
+ * View Model provides the first snapshot id in the list and list length.
+ * It's safe to combine firstSnapshotId+index to identify specific snapshot
+ * in the list. When the list changes, View Model will provide a new
+ * list with new first snapshot id (even if the content is the same).
+ */
+ #firstSnapshotId = 0;
+
+ /**
+ * Cache 120 most recently used lines.
+ * View lives in child and View Model in parent processes.
+ * By caching a few lines we reduce the need to send data between processes.
+ * This improves performance in nearby scrolling scenarios.
+ * 7680 is 8K vertical screen resolution.
+ * Typical line is under 1/4KB long, making around 30KB cache requirement.
+ */
+ #snapshotById = new MostRecentMap(7680 / MegalistView.LINE_HEIGHT);
+
+ #templates = {};
+
+ connectedCallback() {
+ super.connectedCallback();
+ this.ownerDocument.addEventListener("keydown", e => this.#handleKeydown(e));
+ for (const template of this.ownerDocument.getElementsByTagName(
+ "template"
+ )) {
+ this.#templates[template.id] = template.content.firstElementChild;
+ }
+ this.#messageToViewModel("Refresh");
+ }
+
+ createLineElement(index) {
+ if (index < 0 || index >= this.listLength) {
+ return null;
+ }
+
+ const snapshotId = this.#firstSnapshotId + index;
+ const lineElement = this.#templates.lineElement.cloneNode(true);
+ lineElement.dataset.id = snapshotId;
+ lineElement.addEventListener("dblclick", e => {
+ this.#messageToViewModel("Command");
+ e.preventDefault();
+ });
+
+ const data = this.#snapshotById.get(snapshotId);
+ if (data !== "Loading") {
+ if (data) {
+ this.#applyData(snapshotId, data, lineElement);
+ } else {
+ // Put placeholder for this snapshot data to avoid requesting it again
+ this.#snapshotById.set(snapshotId, "Loading");
+
+ // Ask for snapshot data from the View Model.
+ // Note: we could have optimized it further by asking for a range of
+ // indices because any scroll in virtualized list can only add
+ // a continuous range at the top or bottom of the visible area.
+ // However, this optimization is not necessary at the moment as
+ // we typically will request under a 100 of lines at a time.
+ // If we feel like making this improvement, we need to enhance
+ // VirtualizedList to request a range of new elements instead.
+ this.#messageToViewModel("RequestSnapshot", { snapshotId });
+ }
+ }
+
+ return lineElement;
+ }
+
+ /**
+ * Find snapshot element on screen and populate it with data
+ */
+ receiveSnapshot({ snapshotId, snapshot }) {
+ this.#snapshotById.set(snapshotId, snapshot);
+
+ const lineElement = this.shadowRoot.querySelector(
+ `.line[data-id="${snapshotId}"]`
+ );
+ if (lineElement) {
+ this.#applyData(snapshotId, snapshot, lineElement);
+ }
+ }
+
+ #applyData(snapshotId, snapshotData, lineElement) {
+ let elementToFocus;
+ const template =
+ this.#templates[snapshotData.template] ?? this.#templates.lineTemplate;
+
+ const lineContent = template.cloneNode(true);
+ lineContent.querySelector(".label").textContent = snapshotData.label;
+
+ const valueElement = lineContent.querySelector(".value");
+ if (valueElement) {
+ const valueText = lineContent.querySelector("span");
+ if (valueText) {
+ valueText.textContent = snapshotData.value;
+ } else {
+ const valueInput = lineContent.querySelector("input");
+ if (valueInput) {
+ valueInput.value = snapshotData.value;
+ valueInput.addEventListener("keydown", e => {
+ switch (e.code) {
+ case "Enter":
+ this.#messageToViewModel("Command", {
+ snapshotId,
+ commandId: "Save",
+ value: valueInput.value,
+ });
+ break;
+ case "Escape":
+ this.#messageToViewModel("Command", {
+ snapshotId,
+ commandId: "Cancel",
+ });
+ break;
+ default:
+ return;
+ }
+ e.preventDefault();
+ e.stopPropagation();
+ });
+ valueInput.addEventListener("input", () => {
+ // Update local cache so we don't override editing value
+ // while user scrolls up or down a little.
+ const snapshotDataInChild = this.#snapshotById.get(snapshotId);
+ if (snapshotDataInChild) {
+ snapshotDataInChild.value = valueInput.value;
+ }
+ this.#messageToViewModel("Command", {
+ snapshotId,
+ commandId: "EditInProgress",
+ value: valueInput.value,
+ });
+ });
+ elementToFocus = valueInput;
+ } else {
+ valueElement.textContent = snapshotData.value;
+ }
+ }
+
+ if (snapshotData.valueIcon) {
+ const valueIcon = valueElement.querySelector(".icon");
+ if (valueIcon) {
+ valueIcon.src = snapshotData.valueIcon;
+ }
+ }
+
+ if (snapshotData.href) {
+ const linkElement = this.ownerDocument.createElement("a");
+ linkElement.className = valueElement.className;
+ linkElement.href = snapshotData.href;
+ linkElement.replaceChildren(...valueElement.children);
+ valueElement.replaceWith(linkElement);
+ }
+
+ if (snapshotData.stickers?.length) {
+ const stickersElement = lineContent.querySelector(".stickers");
+ for (const sticker of snapshotData.stickers) {
+ const stickerElement = this.ownerDocument.createElement("span");
+ stickerElement.textContent = sticker.label;
+ stickerElement.className = sticker.type;
+ stickersElement.appendChild(stickerElement);
+ }
+ }
+ }
+
+ lineElement.querySelector(".content").replaceWith(lineContent);
+ lineElement.classList.toggle("start", !!snapshotData.start);
+ lineElement.classList.toggle("end", !!snapshotData.end);
+ elementToFocus?.focus();
+ }
+
+ #messageToViewModel(messageName, data) {
+ window.windowGlobalChild
+ .getActor("Megalist")
+ .sendAsyncMessage(messageName, data);
+ }
+
+ #onMessageFromViewModel({ detail }) {
+ const functionName = `receive${detail.name}`;
+ if (!(functionName in this)) {
+ throw new Error(`Received unknown message "${detail.name}"`);
+ }
+ this[functionName](detail.data);
+ }
+
+ receiveUpdateSelection({ selectedIndex }) {
+ this.selectedIndex = selectedIndex;
+ }
+
+ receiveShowSnapshots({ firstSnapshotId, count }) {
+ this.#firstSnapshotId = firstSnapshotId;
+ this.listLength = count;
+
+ // Each new display list starts with the new first snapshot id
+ // so we can forget previously known data.
+ this.#snapshotById.clear();
+ this.shadowRoot.querySelector("virtualized-list").requestRefresh();
+ this.requestUpdate();
+ }
+
+ receiveMegalistUpdateFilter({ searchText }) {
+ this.searchText = searchText;
+ this.requestUpdate();
+ }
+
+ #handleInputChange(e) {
+ const searchText = e.target.value;
+ this.#messageToViewModel("UpdateFilter", { searchText });
+ }
+
+ #handleKeydown(e) {
+ const message = MegalistView.keyToMessage[e.code];
+ if (message) {
+ this.#messageToViewModel(message);
+ e.preventDefault();
+ } else if (e.code == "Enter") {
+ // Do not handle Enter at the virtualized list level when line menu is open
+ if (
+ this.shadowRoot.querySelector(
+ ".line.selected > .menuButton > .menuPopup"
+ )
+ ) {
+ return;
+ }
+
+ if (e.altKey) {
+ // Execute default command1
+ this.#messageToViewModel("Command");
+ } else {
+ // Show line level menu
+ this.shadowRoot
+ .querySelector(".line.selected > .menuButton > button")
+ ?.click();
+ }
+ e.preventDefault();
+ } else if (e.ctrlKey && e.key == "c" && !this.searchText.length) {
+ this.#messageToViewModel("Command", { commandId: "Copy" });
+ e.preventDefault();
+ }
+ }
+
+ #handleClick(e) {
+ const lineElement = e.composedTarget.closest(".line");
+ if (!lineElement) {
+ return;
+ }
+
+ const snapshotId = Number(lineElement.dataset.id);
+ const snapshotData = this.#snapshotById.get(snapshotId);
+ if (!snapshotData) {
+ return;
+ }
+
+ this.#messageToViewModel("SelectSnapshot", { snapshotId });
+ const menuButton = e.composedTarget.closest(".menuButton");
+ if (menuButton) {
+ this.#handleMenuButtonClick(menuButton, snapshotId, snapshotData);
+ }
+
+ e.preventDefault();
+ }
+
+ #handleMenuButtonClick(menuButton, snapshotId, snapshotData) {
+ if (!snapshotData.commands?.length) {
+ return;
+ }
+
+ const popup = this.ownerDocument.createElement("div");
+ popup.className = "menuPopup";
+ popup.addEventListener(
+ "keydown",
+ e => {
+ function focusInternal(next, wrapSelector) {
+ let element = e.composedTarget;
+ do {
+ element = element[next];
+ } while (element && element.tagName != "BUTTON");
+
+ // If we can't find next/prev button, focus the first/last one
+ element ??=
+ e.composedTarget.parentElement.querySelector(wrapSelector);
+ element?.focus();
+ }
+
+ function focusNext() {
+ focusInternal("nextElementSibling", "button");
+ }
+
+ function focusPrev() {
+ focusInternal("previousElementSibling", "button:last-of-type");
+ }
+
+ switch (e.code) {
+ case "Escape":
+ popup.remove();
+ break;
+ case "Tab":
+ if (e.shiftKey) {
+ focusPrev();
+ } else {
+ focusNext();
+ }
+ break;
+ case "ArrowUp":
+ focusPrev();
+ break;
+ case "ArrowDown":
+ focusNext();
+ break;
+ default:
+ return;
+ }
+
+ e.preventDefault();
+ e.stopPropagation();
+ },
+ { capture: true }
+ );
+ popup.addEventListener(
+ "blur",
+ e => {
+ if (
+ e.composedTarget?.closest(".menuPopup") !=
+ e.relatedTarget?.closest(".menuPopup")
+ ) {
+ // TODO: this triggers on macOS before "click" event. Due to this,
+ // we are not receiving the command.
+ popup.remove();
+ }
+ },
+ { capture: true }
+ );
+
+ for (const command of snapshotData.commands) {
+ if (command == "-") {
+ const separator = this.ownerDocument.createElement("div");
+ separator.className = "separator";
+ popup.appendChild(separator);
+ continue;
+ }
+
+ const menuItem = this.ownerDocument.createElement("button");
+ menuItem.textContent = command.label;
+ menuItem.addEventListener("click", e => {
+ this.#messageToViewModel("Command", {
+ snapshotId,
+ commandId: command.id,
+ });
+ popup.remove();
+ e.preventDefault();
+ });
+ popup.appendChild(menuItem);
+ }
+
+ menuButton.querySelector("button").after(popup);
+ popup.querySelector("button")?.focus();
+ }
+
+ render() {
+ return html`
+ <link
+ rel="stylesheet"
+ href="chrome://global/content/megalist/megalist.css"
+ />
+ <div class="container">
+ <search-input
+ .value=${this.searchText}
+ .change=${e => this.#handleInputChange(e)}
+ >
+ </search-input>
+ <virtualized-list
+ .lineCount=${this.listLength}
+ .lineHeight=${MegalistView.LINE_HEIGHT}
+ .selectedIndex=${this.selectedIndex}
+ .createLineElement=${index => this.createLineElement(index)}
+ @click=${e => this.#handleClick(e)}
+ >
+ </virtualized-list>
+ </div>
+ `;
+ }
+}
+
+customElements.define("megalist-view", MegalistView);
diff --git a/toolkit/components/satchel/megalist/content/VirtualizedList.mjs b/toolkit/components/satchel/megalist/content/VirtualizedList.mjs
new file mode 100644
index 0000000000..7903a189eb
--- /dev/null
+++ b/toolkit/components/satchel/megalist/content/VirtualizedList.mjs
@@ -0,0 +1,136 @@
+/* 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/. */
+
+/**
+ * Virtualized List can efficiently show billions of lines provided
+ * that all of them have the same height.
+ *
+ * Caller is responsible for setting createLineElement(index) function to
+ * create elements as they are scrolled into the view.
+ */
+class VirtualizedList extends HTMLElement {
+ lineHeight = 64;
+ #lineCount = 0;
+
+ get lineCount() {
+ return this.#lineCount;
+ }
+
+ set lineCount(value) {
+ this.#lineCount = value;
+ this.#rebuildVisibleLines();
+ }
+
+ #selectedIndex = 0;
+
+ get selectedIndex() {
+ return this.#selectedIndex;
+ }
+
+ set selectedIndex(value) {
+ this.#selectedIndex = value;
+ if (this.#container) {
+ this.updateLineSelection(true);
+ }
+ }
+
+ #container;
+
+ connectedCallback() {
+ this.#container = this.ownerDocument.createElement("ul");
+ this.#container.classList.add("lines-container");
+ this.appendChild(this.#container);
+
+ this.#rebuildVisibleLines();
+ this.addEventListener("scroll", () => this.#rebuildVisibleLines());
+ }
+
+ requestRefresh() {
+ this.#container.replaceChildren();
+ this.#rebuildVisibleLines();
+ }
+
+ updateLineSelection(scrollIntoView) {
+ const lineElements = this.#container.querySelectorAll(".line");
+ let selectedElement;
+
+ for (let lineElement of lineElements) {
+ let isSelected = Number(lineElement.dataset.index) === this.selectedIndex;
+ if (isSelected) {
+ selectedElement = lineElement;
+ }
+ lineElement.classList.toggle("selected", isSelected);
+ }
+
+ if (scrollIntoView) {
+ if (selectedElement) {
+ selectedElement.scrollIntoView({ block: "nearest" });
+ } else {
+ let selectedTop = this.selectedIndex * this.lineHeight;
+ if (this.scrollTop > selectedTop) {
+ this.scrollTop = selectedTop;
+ } else {
+ this.scrollTop = selectedTop - this.clientHeight + this.lineHeight;
+ }
+ }
+ }
+ }
+
+ #rebuildVisibleLines() {
+ if (!this.isConnected || !this.createLineElement) {
+ return;
+ }
+
+ this.#container.style.height = `${this.lineHeight * this.lineCount}px`;
+
+ let firstLineIndex = Math.floor(this.scrollTop / this.lineHeight);
+ let visibleLineCount = Math.ceil(this.clientHeight / this.lineHeight);
+ let lastLineIndex = firstLineIndex + visibleLineCount;
+ let extraLines = Math.ceil(visibleLineCount / 2); // They are present in DOM, but not visible
+
+ firstLineIndex = Math.max(0, firstLineIndex - extraLines);
+ lastLineIndex = Math.min(this.lineCount, lastLineIndex + extraLines);
+
+ let previousChild = null;
+ let visibleLines = new Map();
+
+ for (let child of Array.from(this.#container.children)) {
+ let index = Number(child.dataset.index);
+ if (index < firstLineIndex || index > lastLineIndex) {
+ child.remove();
+ } else {
+ visibleLines.set(index, child);
+ }
+ }
+
+ for (let index = firstLineIndex; index <= lastLineIndex; index++) {
+ let child = visibleLines.get(index);
+ if (!child) {
+ child = this.createLineElement(index);
+
+ if (!child) {
+ // Friday fix :-)
+ //todo: figure out what was on that Friday and how can we fix it
+ continue;
+ }
+
+ child.style.top = `${index * this.lineHeight}px`;
+ child.dataset.index = index;
+
+ if (previousChild) {
+ previousChild.after(child);
+ } else if (this.#container.firstElementChild?.offsetTop > top) {
+ this.#container.firstElementChild.before(child);
+ } else {
+ this.#container.appendChild(child);
+ }
+ }
+ previousChild = child;
+ }
+
+ this.updateLineSelection(false);
+ }
+}
+
+customElements.define("virtualized-list", VirtualizedList);
diff --git a/toolkit/components/satchel/megalist/content/megalist.css b/toolkit/components/satchel/megalist/content/megalist.css
new file mode 100644
index 0000000000..b442a7b60d
--- /dev/null
+++ b/toolkit/components/satchel/megalist/content/megalist.css
@@ -0,0 +1,208 @@
+/* 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/. */
+
+/* Bug 1869845 - Styles in this file are still experimental! */
+
+.container {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ max-height: 100vh;
+
+ > search-input {
+ margin: 20px;
+ }
+}
+
+virtualized-list {
+ position: relative;
+ overflow: auto;
+ margin: 20px;
+
+ .lines-container {
+ padding-inline-start: unset;
+ }
+}
+
+.line {
+ display: flex;
+ align-items: stretch;
+ position: absolute;
+ width: 100%;
+ user-select: none;
+ box-sizing: border-box;
+ height: 64px;
+
+ background-color: var(--in-content-box-background-odd);
+ border-inline: 1px solid var(--in-content-border-color);
+
+ color: var(--in-content-text-color);
+
+ &.start {
+ border-block-start: 1px solid var(--in-content-border-color);
+ border-start-start-radius: 8px;
+ border-start-end-radius: 8px;
+ }
+
+ &.end {
+ border-block-end: 1px solid var(--in-content-border-color);
+ border-end-start-radius: 8px;
+ border-end-end-radius: 8px;
+ height: 54px;
+ }
+
+ > .menuButton {
+ position: relative;
+ visibility: hidden;
+
+ > button {
+ border: none;
+ margin-inline-start: 2px;
+ padding: 2px;
+ background-color: transparent;
+ /* Fix: too lazy to load the svg */
+ width: 32px;
+ color: unset;
+ }
+
+ > .menuPopup {
+ position: absolute;
+ inset-inline-end: 0;
+ box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
+ z-index: 1;
+ background-color: var(--in-content-table-background);
+ padding: 4px;
+
+ > .separator {
+ border-block-start: 1px solid var(--in-content-border-color);
+ margin: 4px 0;
+ }
+
+ > button {
+ text-align: start;
+ border-style: none;
+ padding: 12px;
+ margin-block-end: 2px;
+ width: 100%;
+ text-wrap: nowrap;
+ }
+ }
+ }
+
+ > .content {
+ flex-grow: 1;
+
+ > div {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ padding-inline-start: 10px;
+
+ &:last-child {
+ padding-block-end: 10px;
+ }
+ }
+
+ > .icon {
+ margin-inline-start: 4px;
+ width: 16px;
+ height: 16px;
+ -moz-context-properties: fill;
+ fill: currentColor;
+ }
+
+ > .label {
+ color: var(--text-color-deemphasized);
+ padding-block: 2px 4px;
+ }
+
+ > .value {
+ user-select: text;
+
+ > .icon {
+ -moz-context-properties: fill;
+ fill: currentColor;
+ width: auto;
+ height: 16px;
+ margin-inline: 4px;
+ vertical-align: text-bottom;
+ }
+
+ > .icon:not([src]) {
+ display: none;
+ }
+
+ &:is(a) {
+ color: currentColor;
+ }
+ }
+
+ > .stickers {
+ text-align: end;
+ margin-block-start: 2px;
+
+ > span {
+ padding: 2px;
+ margin-inline-end: 2px;
+ }
+
+ /* Hard-coded colors will be addressed in FXCM-1013 */
+ > span.risk {
+ background-color: slateblue;
+ border: 1px solid darkslateblue;
+ color: whitesmoke;
+ }
+
+ > span.warning {
+ background-color: firebrick;
+ border: 1px solid maroon;
+ color: whitesmoke;
+ }
+ }
+
+ &.section {
+ font-size: larger;
+
+ > .label {
+ display: inline-block;
+ margin: 0;
+ color: unset;
+ }
+
+ > .value {
+ margin-inline-end: 8px;
+ text-align: end;
+ font-size: smaller;
+ color: var(--text-color-deemphasized);
+ user-select: unset;
+ }
+ }
+ }
+
+ &.selected {
+ color: var(--in-content-item-selected-text);
+ background-color: var(--in-content-item-selected);
+
+ > .menuButton {
+ visibility: inherit;
+ }
+ }
+
+ &:hover {
+ color: var(--in-content-item-hover-text);
+ background-color: var(--in-content-item-hover);
+
+ > .menuButton {
+ visibility: visible;
+ }
+ }
+}
+
+.search {
+ padding: 8px;
+ border-radius: 4px;
+ border: 1px solid var(--in-content-border-color);
+ box-sizing: border-box;
+ width: 100%;
+}
diff --git a/toolkit/components/satchel/megalist/content/megalist.ftl b/toolkit/components/satchel/megalist/content/megalist.ftl
new file mode 100644
index 0000000000..69d085a7c5
--- /dev/null
+++ b/toolkit/components/satchel/megalist/content/megalist.ftl
@@ -0,0 +1,126 @@
+# 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/.
+
+filter-placeholder =
+ .placeholder = Search Your Data
+ .key = F
+
+## Commands
+
+command-copy = Copy
+command-reveal = Reveal
+command-conceal = Conceal
+command-toggle = Toggle
+command-open = Open
+command-delete = Remove record
+command-edit = Edit
+command-save = Save
+command-cancel = Cancel
+
+## Passwords
+
+passwords-section-label = Passwords
+passwords-disabled = Passwords are disabled
+
+passwords-command-create = Add Password
+passwords-command-import = Import from a File…
+passwords-command-export = Export Passwords…
+passwords-command-remove-all = Remove All Passwords…
+passwords-command-settings = Settings
+passwords-command-help = Help
+
+passwords-import-file-picker-title = Import Passwords
+passwords-import-file-picker-import-button = Import
+
+# A description for the .csv file format that may be shown as the file type
+# filter by the operating system.
+passwords-import-file-picker-csv-filter-title =
+ { PLATFORM() ->
+ [macos] CSV Document
+ *[other] CSV File
+ }
+# A description for the .tsv file format that may be shown as the file type
+# filter by the operating system. TSV is short for 'tab separated values'.
+passwords-import-file-picker-tsv-filter-title =
+ { PLATFORM() ->
+ [macos] TSV Document
+ *[other] TSV File
+ }
+
+# Variables
+# $count (number) - Number of passwords
+passwords-count =
+ { $count ->
+ [one] { $count } password
+ *[other] { $count } passwords
+ }
+
+# Variables
+# $count (number) - Number of filtered passwords
+# $total (number) - Total number of passwords
+passwords-filtered-count =
+ { $total ->
+ [one] { $count } of { $total } password
+ *[other] { $count } of { $total } passwords
+ }
+
+passwords-origin-label = Website address
+passwords-username-label = Username
+passwords-password-label = Password
+
+## Payments
+
+payments-command-create = Add Payment Method
+
+payments-section-label = Payment methods
+payments-disabled = Payments methods are disabled
+
+# Variables
+# $count (number) - Number of payment methods
+payments-count =
+ { $count ->
+ [one] { $count } payment method
+ *[other] { $count } payment methods
+ }
+
+# Variables
+# $count (number) - Number of filtered payment methods
+# $total (number) - Total number of payment methods
+payments-filtered-count =
+ { $total ->
+ [one] { $count } of { $total } payment method
+ *[other] { $count } of { $total } payment methods
+ }
+
+card-number-label = Card Number
+card-expiration-label = Expires on
+card-holder-label = Name on Card
+
+## Addresses
+
+addresses-command-create = Add Address
+
+addresses-section-label = Addresses
+addresses-disabled = Addresses are disabled
+
+# Variables
+# $count (number) - Number of addresses
+addresses-count =
+ { $count ->
+ [one] { $count } address
+ *[other] { $count } addresses
+ }
+
+# Variables
+# $count (number) - Number of filtered addresses
+# $total (number) - Total number of addresses
+addresses-filtered-count =
+ { $total ->
+ [one] { $count } of { $total } address
+ *[other] { $count } of { $total } addresses
+ }
+
+address-name-label = Name
+address-phone-label = Phone
+address-email-label = Email
diff --git a/toolkit/components/satchel/megalist/content/megalist.html b/toolkit/components/satchel/megalist/content/megalist.html
new file mode 100644
index 0000000000..6ff3f089fc
--- /dev/null
+++ b/toolkit/components/satchel/megalist/content/megalist.html
@@ -0,0 +1,78 @@
+<!-- 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/. -->
+
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8" />
+ <meta
+ name="viewport"
+ content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
+ />
+ <script
+ type="module"
+ src="chrome://global/content/megalist/MegalistView.mjs"
+ ></script>
+ <link rel="stylesheet" href="chrome://global/skin/in-content/common.css" />
+ <link rel="localization" href="preview/megalist.ftl" />
+ </head>
+
+ <body>
+ <megalist-view></megalist-view>
+
+ <template id="lineElement">
+ <li class="line">
+ <div class="content"></div>
+ <div class="menuButton">
+ <button>…</button>
+ </div>
+ </li>
+ </template>
+
+ <template id="collapsedSectionTemplate">
+ <div class="content section">
+ <img
+ class="icon collapsed"
+ draggable="false"
+ src="chrome://global/skin/icons/arrow-down.svg"
+ />
+ <h4 class="label"></h4>
+ </div>
+ </template>
+
+ <template id="expandedSectionTemplate">
+ <div class="content section">
+ <img
+ class="icon expanded"
+ draggable="false"
+ src="chrome://global/skin/icons/arrow-up.svg"
+ />
+ <h4 class="label"></h4>
+ <div class="value"></div>
+ </div>
+ </template>
+
+ <template id="lineTemplate">
+ <div class="content">
+ <div class="label"></div>
+ <div class="value">
+ <img class="icon" />
+ <span></span>
+ </div>
+ <div class="stickers"></div>
+ </div>
+ </template>
+
+ <template id="editingLineTemplate">
+ <div class="content">
+ <div class="label"></div>
+ <div class="value">
+ <img class="icon" />
+ <input type="text" />
+ </div>
+ <div class="stickers"></div>
+ </div>
+ </template>
+ </body>
+</html>
diff --git a/toolkit/components/satchel/megalist/content/search-input.mjs b/toolkit/components/satchel/megalist/content/search-input.mjs
new file mode 100644
index 0000000000..e30d13ef2a
--- /dev/null
+++ b/toolkit/components/satchel/megalist/content/search-input.mjs
@@ -0,0 +1,36 @@
+/* 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/. */
+
+import { html } from "chrome://global/content/vendor/lit.all.mjs";
+import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
+
+export default class SearchInput extends MozLitElement {
+ static get properties() {
+ return {
+ items: { type: Array },
+ change: { type: Function },
+ value: { type: String },
+ };
+ }
+
+ render() {
+ return html` <link
+ rel="stylesheet"
+ href="chrome://global/content/megalist/megalist.css"
+ />
+ <link
+ rel="stylesheet"
+ href="chrome://global/skin/in-content/common.css"
+ />
+ <input
+ class="search"
+ type="search"
+ data-l10n-id="filter-placeholder"
+ @input=${this.change}
+ .value=${this.value}
+ />`;
+ }
+}
+
+customElements.define("search-input", SearchInput);
diff --git a/toolkit/components/satchel/megalist/content/tests/chrome/chrome.toml b/toolkit/components/satchel/megalist/content/tests/chrome/chrome.toml
new file mode 100644
index 0000000000..2d7fd6bccd
--- /dev/null
+++ b/toolkit/components/satchel/megalist/content/tests/chrome/chrome.toml
@@ -0,0 +1,3 @@
+[DEFAULT]
+
+["test_virtualized_list.html"]
diff --git a/toolkit/components/satchel/megalist/content/tests/chrome/test_virtualized_list.html b/toolkit/components/satchel/megalist/content/tests/chrome/test_virtualized_list.html
new file mode 100644
index 0000000000..65ddbcc40b
--- /dev/null
+++ b/toolkit/components/satchel/megalist/content/tests/chrome/test_virtualized_list.html
@@ -0,0 +1,125 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>VirtualizedList Tests</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
+ <link rel="stylesheet" href="chrome://global/content/megalist/megalist.css">
+ <script type="module" src="chrome://global/content/megalist/VirtualizedList.mjs"></script>
+</head>
+<body>
+ <style>
+ </style>
+<p id="display"></p>
+<div id="content">
+ <virtualized-list></virtualized-list>
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript">
+ const virtualizedList = document.querySelector("virtualized-list");
+
+ function dispatchScrollEvent(target, scrollY) {
+ target.scrollTop = scrollY;
+ virtualizedList.dispatchEvent(new Event('scroll'));
+ }
+
+ function updateVisibleItemBoundaries(visibleItemCount, value) {
+ if (value > visibleItemCount.max) {
+ visibleItemCount.max = value;
+ }
+ }
+
+ // Setup
+ virtualizedList.lineHeight = 64;
+ virtualizedList.lineCount = 1000;
+ virtualizedList.selectedIndex = 0;
+ virtualizedList.createLineElement = index => {
+ const lineElement = document.createElement("div");
+ lineElement.classList.add("line");
+ lineElement.textContent = `Row ${index}`;
+ return lineElement;
+ }
+
+ virtualizedList.style.display = "block";
+ virtualizedList.style.height = "300px";
+ virtualizedList.style.width = "500px";
+
+ /**
+ * Tests that the virtualized list renders expected number of items
+ */
+
+ add_task(async function test_rebuildVisibleLines() {
+ let container = virtualizedList.querySelector(".lines-container");
+ let initialLines = container.querySelectorAll(".line");
+ // Get boundaries of visible item count as they are rendered.
+ let visibleItemsCount = {
+ min: initialLines.length,
+ max: initialLines.length,
+ };
+
+ is(
+ container.style.height,
+ `${virtualizedList.lineHeight * virtualizedList.lineCount}px`,
+ "VirtualizedList is correct height."
+ );
+
+ // Scroll down 800px
+ dispatchScrollEvent(virtualizedList, 800);
+ let newRenderedLines = container.querySelectorAll(".line");
+ updateVisibleItemBoundaries(visibleItemsCount, newRenderedLines.length);
+ let firstRow = container.querySelector(".line[data-index='0']");
+ ok(!firstRow, "The first row should be removed.");
+
+ // Scroll down another 800px
+ dispatchScrollEvent(virtualizedList, 800);
+ newRenderedLines = container.querySelectorAll(".line");
+ updateVisibleItemBoundaries(visibleItemsCount, newRenderedLines.length);
+ let thirdRow = container.querySelector(".line[data-index='2']");
+ ok(!thirdRow, "The third row should be removed.");
+
+ // Check that amount of visible lines is within boundaries. This is to
+ // ensure the list is keeping a range of rendered items and
+ // not increasing the element count in the DOM.
+ ok(
+ newRenderedLines.length >= visibleItemsCount.min &&
+ newRenderedLines.length <= visibleItemsCount.max,
+ "Virtual list is removing and adding lines as needed."
+ );
+
+ // Scroll back to top
+ dispatchScrollEvent(virtualizedList, 0);
+ newRenderedLines = container.querySelectorAll(".line");
+ updateVisibleItemBoundaries(visibleItemsCount, newRenderedLines.length);
+ firstRow = container.querySelector(".line[data-index='0']");
+ thirdRow = container.querySelector(".line[data-index='2']");
+ ok(firstRow, "The first row should be rendered again.");
+ ok(firstRow, "The third row should be rendered again.");
+ });
+
+ /**
+ * Tests that item selection is preserved when list is rebuilt
+ */
+ add_task(async function test_updateLineSelection() {
+ let container = virtualizedList.querySelector(".lines-container");
+ let selectedLine = container.querySelector(".selected");
+ is(selectedLine.dataset.index, "0", "The correct line is selected");
+
+ // Scroll down 800px
+ dispatchScrollEvent(virtualizedList, 800);
+ selectedLine = container.querySelector(".selected");
+ ok(!selectedLine, "Selected line is not rendered because it's out of view");
+ is(virtualizedList.selectedIndex, 0, "Selected line is still preserved in list.");
+
+ // Scroll back to top
+ dispatchScrollEvent(virtualizedList, 0);
+ selectedLine = container.querySelector(".selected");
+ is(selectedLine.dataset.index, "0", "The same selected line is rendered.");
+ });
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/satchel/megalist/moz.build b/toolkit/components/satchel/megalist/moz.build
new file mode 100644
index 0000000000..266281a9a8
--- /dev/null
+++ b/toolkit/components/satchel/megalist/moz.build
@@ -0,0 +1,20 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+MOCHITEST_CHROME_MANIFESTS += ["content/tests/chrome/chrome.toml"]
+
+DIRS += [
+ "aggregator",
+]
+
+EXTRA_JS_MODULES["megalist"] += [
+ "MegalistViewModel.sys.mjs",
+]
+
+FINAL_TARGET_FILES.actors += [
+ "MegalistChild.sys.mjs",
+ "MegalistParent.sys.mjs",
+]
diff --git a/toolkit/components/satchel/moz.build b/toolkit/components/satchel/moz.build
index 4b6d08cdbf..fc5cabecd7 100644
--- a/toolkit/components/satchel/moz.build
+++ b/toolkit/components/satchel/moz.build
@@ -12,8 +12,8 @@ XPCSHELL_TESTS_MANIFESTS += ["test/unit/xpcshell.toml"]
BROWSER_CHROME_MANIFESTS += ["test/browser/browser.toml"]
XPIDL_SOURCES += [
- "nsIFormAutoComplete.idl",
"nsIFormFillController.idl",
+ "nsIFormHistoryAutoComplete.idl",
]
XPIDL_MODULE = "satchel"
@@ -26,10 +26,16 @@ LOCAL_INCLUDES += [
"../build",
]
+JAR_MANIFESTS += ["jar.mn"]
+
+DIRS += [
+ "megalist",
+]
+
EXTRA_JS_MODULES += [
"FillHelpers.sys.mjs",
- "FormAutoComplete.sys.mjs",
"FormHistory.sys.mjs",
+ "FormHistoryAutoComplete.sys.mjs",
"FormHistoryStartup.sys.mjs",
"FormScenarios.sys.mjs",
"integrations/FirefoxRelay.sys.mjs",
diff --git a/toolkit/components/satchel/nsFormFillController.cpp b/toolkit/components/satchel/nsFormFillController.cpp
index 1bcbde08df..61d23d157c 100644
--- a/toolkit/components/satchel/nsFormFillController.cpp
+++ b/toolkit/components/satchel/nsFormFillController.cpp
@@ -23,7 +23,7 @@
#include "mozilla/Services.h"
#include "mozilla/StaticPrefs_ui.h"
#include "nsCRT.h"
-#include "nsIFormAutoComplete.h"
+#include "nsIFormHistoryAutoComplete.h"
#include "nsString.h"
#include "nsPIDOMWindow.h"
#include "nsIAutoCompleteResult.h"
@@ -47,12 +47,13 @@ using mozilla::LogLevel;
static mozilla::LazyLogModule sLogger("satchel");
-static nsIFormAutoComplete* GetFormAutoComplete() {
- static nsCOMPtr<nsIFormAutoComplete> sInstance;
+static nsIFormHistoryAutoComplete* GetFormHistoryAutoComplete() {
+ static nsCOMPtr<nsIFormHistoryAutoComplete> sInstance;
static bool sInitialized = false;
if (!sInitialized) {
nsresult rv;
- sInstance = do_GetService("@mozilla.org/satchel/form-autocomplete;1", &rv);
+ sInstance =
+ do_GetService("@mozilla.org/satchel/form-history-autocomplete;1", &rv);
if (NS_SUCCEEDED(rv)) {
ClearOnShutdown(&sInstance);
@@ -64,14 +65,14 @@ static nsIFormAutoComplete* GetFormAutoComplete() {
NS_IMPL_CYCLE_COLLECTION(nsFormFillController, mController, mLoginManagerAC,
mFocusedPopup, mPopups, mLastListener,
- mLastFormAutoComplete)
+ mLastFormHistoryAutoComplete)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFormFillController)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIFormFillController)
NS_INTERFACE_MAP_ENTRY(nsIFormFillController)
NS_INTERFACE_MAP_ENTRY(nsIAutoCompleteInput)
NS_INTERFACE_MAP_ENTRY(nsIAutoCompleteSearch)
- NS_INTERFACE_MAP_ENTRY(nsIFormAutoCompleteObserver)
+ NS_INTERFACE_MAP_ENTRY(nsIFormFillCompleteObserver)
NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
@@ -705,13 +706,13 @@ nsFormFillController::StartSearch(const nsAString& aSearchString,
MaybeObserveDataListMutations();
}
- auto formAutoComplete = GetFormAutoComplete();
- NS_ENSURE_TRUE(formAutoComplete, NS_ERROR_FAILURE);
+ auto* formHistoryAutoComplete = GetFormHistoryAutoComplete();
+ NS_ENSURE_TRUE(formHistoryAutoComplete, NS_ERROR_FAILURE);
- formAutoComplete->AutoCompleteSearchAsync(aSearchParam, aSearchString,
- mFocusedInput, aPreviousResult,
- addDataList, this);
- mLastFormAutoComplete = formAutoComplete;
+ formHistoryAutoComplete->AutoCompleteSearchAsync(
+ aSearchParam, aSearchString, mFocusedInput, aPreviousResult,
+ addDataList, this);
+ mLastFormHistoryAutoComplete = formHistoryAutoComplete;
}
return NS_OK;
@@ -760,10 +761,10 @@ void nsFormFillController::RevalidateDataList() {
NS_IMETHODIMP
nsFormFillController::StopSearch() {
// Make sure to stop and clear this, otherwise the controller will prevent
- // mLastFormAutoComplete from being deleted.
- if (mLastFormAutoComplete) {
- mLastFormAutoComplete->StopAutoCompleteSearch();
- mLastFormAutoComplete = nullptr;
+ // mLastFormHistoryAutoComplete from being deleted.
+ if (mLastFormHistoryAutoComplete) {
+ mLastFormHistoryAutoComplete->StopAutoCompleteSearch();
+ mLastFormHistoryAutoComplete = nullptr;
}
if (mLoginManagerAC) {
@@ -773,7 +774,7 @@ nsFormFillController::StopSearch() {
}
////////////////////////////////////////////////////////////////////////
-//// nsIFormAutoCompleteObserver
+//// nsIFormFillCompleteObserver
NS_IMETHODIMP
nsFormFillController::OnSearchCompletion(nsIAutoCompleteResult* aResult) {
diff --git a/toolkit/components/satchel/nsFormFillController.h b/toolkit/components/satchel/nsFormFillController.h
index eef6addb7a..239c293352 100644
--- a/toolkit/components/satchel/nsFormFillController.h
+++ b/toolkit/components/satchel/nsFormFillController.h
@@ -13,7 +13,7 @@
#include "nsIAutoCompleteController.h"
#include "nsIAutoCompletePopup.h"
#include "nsIDOMEventListener.h"
-#include "nsIFormAutoComplete.h"
+#include "nsIFormHistoryAutoComplete.h"
#include "nsCOMPtr.h"
#include "nsStubMutationObserver.h"
#include "nsTHashMap.h"
@@ -37,7 +37,7 @@ class HTMLInputElement;
class nsFormFillController final : public nsIFormFillController,
public nsIAutoCompleteInput,
public nsIAutoCompleteSearch,
- public nsIFormAutoCompleteObserver,
+ public nsIFormFillCompleteObserver,
public nsIDOMEventListener,
public nsIObserver,
public nsMultiMutationObserver {
@@ -46,7 +46,7 @@ class nsFormFillController final : public nsIFormFillController,
NS_DECL_NSIFORMFILLCONTROLLER
NS_DECL_NSIAUTOCOMPLETESEARCH
NS_DECL_NSIAUTOCOMPLETEINPUT
- NS_DECL_NSIFORMAUTOCOMPLETEOBSERVER
+ NS_DECL_NSIFORMFILLCOMPLETEOBSERVER
NS_DECL_NSIDOMEVENTLISTENER
NS_DECL_NSIOBSERVER
NS_DECL_NSIMUTATIONOBSERVER
@@ -122,7 +122,7 @@ class nsFormFillController final : public nsIFormFillController,
nsCOMPtr<nsIAutoCompleteObserver> mLastListener;
// This is cleared by StopSearch().
- nsCOMPtr<nsIFormAutoComplete> mLastFormAutoComplete;
+ nsCOMPtr<nsIFormHistoryAutoComplete> mLastFormHistoryAutoComplete;
nsString mLastSearchString;
nsTHashMap<nsPtrHashKey<const nsINode>, bool> mPwmgrInputs;
diff --git a/toolkit/components/satchel/nsIFormAutoComplete.idl b/toolkit/components/satchel/nsIFormAutoComplete.idl
deleted file mode 100644
index cc40872dd3..0000000000
--- a/toolkit/components/satchel/nsIFormAutoComplete.idl
+++ /dev/null
@@ -1,44 +0,0 @@
-/* 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/. */
-
-
-#include "nsISupports.idl"
-
-interface nsIAutoCompleteResult;
-interface nsIFormAutoCompleteObserver;
-interface nsIPropertyBag2;
-
-webidl HTMLInputElement;
-
-[scriptable, uuid(bfd9b82b-0ab3-4b6b-9e54-aa961ff4b732)]
-interface nsIFormAutoComplete: nsISupports {
- /**
- * Generate results for a form input autocomplete menu asynchronously.
- */
- void autoCompleteSearchAsync(in AString aInputName,
- in AString aSearchString,
- in HTMLInputElement aField,
- in nsIAutoCompleteResult aPreviousResult,
- in bool aAddDatalist,
- in nsIFormAutoCompleteObserver aListener);
-
- /**
- * If a search is in progress, stop it. Otherwise, do nothing. This is used
- * to cancel an existing search, for example, in preparation for a new search.
- */
- void stopAutoCompleteSearch();
-};
-
-[scriptable, function, uuid(604419ab-55a0-4831-9eca-1b9e67cc4751)]
-interface nsIFormAutoCompleteObserver : nsISupports
-{
- /*
- * Called when a search is complete and the results are ready even if the
- * result set is empty. If the search is cancelled or a new search is
- * started, this is not called.
- *
- * @param result - The search result object
- */
- [can_run_script] void onSearchCompletion(in nsIAutoCompleteResult result);
-};
diff --git a/toolkit/components/satchel/nsIFormFillController.idl b/toolkit/components/satchel/nsIFormFillController.idl
index 25bd2d6738..24d9bf8193 100644
--- a/toolkit/components/satchel/nsIFormFillController.idl
+++ b/toolkit/components/satchel/nsIFormFillController.idl
@@ -5,6 +5,7 @@
#include "nsISupports.idl"
interface nsIAutoCompletePopup;
+interface nsIAutoCompleteResult;
webidl Document;
webidl Element;
@@ -67,3 +68,16 @@ interface nsIFormFillController : nsISupports
*/
[can_run_script] void showPopup();
};
+
+[scriptable, function, uuid(604419ab-55a0-4831-9eca-1b9e67cc4751)]
+interface nsIFormFillCompleteObserver : nsISupports
+{
+ /*
+ * Called when a search is complete and the results are ready even if the
+ * result set is empty. If the search is cancelled or a new search is
+ * started, this is not called.
+ *
+ * @param result - The search result object
+ */
+ [can_run_script] void onSearchCompletion(in nsIAutoCompleteResult result);
+};
diff --git a/toolkit/components/satchel/nsIFormHistoryAutoComplete.idl b/toolkit/components/satchel/nsIFormHistoryAutoComplete.idl
new file mode 100644
index 0000000000..279b09f51e
--- /dev/null
+++ b/toolkit/components/satchel/nsIFormHistoryAutoComplete.idl
@@ -0,0 +1,31 @@
+/* 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/. */
+
+
+#include "nsISupports.idl"
+
+interface nsIAutoCompleteResult;
+interface nsIFormFillCompleteObserver;
+interface nsIPropertyBag2;
+
+webidl HTMLInputElement;
+
+[scriptable, uuid(bfd9b82b-0ab3-4b6b-9e54-aa961ff4b732)]
+interface nsIFormHistoryAutoComplete: nsISupports {
+ /**
+ * Generate results for a form input autocomplete menu asynchronously.
+ */
+ void autoCompleteSearchAsync(in AString aInputName,
+ in AString aSearchString,
+ in HTMLInputElement aField,
+ in nsIAutoCompleteResult aPreviousResult,
+ in boolean aAddDatalist,
+ in nsIFormFillCompleteObserver aListener);
+
+ /**
+ * If a search is in progress, stop it. Otherwise, do nothing. This is used
+ * to cancel an existing search, for example, in preparation for a new search.
+ */
+ void stopAutoCompleteSearch();
+};
diff --git a/toolkit/components/satchel/test/test_capture_limit.html b/toolkit/components/satchel/test/test_capture_limit.html
index 8591544016..61b0a98418 100644
--- a/toolkit/components/satchel/test/test_capture_limit.html
+++ b/toolkit/components/satchel/test/test_capture_limit.html
@@ -21,7 +21,7 @@ add_setup(async () => {
});
add_task(async function captureLimit() {
- // Capture no more than 100 fields per submit. See FormHistoryChild.jsm.
+ // Capture no more than 100 fields per submit. See FormHistoryChild.sys.mjs.
const inputsCount = 100 + 2;
const form = document.getElementById("form1");
for (let i = 1; i <= inputsCount; i++) {
diff --git a/toolkit/components/satchel/test/unit/test_autocomplete.js b/toolkit/components/satchel/test/unit/test_autocomplete.js
index 13f66eb0f2..e64d34ea50 100644
--- a/toolkit/components/satchel/test/unit/test_autocomplete.js
+++ b/toolkit/components/satchel/test/unit/test_autocomplete.js
@@ -39,8 +39,8 @@ function run_test() {
testfile.copyTo(profileDir, "formhistory.sqlite");
- fac = Cc["@mozilla.org/satchel/form-autocomplete;1"].getService(
- Ci.nsIFormAutoComplete
+ fac = Cc["@mozilla.org/satchel/form-history-autocomplete;1"].getService(
+ Ci.nsIFormHistoryAutoComplete
);
timeGroupingSize =
diff --git a/toolkit/components/search/.eslintrc.js b/toolkit/components/search/.eslintrc.js
deleted file mode 100644
index 9aafb4a214..0000000000
--- a/toolkit/components/search/.eslintrc.js
+++ /dev/null
@@ -1,9 +0,0 @@
-/* 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/. */
-
-"use strict";
-
-module.exports = {
- extends: ["plugin:mozilla/require-jsdoc"],
-};
diff --git a/toolkit/components/search/AppProvidedSearchEngine.sys.mjs b/toolkit/components/search/AppProvidedSearchEngine.sys.mjs
index 7401ba115c..ed815b96d1 100644
--- a/toolkit/components/search/AppProvidedSearchEngine.sys.mjs
+++ b/toolkit/components/search/AppProvidedSearchEngine.sys.mjs
@@ -9,6 +9,8 @@ import {
EngineURL,
} from "resource://gre/modules/SearchEngine.sys.mjs";
+import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
+
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
@@ -16,14 +18,56 @@ ChromeUtils.defineESModuleGetters(lazy, {
SearchUtils: "resource://gre/modules/SearchUtils.sys.mjs",
});
+XPCOMUtils.defineLazyServiceGetter(
+ lazy,
+ "idleService",
+ "@mozilla.org/widget/useridleservice;1",
+ "nsIUserIdleService"
+);
+
+// After the user has been idle for 30s, we'll update icons if we need to.
+const ICON_UPDATE_ON_IDLE_DELAY = 30;
+
/**
* Handles loading application provided search engine icons from remote settings.
*/
class IconHandler {
- #iconList = null;
+ /**
+ * The remote settings client for the search engine icons.
+ *
+ * @type {?RemoteSettingsClient}
+ */
#iconCollection = null;
/**
+ * The list of icon records from the remote settings collection.
+ *
+ * @type {?object[]}
+ */
+ #iconList = null;
+
+ /**
+ * A flag that indicates if we have queued an idle observer to update icons.
+ *
+ * @type {boolean}
+ */
+ #queuedIdle = false;
+
+ /**
+ * A map of pending updates that need to be applied to the engines. This is
+ * keyed via record id, so that if multiple updates are queued for the same
+ * record, then we will only update the engine once.
+ *
+ * @type {Map<string, object>}
+ */
+ #pendingUpdatesMap = new Map();
+
+ constructor() {
+ this.#iconCollection = lazy.RemoteSettings("search-config-icons");
+ this.#iconCollection.on("sync", this._onIconListUpdated.bind(this));
+ }
+
+ /**
* Returns the icon for the record that matches the engine identifier
* and the preferred width.
*
@@ -40,14 +84,9 @@ class IconHandler {
await this.#getIconList();
}
- let iconRecords = this.#iconList.filter(r => {
- return r.engineIdentifiers.some(i => {
- if (i.endsWith("*")) {
- return engineIdentifier.startsWith(i.slice(0, -1));
- }
- return engineIdentifier == i;
- });
- });
+ let iconRecords = this.#iconList.filter(r =>
+ this._identifierMatches(engineIdentifier, r.engineIdentifiers)
+ );
if (!iconRecords.length) {
console.warn("No icon found for", engineIdentifier);
@@ -66,28 +105,110 @@ class IconHandler {
}
}
- let iconURL;
+ let iconData;
try {
- iconURL = await this.#iconCollection.attachments.get(iconRecord);
+ iconData = await this.#iconCollection.attachments.get(iconRecord);
} catch (ex) {
console.error(ex);
- return null;
}
- if (!iconURL) {
+ if (!iconData) {
console.warn("Unable to find the icon for", engineIdentifier);
+ // Queue an update in case we haven't downloaded it yet.
+ this.#pendingUpdatesMap.set(iconRecord.id, iconRecord);
+ this.#maybeQueueIdle();
return null;
}
+
+ if (iconData.record.last_modified != iconRecord.last_modified) {
+ // The icon we have stored is out of date, queue an update so that we'll
+ // download the new icon.
+ this.#pendingUpdatesMap.set(iconRecord.id, iconRecord);
+ this.#maybeQueueIdle();
+ }
return URL.createObjectURL(
- new Blob([iconURL.buffer]),
+ new Blob([iconData.buffer]),
iconRecord.attachment.mimetype
);
}
+ QueryInterface = ChromeUtils.generateQI(["nsIObserver"]);
+
+ /**
+ * Called when there is an update queued and the user has been observed to be
+ * idle for ICON_UPDATE_ON_IDLE_DELAY seconds.
+ *
+ * This will always download new icons (added or updated), even if there is
+ * no current engine that matches the identifiers. This is to ensure that we
+ * have pre-populated the cache if the engine is added later for this user.
+ *
+ * We do not handle deletes, as remote settings will handle the cleanup of
+ * removed records. We also do not expect the case where an icon is removed
+ * for an active engine.
+ *
+ * @param {nsISupports} subject
+ * The subject of the observer.
+ * @param {string} topic
+ * The topic of the observer.
+ */
+ async observe(subject, topic) {
+ if (topic != "idle") {
+ return;
+ }
+
+ this.#queuedIdle = false;
+ lazy.idleService.removeIdleObserver(this, ICON_UPDATE_ON_IDLE_DELAY);
+
+ // Update the icon list, in case engines will call getIcon() again.
+ await this.#getIconList();
+
+ let appProvidedEngines = await Services.search.getAppProvidedEngines();
+ for (let record of this.#pendingUpdatesMap.values()) {
+ let iconData;
+ try {
+ iconData = await this.#iconCollection.attachments.download(record);
+ } catch (ex) {
+ console.error("Could not download new icon", ex);
+ continue;
+ }
+
+ for (let engine of appProvidedEngines) {
+ await engine.maybeUpdateIconURL(
+ record.engineIdentifiers,
+ URL.createObjectURL(
+ new Blob([iconData.buffer]),
+ record.attachment.mimetype
+ )
+ );
+ }
+ }
+
+ this.#pendingUpdatesMap.clear();
+ }
+
+ /**
+ * Checks if the identifier matches any of the engine identifiers.
+ *
+ * @param {string} identifier
+ * The identifier of the engine.
+ * @param {string[]} engineIdentifiers
+ * The list of engine identifiers to match against. This can include
+ * wildcards at the end of strings.
+ * @returns {boolean}
+ * Returns true if the identifier matches any of the engine identifiers.
+ */
+ _identifierMatches(identifier, engineIdentifiers) {
+ return engineIdentifiers.some(i => {
+ if (i.endsWith("*")) {
+ return identifier.startsWith(i.slice(0, -1));
+ }
+ return identifier == i;
+ });
+ }
+
/**
* Obtains the icon list from the remote settings collection.
*/
async #getIconList() {
- this.#iconCollection = lazy.RemoteSettings("search-config-icons");
try {
this.#iconList = await this.#iconCollection.get();
} catch (ex) {
@@ -98,6 +219,41 @@ class IconHandler {
console.error("Failed to obtain search engine icon list records");
}
}
+
+ /**
+ * Called via a callback when remote settings updates the icon list. This
+ * stores potential updates and queues an idle observer to apply them.
+ *
+ * @param {object} payload
+ * The payload from the remote settings collection.
+ * @param {object} payload.data
+ * The payload data from the remote settings collection.
+ * @param {object[]} payload.data.created
+ * The list of created records.
+ * @param {object[]} payload.data.updated
+ * The list of updated records.
+ */
+ async _onIconListUpdated({ data: { created, updated } }) {
+ created.forEach(record => {
+ this.#pendingUpdatesMap.set(record.id, record);
+ });
+ for (let record of updated) {
+ if (record.new) {
+ this.#pendingUpdatesMap.set(record.new.id, record.new);
+ }
+ }
+ this.#maybeQueueIdle();
+ }
+
+ /**
+ * Queues an idle observer if there are pending updates.
+ */
+ #maybeQueueIdle() {
+ if (this.#pendingUpdatesMap && !this.#queuedIdle) {
+ this.#queuedIdle = true;
+ lazy.idleService.addIdleObserver(this, ICON_UPDATE_ON_IDLE_DELAY);
+ }
+ }
}
/**
@@ -113,19 +269,28 @@ export class AppProvidedSearchEngine extends SearchEngine {
static iconHandler = new IconHandler();
/**
- * @typedef {?Promise<string>}
- * A promise for the blob URL of the icon. We save the promise to avoid
- * reentrancy issues.
+ * A promise for the blob URL of the icon. We save the promise to avoid
+ * reentrancy issues.
+ *
+ * @type {?Promise<string>}
*/
#blobURLPromise = null;
/**
- * @typedef {?string}
- * The identifier from the configuration.
+ * The identifier from the configuration.
+ *
+ * @type {?string}
*/
#configurationId = null;
/**
+ * Whether or not this is a general purpose search engine.
+ *
+ * @type {boolean}
+ */
+ #isGeneralPurposeSearchEngine = false;
+
+ /**
* @param {object} options
* The options for this search engine.
* @param {object} options.config
@@ -231,11 +396,15 @@ export class AppProvidedSearchEngine extends SearchEngine {
return true;
}
+ /**
+ * Whether or not this engine is a "general" search engine, e.g. is it for
+ * generally searching the web, or does it have a specific purpose like
+ * shopping.
+ *
+ * @returns {boolean}
+ */
get isGeneralPurposeEngine() {
- return !!(
- this._extensionID &&
- lazy.SearchUtils.GENERAL_SEARCH_ENGINE_IDS.has(this._extensionID)
- );
+ return this.#isGeneralPurposeSearchEngine;
}
/**
@@ -258,6 +427,36 @@ export class AppProvidedSearchEngine extends SearchEngine {
}
/**
+ * This will update the icon URL for the search engine if the engine
+ * identifier matches the given engine identifiers.
+ *
+ * @param {string[]} engineIdentifiers
+ * The engine identifiers to check against.
+ * @param {string} blobURL
+ * The new icon URL for the search engine.
+ */
+ async maybeUpdateIconURL(engineIdentifiers, blobURL) {
+ // TODO: Bug 1875912. Once newSearchConfigEnabled has been enabled, we will
+ // be able to use `this.id` instead of `this.#configurationId`. At that
+ // point, `IconHandler._identifierMatches` can be made into a private
+ // function, as this if statement can be handled within `IconHandler.observe`.
+ if (
+ !AppProvidedSearchEngine.iconHandler._identifierMatches(
+ this.#configurationId,
+ engineIdentifiers
+ )
+ ) {
+ return;
+ }
+ if (this.#blobURLPromise) {
+ URL.revokeObjectURL(await this.#blobURLPromise);
+ this.#blobURLPromise = null;
+ }
+ this.#blobURLPromise = Promise.resolve(blobURL);
+ lazy.SearchUtils.notifyAction(this, lazy.SearchUtils.MODIFIED_TYPE.CHANGED);
+ }
+
+ /**
* Creates a JavaScript object that represents this engine.
*
* @returns {object}
@@ -283,6 +482,12 @@ export class AppProvidedSearchEngine extends SearchEngine {
#init(engineConfig) {
this._orderHint = engineConfig.orderHint;
this._telemetryId = engineConfig.identifier;
+ this.#isGeneralPurposeSearchEngine =
+ engineConfig.classification == "general";
+
+ if (engineConfig.charset) {
+ this._queryCharset = engineConfig.charset;
+ }
if (engineConfig.telemetrySuffix) {
this._telemetryId += `-${engineConfig.telemetrySuffix}`;
diff --git a/toolkit/components/search/SearchEngineSelector.sys.mjs b/toolkit/components/search/SearchEngineSelector.sys.mjs
index 0c9fb7cb70..acc1044ced 100644
--- a/toolkit/components/search/SearchEngineSelector.sys.mjs
+++ b/toolkit/components/search/SearchEngineSelector.sys.mjs
@@ -261,20 +261,24 @@ export class SearchEngineSelector {
continue;
}
- let variants =
- config.variants?.filter(variant =>
- this.#matchesUserEnvironment(variant, userEnv)
- ) ?? [];
+ let variant = config.variants?.findLast(variant =>
+ this.#matchesUserEnvironment(variant, userEnv)
+ );
- if (!variants.length) {
+ if (!variant) {
continue;
}
+ let subVariant = variant.subVariants?.findLast(subVariant =>
+ this.#matchesUserEnvironment(subVariant, userEnv)
+ );
+
let engine = structuredClone(config.base);
engine.identifier = config.identifier;
+ engine = this.#deepCopyObject(engine, variant);
- for (let variant of variants) {
- engine = this.#deepCopyObject(engine, variant);
+ if (subVariant) {
+ engine = this.#deepCopyObject(engine, subVariant);
}
for (let override of this._configurationOverrides) {
@@ -359,6 +363,10 @@ export class SearchEngineSelector {
continue;
}
+ if (["subVariants"].includes(key)) {
+ continue;
+ }
+
if (typeof source[key] == "object" && !Array.isArray(source[key])) {
if (key in target) {
this.#deepCopyObject(target[key], source[key]);
@@ -431,14 +439,15 @@ export class SearchEngineSelector {
user.version
) &&
this.#matchesChannel(config.environment.channels, user.channel) &&
- this.#matchesApplication(config.environment.applications, user.appName)
+ this.#matchesApplication(config.environment.applications, user.appName) &&
+ !this.#hasDeviceType(config.environment)
);
}
/**
* @param {string} userDistro
* The distribution from the user's environment.
- * @param {Array} configDistro
+ * @param {string[]} configDistro
* An array of distributions for the particular environment in the config.
* @returns {boolean}
* True if the user's distribution is included in the config distribution
@@ -497,7 +506,7 @@ export class SearchEngineSelector {
}
/**
- * @param {Array} configChannels
+ * @param {string[]} configChannels
* Release channels such as nightly, beta, release, esr.
* @param {string} userChannel
* The user's channel.
@@ -514,7 +523,7 @@ export class SearchEngineSelector {
}
/**
- * @param {Array} configApps
+ * @param {string[]} configApps
* The applications such as firefox, firefox-android, firefox-ios,
* focus-android, and focus-ios.
* @param {string} userApp
@@ -532,6 +541,21 @@ export class SearchEngineSelector {
}
/**
+ * Generally the device type option should only be used when the application
+ * is selected to be on an android or iOS based product. However, we support
+ * rejecting if this is non-empty in case of future requirements that we haven't
+ * predicted.
+ *
+ * @param {object} environment
+ * An environment section from the engine configuration.
+ * @returns {boolean}
+ * Returns true if there is a device type section and it is not empty.
+ */
+ #hasDeviceType(environment) {
+ return !!environment.deviceType?.length;
+ }
+
+ /**
* Determines whether the region and locale constraints in the config
* environment applies to a user given what region and locale they are using.
*
diff --git a/toolkit/components/search/SearchService.sys.mjs b/toolkit/components/search/SearchService.sys.mjs
index b9de8e0bb3..a645cc05e8 100644
--- a/toolkit/components/search/SearchService.sys.mjs
+++ b/toolkit/components/search/SearchService.sys.mjs
@@ -173,7 +173,7 @@ class ParseSubmissionResult {
/**
* String containing the sought terms. This can be an empty string in case no
- * terms were specified or the URL does not represent a search submission.*
+ * terms were specified or the URL does not represent a search submission.
*
* @type {string}
*/
@@ -967,6 +967,10 @@ export class SearchService {
);
}
+ getAlternateDomains(domain) {
+ return lazy.SearchStaticData.getAlternateDomains(domain);
+ }
+
/**
* This is a nsITimerCallback for the timerManager notification that is
* registered for handling updates to search engines. Only OpenSearch engines
@@ -991,9 +995,10 @@ export class SearchService {
/**
* A deferred promise that is resolved when initialization has finished.
*
+ * Resolved when initalization has successfully finished, and rejected if it
+ * has failed.
+ *
* @type {Promise}
- * Resolved when initalization has successfully finished, and rejected if it
- * has failed.
*/
#initDeferredPromise = Promise.withResolvers();
@@ -1078,10 +1083,10 @@ export class SearchService {
* engine, as suggested by the configuration.
* For the legacy configuration, this is the user visible name.
*
- * @type {object}
- *
* This is prefixed with _ rather than # because it is
* called in a test.
+ *
+ * @type {object}
*/
_searchDefault = null;
@@ -1113,6 +1118,16 @@ export class SearchService {
#startupRemovedExtensions = new Set();
/**
+ * Used in #parseSubmissionMap
+ *
+ * @typedef {object} submissionMapEntry
+ * @property {nsISearchEngine} engine
+ * The search engine.
+ * @property {string} termsParameterName
+ * The search term parameter name.
+ */
+
+ /**
* This map is built lazily after the available search engines change. It
* allows quick parsing of an URL representing a search submission into the
* search engine name and original terms.
@@ -1120,12 +1135,7 @@ export class SearchService {
* The keys are strings containing the domain name and lowercase path of the
* engine submission, for example "www.google.com/search".
*
- * The values are objects with these properties:
- * {
- * engine: The associated nsISearchEngine.
- * termsParameterName: Name of the URL parameter containing the search
- * terms, for example "q".
- * }
+ * @type {Map<string, submissionMapEntry>|null}
*/
#parseSubmissionMap = null;
diff --git a/toolkit/components/search/SearchSettings.sys.mjs b/toolkit/components/search/SearchSettings.sys.mjs
index fed0dd1808..e355316595 100644
--- a/toolkit/components/search/SearchSettings.sys.mjs
+++ b/toolkit/components/search/SearchSettings.sys.mjs
@@ -94,11 +94,14 @@ export class SearchSettings {
#settings = null;
/**
- * @type {object} A deep copy of #settings.
- * #cachedSettings is updated when we read the settings from disk and when
- * we write settings to disk. #cachedSettings is compared with #settings
- * before we do a write to disk. If there's no change to the settings
- * attributes, then we don't write the settings to disk.
+ * #cachedSettings is updated when we read the settings from disk and when
+ * we write settings to disk. #cachedSettings is compared with #settings
+ * before we do a write to disk. If there's no change to the settings
+ * attributes, then we don't write the settings to disk.
+ *
+ * This is a deep copy of #settings.
+ *
+ * @type {object}
*/
#cachedSettings = {};
diff --git a/toolkit/components/search/SearchSuggestions.sys.mjs b/toolkit/components/search/SearchSuggestions.sys.mjs
index 32cd25a0cd..4a43975576 100644
--- a/toolkit/components/search/SearchSuggestions.sys.mjs
+++ b/toolkit/components/search/SearchSuggestions.sys.mjs
@@ -4,8 +4,9 @@
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
- FormAutoCompleteResult: "resource://gre/modules/FormAutoComplete.sys.mjs",
- FormHistoryClient: "resource://gre/modules/FormAutoComplete.sys.mjs",
+ FormHistoryAutoCompleteResult:
+ "resource://gre/modules/FormHistoryAutoComplete.sys.mjs",
+ FormHistoryClient: "resource://gre/modules/FormHistoryAutoComplete.sys.mjs",
SearchSuggestionController:
"resource://gre/modules/SearchSuggestionController.sys.mjs",
@@ -29,7 +30,7 @@ class SuggestAutoComplete {
/**
* Notifies the front end of new results.
*
- * @param {FormAutoCompleteResult} result
+ * @param {FormHistoryAutoCompleteResult} result
* Any previous form history result.
* @private
*/
@@ -168,7 +169,7 @@ class SuggestAutoComplete {
...historyEntry,
})
);
- let autoCompleteResult = new lazy.FormAutoCompleteResult(
+ let autoCompleteResult = new lazy.FormHistoryAutoCompleteResult(
client,
formHistoryEntries,
this.#suggestionController.formHistoryParam,
diff --git a/toolkit/components/search/SearchUtils.sys.mjs b/toolkit/components/search/SearchUtils.sys.mjs
index 27c8f8ad03..1262bb28b1 100644
--- a/toolkit/components/search/SearchUtils.sys.mjs
+++ b/toolkit/components/search/SearchUtils.sys.mjs
@@ -434,12 +434,12 @@ XPCOMUtils.defineLazyPreferenceGetter(
false
);
-XPCOMUtils.defineLazyPreferenceGetter(
- SearchUtils,
- "newSearchConfigEnabled",
- "browser.search.newSearchConfig.enabled",
- false
-);
+ChromeUtils.defineLazyGetter(SearchUtils, "newSearchConfigEnabled", () => {
+ return Services.prefs.getBoolPref(
+ "browser.search.newSearchConfig.enabled",
+ false
+ );
+});
// Can't use defineLazyPreferenceGetter because we want the value
// from the default branch
diff --git a/toolkit/components/search/docs/SearchEngineConfiguration.rst b/toolkit/components/search/docs/SearchEngineConfiguration.rst
index c782f9f7c3..ca6009a0ef 100644
--- a/toolkit/components/search/docs/SearchEngineConfiguration.rst
+++ b/toolkit/components/search/docs/SearchEngineConfiguration.rst
@@ -9,19 +9,23 @@ user's region and locale.
Configuration Management
========================
-The application stores a dump of the configuration that is used for first
-initialisation. Subsequent updates to the configuration are either updates to the
-static dump, or they may be served via remote servers.
+The configuration is delivered and managed via `remote settings`_. There are
+:searchfox:`dumps <services/settings/dumps/main/>` of the configuration
+that are shipped with the application, for use on first startup of a fresh profile,
+or when a client has not been able to receive remote settings updates for
+whatever reason.
-The mechanism of delivering the settings dumps to the Search Service is
-`the remote settings`_.
-
-Remote settings
----------------
+Remote Settings Bucket
+----------------------
The remote settings bucket for the search engine configuration list is
-``search-config``. The version that is currently being delivered
-to clients can be `viewed live`_.
+``search-config-v2``. The version that is currently being delivered
+to clients can be `viewed live`_. There are additional remote settings buckets
+with information for each search engine. These buckets are listed below.
+
+- `search-config-icons`_ is a mapping of icons to a search engine.
+- `search-config-overrides-v2`_ contains information that may override engines
+ properties in search-config-v2.
Configuration Schema
====================
@@ -30,43 +34,13 @@ The configuration format is defined via a `JSON schema`_. The search engine
configuration schema is `stored in mozilla-central`_ and is uploaded to the
Remote Settings server at convenient times after it changes.
-An outline of the schema may be found on the `Search Configuration Schema`_ page.
-
-Updating Search Engine WebExtensions
-====================================
-
-Updates for application provided search engine WebExtensions are provided via
-`Normandy`_.
-
-It is likely that updates for search engine WebExtensions will be
-received separately to configuration updates which may or may not be directly
-related. As a result several situations may occur:
-
- - The updated WebExtension is for an app-provided engine already in-use by
- the user.
-
- - In this case, the search service will apply the changes to the
- app-provided engine's data.
-
- - A WebExtension addition/update is for an app-provided engine that is not
- in-use by the user, or not in the configuration.
-
- - In this case, the search service will ignore the WebExtension.
- - If the configuration (search or user) is updated later and the
- new engine is added, then the Search Service will start to use the
- new engine.
-
- - A configuration update is received that needs a WebExtension that is
- not found locally.
-
- - In this case, the search service will ignore the missing engine and
- continue without it.
- - When the WebExtension is delivered, the search engine will then be
- installed and added.
+An outline of the schemas may be found on the `Search Configuration Schema`_ page.
-.. _the remote settings: /services/settings/index.html
+.. _remote settings: /services/settings/index.html
.. _JSON schema: https://json-schema.org/
.. _stored in mozilla-central: https://searchfox.org/mozilla-central/source/toolkit/components/search/schema/
.. _Search Configuration Schema: SearchConfigurationSchema.html
-.. _viewed live: https://firefox.settings.services.mozilla.com/v1/buckets/main/collections/search-config/records
-.. _Normandy: /toolkit/components/normandy/normandy/services.html
+.. _viewed live: https://firefox.settings.services.mozilla.com/v1/buckets/main/collections/search-config-v2/records
+.. _search-config-icons: https://firefox.settings.services.mozilla.com/v1/buckets/main/collections/search-config-icons/records
+.. _search-config-overrides-v2: https://firefox.settings.services.mozilla.com/v1/buckets/main/collections/search-config-overrides-v2/records
+.. _search-default-override-allowlist: https://firefox.settings.services.mozilla.com/v1/buckets/main/collections/search-default-override-allowlist/records
diff --git a/toolkit/components/search/docs/SearchEngineConfigurationArchive.rst b/toolkit/components/search/docs/SearchEngineConfigurationArchive.rst
new file mode 100644
index 0000000000..a6aba91a42
--- /dev/null
+++ b/toolkit/components/search/docs/SearchEngineConfigurationArchive.rst
@@ -0,0 +1,72 @@
+======================================
+Search Engine Configuration (Archived)
+======================================
+
+The search engine configuration is a mapping that is used to determine the
+list of search engines for each user. The mapping is primarily based on the
+user's region and locale.
+
+Configuration Management
+========================
+
+The application stores a dump of the configuration that is used for first
+initialisation. Subsequent updates to the configuration are either updates to the
+static dump, or they may be served via remote servers.
+
+The mechanism of delivering the settings dumps to the Search Service is
+`the remote settings`_.
+
+Remote settings
+---------------
+
+The remote settings bucket for the search engine configuration list is
+``search-config``. The version that is currently being delivered
+to clients can be `viewed live`_.
+
+Configuration Schema
+====================
+
+The configuration format is defined via a `JSON schema`_. The search engine
+configuration schema is `stored in mozilla-central`_ and is uploaded to the
+Remote Settings server at convenient times after it changes.
+
+An outline of the schema may be found on the `Search Configuration Schema`_ page.
+
+Updating Search Engine WebExtensions
+====================================
+
+Updates for application provided search engine WebExtensions are provided via
+`Normandy`_.
+
+It is likely that updates for search engine WebExtensions will be
+received separately to configuration updates which may or may not be directly
+related. As a result several situations may occur:
+
+ - The updated WebExtension is for an app-provided engine already in-use by
+ the user.
+
+ - In this case, the search service will apply the changes to the
+ app-provided engine's data.
+
+ - A WebExtension addition/update is for an app-provided engine that is not
+ in-use by the user, or not in the configuration.
+
+ - In this case, the search service will ignore the WebExtension.
+ - If the configuration (search or user) is updated later and the
+ new engine is added, then the Search Service will start to use the
+ new engine.
+
+ - A configuration update is received that needs a WebExtension that is
+ not found locally.
+
+ - In this case, the search service will ignore the missing engine and
+ continue without it.
+ - When the WebExtension is delivered, the search engine will then be
+ installed and added.
+
+.. _the remote settings: /services/settings/index.html
+.. _JSON schema: https://json-schema.org/
+.. _stored in mozilla-central: https://searchfox.org/mozilla-central/source/toolkit/components/search/schema/
+.. _Search Configuration Schema: SearchConfigurationSchema.html
+.. _viewed live: https://firefox.settings.services.mozilla.com/v1/buckets/main/collections/search-config/records
+.. _Normandy: /toolkit/components/normandy/normandy/services.html
diff --git a/toolkit/components/search/docs/index.rst b/toolkit/components/search/docs/index.rst
index d8d4bc8d8f..10ae8118bf 100644
--- a/toolkit/components/search/docs/index.rst
+++ b/toolkit/components/search/docs/index.rst
@@ -32,6 +32,14 @@ Contents
Preferences
Telemetry
+Contents for search-config (archived)
+=====================================
+
+.. toctree::
+ :maxdepth: 2
+
+ SearchEngineConfigurationArchive
+
API Reference
-------------
diff --git a/toolkit/components/search/nsISearchService.idl b/toolkit/components/search/nsISearchService.idl
index 4421c25974..b59e9a9399 100644
--- a/toolkit/components/search/nsISearchService.idl
+++ b/toolkit/components/search/nsISearchService.idl
@@ -290,13 +290,13 @@ interface nsISearchService : nsISupports
* @return |true| if the search service is now initialized, |false| if
* initialization has not been triggered yet.
*/
- readonly attribute bool isInitialized;
+ readonly attribute boolean isInitialized;
/**
* Determine whether initialization has been completed successfully.
*
*/
- readonly attribute bool hasSuccessfullyInitialized;
+ readonly attribute boolean hasSuccessfullyInitialized;
/**
@@ -548,4 +548,14 @@ interface nsISearchService : nsISupports
* "https://www.google.com/search?q=terms".
*/
nsISearchParseSubmissionResult parseSubmissionURL(in AString url);
+
+ /**
+ * Returns a list of alternate domains for a given search engine domain.
+ *
+ * @param domain
+ * The domain of the search engine.
+ * @returns {Array}
+ * An array which contains all alternate domains.
+ */
+ Array<ACString> getAlternateDomains(in ACString domain);
};
diff --git a/toolkit/components/search/schema/search-config-v2-schema.json b/toolkit/components/search/schema/search-config-v2-schema.json
index cfd0124fa3..7b335ed2a6 100644
--- a/toolkit/components/search/schema/search-config-v2-schema.json
+++ b/toolkit/components/search/schema/search-config-v2-schema.json
@@ -102,13 +102,12 @@
},
"applications": {
"title": "Application Identifiers",
- "description": "The application(s) this section applies to (default/not specified is everywhere).",
+ "description": "The application(s) this section applies to (default/not specified is everywhere). `firefox` relates to Firefox Desktop.",
"type": "array",
"items": {
"type": "string",
"pattern": "^[a-z-]{0,100}$",
"enum": [
- "",
"firefox",
"firefox-android",
"firefox-ios",
@@ -129,12 +128,23 @@
"description": "The maximum application version this section applies to (less-than comparison).",
"type": "string",
"pattern": "^[0-9a-z.]{0,20}$"
+ },
+ "deviceType": {
+ "title": "Device Type",
+ "description": "The device type(s) this section applies to. On desktop when this property is specified and non-empty, the associated section will be ignored.",
+ "type": "array",
+ "items": {
+ "type": "string",
+ "pattern": "^[a-z-]{0,100}$",
+ "enum": ["smartphone", "tablet"]
+ },
+ "uniqueItems": true
}
}
},
"partnerCode": {
"title": "Partner Code",
- "description": "The partner code for the engine or variant. This will be inserted into parameters which include '{pc}'",
+ "description": "The partner code for the engine or variant. This will be inserted into parameters which include '{partnerCode}'",
"type": "string",
"pattern": "^[a-zA-Z0-9-_]*$"
},
@@ -191,7 +201,7 @@
},
"value": {
"title": "Value",
- "description": "The parameter value, this may be a static value, or additionally contain a parameter replacement, e.g. {inputEncoding}. For the partner code parameter, this field should be {pc}.",
+ "description": "The parameter value, this may be a static value, or additionally contain a parameter replacement, e.g. {inputEncoding}. For the partner code parameter, this field should be {partnerCode}.",
"type": "string",
"pattern": "^[a-zA-Z0-9-_{}]*$"
},
@@ -327,7 +337,7 @@
},
"variants": {
"title": "Variants",
- "description": "This section describes variations of this search engine that may occur depending on the user's environment. If multiple sections match a user's environment, then all matching sections are applied cumulatively in the order in the array.",
+ "description": "This section describes variations of this search engine that may occur depending on the user's environment. The last variant that matches the user's environment will be applied to the engine, subvariants may also be applied.",
"type": "array",
"items": {
"type": "object",
@@ -345,18 +355,46 @@
},
"telemetrySuffix": {
"title": "Telemetry Suffix",
- "description": "Suffix that is appended to the search engine identifier following a dash, i.e. `<identifier>-<suffix>`. There should always be a suffix supplied if the partner code is different.",
+ "description": "Suffix that is appended to the search engine identifier following a dash, i.e. `<identifier>-<suffix>`. There should always be a suffix supplied if the partner code is different for a reason other than being on a different platform.",
"type": "string",
"pattern": "^[a-zA-Z0-9-]*$"
},
"urls": {
"$ref": "#/definitions/urls"
+ },
+ "subVariants": {
+ "title": "Subvariants",
+ "description": "This section describes subvariations of this search engine that may occur depending on the user's environment. The last subvariant that matches the user's environment will be applied to the engine.",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "environment": {
+ "$ref": "#/definitions/environment"
+ },
+ "partnerCode": {
+ "$ref": "#/definitions/partnerCode"
+ },
+ "optional": {
+ "title": "Optional",
+ "description": "This search engine is presented as an option that the user may enable. It is not included in the initial list of search engines. If not specified, defaults to false.",
+ "type": "boolean"
+ },
+ "telemetrySuffix": {
+ "title": "Telemetry Suffix",
+ "description": "Suffix that is appended to the search engine identifier following a dash, i.e. `<identifier>-<suffix>`. There should always be a suffix supplied if the partner code is different for a reason other than being on a different platform.",
+ "type": "string",
+ "pattern": "^[a-zA-Z0-9-]*$"
+ },
+ "urls": {
+ "$ref": "#/definitions/urls"
+ }
+ },
+ "required": ["environment"]
+ }
}
},
- "required": ["environment"],
- "dependencies": {
- "partnerCode": ["telemetrySuffix"]
- }
+ "required": ["environment"]
}
}
},
diff --git a/toolkit/components/search/tests/xpcshell/data/search-config-v2.json b/toolkit/components/search/tests/xpcshell/data/search-config-v2.json
index 569e16dfe4..bca7cc3bdf 100644
--- a/toolkit/components/search/tests/xpcshell/data/search-config-v2.json
+++ b/toolkit/components/search/tests/xpcshell/data/search-config-v2.json
@@ -112,6 +112,7 @@
"recordType": "engine",
"identifier": "engine-resourceicon",
"base": {
+ "classification": "general",
"name": "engine-resourceicon",
"urls": {
"search": {
@@ -151,6 +152,7 @@
"recordType": "engine",
"identifier": "engine-reordered",
"base": {
+ "classification": "general",
"name": "Test search engine (Reordered)",
"urls": {
"search": {
diff --git a/toolkit/components/search/tests/xpcshell/data1/search-config-v2.json b/toolkit/components/search/tests/xpcshell/data1/search-config-v2.json
index 98bdfa26ff..ac5f7f77cd 100644
--- a/toolkit/components/search/tests/xpcshell/data1/search-config-v2.json
+++ b/toolkit/components/search/tests/xpcshell/data1/search-config-v2.json
@@ -9,7 +9,7 @@
"searchTermParamName": "q"
}
},
- "classification": "unknown"
+ "classification": "general"
},
"variants": [{ "environment": { "allRegionsAndLocales": true } }],
"identifier": "engine1",
@@ -24,7 +24,7 @@
"searchTermParamName": "q"
}
},
- "classification": "unknown"
+ "classification": "general"
},
"variants": [{ "environment": { "allRegionsAndLocales": true } }],
"identifier": "engine2",
@@ -39,7 +39,7 @@
"searchTermParamName": "q"
}
},
- "classification": "unknown"
+ "classification": "general"
},
"variants": [
{
@@ -58,7 +58,7 @@
"searchTermParamName": "q"
}
},
- "classification": "unknown"
+ "classification": "general"
},
"variants": [
{
diff --git a/toolkit/components/search/tests/xpcshell/head_search.js b/toolkit/components/search/tests/xpcshell/head_search.js
index 1c0504e277..72bc90185c 100644
--- a/toolkit/components/search/tests/xpcshell/head_search.js
+++ b/toolkit/components/search/tests/xpcshell/head_search.js
@@ -307,6 +307,101 @@ async function setupRemoteSettings() {
}
/**
+ * Reads the specified file from the data directory and returns its contents as
+ * an Uint8Array.
+ *
+ * @param {string} filename
+ * The name of the file to read.
+ * @returns {Promise<Uint8Array>}
+ * The contents of the file in an Uint8Array.
+ */
+async function getFileDataBuffer(filename) {
+ return IOUtils.read(PathUtils.join(do_get_cwd().path, "data", filename));
+}
+
+/**
+ * Creates a mock attachment record for use in remote settings related tests.
+ *
+ * @param {object} item
+ * An object containing the details of the attachment.
+ * @param {string} item.filename
+ * The name of the attachmnet file in the data directory.
+ * @param {string[]} item.engineIdentifiers
+ * The engine identifiers for the attachment.
+ * @param {number} item.imageSize
+ * The size of the image.
+ * @param {string} [item.id]
+ * The ID to use for the record. If not provided, a new UUID will be generated.
+ * @param {number} [item.lastModified]
+ * The last modified time for the record. Defaults to the current time.
+ */
+async function mockRecordWithAttachment({
+ filename,
+ engineIdentifiers,
+ imageSize,
+ id = Services.uuid.generateUUID().toString(),
+ lastModified = Date.now(),
+}) {
+ let buffer = await getFileDataBuffer(filename);
+
+ // Generate a hash.
+ let hasher = Cc["@mozilla.org/security/hash;1"].createInstance(
+ Ci.nsICryptoHash
+ );
+ hasher.init(Ci.nsICryptoHash.SHA256);
+ hasher.update(buffer, buffer.length);
+
+ let hash = hasher.finish(false);
+ hash = Array.from(hash, (_, i) =>
+ ("0" + hash.charCodeAt(i).toString(16)).slice(-2)
+ ).join("");
+
+ let record = {
+ id,
+ engineIdentifiers,
+ imageSize,
+ attachment: {
+ hash,
+ location: `${filename}`,
+ filename,
+ size: buffer.byteLength,
+ mimetype: "application/json",
+ },
+ last_modified: lastModified,
+ };
+
+ let attachment = {
+ record,
+ blob: new Blob([buffer]),
+ };
+
+ return { record, attachment };
+}
+
+/**
+ * Inserts an attachment record into the remote settings collection.
+ *
+ * @param {RemoteSettingsClient} client
+ * The remote settings client to use.
+ * @param {object} item
+ * An object containing the details of the attachment - see mockRecordWithAttachment.
+ * @param {boolean} [addAttachmentToCache]
+ * Whether to add the attachment file to the cache. Defaults to true.
+ */
+async function insertRecordIntoCollection(
+ client,
+ item,
+ addAttachmentToCache = true
+) {
+ let { record, attachment } = await mockRecordWithAttachment(item);
+ await client.db.create(record);
+ if (addAttachmentToCache) {
+ await client.attachments.cacheImpl.set(record.id, attachment);
+ }
+ await client.db.importChanges({}, record.last_modified);
+}
+
+/**
* Helper function that sets up a server and respnds to region
* fetch requests.
*
diff --git a/toolkit/components/search/tests/xpcshell/searchconfigs/head_searchconfig.js b/toolkit/components/search/tests/xpcshell/searchconfigs/head_searchconfig.js
index 72c4d4f04f..c58ac1c25b 100644
--- a/toolkit/components/search/tests/xpcshell/searchconfigs/head_searchconfig.js
+++ b/toolkit/components/search/tests/xpcshell/searchconfigs/head_searchconfig.js
@@ -115,7 +115,7 @@ class SearchConfigTest {
async setup(version = "42.0") {
if (SearchUtils.newSearchConfigEnabled) {
updateAppInfo({
- name: "XPCShell",
+ name: "firefox",
ID: "xpcshell@tests.mozilla.org",
version,
platformVersion: version,
diff --git a/toolkit/components/search/tests/xpcshell/searchconfigs/test_qwant.js b/toolkit/components/search/tests/xpcshell/searchconfigs/test_qwant.js
index 4024385729..eab28a7ba9 100644
--- a/toolkit/components/search/tests/xpcshell/searchconfigs/test_qwant.js
+++ b/toolkit/components/search/tests/xpcshell/searchconfigs/test_qwant.js
@@ -14,6 +14,9 @@ const test = new SearchConfigTest({
{
locales: ["fr"],
},
+ {
+ regions: ["be", "ch", "es", "fr", "it", "nl"],
+ },
],
},
details: [
@@ -28,9 +31,30 @@ const test = new SearchConfigTest({
});
add_setup(async function () {
- await test.setup();
+ if (SearchUtils.newSearchConfigEnabled) {
+ await test.setup();
+ } else {
+ await test.setup("124.0");
+ }
});
add_task(async function test_searchConfig_qwant() {
await test.run();
});
+
+add_task(
+ { skip_if: () => SearchUtils.newSearchConfigEnabled },
+ async function test_searchConfig_qwant_pre124() {
+ const version = "123.0";
+ AddonTestUtils.createAppInfo(
+ "xpcshell@tests.mozilla.org",
+ "XPCShell",
+ version,
+ version
+ );
+ // For pre-124, Qwant is not available in the extra regions.
+ test._config.available.included.pop();
+
+ await test.run();
+ }
+);
diff --git a/toolkit/components/search/tests/xpcshell/searchconfigs/test_searchconfig_validates.js b/toolkit/components/search/tests/xpcshell/searchconfigs/test_searchconfig_validates.js
index 86686b62f7..f727d60719 100644
--- a/toolkit/components/search/tests/xpcshell/searchconfigs/test_searchconfig_validates.js
+++ b/toolkit/components/search/tests/xpcshell/searchconfigs/test_searchconfig_validates.js
@@ -172,6 +172,32 @@ add_task(
add_task(
{ skip_if: () => !SearchUtils.newSearchConfigEnabled },
+ async function test_search_config_valid_partner_codes() {
+ delete SearchUtils.newSearchConfigEnabled;
+ SearchUtils.newSearchConfigEnabled = true;
+
+ let selector = new SearchEngineSelector(() => {});
+
+ for (let entry of await selector.getEngineConfiguration()) {
+ if (entry.recordType == "engine") {
+ for (let variant of entry.variants) {
+ if (
+ "partnerCode" in variant &&
+ "distributions" in variant.environment
+ ) {
+ Assert.ok(
+ variant.telemetrySuffix,
+ `${entry.identifier} should have a telemetrySuffix when a distribution is specified with a partnerCode.`
+ );
+ }
+ }
+ }
+ }
+ }
+);
+
+add_task(
+ { skip_if: () => !SearchUtils.newSearchConfigEnabled },
async function test_search_config_override_validates_to_schema() {
let selector = new SearchEngineSelector(() => {});
diff --git a/toolkit/components/search/tests/xpcshell/test_appProvided_engine.js b/toolkit/components/search/tests/xpcshell/test_appProvided_engine.js
new file mode 100644
index 0000000000..56297a9d2c
--- /dev/null
+++ b/toolkit/components/search/tests/xpcshell/test_appProvided_engine.js
@@ -0,0 +1,182 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that ensure application provided engines have all base fields set up
+ * correctly from the search configuration.
+ */
+
+"use strict";
+
+let CONFIG = [
+ {
+ identifier: "testEngine",
+ recordType: "engine",
+ base: {
+ aliases: ["testEngine1", "testEngine2"],
+ charset: "EUC-JP",
+ classification: "general",
+ name: "testEngine name",
+ partnerCode: "pc",
+ urls: {
+ search: {
+ base: "https://example.com/1",
+ // Method defaults to GET
+ params: [
+ { name: "partnerCode", value: "{partnerCode}" },
+ { name: "starbase", value: "Regula I" },
+ { name: "experiment", value: "Genesis" },
+ {
+ name: "accessPoint",
+ searchAccessPoint: {
+ addressbar: "addressbar",
+ contextmenu: "contextmenu",
+ homepage: "homepage",
+ newtab: "newtab",
+ searchbar: "searchbar",
+ },
+ },
+ ],
+ searchTermParamName: "search",
+ },
+ suggestions: {
+ base: "https://example.com/2",
+ method: "POST",
+ searchTermParamName: "suggestions",
+ },
+ trending: {
+ base: "https://example.com/3",
+ searchTermParamName: "trending",
+ },
+ },
+ },
+ variants: [{ environment: { allRegionsAndLocales: true } }],
+ },
+ {
+ identifier: "testOtherValuesEngine",
+ recordType: "engine",
+ base: {
+ classification: "unknown",
+ name: "testOtherValuesEngine name",
+ urls: {
+ search: {
+ base: "https://example.com/1",
+ searchTermParamName: "search",
+ },
+ },
+ },
+ variants: [{ environment: { allRegionsAndLocales: true } }],
+ },
+ {
+ recordType: "defaultEngines",
+ globalDefault: "engine_no_initial_icon",
+ specificDefaults: [],
+ },
+ {
+ recordType: "engineOrders",
+ orders: [],
+ },
+];
+
+add_setup(async function () {
+ await SearchTestUtils.useTestEngines("simple-engines", null, CONFIG);
+ await Services.search.init();
+});
+
+add_task(async function test_engine_with_all_params_set() {
+ let engine = Services.search.getEngineById(
+ "testEngine@search.mozilla.orgdefault"
+ );
+ Assert.ok(engine, "Should have found the engine");
+
+ Assert.equal(
+ engine.name,
+ "testEngine name",
+ "Should have the correct engine name"
+ );
+ Assert.deepEqual(
+ engine.aliases,
+ ["@testEngine1", "@testEngine2"],
+ "Should have the correct aliases"
+ );
+ Assert.ok(
+ engine.isGeneralPurposeEngine,
+ "Should be a general purpose engine"
+ );
+ Assert.equal(
+ engine.wrappedJSObject.queryCharset,
+ "EUC-JP",
+ "Should have the correct encoding"
+ );
+
+ let submission = engine.getSubmission("test");
+ Assert.equal(
+ submission.uri.spec,
+ "https://example.com/1?partnerCode=pc&starbase=Regula%20I&experiment=Genesis&accessPoint=searchbar&search=test",
+ "Should have the correct search URL"
+ );
+ Assert.ok(!submission.postData, "Should not have postData for a GET url");
+
+ let suggestSubmission = engine.getSubmission(
+ "test",
+ SearchUtils.URL_TYPE.SUGGEST_JSON
+ );
+ Assert.equal(
+ suggestSubmission.uri.spec,
+ "https://example.com/2",
+ "Should have the correct suggestion URL"
+ );
+ Assert.equal(
+ suggestSubmission.postData.data.data,
+ "suggestions=test",
+ "Should have the correct postData for a POST URL"
+ );
+
+ let trendingSubmission = engine.getSubmission(
+ "test",
+ SearchUtils.URL_TYPE.TRENDING_JSON
+ );
+ Assert.equal(
+ trendingSubmission.uri.spec,
+ "https://example.com/3?trending=test"
+ );
+ Assert.ok(!submission.postData, "Should not have postData for a GET url");
+});
+
+add_task(async function test_engine_with_some_params_set() {
+ let engine = Services.search.getEngineById(
+ "testOtherValuesEngine@search.mozilla.orgdefault"
+ );
+ Assert.ok(engine, "Should have found the engine");
+
+ Assert.equal(
+ engine.name,
+ "testOtherValuesEngine name",
+ "Should have the correct engine name"
+ );
+ Assert.deepEqual(engine.aliases, [], "Should have no aliases");
+ Assert.ok(
+ !engine.isGeneralPurposeEngine,
+ "Should not be a general purpose engine"
+ );
+ Assert.equal(
+ engine.wrappedJSObject.queryCharset,
+ "UTF-8",
+ "Should default to UTF-8 charset"
+ );
+ Assert.equal(
+ engine.getSubmission("test").uri.spec,
+ "https://example.com/1?search=test",
+ "Should have the correct search URL"
+ );
+ Assert.equal(
+ engine.getSubmission("test", SearchUtils.URL_TYPE.SUGGEST_JSON),
+ null,
+ "Should not have a suggestions URL"
+ );
+ Assert.equal(
+ engine.getSubmission("test", SearchUtils.URL_TYPE.TRENDING_JSON),
+ null,
+ "Should not have a trending URL"
+ );
+});
diff --git a/toolkit/components/search/tests/xpcshell/test_appProvided_icons.js b/toolkit/components/search/tests/xpcshell/test_appProvided_icons.js
index e4d8033993..f6cc8b2415 100644
--- a/toolkit/components/search/tests/xpcshell/test_appProvided_icons.js
+++ b/toolkit/components/search/tests/xpcshell/test_appProvided_icons.js
@@ -53,7 +53,11 @@ let TESTS = [
icons: [
{
filename: "remoteIcon.ico",
- engineIdentifiers: ["engine_non_default_sized_icon"],
+ engineIdentifiers: [
+ // This also tests multiple engine idenifiers works.
+ "enterprise_shuttle",
+ "engine_non_default_sized_icon",
+ ],
imageSize: 32,
},
],
@@ -77,64 +81,6 @@ let TESTS = [
},
];
-async function getFileDataBuffer(filename) {
- let data = await IOUtils.read(
- PathUtils.join(do_get_cwd().path, "data", filename)
- );
- return new TextEncoder().encode(data).buffer;
-}
-
-async function mockRecordWithAttachment({
- filename,
- engineIdentifiers,
- imageSize,
-}) {
- let buffer = await getFileDataBuffer(filename);
-
- let stream = Cc["@mozilla.org/io/arraybuffer-input-stream;1"].createInstance(
- Ci.nsIArrayBufferInputStream
- );
- stream.setData(buffer, 0, buffer.byteLength);
-
- // Generate a hash.
- let hasher = Cc["@mozilla.org/security/hash;1"].createInstance(
- Ci.nsICryptoHash
- );
- hasher.init(Ci.nsICryptoHash.SHA256);
- hasher.updateFromStream(stream, -1);
- let hash = hasher.finish(false);
- hash = Array.from(hash, (_, i) =>
- ("0" + hash.charCodeAt(i).toString(16)).slice(-2)
- ).join("");
-
- let record = {
- id: Services.uuid.generateUUID().toString(),
- engineIdentifiers,
- imageSize,
- attachment: {
- hash,
- location: `main-workspace/search-config-icons/${filename}`,
- filename,
- size: buffer.byteLength,
- mimetype: "application/json",
- },
- };
-
- let attachment = {
- record,
- blob: new Blob([buffer]),
- };
-
- return { record, attachment };
-}
-
-async function insertRecordIntoCollection(client, db, item) {
- let { record, attachment } = await mockRecordWithAttachment(item);
- await db.create(record);
- await client.attachments.cacheImpl.set(record.id, attachment);
- await db.importChanges({}, Date.now());
-}
-
add_setup(async function () {
let client = RemoteSettings("search-config-icons");
let db = client.db;
@@ -159,10 +105,7 @@ add_setup(async function () {
if ("icons" in test) {
for (let icon of test.icons) {
- await insertRecordIntoCollection(client, db, {
- ...icon,
- id: test.engineId,
- });
+ await insertRecordIntoCollection(client, { ...icon });
}
}
}
diff --git a/toolkit/components/search/tests/xpcshell/test_appProvided_icons_updates.js b/toolkit/components/search/tests/xpcshell/test_appProvided_icons_updates.js
new file mode 100644
index 0000000000..4159b9f0fb
--- /dev/null
+++ b/toolkit/components/search/tests/xpcshell/test_appProvided_icons_updates.js
@@ -0,0 +1,324 @@
+/* Any copyright is dedicated to the Public Domain.
+https://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests to ensure that icons for application provided engines are correctly
+ * updated from remote settings.
+ */
+
+"use strict";
+
+// A skeleton configuration that gets filled in from TESTS during `add_setup`.
+let CONFIG = [
+ {
+ identifier: "engine_no_initial_icon",
+ recordType: "engine",
+ base: {
+ name: "engine_no_initial_icon name",
+ urls: {
+ search: {
+ base: "https://example.com/1",
+ searchTermParamName: "q",
+ },
+ },
+ },
+ variants: [{ environment: { allRegionsAndLocales: true } }],
+ },
+ {
+ identifier: "engine_icon_updates",
+ recordType: "engine",
+ base: {
+ name: "engine_icon_updates name",
+ urls: {
+ search: {
+ base: "https://example.com/2",
+ searchTermParamName: "q",
+ },
+ },
+ },
+ variants: [{ environment: { allRegionsAndLocales: true } }],
+ },
+ {
+ identifier: "engine_icon_not_local",
+ recordType: "engine",
+ base: {
+ name: "engine_icon_not_local name",
+ urls: {
+ search: {
+ base: "https://example.com/3",
+ searchTermParamName: "q",
+ },
+ },
+ },
+ variants: [{ environment: { allRegionsAndLocales: true } }],
+ },
+ {
+ identifier: "engine_icon_out_of_date",
+ recordType: "engine",
+ base: {
+ name: "engine_icon_out_of_date name",
+ urls: {
+ search: {
+ base: "https://example.com/4",
+ searchTermParamName: "q",
+ },
+ },
+ },
+ variants: [{ environment: { allRegionsAndLocales: true } }],
+ },
+ {
+ recordType: "defaultEngines",
+ globalDefault: "engine_no_initial_icon",
+ specificDefaults: [],
+ },
+ {
+ recordType: "engineOrders",
+ orders: [],
+ },
+];
+
+async function assertIconMatches(actualIconData, expectedIcon) {
+ let expectedBuffer = new Uint8Array(await getFileDataBuffer(expectedIcon));
+
+ Assert.equal(
+ actualIconData.length,
+ expectedBuffer.length,
+ "Should have received matching buffer lengths for the expected icon"
+ );
+ Assert.ok(
+ actualIconData.every((value, index) => value === expectedBuffer[index]),
+ "Should have received matching data for the expected icon"
+ );
+}
+
+async function assertEngineIcon(engineName, expectedIcon) {
+ let engine = Services.search.getEngineByName(engineName);
+ let engineIconURL = await engine.getIconURL(16);
+
+ if (expectedIcon) {
+ Assert.notEqual(
+ engineIconURL,
+ null,
+ "Should have an icon URL for the engine."
+ );
+
+ let response = await fetch(engineIconURL);
+ let buffer = new Uint8Array(await response.arrayBuffer());
+
+ await assertIconMatches(buffer, expectedIcon);
+ } else {
+ Assert.equal(
+ engineIconURL,
+ null,
+ "Should not have an icon URL for the engine."
+ );
+ }
+}
+
+let originalIconId = Services.uuid.generateUUID().toString();
+let client;
+
+add_setup(async function setup() {
+ useHttpServer();
+ SearchTestUtils.useMockIdleService();
+
+ client = RemoteSettings("search-config-icons");
+ await client.db.clear();
+
+ sinon.stub(client.attachments, "_baseAttachmentsURL").returns(gDataUrl);
+
+ // Add some initial records and attachments into the remote settings collection.
+ await insertRecordIntoCollection(client, {
+ id: originalIconId,
+ filename: "remoteIcon.ico",
+ // This uses a wildcard match to test the icon is still applied correctly.
+ engineIdentifiers: ["engine_icon_upd*"],
+ imageSize: 16,
+ });
+ // This attachment is not cached, so we don't have it locally.
+ await insertRecordIntoCollection(
+ client,
+ {
+ id: Services.uuid.generateUUID().toString(),
+ filename: "bigIcon.ico",
+ engineIdentifiers: [
+ // This also tests multiple engine idenifiers works.
+ "enterprise",
+ "next_generation",
+ "engine_icon_not_local",
+ ],
+ imageSize: 16,
+ },
+ false
+ );
+
+ // Add a record that is out of date, and update it with a newer one, but don't
+ // cache the attachment for the new one.
+ let outOfDateRecordId = Services.uuid.generateUUID().toString();
+ await insertRecordIntoCollection(
+ client,
+ {
+ id: outOfDateRecordId,
+ filename: "remoteIcon.ico",
+ engineIdentifiers: ["engine_icon_out_of_date"],
+ imageSize: 16,
+ // 10 minutes ago.
+ lastModified: Date.now() - 600000,
+ },
+ true
+ );
+ let { record } = await mockRecordWithAttachment({
+ id: outOfDateRecordId,
+ filename: "bigIcon.ico",
+ engineIdentifiers: ["engine_icon_out_of_date"],
+ imageSize: 16,
+ });
+ await client.db.update(record);
+ await client.db.importChanges({}, record.lastModified);
+
+ await SearchTestUtils.useTestEngines("simple-engines", null, CONFIG);
+ await Services.search.init();
+
+ // Testing that an icon is not local generates a `Could not find {id}...`
+ // message.
+ consoleAllowList.push("Could not find");
+});
+
+add_task(async function test_icon_added_unknown_engine() {
+ // If the engine is unknown, and this is a new icon, we should still download
+ // the icon, in case the engine is added to the configuration later.
+ let newIconId = Services.uuid.generateUUID().toString();
+
+ let mock = await mockRecordWithAttachment({
+ id: newIconId,
+ filename: "bigIcon.ico",
+ engineIdentifiers: ["engine_unknown"],
+ imageSize: 16,
+ });
+ await client.db.update(mock.record, Date.now());
+
+ await client.emit("sync", {
+ data: {
+ current: [mock.record],
+ created: [mock.record],
+ updated: [],
+ deleted: [],
+ },
+ });
+
+ SearchTestUtils.idleService._fireObservers("idle");
+
+ let icon;
+ await TestUtils.waitForCondition(async () => {
+ try {
+ icon = await client.attachments.get(mock.record);
+ } catch (ex) {
+ // Do nothing.
+ }
+ return !!icon;
+ }, "Should have loaded the icon into the attachments store.");
+
+ await assertIconMatches(new Uint8Array(icon.buffer), "bigIcon.ico");
+});
+
+add_task(async function test_icon_added_existing_engine() {
+ // If the engine is unknown, and this is a new icon, we should still download
+ // it, in case the engine is added to the configuration later.
+ let newIconId = Services.uuid.generateUUID().toString();
+
+ let mock = await mockRecordWithAttachment({
+ id: newIconId,
+ filename: "bigIcon.ico",
+ engineIdentifiers: ["engine_no_initial_icon"],
+ imageSize: 16,
+ });
+ await client.db.update(mock.record, Date.now());
+
+ let promiseEngineUpdated = SearchTestUtils.promiseSearchNotification(
+ SearchUtils.MODIFIED_TYPE.CHANGED,
+ SearchUtils.TOPIC_ENGINE_MODIFIED
+ );
+
+ await client.emit("sync", {
+ data: {
+ current: [mock.record],
+ created: [mock.record],
+ updated: [],
+ deleted: [],
+ },
+ });
+
+ SearchTestUtils.idleService._fireObservers("idle");
+
+ await promiseEngineUpdated;
+ await assertEngineIcon("engine_no_initial_icon name", "bigIcon.ico");
+});
+
+add_task(async function test_icon_updated() {
+ // Test that when an update for an engine icon is received, the engine is
+ // correctly updated.
+
+ // Check the engine has the expected icon to start with.
+ await assertEngineIcon("engine_icon_updates name", "remoteIcon.ico");
+
+ // Update the icon for the engine.
+ let mock = await mockRecordWithAttachment({
+ id: originalIconId,
+ filename: "bigIcon.ico",
+ engineIdentifiers: ["engine_icon_upd*"],
+ imageSize: 16,
+ });
+ await client.db.update(mock.record, Date.now());
+
+ let promiseEngineUpdated = SearchTestUtils.promiseSearchNotification(
+ SearchUtils.MODIFIED_TYPE.CHANGED,
+ SearchUtils.TOPIC_ENGINE_MODIFIED
+ );
+
+ await client.emit("sync", {
+ data: {
+ current: [mock.record],
+ created: [],
+ updated: [{ new: mock.record }],
+ deleted: [],
+ },
+ });
+ SearchTestUtils.idleService._fireObservers("idle");
+
+ await promiseEngineUpdated;
+ await assertEngineIcon("engine_icon_updates name", "bigIcon.ico");
+});
+
+add_task(async function test_icon_not_local() {
+ // Tests that a download is queued and triggered when the icon for an engine
+ // is not in either the local dump nor the cache.
+
+ await assertEngineIcon("engine_icon_not_local name", null);
+
+ // A download should have been queued, so fire idle to trigger it.
+ let promiseEngineUpdated = SearchTestUtils.promiseSearchNotification(
+ SearchUtils.MODIFIED_TYPE.CHANGED,
+ SearchUtils.TOPIC_ENGINE_MODIFIED
+ );
+ SearchTestUtils.idleService._fireObservers("idle");
+ await promiseEngineUpdated;
+
+ await assertEngineIcon("engine_icon_not_local name", "bigIcon.ico");
+});
+
+add_task(async function test_icon_out_of_date() {
+ // Tests that a download is queued and triggered when the icon for an engine
+ // is not in either the local dump nor the cache.
+
+ await assertEngineIcon("engine_icon_out_of_date name", "remoteIcon.ico");
+
+ // A download should have been queued, so fire idle to trigger it.
+ let promiseEngineUpdated = SearchTestUtils.promiseSearchNotification(
+ SearchUtils.MODIFIED_TYPE.CHANGED,
+ SearchUtils.TOPIC_ENGINE_MODIFIED
+ );
+ SearchTestUtils.idleService._fireObservers("idle");
+ await promiseEngineUpdated;
+
+ await assertEngineIcon("engine_icon_out_of_date name", "bigIcon.ico");
+});
diff --git a/toolkit/components/search/tests/xpcshell/test_defaultEngine_fallback.js b/toolkit/components/search/tests/xpcshell/test_defaultEngine_fallback.js
index 12cb6568e7..2f269cc016 100644
--- a/toolkit/components/search/tests/xpcshell/test_defaultEngine_fallback.js
+++ b/toolkit/components/search/tests/xpcshell/test_defaultEngine_fallback.js
@@ -17,6 +17,15 @@
let appDefault;
let appPrivateDefault;
+async function getSearchConfig() {
+ let workDir = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
+ let configFileName =
+ "file://" + PathUtils.join(workDir.path, "data", "search-config-v2.json");
+
+ let response = await fetch(configFileName);
+ return response.json();
+}
+
add_setup(async function () {
useHttpServer();
await SearchTestUtils.useTestEngines();
@@ -292,10 +301,33 @@ add_task(async function test_default_fallback_remove_default_no_visible() {
add_task(
async function test_default_fallback_remove_default_no_visible_or_general() {
- // Reset.
Services.search.restoreDefaultEngines();
- Services.search.defaultEngine = Services.search.defaultPrivateEngine =
- appPrivateDefault;
+
+ // For this test, we need to change any general search engines to unknown,
+ // so that we can test what happens in the unlikely event that there are no
+ // general search engines.
+ if (SearchUtils.newSearchConfigEnabled) {
+ let searchConfig = await getSearchConfig();
+ for (let entry of searchConfig.data) {
+ if (
+ entry.recordType == "engine" &&
+ entry.base.classification == "general"
+ ) {
+ entry.base.classification = "unknown";
+ }
+ }
+ const settings = await RemoteSettings(SearchUtils.SETTINGS_KEY);
+ settings.get.returns(searchConfig.data);
+ Services.search.wrappedJSObject.reset();
+ await Services.search.init();
+
+ appPrivateDefault = await Services.search.getDefaultPrivate();
+
+ Services.search.defaultEngine = appPrivateDefault;
+ } else {
+ Services.search.defaultEngine = Services.search.defaultPrivateEngine =
+ appPrivateDefault;
+ }
// Remove all but the default engine.
let visibleEngines = await Services.search.getVisibleEngines();
@@ -310,7 +342,9 @@ add_task(
"Should only have one visible engine"
);
- SearchUtils.GENERAL_SEARCH_ENGINE_IDS.clear();
+ if (!SearchUtils.newSearchConfigEnabled) {
+ SearchUtils.GENERAL_SEARCH_ENGINE_IDS.clear();
+ }
const observer = new SearchObserver(
[
diff --git a/toolkit/components/search/tests/xpcshell/test_engine_selector_environment.js b/toolkit/components/search/tests/xpcshell/test_engine_selector_environment.js
index bf56984cde..401392b955 100644
--- a/toolkit/components/search/tests/xpcshell/test_engine_selector_environment.js
+++ b/toolkit/components/search/tests/xpcshell/test_engine_selector_environment.js
@@ -388,6 +388,55 @@ const CONFIG_VERSIONS = [
},
];
+const CONFIG_DEVICE_TYPE_LAYOUT = [
+ {
+ recordType: "engine",
+ identifier: "engine-no-device-type",
+ base: {},
+ variants: [
+ {
+ environment: {
+ allRegionsAndLocales: true,
+ },
+ },
+ ],
+ },
+ {
+ recordType: "engine",
+ identifier: "engine-single-device-type",
+ base: {},
+ variants: [
+ {
+ environment: {
+ allRegionsAndLocales: true,
+ deviceType: ["tablet"],
+ },
+ },
+ ],
+ },
+ {
+ recordType: "engine",
+ identifier: "engine-multiple-device-type",
+ base: {},
+ variants: [
+ {
+ environment: {
+ allRegionsAndLocales: true,
+ deviceType: ["tablet", "smartphone"],
+ },
+ },
+ ],
+ },
+ {
+ recordType: "defaultEngines",
+ specificDefaults: [],
+ },
+ {
+ recordType: "engineOrders",
+ orders: [],
+ },
+];
+
const engineSelector = new SearchEngineSelector();
let settings;
let settingOverrides;
@@ -793,3 +842,15 @@ add_task(async function test_engine_selector_does_not_match_optional_engines() {
"Should match engines where optional flag is false or undefined"
);
});
+
+add_task(async function test_engine_selector_match_device_type() {
+ await assertActualEnginesEqualsExpected(
+ CONFIG_DEVICE_TYPE_LAYOUT,
+ {
+ locale: "en-CA",
+ region: "CA",
+ },
+ ["engine-no-device-type"],
+ "Should only match engines with no device type."
+ );
+});
diff --git a/toolkit/components/search/tests/xpcshell/test_engine_selector_subvariants.js b/toolkit/components/search/tests/xpcshell/test_engine_selector_subvariants.js
new file mode 100644
index 0000000000..ed61362ca6
--- /dev/null
+++ b/toolkit/components/search/tests/xpcshell/test_engine_selector_subvariants.js
@@ -0,0 +1,142 @@
+/* Any copyright is dedicated to the Public Domain.
+https://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const CONFIG = [
+ {
+ recordType: "engine",
+ identifier: "engine-1",
+ base: {},
+ variants: [
+ {
+ environment: {
+ allRegionsAndLocales: true,
+ },
+ partnerCode: "variant-partner-code",
+ subVariants: [
+ {
+ environment: { regions: ["CA", "FR"] },
+ telemetrySuffix: "subvariant-telemetry",
+ },
+ {
+ environment: { regions: ["GB", "FR"] },
+ partnerCode: "subvariant-partner-code",
+ },
+ ],
+ },
+ ],
+ },
+ {
+ recordType: "defaultEngines",
+ specificDefaults: [],
+ },
+ {
+ recordType: "engineOrders",
+ orders: [],
+ },
+];
+
+const engineSelector = new SearchEngineSelector();
+let settings;
+let configStub;
+
+/**
+ * This function asserts if the actual engines returned equals the expected
+ * engines.
+ *
+ * @param {object} config
+ * A fake search config containing engines.
+ * @param {object} userEnv
+ * A fake user's environment including locale and region, experiment, etc.
+ * @param {Array} expectedEngines
+ * The array of expected engines to be returned from the fake config.
+ * @param {string} message
+ * The assertion message.
+ */
+async function assertActualEnginesEqualsExpected(
+ config,
+ userEnv,
+ expectedEngines,
+ message
+) {
+ engineSelector._configuration = null;
+ configStub.returns(config);
+ let { engines } = await engineSelector.fetchEngineConfiguration(userEnv);
+
+ Assert.deepEqual(engines, expectedEngines, message);
+}
+
+add_setup(async function () {
+ settings = await RemoteSettings(SearchUtils.NEW_SETTINGS_KEY);
+ configStub = sinon.stub(settings, "get");
+});
+
+add_task(async function test_no_subvariants_match() {
+ await assertActualEnginesEqualsExpected(
+ CONFIG,
+ {
+ locale: "fi",
+ region: "FI",
+ },
+ [
+ {
+ identifier: "engine-1",
+ partnerCode: "variant-partner-code",
+ },
+ ],
+ "Should match no subvariants."
+ );
+});
+
+add_task(async function test_matching_subvariant_with_properties() {
+ await assertActualEnginesEqualsExpected(
+ CONFIG,
+ {
+ locale: "en-GB",
+ region: "GB",
+ },
+ [
+ {
+ identifier: "engine-1",
+ partnerCode: "subvariant-partner-code",
+ },
+ ],
+ "Should match subvariant with subvariant properties."
+ );
+});
+
+add_task(async function test_matching_variant_and_subvariant_with_properties() {
+ await assertActualEnginesEqualsExpected(
+ CONFIG,
+ {
+ locale: "en-CA",
+ region: "CA",
+ },
+ [
+ {
+ identifier: "engine-1",
+ partnerCode: "variant-partner-code",
+ telemetrySuffix: "subvariant-telemetry",
+ },
+ ],
+ "Should match subvariant with subvariant properties."
+ );
+});
+
+add_task(async function test_matching_two_subvariant_with_properties() {
+ await assertActualEnginesEqualsExpected(
+ CONFIG,
+ {
+ locale: "fr",
+ region: "FR",
+ },
+ [
+ {
+ identifier: "engine-1",
+ partnerCode: "subvariant-partner-code",
+ },
+ ],
+ "Should match the last subvariant with subvariant properties."
+ );
+});
diff --git a/toolkit/components/search/tests/xpcshell/test_engine_selector_variants.js b/toolkit/components/search/tests/xpcshell/test_engine_selector_variants.js
index 0fd57f2094..51a7f0de09 100644
--- a/toolkit/components/search/tests/xpcshell/test_engine_selector_variants.js
+++ b/toolkit/components/search/tests/xpcshell/test_engine_selector_variants.js
@@ -122,7 +122,7 @@ add_task(async function test_no_variants_match() {
);
});
-add_task(async function test_match_and_apply_all_variants() {
+add_task(async function test_match_and_apply_last_variants() {
await assertActualEnginesEqualsExpected(
CONFIG,
{
@@ -133,11 +133,10 @@ add_task(async function test_match_and_apply_all_variants() {
{
identifier: "engine-1",
urls: { search: { params: [{ name: "partner-code", value: "foo" }] } },
- telemetrySuffix: "telemetry",
searchTermParamName: "search-param",
},
],
- "Should match all variants and apply each variant property cumulatively."
+ "Should match and apply last variant."
);
});
diff --git a/toolkit/components/search/tests/xpcshell/test_searchSuggest.js b/toolkit/components/search/tests/xpcshell/test_searchSuggest.js
index 4a30eb741a..d24970534f 100644
--- a/toolkit/components/search/tests/xpcshell/test_searchSuggest.js
+++ b/toolkit/components/search/tests/xpcshell/test_searchSuggest.js
@@ -3,7 +3,7 @@
/* eslint-disable mozilla/no-arbitrary-setTimeout */
/**
- * Testing search suggestions from SearchSuggestionController.jsm.
+ * Testing search suggestions from SearchSuggestionController.sys.mjs.
*/
"use strict";
@@ -23,7 +23,7 @@ const SEARCH_TELEMETRY_LATENCY = "SEARCH_SUGGESTIONS_LATENCY_MS";
// We must make sure the FormHistoryStartup component is
// initialized in order for it to respond to FormHistory
-// requests from nsFormAutoComplete.js.
+// requests from FormHistoryAutoComplete.sys.mjs.
var formHistoryStartup = Cc[
"@mozilla.org/satchel/form-history-startup;1"
].getService(Ci.nsIObserver);
diff --git a/toolkit/components/search/tests/xpcshell/test_searchSuggest_cookies.js b/toolkit/components/search/tests/xpcshell/test_searchSuggest_cookies.js
index 042c74d86a..bb4dda9e3f 100644
--- a/toolkit/components/search/tests/xpcshell/test_searchSuggest_cookies.js
+++ b/toolkit/components/search/tests/xpcshell/test_searchSuggest_cookies.js
@@ -2,7 +2,7 @@
* http://creativecommons.org/publicdomain/zero/1.0/ */
/**
- * Test that search suggestions from SearchSuggestionController.jsm don't store
+ * Test that search suggestions from SearchSuggestionController.sys.mjs don't store
* cookies.
*/
@@ -14,7 +14,7 @@ const { SearchSuggestionController } = ChromeUtils.importESModule(
// We must make sure the FormHistoryStartup component is
// initialized in order for it to respond to FormHistory
-// requests from nsFormAutoComplete.js.
+// requests from FormHistoryAutoComplete.sys.mjs.
var formHistoryStartup = Cc[
"@mozilla.org/satchel/form-history-startup;1"
].getService(Ci.nsIObserver);
diff --git a/toolkit/components/search/tests/xpcshell/test_searchSuggest_private.js b/toolkit/components/search/tests/xpcshell/test_searchSuggest_private.js
index 063f3ada49..b7750c0f30 100644
--- a/toolkit/components/search/tests/xpcshell/test_searchSuggest_private.js
+++ b/toolkit/components/search/tests/xpcshell/test_searchSuggest_private.js
@@ -2,7 +2,7 @@
* http://creativecommons.org/publicdomain/zero/1.0/ */
/**
- * Test that search suggestions from SearchSuggestionController.jsm operate
+ * Test that search suggestions from SearchSuggestionController.sys.mjs operate
* correctly in private mode.
*/
diff --git a/toolkit/components/search/tests/xpcshell/test_search_config_v2_nimbus.js b/toolkit/components/search/tests/xpcshell/test_search_config_v2_nimbus.js
new file mode 100644
index 0000000000..56746a614f
--- /dev/null
+++ b/toolkit/components/search/tests/xpcshell/test_search_config_v2_nimbus.js
@@ -0,0 +1,85 @@
+/* Any copyright is dedicated to the Public Domain.
+https://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* Test to verify search-config-v2 preference is correctly toggled via a Nimbus
+ variable. */
+
+"use strict";
+
+ChromeUtils.defineESModuleGetters(this, {
+ SearchTestUtils: "resource://testing-common/SearchTestUtils.sys.mjs",
+ ExperimentAPI: "resource://nimbus/ExperimentAPI.sys.mjs",
+ ExperimentFakes: "resource://testing-common/NimbusTestUtils.sys.mjs",
+ ExperimentManager: "resource://nimbus/lib/ExperimentManager.sys.mjs",
+ AppProvidedSearchEngine:
+ "resource://gre/modules/AppProvidedSearchEngine.sys.mjs",
+});
+
+add_task(async function test_nimbus_experiment_enabled() {
+ Assert.equal(
+ Services.prefs.getBoolPref("browser.search.newSearchConfig.enabled"),
+ false,
+ "newSearchConfig.enabled PREF should initially be false."
+ );
+
+ await ExperimentManager.onStartup();
+ await ExperimentAPI.ready();
+
+ let doExperimentCleanup = await ExperimentFakes.enrollWithFeatureConfig(
+ {
+ featureId: "search",
+ value: {
+ newSearchConfigEnabled: true,
+ },
+ },
+ { isRollout: true }
+ );
+
+ Assert.equal(
+ Services.prefs.getBoolPref("browser.search.newSearchConfig.enabled"),
+ true,
+ "After toggling the Nimbus variable, the current value of newSearchConfig.enabled PREF should be true."
+ );
+
+ Assert.equal(
+ SearchUtils.newSearchConfigEnabled,
+ true,
+ "After toggling the Nimbus variable, newSearchConfig.enabled should be cached as true."
+ );
+
+ await AddonTestUtils.promiseStartupManager();
+ await Services.search.init();
+ await SearchTestUtils.useTestEngines();
+
+ let { engines: engines2 } =
+ await Services.search.wrappedJSObject._fetchEngineSelectorEngines();
+
+ Assert.ok(
+ engines2.some(engine => engine.identifier),
+ "Engines in the search-config-v2 format should have an identifier."
+ );
+
+ let appProvidedEngines =
+ await Services.search.wrappedJSObject.getAppProvidedEngines();
+
+ Assert.ok(
+ appProvidedEngines.every(
+ engine => engine instanceof AppProvidedSearchEngine
+ ),
+ "All application provided engines for search-config-v2 should be instances of AppProvidedSearchEngine."
+ );
+
+ await doExperimentCleanup();
+
+ Assert.equal(
+ Services.prefs.getBoolPref("browser.search.newSearchConfig.enabled"),
+ false,
+ "After experiment unenrollment, the newSearchConfig.enabled should be false."
+ );
+
+ Assert.equal(
+ SearchUtils.newSearchConfigEnabled,
+ true,
+ "After experiment unenrollment, newSearchConfig.enabled should be cached as true."
+ );
+});
diff --git a/toolkit/components/search/tests/xpcshell/test_settings_persist.js b/toolkit/components/search/tests/xpcshell/test_settings_persist.js
index 5c2cbd85c4..e3310a1fa2 100644
--- a/toolkit/components/search/tests/xpcshell/test_settings_persist.js
+++ b/toolkit/components/search/tests/xpcshell/test_settings_persist.js
@@ -81,7 +81,7 @@ add_setup(async function () {
registerCleanupFunction(AddonTestUtils.promiseShutdownManager);
await AddonTestUtils.promiseStartupManager();
// This is only needed as otherwise events will not be properly notified
- // due to https://searchfox.org/mozilla-central/source/toolkit/components/search/SearchUtils.jsm#186
+ // due to https://searchfox.org/mozilla-central/rev/5f0a7ca8968ac5cef8846e1d970ef178b8b76dcc/toolkit/components/search/SearchSettings.sys.mjs#41-42
let settingsFileWritten = promiseAfterSettings();
await Services.search.init(false);
Services.search.wrappedJSObject._removeObservers();
diff --git a/toolkit/components/search/tests/xpcshell/xpcshell.toml b/toolkit/components/search/tests/xpcshell/xpcshell.toml
index 7dd023cbec..899ac2d711 100644
--- a/toolkit/components/search/tests/xpcshell/xpcshell.toml
+++ b/toolkit/components/search/tests/xpcshell/xpcshell.toml
@@ -78,9 +78,18 @@ support-files = [
["test_appDefaultEngine.js"]
+["test_appProvided_engine.js"]
+prefs = ["browser.search.newSearchConfig.enabled=true"]
+support-files = [
+ "../../schema/search-config-v2-schema.json",
+]
+
["test_appProvided_icons.js"]
prefs = ["browser.search.newSearchConfig.enabled=true"]
+["test_appProvided_icons_updates.js"]
+prefs = ["browser.search.newSearchConfig.enabled=true"]
+
["test_async.js"]
["test_config_engine_params.js"]
@@ -128,6 +137,8 @@ tags = "remotesettings searchmain"
["test_engine_selector_environment.js"]
+["test_engine_selector_subvariants.js"]
+
["test_engine_selector_variants.js"]
["test_engine_set_alias.js"]
@@ -254,6 +265,10 @@ support-files = [
["test_searchUrlDomain.js"]
+["test_search_config_v2_nimbus.js"]
+prefs = ["browser.search.newSearchConfig.enabled=false"]
+skip-if = ["appname == 'thunderbird'"] # Test relies on normandy.
+
["test_selectedEngine.js"]
["test_sendSubmissionURL.js"]
diff --git a/toolkit/components/shopping/content/ShoppingProduct.mjs b/toolkit/components/shopping/content/ShoppingProduct.mjs
index d457ac1579..f3890a0d74 100644
--- a/toolkit/components/shopping/content/ShoppingProduct.mjs
+++ b/toolkit/components/shopping/content/ShoppingProduct.mjs
@@ -552,7 +552,7 @@ export class ShoppingProduct extends EventEmitter {
false
);
}
- let abortHandler = e => {
+ let abortHandler = () => {
Glean?.shoppingProduct?.requestAborted.record();
obliviousHttpChannel.cancel(Cr.NS_BINDING_ABORTED);
};
diff --git a/toolkit/components/shopping/test/browser/browser_shopping_ads_test.js b/toolkit/components/shopping/test/browser/browser_shopping_ads_test.js
index 1061cf7fa6..05426a08ff 100644
--- a/toolkit/components/shopping/test/browser/browser_shopping_ads_test.js
+++ b/toolkit/components/shopping/test/browser/browser_shopping_ads_test.js
@@ -16,7 +16,7 @@ function recommendedAdsEventListener(eventName, sidebar) {
content.document.querySelector("shopping-container").wrappedJSObject;
let adEl = shoppingContainer.recommendedAdEl;
return ContentTaskUtils.waitForEvent(adEl, name, false, null, true).then(
- ev => null
+ () => null
);
}
);
diff --git a/toolkit/components/shopping/test/browser/browser_shopping_sidebar_messages.js b/toolkit/components/shopping/test/browser/browser_shopping_sidebar_messages.js
index 969b49481d..8cab6b49d8 100644
--- a/toolkit/components/shopping/test/browser/browser_shopping_sidebar_messages.js
+++ b/toolkit/components/shopping/test/browser/browser_shopping_sidebar_messages.js
@@ -33,7 +33,7 @@ add_task(async function test_sidebar_error() {
await SpecialPowers.spawn(
sidebar.querySelector("browser"),
[],
- async prodInfo => {
+ async () => {
let doc = content.document;
let shoppingContainer =
doc.querySelector("shopping-container").wrappedJSObject;
@@ -79,7 +79,7 @@ add_task(async function test_sidebar_analysis_status_page_not_supported() {
await SpecialPowers.spawn(
sidebar.querySelector("browser"),
[],
- async prodInfo => {
+ async () => {
let doc = content.document;
let shoppingContainer =
doc.querySelector("shopping-container").wrappedJSObject;
@@ -125,7 +125,7 @@ add_task(async function test_sidebar_analysis_status_unprocessable() {
await SpecialPowers.spawn(
sidebar.querySelector("browser"),
[],
- async prodInfo => {
+ async () => {
let doc = content.document;
let shoppingContainer =
doc.querySelector("shopping-container").wrappedJSObject;
@@ -174,7 +174,7 @@ add_task(async function test_sidebar_analysis_status_not_enough_reviews() {
await SpecialPowers.spawn(
sidebar.querySelector("browser"),
[],
- async prodInfo => {
+ async () => {
let doc = content.document;
let shoppingContainer =
doc.querySelector("shopping-container").wrappedJSObject;
diff --git a/toolkit/components/shopping/test/browser/head.js b/toolkit/components/shopping/test/browser/head.js
index af676bbc33..f2b303ce12 100644
--- a/toolkit/components/shopping/test/browser/head.js
+++ b/toolkit/components/shopping/test/browser/head.js
@@ -46,7 +46,7 @@ async function promiseSidebarUpdated(sidebar, expectedProduct) {
return !!e.detail.data && isProductCurrent();
},
true
- ).then(e => true);
+ ).then(() => true);
});
}
@@ -65,7 +65,7 @@ async function promiseSidebarAdsUpdated(sidebar, expectedProduct) {
true,
null,
true
- ).then(e => true);
+ ).then(() => true);
});
}
diff --git a/toolkit/components/startup/public/nsIAppStartup.idl b/toolkit/components/startup/public/nsIAppStartup.idl
index dec948b503..e8b407faeb 100644
--- a/toolkit/components/startup/public/nsIAppStartup.idl
+++ b/toolkit/components/startup/public/nsIAppStartup.idl
@@ -81,7 +81,7 @@ interface nsIAppStartup : nsISupports
* was called (and therefore the application crashed).
* @return whether safe mode is necessary
*/
- bool trackStartupCrashBegin();
+ boolean trackStartupCrashBegin();
/**
* We have succesfully started without crashing. Clear flags that were
@@ -149,7 +149,7 @@ interface nsIAppStartup : nsISupports
* of a hidden window or if the user disallowed a window
* to be closed.
*/
- bool quit(in uint32_t aMode, [optional] in int32_t aExitCode);
+ boolean quit(in uint32_t aMode, [optional] in int32_t aExitCode);
/**
* These values must match the xpcom/base/ShutdownPhase.h values.
@@ -190,7 +190,7 @@ interface nsIAppStartup : nsISupports
*
* @return true if we are in or beyond the given phase.
*/
- bool isInOrBeyondShutdownPhase(in nsIAppStartup_IDLShutdownPhase aPhase);
+ boolean isInOrBeyondShutdownPhase(in nsIAppStartup_IDLShutdownPhase aPhase);
/**
* True if the application is in the process of shutting down.
@@ -240,7 +240,7 @@ interface nsIAppStartup : nsISupports
/**
* Whether or not we showed the startup skeleton UI.
*/
- readonly attribute bool showedPreXULSkeletonUI;
+ readonly attribute boolean showedPreXULSkeletonUI;
/**
* Returns an object with main, process, firstPaint, sessionRestored properties.
diff --git a/toolkit/components/startup/tests/browser/browser_bug511456.js b/toolkit/components/startup/tests/browser/browser_bug511456.js
index c080a1596f..04152a6103 100644
--- a/toolkit/components/startup/tests/browser/browser_bug511456.js
+++ b/toolkit/components/startup/tests/browser/browser_bug511456.js
@@ -45,7 +45,7 @@ function test() {
win2.close();
// Leave the page the second time.
- waitForOnBeforeUnloadDialog(browser, (btnLeave, btnStay) => {
+ waitForOnBeforeUnloadDialog(browser, btnLeave => {
btnLeave.click();
});
diff --git a/toolkit/components/startup/tests/browser/browser_bug537449.js b/toolkit/components/startup/tests/browser/browser_bug537449.js
index 1b54117a7d..10e54ea6d2 100644
--- a/toolkit/components/startup/tests/browser/browser_bug537449.js
+++ b/toolkit/components/startup/tests/browser/browser_bug537449.js
@@ -44,7 +44,7 @@ function test() {
win2.close();
// Leave the page the second time.
- waitForOnBeforeUnloadDialog(browser, (btnLeave, btnStay) => {
+ waitForOnBeforeUnloadDialog(browser, btnLeave => {
btnLeave.click();
});
diff --git a/toolkit/components/taskscheduler/TaskSchedulerMacOSImpl.sys.mjs b/toolkit/components/taskscheduler/TaskSchedulerMacOSImpl.sys.mjs
index 3cd5b5c51d..ae15738513 100644
--- a/toolkit/components/taskscheduler/TaskSchedulerMacOSImpl.sys.mjs
+++ b/toolkit/components/taskscheduler/TaskSchedulerMacOSImpl.sys.mjs
@@ -292,12 +292,12 @@ export var MacOSImpl = {
return serializer.serializeToString(doc);
},
- _formatLabelForThisApp(id, options) {
+ _formatLabelForThisApp(id) {
let installHash = lazy.XreDirProvider.getInstallHash();
return `${AppConstants.MOZ_MACBUNDLE_ID}.${installHash}.${id}`;
},
- _labelMatchesThisApp(label, options) {
+ _labelMatchesThisApp(label) {
let installHash = lazy.XreDirProvider.getInstallHash();
return (
label &&
diff --git a/toolkit/components/telemetry/Events.yaml b/toolkit/components/telemetry/Events.yaml
index ddba1c690d..5d3d433a05 100644
--- a/toolkit/components/telemetry/Events.yaml
+++ b/toolkit/components/telemetry/Events.yaml
@@ -4270,3 +4270,40 @@ screenshots:
- 1801019
expiry_version: "never"
release_channel_collection: opt-out
+
+session_restore:
+ backup_can_be_loaded:
+ objects: ["session_file"]
+ description: >
+ Recorded when a file is able to be successfully read on startup
+ record_in_processes:
+ - main
+ products:
+ - "firefox"
+ extra_keys:
+ can_load: Whether or not the startup file can be read/loaded
+ path_key: A symbolic name for the backup file, should be one of "clean", "recovery", "recoveryBackup", "cleanBackup", or "upgradeBackup"
+ loadfail_reason: Reason why the file cannot be loaded, N/A if can be loaded
+ notification_emails:
+ - "session-restore-telemetry-alerts@mozilla.com"
+ bug_numbers:
+ - 1874742
+ expiry_version: "never"
+ release_channel_collection: opt-out
+ shutdown_success:
+ objects: ["session_startup"]
+ description: >
+ Report shutdown success
+ record_in_processes:
+ - main
+ products:
+ - "firefox"
+ extra_keys:
+ shutdown_ok: Whether or not the shutdown happened successfully, unsuccessful if previous session crashed
+ shutdown_reason: Reason why shutdown didn't happen successfully, N/A if previous session didn't crashed
+ notification_emails:
+ - "session-restore-telemetry-alerts@mozilla.com"
+ bug_numbers:
+ - 1874742
+ expiry_version: "never"
+ release_channel_collection: opt-out
diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json
index 3be0da3295..4f0f0e2329 100644
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -794,6 +794,7 @@
"jcoppeard@mozilla.com"
],
"expires_in_version": "never",
+ "releaseChannelCollection": "opt-out",
"kind": "exponential",
"high": 100,
"n_buckets": 20,
@@ -808,6 +809,7 @@
"jcoppeard@mozilla.com"
],
"expires_in_version": "never",
+ "releaseChannelCollection": "opt-out",
"kind": "exponential",
"high": 100,
"n_buckets": 20,
@@ -837,6 +839,7 @@
"smaug@mozilla.com"
],
"expires_in_version": "never",
+ "releaseChannelCollection": "opt-out",
"kind": "exponential",
"high": 10000,
"n_buckets": 50,
@@ -851,7 +854,6 @@
"jcoppeard@mozilla.com"
],
"expires_in_version": "never",
- "releaseChannelCollection": "opt-out",
"kind": "exponential",
"high": 200,
"n_buckets": 50,
@@ -893,7 +895,6 @@
"sdetar@mozilla.com"
],
"expires_in_version": "never",
- "releaseChannelCollection": "opt-out",
"kind": "exponential",
"high": 10000,
"n_buckets": 50,
@@ -932,6 +933,7 @@
"products": ["firefox"],
"alert_emails": ["dev-telemetry-gc-alerts@mozilla.org"],
"expires_in_version": "never",
+ "releaseChannelCollection": "opt-out",
"kind": "exponential",
"high": 10000,
"n_buckets": 50,
@@ -943,6 +945,7 @@
"products": ["firefox"],
"alert_emails": ["dev-telemetry-gc-alerts@mozilla.org"],
"expires_in_version": "never",
+ "releaseChannelCollection": "opt-out",
"kind": "exponential",
"high": 10000,
"n_buckets": 50,
@@ -954,6 +957,7 @@
"products": ["firefox"],
"alert_emails": ["dev-telemetry-gc-alerts@mozilla.org"],
"expires_in_version": "never",
+ "releaseChannelCollection": "opt-out",
"kind": "exponential",
"high": 10000,
"n_buckets": 50,
@@ -968,7 +972,6 @@
"jcoppeard@mozilla.com"
],
"expires_in_version": "never",
- "releaseChannelCollection": "opt-out",
"kind": "exponential",
"high": 150000,
"n_buckets": 50,
@@ -983,7 +986,6 @@
"jcoppeard@mozilla.com"
],
"expires_in_version": "never",
- "releaseChannelCollection": "opt-out",
"kind": "exponential",
"high": 10000,
"n_buckets": 50,
@@ -998,7 +1000,6 @@
"jcoppeard@mozilla.com"
],
"expires_in_version": "never",
- "releaseChannelCollection": "opt-out",
"kind": "exponential",
"high": 10000,
"n_buckets": 50,
@@ -1010,6 +1011,7 @@
"products": ["firefox"],
"alert_emails": ["dev-telemetry-gc-alerts@mozilla.org"],
"expires_in_version": "never",
+ "releaseChannelCollection": "opt-out",
"kind": "exponential",
"high": 10000,
"n_buckets": 50,
@@ -1044,6 +1046,7 @@
"products": ["firefox"],
"alert_emails": ["dev-telemetry-gc-alerts@mozilla.org"],
"expires_in_version": "never",
+ "releaseChannelCollection": "opt-out",
"kind": "linear",
"high": 100,
"n_buckets": 20,
@@ -1113,6 +1116,7 @@
"products": ["firefox"],
"alert_emails": ["dev-telemetry-gc-alerts@mozilla.org"],
"expires_in_version": "never",
+ "releaseChannelCollection": "opt-out",
"kind": "exponential",
"high": 1000000,
"n_buckets": 100,
@@ -1142,7 +1146,6 @@
"jcoppeard@mozilla.com"
],
"expires_in_version": "never",
- "releaseChannelCollection": "opt-out",
"kind": "exponential",
"high": 100,
"n_buckets": 20,
@@ -1199,7 +1202,6 @@
"kind": "linear",
"high": 100,
"n_buckets": 50,
- "releaseChannelCollection": "opt-out",
"bug_numbers": [1563755],
"description": "The percentage of tenured GC things that survived a collection."
},
@@ -1229,7 +1231,6 @@
"kind": "exponential",
"high": 120,
"n_buckets": 50,
- "releaseChannelCollection": "opt-out",
"bug_numbers": [1556467],
"description": "Time spent in between garbage collections for the main runtime (seconds)"
},
@@ -1272,7 +1273,6 @@
],
"expires_in_version": "never",
"kind": "exponential",
- "releaseChannelCollection": "opt-out",
"low": 1,
"high": 50000,
"n_buckets": 100,
@@ -1625,6 +1625,7 @@
"products": ["firefox", "fennec", "thunderbird"],
"alert_emails": ["memshrink-telemetry-alerts@mozilla.com"],
"expires_in_version": "never",
+ "releaseChannelCollection": "opt-out",
"kind": "exponential",
"low": 1024,
"high": 16777216,
@@ -3643,7 +3644,7 @@
"releaseChannelCollection": "opt-out",
"description": "Recorded once for each HTTP 401 response. The value records the type of authentication and the TLS-enabled status. (0=basic/clear, 1=basic/tls, 2=digest/clear, 3=digest/tls, 4=ntlm/clear, 5=ntlm/tls, 6=negotiate/clear, 7=negotiate/tls)"
},
- "HTTP_CHILD_OMT_STATS": {
+ "HTTP_CHILD_OMT_STATS_2": {
"record_in_processes": ["content"],
"products": ["firefox", "fennec"],
"alert_emails": ["necko@mozilla.com"],
@@ -3657,7 +3658,8 @@
"successMainThread",
"failListener",
"failListenerChain",
- "notRequested"
+ "notRequested",
+ "successOnlyDecomp"
]
},
"TLS_EARLY_DATA_NEGOTIATED": {
@@ -4814,7 +4816,7 @@
"expires_in_version": "never",
"kind": "enumerated",
"n_values": 6,
- "description": "encoding removed: 0=unknown, 1=gzip, 2=deflate, 3=brotli"
+ "description": "encoding removed: 0=unknown, 1=gzip, 2=deflate, 3=brotli, 4=zstd"
},
"CACHE_LM_INCONSISTENT": {
"record_in_processes": ["main", "content"],
@@ -8742,6 +8744,7 @@
"record_in_processes": ["main", "content"],
"products": ["firefox", "fennec"],
"alert_emails": ["session-restore-telemetry-alerts@mozilla.com"],
+ "releaseChannelCollection": "opt-out",
"expires_in_version": "default",
"kind": "boolean",
"description": "Session restore: Whether none of the backup files contained parse-able JSON"
@@ -8816,17 +8819,6 @@
"n_values": 50,
"description": "Session restore: Number of tabs restored eagerly in the session that has just been restored."
},
- "FX_SESSION_RESTORE_CLOSED_TABS_NOT_SAVED": {
- "record_in_processes": ["main", "content"],
- "products": ["firefox"],
- "expires_in_version": "127",
- "alert_emails": ["firefox-view-engineers@mozilla.com"],
- "releaseChannelCollection": "opt-out",
- "bug_numbers": [1848459],
- "kind": "enumerated",
- "n_values": 25,
- "description": "Session restore: Number of closed tabs that are NOT saved due to lack of open tabs worth saving on window close."
- },
"FX_TABLETMODE_PAGE_LOAD": {
"record_in_processes": ["main", "content"],
"products": ["firefox", "fennec"],
@@ -13182,83 +13174,6 @@
"alert_emails": ["perf-telemetry-alerts@mozilla.com"],
"description": "Scaling percentage for the display where the first window is opened"
},
- "SHUTDOWN_PHASE_DURATION_TICKS_QUIT_APPLICATION": {
- "record_in_processes": ["main", "content"],
- "products": ["firefox", "fennec", "thunderbird"],
- "expires_in_version": "never",
- "kind": "exponential",
- "high": 65,
- "n_buckets": 10,
- "bug_numbers": [1689953],
- "alert_emails": ["dothayer@mozilla.com, jstutte@mozilla.com"],
- "description": "Duration of shutdown phase quit-application, as measured by the shutdown terminator in ticks of 100ms"
- },
- "SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_CHANGE_NET_TEARDOWN": {
- "record_in_processes": ["main", "content"],
- "products": ["firefox", "fennec", "thunderbird"],
- "expires_in_version": "never",
- "kind": "exponential",
- "high": 65,
- "n_buckets": 10,
- "bug_numbers": [1689953],
- "alert_emails": ["dothayer@mozilla.com, jstutte@mozilla.com"],
- "description": "Duration of shutdown phase profile-change-net-teardown, as measured by the shutdown terminator in ticks of 100ms"
- },
- "SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_CHANGE_TEARDOWN": {
- "record_in_processes": ["main", "content"],
- "products": ["firefox", "fennec", "thunderbird"],
- "expires_in_version": "never",
- "kind": "exponential",
- "high": 65,
- "n_buckets": 10,
- "bug_numbers": [1689953],
- "alert_emails": ["dothayer@mozilla.com, jstutte@mozilla.com"],
- "description": "Duration of shutdown phase profile-change-teardown, as measured by the shutdown terminator in ticks of 100ms"
- },
- "SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_BEFORE_CHANGE": {
- "record_in_processes": ["main", "content"],
- "products": ["firefox", "fennec", "thunderbird"],
- "expires_in_version": "never",
- "kind": "exponential",
- "high": 65,
- "n_buckets": 10,
- "bug_numbers": [1689953],
- "alert_emails": ["dothayer@mozilla.com, jstutte@mozilla.com"],
- "description": "Duration of shutdown phase profile-before-change, as measured by the shutdown terminator in ticks of 100ms"
- },
- "SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_BEFORE_CHANGE_QM": {
- "record_in_processes": ["main", "content"],
- "products": ["firefox", "fennec", "thunderbird"],
- "expires_in_version": "never",
- "kind": "exponential",
- "high": 65,
- "n_buckets": 10,
- "bug_numbers": [1689953],
- "alert_emails": ["dothayer@mozilla.com, jstutte@mozilla.com"],
- "description": "Duration of shutdown phase profile-before-change-qm, as measured by the shutdown terminator in ticks of 100ms"
- },
- "SHUTDOWN_PHASE_DURATION_TICKS_XPCOM_WILL_SHUTDOWN": {
- "record_in_processes": ["main", "content"],
- "products": ["firefox", "fennec", "thunderbird"],
- "expires_in_version": "never",
- "kind": "exponential",
- "high": 65,
- "n_buckets": 10,
- "bug_numbers": [1689953],
- "alert_emails": ["dothayer@mozilla.com, jstutte@mozilla.com"],
- "description": "Duration of shutdown phase xpcom-will-shutdown, as measured by the shutdown terminator in ticks of 100ms"
- },
- "SHUTDOWN_PHASE_DURATION_TICKS_XPCOM_SHUTDOWN": {
- "record_in_processes": ["main", "content"],
- "products": ["firefox", "fennec", "thunderbird"],
- "expires_in_version": "never",
- "kind": "exponential",
- "high": 65,
- "n_buckets": 10,
- "bug_numbers": [1689953],
- "alert_emails": ["dothayer@mozilla.com, jstutte@mozilla.com"],
- "description": "Duration of shutdown phase xpcom-shutdown, as measured by the shutdown terminator in ticks of 100ms"
- },
"TAP_TO_LOAD_ENABLED": {
"record_in_processes": ["main", "content"],
"products": ["firefox", "fennec"],
diff --git a/toolkit/components/telemetry/Scalars.yaml b/toolkit/components/telemetry/Scalars.yaml
index fee6594b81..f63b8b5fdb 100644
--- a/toolkit/components/telemetry/Scalars.yaml
+++ b/toolkit/components/telemetry/Scalars.yaml
@@ -172,6 +172,246 @@ browser.backup:
- 'firefox'
record_in_processes:
- 'main'
+ places_size:
+ bug_numbers:
+ - 1883642
+ description: >
+ The total file size of the places.sqlite db located in the current profile
+ directory, in kilobytes.
+ expires: never
+ kind: uint
+ notification_emails:
+ - mconley@mozilla.com
+ release_channel_collection: opt-out
+ products:
+ - 'firefox'
+ record_in_processes:
+ - 'main'
+ favicons_size:
+ bug_numbers:
+ - 1883642
+ description: >
+ The total file size of the favicons.sqlite db located in the current profile
+ directory, in kilobytes.
+ expires: never
+ kind: uint
+ notification_emails:
+ - mconley@mozilla.com
+ release_channel_collection: opt-out
+ products:
+ - 'firefox'
+ record_in_processes:
+ - 'main'
+ credentials_data_size:
+ bug_numbers:
+ - 1883736
+ description: >
+ The total size of logins, payment method, and form autofill related files
+ in the current profile directory, in kilobytes.
+ expires: never
+ kind: uint
+ notification_emails:
+ - mconley@mozilla.com
+ release_channel_collection: opt-out
+ products:
+ - 'firefox'
+ record_in_processes:
+ - 'main'
+ security_data_size:
+ bug_numbers:
+ - 1883736
+ description: >
+ The total size of files needed for NSS initialization parameters and security
+ certificate settings in the current profile directory, in kilobytes.
+ expires: never
+ kind: uint
+ notification_emails:
+ - mconley@mozilla.com
+ release_channel_collection: opt-out
+ products:
+ - 'firefox'
+ record_in_processes:
+ - 'main'
+ preferences_size:
+ bug_numbers:
+ - 1883739
+ description: >
+ The total size of files relating to user preferences and permissions in the current profile
+ directory, in kilobytes.
+ expires: never
+ kind: uint
+ notification_emails:
+ - mconley@mozilla.com
+ release_channel_collection: opt-out
+ products:
+ - 'firefox'
+ record_in_processes:
+ - 'main'
+ misc_data_size:
+ bug_numbers:
+ - 1883747
+ - 1887746
+ description: >
+ The total size of files for telemetry, site storage, media device origin mapping,
+ chrome privileged IndexedDB databases, and Mozilla Accounts in the current profile directory,
+ rounded to the nearest tenth kilobyte.
+ expires: never
+ kind: uint
+ notification_emails:
+ - mconley@mozilla.com
+ release_channel_collection: opt-out
+ products:
+ - 'firefox'
+ record_in_processes:
+ - 'main'
+ cookies_size:
+ bug_numbers:
+ - 1883740
+ description: >
+ The total file size of the cookies.sqlite db located in the current profile
+ directory, in kilobytes.
+ expires: never
+ kind: uint
+ notification_emails:
+ - mconley@mozilla.com
+ release_channel_collection: opt-out
+ products:
+ - 'firefox'
+ record_in_processes:
+ - 'main'
+ form_history_size:
+ bug_numbers:
+ - 1883740
+ description: >
+ The file size of the formhistory.sqlite db located in the current profile
+ directory, in kilobytes.
+ expires: never
+ kind: uint
+ notification_emails:
+ - mconley@mozilla.com
+ release_channel_collection: opt-out
+ products:
+ - 'firefox'
+ record_in_processes:
+ - 'main'
+ session_store_backups_directory_size:
+ bug_numbers:
+ - 1883740
+ description: >
+ The total size of the session store backups directory, in kilobytes.
+ expires: never
+ kind: uint
+ notification_emails:
+ - mconley@mozilla.com
+ release_channel_collection: opt-out
+ products:
+ - 'firefox'
+ record_in_processes:
+ - 'main'
+ session_store_size:
+ bug_numbers:
+ - 1883740
+ description: >
+ The size of uncompressed session store json, in kilobytes.
+ expires: never
+ kind: uint
+ notification_emails:
+ - mconley@mozilla.com
+ release_channel_collection: opt-out
+ products:
+ - 'firefox'
+ record_in_processes:
+ - 'main'
+ extensions_json_size:
+ bug_numbers:
+ - 1883655
+ description: >
+ The total file size of the extensions json files located in the current
+ profile directory,rounded to the nearest 10 kilobytes.
+ expires: never
+ kind: uint
+ notification_emails:
+ - mconley@mozilla.com
+ release_channel_collection: opt-out
+ products:
+ - 'firefox'
+ record_in_processes:
+ - 'main'
+ extension_store_permissions_data_size:
+ bug_numbers:
+ - 1883655
+ description: >
+ The file size of the current profiles extension-store-permissions/data.safe.bin
+ file, rounded to the nearest 10 kilobytes.
+ expires: never
+ kind: uint
+ notification_emails:
+ - mconley@mozilla.com
+ release_channel_collection: opt-out
+ products:
+ - 'firefox'
+ record_in_processes:
+ - 'main'
+ storage_sync_size:
+ bug_numbers:
+ - 1883655
+ description: >
+ The file size of the current profiles storage-sync-v2.sqlite database,
+ rounded to the nearest 10 kilobytes.
+ expires: never
+ kind: uint
+ notification_emails:
+ - mconley@mozilla.com
+ release_channel_collection: opt-out
+ products:
+ - 'firefox'
+ record_in_processes:
+ - 'main'
+ browser_extension_data_size:
+ bug_numbers:
+ - 1883655
+ description: >
+ The total size of the current profiles storage.local legacy JSON backend
+ in the browser-extension-data directory, rounded to the nearest 10 kilobytes.
+ expires: never
+ kind: uint
+ notification_emails:
+ - mconley@mozilla.com
+ release_channel_collection: opt-out
+ products:
+ - 'firefox'
+ record_in_processes:
+ - 'main'
+ extensions_xpi_directory_size:
+ bug_numbers:
+ - 1883655
+ description: >
+ The total size of the current profiles extensions directory,
+ rounded to the nearest 10 kilobytes.
+ expires: never
+ kind: uint
+ notification_emails:
+ - mconley@mozilla.com
+ release_channel_collection: opt-out
+ products:
+ - 'firefox'
+ record_in_processes:
+ - 'main'
+ extensions_storage_size:
+ bug_numbers:
+ - 1883655
+ description: >
+ The total size of all extensions storage directories,
+ rounded to the nearest 10 kilobytes.
+ expires: never
+ kind: uint
+ notification_emails:
+ - mconley@mozilla.com
+ release_channel_collection: opt-out
+ products:
+ - 'firefox'
+ record_in_processes:
+ - 'main'
# The following section contains the browser engagement scalars.
browser.engagement:
@@ -1682,23 +1922,6 @@ pwmgr:
record_in_processes:
- main
-bloburl:
- resolve_stopped:
- bug_numbers:
- - 1843158
- description: >
- Counts how many times we do not resolve a blob URL
- because of different partition keys
- expires: "127"
- kind: uint
- notification_emails:
- - amadan@mozilla.com
- release_channel_collection: opt-out
- products:
- - 'firefox'
- record_in_processes:
- - content
-
contentblocking:
cryptomining_blocking_enabled:
bug_numbers:
diff --git a/toolkit/components/telemetry/app/TelemetryEnvironment.sys.mjs b/toolkit/components/telemetry/app/TelemetryEnvironment.sys.mjs
index 76bc2579eb..18d46a3565 100644
--- a/toolkit/components/telemetry/app/TelemetryEnvironment.sys.mjs
+++ b/toolkit/components/telemetry/app/TelemetryEnvironment.sys.mjs
@@ -358,6 +358,10 @@ const DEFAULT_ENVIRONMENT_PREFS = new Map([
{ what: RECORD_DEFAULTPREF_VALUE },
],
["xpinstall.signatures.required", { what: RECORD_PREF_VALUE }],
+ [
+ "xpinstall.signatures.weakSignaturesTemporarilyAllowed",
+ { what: RECORD_PREF_VALUE },
+ ],
["nimbus.debug", { what: RECORD_PREF_VALUE }],
]);
@@ -837,6 +841,7 @@ EnvironmentAddonBuilder.prototype = {
hasBinaryComponents: false,
installDay: Utils.millisecondsToDays(installDate.getTime()),
signedState: addon.signedState,
+ signedTypes: JSON.stringify(addon.signedTypes),
quarantineIgnoredByApp: enforceBoolean(
addon.quarantineIgnoredByApp
),
diff --git a/toolkit/components/telemetry/app/TelemetryUtils.sys.mjs b/toolkit/components/telemetry/app/TelemetryUtils.sys.mjs
index 809460aebc..f56b8229dc 100644
--- a/toolkit/components/telemetry/app/TelemetryUtils.sys.mjs
+++ b/toolkit/components/telemetry/app/TelemetryUtils.sys.mjs
@@ -29,7 +29,8 @@ export var TelemetryUtils = {
*
* Here is an example of listening for that event:
*
- * const { TelemetryUtils } = ChromeUtils.import("resource://gre/modules/TelemetryUtils.jsm");
+ * const { TelemetryUtils } =
+ * ChromeUtils.importESModule("resource://gre/modules/TelemetryUtils.sys.mjs");
*
* class YourClass {
* constructor() {
diff --git a/toolkit/components/telemetry/core/ipc/TelemetryComms.h b/toolkit/components/telemetry/core/ipc/TelemetryComms.h
index 75f59209b0..57c875dedb 100644
--- a/toolkit/components/telemetry/core/ipc/TelemetryComms.h
+++ b/toolkit/components/telemetry/core/ipc/TelemetryComms.h
@@ -12,6 +12,7 @@
#include "mozilla/TelemetryProcessEnums.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Variant.h"
+#include "mozilla/dom/WebGLIpdl.h"
#include "nsITelemetry.h"
namespace mozilla {
@@ -96,6 +97,13 @@ struct DiscardedData {
uint32_t mDiscardedScalarActions;
uint32_t mDiscardedKeyedScalarActions;
uint32_t mDiscardedChildEvents;
+
+ auto MutTiedFields() {
+ return std::tie(mDiscardedHistogramAccumulations,
+ mDiscardedKeyedHistogramAccumulations,
+ mDiscardedScalarActions, mDiscardedKeyedScalarActions,
+ mDiscardedChildEvents);
+ }
};
} // namespace Telemetry
@@ -393,7 +401,7 @@ struct ParamTraits<mozilla::Telemetry::EventExtraEntry> {
template <>
struct ParamTraits<mozilla::Telemetry::DiscardedData>
- : public PlainOldDataSerializer<mozilla::Telemetry::DiscardedData> {};
+ : public ParamTraits_TiedFields<mozilla::Telemetry::DiscardedData> {};
} // namespace IPC
diff --git a/toolkit/components/telemetry/docs/data/environment.rst b/toolkit/components/telemetry/docs/data/environment.rst
index 77d3d1ea6e..9e966fe707 100644
--- a/toolkit/components/telemetry/docs/data/environment.rst
+++ b/toolkit/components/telemetry/docs/data/environment.rst
@@ -282,6 +282,7 @@ Structure:
installDay: <number>, // days since UNIX epoch, 0 on failure
updateDay: <number>, // days since UNIX epoch, 0 on failure
signedState: <integer>, // whether the add-on is signed by AMO, only present for extensions
+ signedTypes: <string>, // JSON-stringified array of signature types found (see nsIAppSignatureInfo's SignatureAlgorithm enum)
isSystem: <bool>, // true if this is a System Add-on
isWebExtension: <bool>, // true if this is a WebExtension
multiprocessCompatible: <bool>, // true if this add-on does *not* require e10s shims
@@ -475,6 +476,10 @@ The following is a partial list of `collected preferences <https://searchfox.org
- ``intl.ime.use_composition_events_for_insert_text``: Whether a set of composition events is fired when user inserts text without keyboard events nor composing state of a composition (only on Linux and macOS).
+- ``xpinstall.signatures.required``: Whether XPI files cryptographic signatures are being verified and enforced.
+
+- ``xpinstall.signatures.weakSignaturesTemporarilyAllowed``: Whether new XPI files only signed with weak signature algorithms are still allowed to be installed
+
attribution
~~~~~~~~~~~
diff --git a/toolkit/components/telemetry/tests/addons/signed-webext/.web-extension-id b/toolkit/components/telemetry/tests/addons/signed-webext/.web-extension-id
deleted file mode 100644
index e78cecf6d6..0000000000
--- a/toolkit/components/telemetry/tests/addons/signed-webext/.web-extension-id
+++ /dev/null
@@ -1,3 +0,0 @@
-# This file was created by https://github.com/mozilla/web-ext
-# Your auto-generated extension ID for addons.mozilla.org is:
-tel-signed-webext@tests.mozilla.org \ No newline at end of file
diff --git a/toolkit/components/telemetry/tests/addons/signed-webext/META-INF/manifest.mf b/toolkit/components/telemetry/tests/addons/signed-webext/META-INF/manifest.mf
deleted file mode 100644
index 6be3a6e32f..0000000000
--- a/toolkit/components/telemetry/tests/addons/signed-webext/META-INF/manifest.mf
+++ /dev/null
@@ -1,7 +0,0 @@
-Manifest-Version: 1.0
-
-Name: manifest.json
-Digest-Algorithms: MD5 SHA1
-MD5-Digest: vh0VF5quc9YIhMhIsZgKcg==
-SHA1-Digest: DNXAbrHJ4ncET5W+qtJl4+45D6s=
-
diff --git a/toolkit/components/telemetry/tests/addons/signed-webext/META-INF/mozilla.rsa b/toolkit/components/telemetry/tests/addons/signed-webext/META-INF/mozilla.rsa
deleted file mode 100644
index b202c515a8..0000000000
--- a/toolkit/components/telemetry/tests/addons/signed-webext/META-INF/mozilla.rsa
+++ /dev/null
Binary files differ
diff --git a/toolkit/components/telemetry/tests/addons/signed-webext/META-INF/mozilla.sf b/toolkit/components/telemetry/tests/addons/signed-webext/META-INF/mozilla.sf
deleted file mode 100644
index 72af14d816..0000000000
--- a/toolkit/components/telemetry/tests/addons/signed-webext/META-INF/mozilla.sf
+++ /dev/null
@@ -1,4 +0,0 @@
-Signature-Version: 1.0
-MD5-Digest-Manifest: tbeA48G5pe86kvUbd4rsXA==
-SHA1-Digest-Manifest: jyvWt2v9XUnYHrvrlSi4BdyZV/0=
-
diff --git a/toolkit/components/telemetry/tests/addons/signed-webext/manifest.json b/toolkit/components/telemetry/tests/addons/signed-webext/manifest.json
deleted file mode 100644
index 437b415fef..0000000000
--- a/toolkit/components/telemetry/tests/addons/signed-webext/manifest.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "manifest_version": 2,
- "name": "XPI Telemetry Signed Test",
- "description": "A signed webextension",
- "version": "1.0",
-
- "applications": {
- "gecko": {
- "id": "tel-signed-webext@tests.mozilla.org"
- }
- }
-}
diff --git a/toolkit/components/telemetry/tests/marionette/harness/telemetry_harness/fog_testcase.py b/toolkit/components/telemetry/tests/marionette/harness/telemetry_harness/fog_testcase.py
index c5bc54e9d2..9bcf29d2f4 100644
--- a/toolkit/components/telemetry/tests/marionette/harness/telemetry_harness/fog_testcase.py
+++ b/toolkit/components/telemetry/tests/marionette/harness/telemetry_harness/fog_testcase.py
@@ -55,6 +55,8 @@ class FOGTestCase(TelemetryTestCase):
"logging.fog_control::*": 5,
"logging.glean::*": 5,
"logging.glean_core::*": 5,
+ # Slow down the user inactivity timeout (bug 1690728)
+ "dom.events.user_interaction_interval": 60000,
}
)
diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
index a67788623d..9990d83fdf 100644
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
@@ -715,16 +715,18 @@ add_task(async function test_addons() {
add_task(async function test_signedAddon() {
AddonTestUtils.useRealCertChecks = true;
- const ADDON_INSTALL_URL = gDataRoot + "signed-webext.xpi";
- const ADDON_ID = "tel-signed-webext@tests.mozilla.org";
+ const { PKCS7_WITH_SHA1, COSE_WITH_SHA256 } = Ci.nsIAppSignatureInfo;
+
+ const ADDON_INSTALL_URL = gDataRoot + "amosigned.xpi";
+ const ADDON_ID = "amosigned-xpi@tests.mozilla.org";
const ADDON_INSTALL_DATE = truncateToDays(Date.now());
const EXPECTED_ADDON_DATA = {
blocklisted: false,
- description: "A signed webextension",
- name: "XPI Telemetry Signed Test",
+ description: null,
+ name: "XPI Test",
userDisabled: false,
appDisabled: false,
- version: "1.0",
+ version: "2.2",
scope: 1,
type: "extension",
foreignInstall: false,
@@ -732,6 +734,7 @@ add_task(async function test_signedAddon() {
installDay: ADDON_INSTALL_DATE,
updateDay: ADDON_INSTALL_DATE,
signedState: AddonManager.SIGNEDSTATE_SIGNED,
+ signedTypes: JSON.stringify([COSE_WITH_SHA256, PKCS7_WITH_SHA1]),
quarantineIgnoredByUser: false,
// quarantineIgnoredByApp expected to be false because
// the test addon is signed as a non-privileged (see signedState),
diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryReportingPolicy.js b/toolkit/components/telemetry/tests/unit/test_TelemetryReportingPolicy.js
index 01041172d0..df5dc151ad 100644
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryReportingPolicy.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryReportingPolicy.js
@@ -199,6 +199,12 @@ add_task(async function test_prefs() {
"Accepting the policy again should let us upload data."
);
+ // macOS has the app.update.channel pref locked. Check if it needs to be
+ // unlocked before proceeding with the test.
+ if (Services.prefs.getDefaultBranch("").prefIsLocked("app.update.channel")) {
+ Services.prefs.getDefaultBranch("").unlockPref("app.update.channel");
+ }
+
// Set a new, per channel, minimum policy version. Start by setting a test current channel.
Services.prefs
.getDefaultBranch("")
diff --git a/toolkit/components/telemetry/tests/unit/xpcshell.toml b/toolkit/components/telemetry/tests/unit/xpcshell.toml
index dbff9c627c..0c660888ed 100644
--- a/toolkit/components/telemetry/tests/unit/xpcshell.toml
+++ b/toolkit/components/telemetry/tests/unit/xpcshell.toml
@@ -17,6 +17,7 @@ support-files = [
"testUnicodePDBAArch64.dll",
"testNoPDBAArch64.dll",
"!/toolkit/mozapps/extensions/test/xpcshell/head_addons.js",
+ "../../../../mozapps/extensions/test/xpinstall/amosigned.xpi",
]
generated-files = [
"system.xpi",
diff --git a/toolkit/components/terminator/TerminatorTelemetry.sys.mjs b/toolkit/components/terminator/TerminatorTelemetry.sys.mjs
deleted file mode 100644
index 9903027436..0000000000
--- a/toolkit/components/terminator/TerminatorTelemetry.sys.mjs
+++ /dev/null
@@ -1,119 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set ts=2 et sw=2 tw=80 filetype=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/. */
-
-/**
- * Read the data saved by nsTerminator during shutdown and feed it to the
- * relevant telemetry histograms.
- */
-
-const lazy = {};
-
-ChromeUtils.defineESModuleGetters(lazy, {
- setTimeout: "resource://gre/modules/Timer.sys.mjs",
-});
-
-export function nsTerminatorTelemetry() {
- this._wasNotified = false;
- this._deferred = Promise.withResolvers();
-
- IOUtils.sendTelemetry.addBlocker(
- "TerminatoryTelemetry: Waiting to submit telemetry",
- this._deferred.promise,
- () => ({
- wasNotified: this._wasNotified,
- })
- );
-}
-
-var HISTOGRAMS = {
- "quit-application": "SHUTDOWN_PHASE_DURATION_TICKS_QUIT_APPLICATION",
- "profile-change-net-teardown":
- "SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_CHANGE_NET_TEARDOWN",
- "profile-change-teardown":
- "SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_CHANGE_TEARDOWN",
- "profile-before-change":
- "SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_BEFORE_CHANGE",
- "profile-before-change-qm":
- "SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_BEFORE_CHANGE_QM",
- "xpcom-will-shutdown": "SHUTDOWN_PHASE_DURATION_TICKS_XPCOM_WILL_SHUTDOWN",
- "xpcom-shutdown": "SHUTDOWN_PHASE_DURATION_TICKS_XPCOM_SHUTDOWN",
-
- // The following keys appear in the JSON, but do not have associated
- // histograms.
- "xpcom-shutdown-threads": null,
- XPCOMShutdownFinal: null,
- CCPostLastCycleCollection: null,
-};
-
-nsTerminatorTelemetry.prototype = {
- classID: Components.ID("{3f78ada1-cba2-442a-82dd-d5fb300ddea7}"),
-
- // nsISupports
-
- QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
-
- // nsIObserver
-
- observe: function DS_observe(aSubject, aTopic, aData) {
- this._wasNotified = true;
-
- (async () => {
- try {
- //
- // This data is hardly critical, reading it can wait for a few seconds.
- //
- await new Promise(resolve => lazy.setTimeout(resolve, 3000));
-
- let PATH = PathUtils.join(
- Services.dirsvc.get("ProfLD", Ci.nsIFile).path,
- "ShutdownDuration.json"
- );
- let data;
- try {
- data = await IOUtils.readJSON(PATH);
- } catch (ex) {
- if (DOMException.isInstance(ex) && ex.name == "NotFoundError") {
- return;
- }
- // Let other errors be reported by Promise's error-reporting.
- throw ex;
- }
-
- // Clean up
- await IOUtils.remove(PATH);
- await IOUtils.remove(PATH + ".tmp");
-
- for (let k of Object.keys(data)) {
- let id = HISTOGRAMS[k];
- if (id === null) {
- // No histogram associated with this entry.
- continue;
- }
-
- try {
- let histogram = Services.telemetry.getHistogramById(id);
- histogram.add(Number.parseInt(data[k]));
- } catch (ex) {
- // Make sure that the error is reported and causes test failures,
- // but otherwise, ignore it.
- Promise.reject(ex);
- continue;
- }
- }
-
- // Inform observers that we are done.
- Services.obs.notifyObservers(
- null,
- "shutdown-terminator-telemetry-updated"
- );
- } finally {
- this._deferred.resolve();
- }
- })();
- },
-};
-
-// Module
diff --git a/toolkit/components/terminator/components.conf b/toolkit/components/terminator/components.conf
deleted file mode 100644
index 14c4c2c562..0000000000
--- a/toolkit/components/terminator/components.conf
+++ /dev/null
@@ -1,15 +0,0 @@
-# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-Classes = [
- {
- 'cid': '{3f78ada1-cba2-442a-82dd-d5fb300ddea7}',
- 'contract_ids': ['@mozilla.org/toolkit/shutdown-terminator-telemetry;1'],
- 'esModule': 'resource://gre/modules/TerminatorTelemetry.sys.mjs',
- 'constructor': 'nsTerminatorTelemetry',
- 'categories': {'profile-after-change': 'nsTerminatorTelemetry'},
- },
-]
diff --git a/toolkit/components/terminator/moz.build b/toolkit/components/terminator/moz.build
index f869b86d95..a13789c2da 100644
--- a/toolkit/components/terminator/moz.build
+++ b/toolkit/components/terminator/moz.build
@@ -21,12 +21,10 @@ EXTRA_COMPONENTS += [
"terminator.manifest",
]
-EXTRA_JS_MODULES += [
- "TerminatorTelemetry.sys.mjs",
+XPIDL_SOURCES += [
+ "nsITerminatorTest.idl",
]
-XPCOM_MANIFESTS += [
- "components.conf",
-]
+XPIDL_MODULE = "toolkit_terminator"
FINAL_LIBRARY = "xul"
diff --git a/toolkit/components/terminator/nsITerminatorTest.idl b/toolkit/components/terminator/nsITerminatorTest.idl
new file mode 100644
index 0000000000..22f06a11dc
--- /dev/null
+++ b/toolkit/components/terminator/nsITerminatorTest.idl
@@ -0,0 +1,17 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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/. */
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(a76599ef-78a4-441f-b258-4908dedef42d)]
+interface nsITerminatorTest : nsISupports
+{
+
+ /**
+ * This method is used to test the shutdown phases of nsTerminator. It is
+ * not meant to be used outside of tests.
+ */
+ [implicit_jscontext] jsval getTicksForShutdownPhases();
+};
diff --git a/toolkit/components/terminator/nsTerminator.cpp b/toolkit/components/terminator/nsTerminator.cpp
index 5a1f9693b8..6339962022 100644
--- a/toolkit/components/terminator/nsTerminator.cpp
+++ b/toolkit/components/terminator/nsTerminator.cpp
@@ -245,131 +245,9 @@ void RunWatchdog(void* arg) {
}
}
-////////////////////////////////////////////
-//
-// Writer thread
-//
-// This nspr thread is in charge of writing to disk statistics produced by the
-// watchdog thread and collected by the main thread. Note that we use a nspr
-// thread rather than usual XPCOM I/O simply because we outlive XPCOM and its
-// threads.
-//
-
-//
-// Communication between the main thread and the writer thread.
-//
-// Main thread:
-//
-// * Whenever a shutdown step has been completed, the main thread
-// obtains the number of ticks from the watchdog threads, builds
-// a string representing all the data gathered so far, places
-// this string in `gWriteData`, and wakes up the writer thread
-// using `gWriteReady`. If `gWriteData` already contained a non-null
-// pointer, this means that the writer thread is lagging behind the
-// main thread, and the main thread cleans up the memory.
-//
-// Writer thread:
-//
-// * When awake, the writer thread swaps `gWriteData` to nullptr. If
-// `gWriteData` contained data to write, the . If so, the writer
-// thread writes the data to a file named "ShutdownDuration.json.tmp",
-// then moves that file to "ShutdownDuration.json" and cleans up the
-// data. If `gWriteData` contains a nullptr, the writer goes to sleep
-// until it is awkened using `gWriteReady`.
-//
-//
-// The data written by the writer thread will be read by another
-// module upon the next restart and fed to Telemetry.
-//
-Atomic<nsCString*> gWriteData(nullptr);
-PRMonitor* gWriteReady = nullptr;
-
-void RunWriter(void* arg) {
- AUTO_PROFILER_REGISTER_THREAD("Shutdown Statistics Writer");
- NS_SetCurrentThreadName("Shutdown Statistics Writer");
-
- MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(arg);
- // Shutdown will generally complete before we have a chance to
- // deallocate. This is not a leak.
-
- // Setup destinationPath and tmpFilePath
-
- nsCString destinationPath;
- destinationPath.Adopt(static_cast<char*>(arg));
- nsAutoCString tmpFilePath;
- tmpFilePath.Append(destinationPath);
- tmpFilePath.AppendLiteral(".tmp");
-
- // Cleanup any file leftover from a previous run
- Unused << PR_Delete(tmpFilePath.get());
- Unused << PR_Delete(destinationPath.get());
-
- while (true) {
- //
- // Check whether we have received data from the main thread.
- //
- // We perform the check before waiting on `gWriteReady` as we may
- // have received data while we were busy writing.
- //
- // Also note that gWriteData may have been modified several times
- // since we last checked. That's ok, we are not losing any important
- // data (since we keep adding data), and we are not leaking memory
- // (since the main thread deallocates any data that hasn't been
- // consumed by the writer thread).
- //
- UniquePtr<nsCString> data(gWriteData.exchange(nullptr));
- if (!data) {
- // Data is not available yet.
- // Wait until the main thread provides it.
- PR_EnterMonitor(gWriteReady);
- PR_Wait(gWriteReady, PR_INTERVAL_NO_TIMEOUT);
- PR_ExitMonitor(gWriteReady);
- continue;
- }
-
- MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(data.get());
- // Shutdown may complete before we have a chance to deallocate.
- // This is not a leak.
-
- //
- // Write to a temporary file
- //
- // In case of any error, we simply give up. Since the data is
- // hardly critical, we don't want to spend too much effort
- // salvaging it.
- //
- UniquePtr<PRFileDesc, PR_CloseDelete> tmpFileDesc(PR_Open(
- tmpFilePath.get(), PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE, 00600));
-
- // Shutdown may complete before we have a chance to close the file.
- // This is not a leak.
- MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(tmpFileDesc.get());
-
- if (tmpFileDesc == nullptr) {
- break;
- }
- if (PR_Write(tmpFileDesc.get(), data->get(), data->Length()) == -1) {
- break;
- }
- tmpFileDesc.reset();
-
- //
- // Rename on top of destination file.
- //
- // This is not sufficient to guarantee that the destination file
- // will be written correctly, but, again, we don't care enough
- // about the data to make more efforts.
- //
- Unused << PR_Delete(destinationPath.get());
- if (PR_Rename(tmpFilePath.get(), destinationPath.get()) != PR_SUCCESS) {
- break;
- }
- }
-}
-
} // namespace
-NS_IMPL_ISUPPORTS(nsTerminator, nsIObserver)
+NS_IMPL_ISUPPORTS(nsTerminator, nsIObserver, nsITerminatorTest)
nsTerminator::nsTerminator() : mInitialized(false), mCurrentStep(-1) {}
@@ -379,12 +257,6 @@ void nsTerminator::Start() {
MOZ_ASSERT(!mInitialized);
StartWatchdog();
-#if !defined(NS_FREE_PERMANENT_DATA)
- // Only allow nsTerminator to write on non-leak-checked builds so we don't
- // get leak warnings on shutdown for intentional leaks (see bug 1242084).
- // This will be enabled again by bug 1255484 when 1255478 lands.
- StartWriter();
-#endif // !defined(NS_FREE_PERMANENT_DATA)
mInitialized = true;
}
@@ -470,41 +342,6 @@ void nsTerminator::StartWatchdog() {
MOZ_ASSERT(watchdogThread);
}
-// Prepare, allocate and start the writer thread. By design, it will never
-// finish, nor be deallocated. In case of error, we degrade
-// gracefully to not writing Telemetry data.
-void nsTerminator::StartWriter() {
- if (!Telemetry::CanRecordExtended()) {
- return;
- }
- nsCOMPtr<nsIFile> profLD;
- nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
- getter_AddRefs(profLD));
- if (NS_FAILED(rv)) {
- return;
- }
-
- rv = profLD->Append(u"ShutdownDuration.json"_ns);
- if (NS_FAILED(rv)) {
- return;
- }
-
- nsAutoString path;
- rv = profLD->GetPath(path);
- if (NS_FAILED(rv)) {
- return;
- }
-
- gWriteReady = PR_NewMonitor();
- MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(
- gWriteReady); // We will never deallocate this object
- PRThread* writerThread = CreateSystemThread(RunWriter, ToNewUTF8String(path));
-
- if (!writerThread) {
- return;
- }
-}
-
// This helper is here to preserve the existing crash reporting behavior
// based on observer topic names, using the shutdown phase name only for
// phases without associated topic.
@@ -531,12 +368,6 @@ void nsTerminator::AdvancePhase(mozilla::ShutdownPhase aPhase) {
}
UpdateHeartbeat(step);
-#if !defined(NS_FREE_PERMANENT_DATA)
- // Only allow nsTerminator to write on non-leak checked builds so we don't get
- // leak warnings on shutdown for intentional leaks (see bug 1242084). This
- // will be enabled again by bug 1255484 when 1255478 lands.
- UpdateTelemetry();
-#endif // !defined(NS_FREE_PERMANENT_DATA)
UpdateCrashReport(GetReadableNameForPhase(aPhase));
}
@@ -555,58 +386,25 @@ void nsTerminator::UpdateHeartbeat(int32_t aStep) {
}
}
-void nsTerminator::UpdateTelemetry() {
- if (!Telemetry::CanRecordExtended() || !gWriteReady) {
- return;
- }
-
- //
- // We need Telemetry data on the effective duration of each step,
- // to be able to tune the time-to-crash of each of both the
- // Terminator and AsyncShutdown. However, at this stage, it is too
- // late to record such data into Telemetry, so we write it to disk
- // and read it upon the next startup.
- //
-
- // Build JSON.
- UniquePtr<nsCString> telemetryData(new nsCString());
- telemetryData->AppendLiteral("{");
- size_t fields = 0;
- for (auto& shutdownStep : sShutdownSteps) {
- if (shutdownStep.mTicks < 0) {
- // Ignore this field.
- continue;
- }
- if (fields++ > 0) {
- telemetryData->AppendLiteral(", ");
- }
- telemetryData->AppendLiteral(R"(")");
- telemetryData->Append(GetReadableNameForPhase(shutdownStep.mPhase));
- telemetryData->AppendLiteral(R"(": )");
- telemetryData->AppendInt(shutdownStep.mTicks);
- }
- telemetryData->AppendLiteral("}");
-
- if (fields == 0) {
- // Nothing to write
- return;
- }
-
- //
- // Send data to the worker thread.
- //
- delete gWriteData.exchange(
- telemetryData.release()); // Clear any data that hasn't been written yet
-
- // In case the worker thread was sleeping, wake it up.
- PR_EnterMonitor(gWriteReady);
- PR_Notify(gWriteReady);
- PR_ExitMonitor(gWriteReady);
-}
-
void nsTerminator::UpdateCrashReport(const char* aTopic) {
// In case of crash, we wish to know where in shutdown we are
CrashReporter::RecordAnnotationCString(
CrashReporter::Annotation::ShutdownProgress, aTopic);
}
+
+NS_IMETHODIMP
+nsTerminator::GetTicksForShutdownPhases(JSContext* aCx,
+ JS::MutableHandle<JS::Value> aRetval) {
+ JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
+ aRetval.setObject(*obj);
+
+ for (auto& shutdownStep : sShutdownSteps) {
+ if (shutdownStep.mTicks >= 0) {
+ JS_DefineProperty(aCx, obj, GetReadableNameForPhase(shutdownStep.mPhase),
+ shutdownStep.mTicks, JSPROP_ENUMERATE);
+ }
+ }
+
+ return NS_OK;
+} // namespace mozilla
} // namespace mozilla
diff --git a/toolkit/components/terminator/nsTerminator.h b/toolkit/components/terminator/nsTerminator.h
index 46c3e17b92..0de6ebe778 100644
--- a/toolkit/components/terminator/nsTerminator.h
+++ b/toolkit/components/terminator/nsTerminator.h
@@ -9,13 +9,15 @@
#include "nsISupports.h"
#include "nsIObserver.h"
+#include "nsITerminatorTest.h"
namespace mozilla {
-class nsTerminator final : public nsIObserver {
+class nsTerminator final : public nsIObserver, public nsITerminatorTest {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
+ NS_DECL_NSITERMINATORTEST
nsTerminator();
void AdvancePhase(mozilla::ShutdownPhase aPhase);
diff --git a/toolkit/components/terminator/tests/xpcshell/test_terminator_advance_phases.js b/toolkit/components/terminator/tests/xpcshell/test_terminator_advance_phases.js
new file mode 100644
index 0000000000..42f0e3686f
--- /dev/null
+++ b/toolkit/components/terminator/tests/xpcshell/test_terminator_advance_phases.js
@@ -0,0 +1,157 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+/* eslint-disable mozilla/no-arbitrary-setTimeout */
+
+"use strict";
+
+// Test that the Shutdown Terminator advances through the shutdown phases
+// correctly.
+
+const { setTimeout } = ChromeUtils.importESModule(
+ "resource://gre/modules/Timer.sys.mjs"
+);
+
+var terminator;
+
+var HEARTBEAT_MS = 100;
+
+let KEYS = [
+ "quit-application",
+ "profile-change-net-teardown",
+ "profile-change-teardown",
+ "profile-before-change",
+ "profile-before-change-qm",
+ "xpcom-will-shutdown",
+ "xpcom-shutdown",
+ "xpcom-shutdown-threads",
+ "XPCOMShutdownFinal",
+ "CCPostLastCycleCollection",
+];
+
+let DATA = [];
+let MeasuredDurations = [];
+
+add_task(async function init() {
+ do_get_profile();
+
+ // Initialize the terminator
+ // (normally, this is done through the manifest file, but xpcshell
+ // doesn't take them into account).
+ info("Initializing the Terminator");
+ terminator = Cc["@mozilla.org/toolkit/shutdown-terminator;1"].createInstance(
+ Ci.nsIObserver
+ );
+
+ Assert.ok(
+ terminator instanceof Ci.nsITerminatorTest,
+ "Terminator should implement nsITerminatorTest"
+ );
+});
+
+var currentPhase = 0;
+
+var advancePhase = async function () {
+ let key = "terminator-test-" + KEYS[currentPhase];
+ let msDuration = 200 + HEARTBEAT_MS * currentPhase;
+
+ info("Advancing shutdown phase to " + KEYS[currentPhase]);
+ terminator.observe(null, key, null);
+ await new Promise(resolve => setTimeout(resolve, msDuration));
+
+ let data = terminator.getTicksForShutdownPhases();
+
+ Assert.ok(
+ KEYS[currentPhase] in data,
+ "The KEYS object contains the expected key"
+ );
+ Assert.equal(
+ Object.keys(data).length,
+ currentPhase + 1,
+ "KEYS object does not contain more durations than expected"
+ );
+
+ DATA[currentPhase] = data;
+ currentPhase++;
+ if (currentPhase < KEYS.length) {
+ return true;
+ }
+ return false;
+};
+
+// This is a timing affected test, as we want to check if the time measurements
+// from the terminator are reasonable. Bug 1768795 assumes that they tend to
+// be lower than wall-clock, in particular on MacOS, confirmed by the logs on
+// intermittent bug 1760094. This is not a big deal for the terminator's
+// general functionality (timeouts might just come a little later than
+// expected nominally), but it makes testing harder and the transferred
+// telemetry data slightly less reliable (shutdowns might appear shorter than
+// they really were). So this test is just happy if there is any data that
+// is not too long wrt what we expect. If we ever want to fix bug 1768795,
+// we can check for a more reasonable lower boundary, too.
+add_task(async function test_record() {
+ info("Collecting duration data for all known phases");
+
+ let morePhases = true;
+ while (morePhases) {
+ let beforeWait = Date.now();
+
+ morePhases = await advancePhase();
+
+ // We measure the effective time that passed as wall-clock and include all
+ // file IO overhead as the terminator will do so in its measurement, too.
+ MeasuredDurations[currentPhase - 1] = Math.floor(
+ (Date.now() - beforeWait) / HEARTBEAT_MS
+ );
+ }
+
+ Assert.equal(DATA.length, KEYS.length, "We have data for each phase");
+
+ for (let i = 0; i < KEYS.length; i++) {
+ let lastDuration = DATA[KEYS.length - 1][KEYS[i]];
+ Assert.equal(
+ typeof lastDuration,
+ "number",
+ "Duration of phase " + i + ":" + KEYS[i] + " is a number"
+ );
+
+ // The durations are only meaningful after we advanced to the next phase.
+ if (i < KEYS.length - 1) {
+ // So we read it from the data written for the following phase.
+ let ticksDuration = DATA[i + 1][KEYS[i]];
+ let measuredDuration = MeasuredDurations[i];
+ info(
+ "measuredDuration:" + measuredDuration + " - " + typeof measuredDuration
+ );
+ Assert.lessOrEqual(
+ ticksDuration,
+ measuredDuration + 2,
+ "Duration of phase " + i + ":" + KEYS[i] + " is not too long"
+ );
+ Assert.greaterOrEqual(
+ ticksDuration,
+ 0, // TODO: Raise the lower boundary after bug 1768795.
+ "Duration of phase " + i + ":" + KEYS[i] + " is not too short"
+ );
+ }
+ // This check is done only for phases <= xpcom-shutdown-threads
+ // where we have two data points.
+ if (i < KEYS.length - 2) {
+ let ticksDuration = DATA[i + 1][KEYS[i]];
+ Assert.equal(
+ ticksDuration,
+ DATA[KEYS.length - 1][KEYS[i]],
+ "Duration of phase " + i + ":" + KEYS[i] + " hasn't changed"
+ );
+ }
+ }
+
+ // Note that after this check the KEYS array remains sorted, so this
+ // must be the last check to not get confused.
+ Assert.equal(
+ Object.keys(DATA[KEYS.length - 1])
+ .sort()
+ .join(", "),
+ KEYS.sort().join(", "),
+ "The KEYS object contains all expected keys"
+ );
+});
diff --git a/toolkit/components/terminator/tests/xpcshell/test_terminator_record.js b/toolkit/components/terminator/tests/xpcshell/test_terminator_record.js
deleted file mode 100644
index 62cee636f3..0000000000
--- a/toolkit/components/terminator/tests/xpcshell/test_terminator_record.js
+++ /dev/null
@@ -1,171 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-/* eslint-disable mozilla/no-arbitrary-setTimeout */
-
-"use strict";
-
-// Test that the Shutdown Terminator records durations correctly
-
-const { setTimeout } = ChromeUtils.importESModule(
- "resource://gre/modules/Timer.sys.mjs"
-);
-
-var PATH;
-var PATH_TMP;
-var terminator;
-
-var HEARTBEAT_MS = 100;
-
-let KEYS = [
- "quit-application",
- "profile-change-net-teardown",
- "profile-change-teardown",
- "profile-before-change",
- "profile-before-change-qm",
- "xpcom-will-shutdown",
- "xpcom-shutdown",
- "xpcom-shutdown-threads",
- "XPCOMShutdownFinal",
- "CCPostLastCycleCollection",
-];
-
-let DATA = [];
-let MeasuredDurations = [];
-
-add_task(async function init() {
- do_get_profile();
- PATH = PathUtils.join(PathUtils.localProfileDir, "ShutdownDuration.json");
- PATH_TMP = PATH + ".tmp";
-
- // Initialize the terminator
- // (normally, this is done through the manifest file, but xpcshell
- // doesn't take them into account).
- info("Initializing the Terminator");
- terminator = Cc["@mozilla.org/toolkit/shutdown-terminator;1"].createInstance(
- Ci.nsIObserver
- );
-});
-
-var promiseShutdownDurationData = async function () {
- // Wait until PATH exists.
- // Timeout if it is never created.
- while (true) {
- if (await IOUtils.exists(PATH)) {
- break;
- }
-
- // Wait just a very short period to not increase measured values.
- // Usually the file should appear almost immediately.
- await new Promise(resolve => setTimeout(resolve, 50));
- }
-
- return IOUtils.readJSON(PATH);
-};
-
-var currentPhase = 0;
-
-var advancePhase = async function () {
- let key = "terminator-test-" + KEYS[currentPhase];
- let msDuration = 200 + HEARTBEAT_MS * currentPhase;
-
- info("Advancing shutdown phase to " + KEYS[currentPhase]);
- terminator.observe(null, key, null);
- await new Promise(resolve => setTimeout(resolve, msDuration));
-
- let data = await promiseShutdownDurationData();
-
- Assert.ok(KEYS[currentPhase] in data, "The file contains the expected key");
- Assert.equal(
- Object.keys(data).length,
- currentPhase + 1,
- "File does not contain more durations than expected"
- );
-
- DATA[currentPhase] = data;
- currentPhase++;
- if (currentPhase < KEYS.length) {
- return true;
- }
- return false;
-};
-
-// This is a timing affected test, as we want to check if the time measurements
-// from the terminator are reasonable. Bug 1768795 assumes that they tend to
-// be lower than wall-clock, in particular on MacOS, confirmed by the logs on
-// intermittent bug 1760094. This is not a big deal for the terminator's
-// general functionality (timeouts might just come a little later than
-// expected nominally), but it makes testing harder and the transferred
-// telemetry data slightly less reliable (shutdowns might appear shorter than
-// they really were). So this test is just happy if there is any data that
-// is not too long wrt what we expect. If we ever want to fix bug 1768795,
-// we can check for a more reasonable lower boundary, too.
-add_task(async function test_record() {
- info("Collecting duration data for all known phases");
-
- let morePhases = true;
- while (morePhases) {
- let beforeWait = Date.now();
-
- morePhases = await advancePhase();
-
- await IOUtils.remove(PATH);
- await IOUtils.remove(PATH_TMP);
-
- // We measure the effective time that passed as wall-clock and include all
- // file IO overhead as the terminator will do so in its measurement, too.
- MeasuredDurations[currentPhase - 1] = Math.floor(
- (Date.now() - beforeWait) / HEARTBEAT_MS
- );
- }
-
- Assert.equal(DATA.length, KEYS.length, "We have data for each phase");
-
- for (let i = 0; i < KEYS.length; i++) {
- let lastDuration = DATA[KEYS.length - 1][KEYS[i]];
- Assert.equal(
- typeof lastDuration,
- "number",
- "Duration of phase " + i + ":" + KEYS[i] + " is a number"
- );
-
- // The durations are only meaningful after we advanced to the next phase.
- if (i < KEYS.length - 1) {
- // So we read it from the data written for the following phase.
- let ticksDuration = DATA[i + 1][KEYS[i]];
- let measuredDuration = MeasuredDurations[i];
- info(
- "measuredDuration:" + measuredDuration + " - " + typeof measuredDuration
- );
- Assert.lessOrEqual(
- ticksDuration,
- measuredDuration + 2,
- "Duration of phase " + i + ":" + KEYS[i] + " is not too long"
- );
- Assert.greaterOrEqual(
- ticksDuration,
- 0, // TODO: Raise the lower boundary after bug 1768795.
- "Duration of phase " + i + ":" + KEYS[i] + " is not too short"
- );
- }
- // This check is done only for phases <= xpcom-shutdown-threads
- // where we have two data points.
- if (i < KEYS.length - 2) {
- let ticksDuration = DATA[i + 1][KEYS[i]];
- Assert.equal(
- ticksDuration,
- DATA[KEYS.length - 1][KEYS[i]],
- "Duration of phase " + i + ":" + KEYS[i] + " hasn't changed"
- );
- }
- }
-
- // Note that after this check the KEYS array remains sorted, so this
- // must be the last check to not get confused.
- Assert.equal(
- Object.keys(DATA[KEYS.length - 1])
- .sort()
- .join(", "),
- KEYS.sort().join(", "),
- "The last file contains all expected keys"
- );
-});
diff --git a/toolkit/components/terminator/tests/xpcshell/test_terminator_reload.js b/toolkit/components/terminator/tests/xpcshell/test_terminator_reload.js
deleted file mode 100644
index 79aecd818b..0000000000
--- a/toolkit/components/terminator/tests/xpcshell/test_terminator_reload.js
+++ /dev/null
@@ -1,88 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-// Test that the Shutdown Terminator reloads durations correctly
-
-const HISTOGRAMS = {
- "quit-application": "SHUTDOWN_PHASE_DURATION_TICKS_QUIT_APPLICATION",
- "profile-change-net-teardown":
- "SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_CHANGE_NET_TEARDOWN",
- "profile-change-teardown":
- "SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_CHANGE_TEARDOWN",
- "profile-before-change":
- "SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_BEFORE_CHANGE",
- "profile-before-change-qm":
- "SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_BEFORE_CHANGE_QM",
- "xpcom-will-shutdown": "SHUTDOWN_PHASE_DURATION_TICKS_XPCOM_WILL_SHUTDOWN",
- "xpcom-shutdown": "SHUTDOWN_PHASE_DURATION_TICKS_XPCOM_SHUTDOWN",
-};
-
-let PATH;
-
-add_setup(async function init() {
- do_get_profile();
- PATH = PathUtils.join(PathUtils.localProfileDir, "ShutdownDuration.json");
-});
-
-add_task(async function test_reload() {
- info("Forging data");
- let data = {};
- let telemetrySnapshots = Services.telemetry.getSnapshotForHistograms(
- "main",
- false /* clear */
- ).parent;
- let i = 0;
- for (let k of Object.keys(HISTOGRAMS)) {
- let id = HISTOGRAMS[k];
- data[k] = i++;
- Assert.equal(
- telemetrySnapshots[id] || undefined,
- undefined,
- "Histogram " + id + " is empty"
- );
- }
-
- // Extra fields that nsTerminator reports that we do not have histograms for.
- data["xpcom-shutdown-threads"] = 123;
- data.XPCOMShutdownFinal = 456;
- data.CCPostLastCycleCollection = 789;
-
- await IOUtils.writeJSON(PATH, data);
-
- const TOPIC = "shutdown-terminator-telemetry-updated";
-
- let wait = new Promise(resolve =>
- Services.obs.addObserver(function observer() {
- info("Telemetry has been updated");
- Services.obs.removeObserver(observer, TOPIC);
- resolve();
- }, TOPIC)
- );
-
- info("Starting nsTerminatorTelemetry");
- let tt = Cc[
- "@mozilla.org/toolkit/shutdown-terminator-telemetry;1"
- ].createInstance(Ci.nsIObserver);
- tt.observe(null, "profile-after-change", "");
-
- info("Waiting until telemetry is updated");
- // Now wait until Telemetry is updated
- await wait;
-
- telemetrySnapshots = Services.telemetry.getSnapshotForHistograms(
- "main",
- false /* clear */
- ).parent;
- for (let k of Object.keys(HISTOGRAMS)) {
- let id = HISTOGRAMS[k];
- info("Testing histogram " + id);
- let snapshot = telemetrySnapshots[id];
- let count = 0;
- for (let x of Object.values(snapshot.values)) {
- count += x;
- }
- Assert.equal(count, 1, "We have added one item");
- }
-});
diff --git a/toolkit/components/terminator/tests/xpcshell/xpcshell.toml b/toolkit/components/terminator/tests/xpcshell/xpcshell.toml
index cb328ca2c8..67cc36f265 100644
--- a/toolkit/components/terminator/tests/xpcshell/xpcshell.toml
+++ b/toolkit/components/terminator/tests/xpcshell/xpcshell.toml
@@ -1,15 +1,10 @@
[DEFAULT]
head = ""
-["test_terminator_record.js"]
+["test_terminator_advance_phases.js"]
skip-if = [
"debug",
"asan", # Disabled by bug 1242084, bug 1255484 will enable it again
"ccov", # Bug 1607583 tracks the ccov failure
"tsan", # Bug 1683730 made this timeout for tsan.
]
-run-sequentially = "very high failure rate in parallel"
-
-["test_terminator_reload.js"]
-skip-if = ["os == 'android'"]
-run-sequentially = "very high failure rate in parallel"
diff --git a/toolkit/components/thumbnails/BackgroundPageThumbs.sys.mjs b/toolkit/components/thumbnails/BackgroundPageThumbs.sys.mjs
index 8467653acb..3040adc6a1 100644
--- a/toolkit/components/thumbnails/BackgroundPageThumbs.sys.mjs
+++ b/toolkit/components/thumbnails/BackgroundPageThumbs.sys.mjs
@@ -219,7 +219,7 @@ export const BackgroundPageThumbs = {
"nsISupportsWeakReference",
]),
};
- this._listener.onStateChange = (wbp, request, stateFlags, status) => {
+ this._listener.onStateChange = (wbp, request, stateFlags) => {
if (!request) {
return;
}
@@ -591,7 +591,7 @@ Capture.prototype = {
this._done(browser, null, TEL_CAPTURE_DONE_BAD_URI);
}
},
- failure => {
+ () => {
// The query can fail when a crash occurs while loading. The error causes
// thumbnail crash tests to fail with an uninteresting error message.
}
diff --git a/toolkit/components/thumbnails/PageThumbs.sys.mjs b/toolkit/components/thumbnails/PageThumbs.sys.mjs
index 4d103c8bd8..da7e187d85 100644
--- a/toolkit/components/thumbnails/PageThumbs.sys.mjs
+++ b/toolkit/components/thumbnails/PageThumbs.sys.mjs
@@ -262,7 +262,7 @@ export var PageThumbs = {
aBrowser.browsingContext.currentWindowGlobal.getActor("Thumbnails");
return thumbnailsActor
.sendQuery("Browser:Thumbnail:CheckState")
- .catch(err => {
+ .catch(() => {
return false;
});
}
@@ -835,7 +835,7 @@ export var PageThumbsExpiration = {
}
},
- notify: function Expiration_notify(aTimer) {
+ notify: function Expiration_notify() {
let urls = [];
let filtersToWaitFor = this._filters.length;
diff --git a/toolkit/components/thumbnails/test/browser_thumbnails_bg_captureIfMissing.js b/toolkit/components/thumbnails/test/browser_thumbnails_bg_captureIfMissing.js
index 15cf4d7067..b3b4c1f305 100644
--- a/toolkit/components/thumbnails/test/browser_thumbnails_bg_captureIfMissing.js
+++ b/toolkit/components/thumbnails/test/browser_thumbnails_bg_captureIfMissing.js
@@ -3,7 +3,7 @@
add_task(async function thumbnails_bg_captureIfMissing() {
let numNotifications = 0;
- function observe(subject, topic, data) {
+ function observe(subject, topic) {
is(topic, "page-thumbnail:create", "got expected topic");
numNotifications += 1;
}
diff --git a/toolkit/components/thumbnails/test/browser_thumbnails_bug727765.js b/toolkit/components/thumbnails/test/browser_thumbnails_bug727765.js
index 8e953e0ffa..d4dd7caca7 100644
--- a/toolkit/components/thumbnails/test/browser_thumbnails_bug727765.js
+++ b/toolkit/components/thumbnails/test/browser_thumbnails_bug727765.js
@@ -21,7 +21,7 @@ add_task(async function thumbnails_bg_bug727765() {
gBrowser,
url: URL,
},
- async browser => {
+ async () => {
await captureAndCheckColor(255, 0, 0, "we have a red thumbnail");
// Check the thumbnail color of the bottom right pixel.
diff --git a/toolkit/components/thumbnails/test/browser_thumbnails_bug818225.js b/toolkit/components/thumbnails/test/browser_thumbnails_bug818225.js
index 5f557c40a4..37f9b2f7d7 100644
--- a/toolkit/components/thumbnails/test/browser_thumbnails_bug818225.js
+++ b/toolkit/components/thumbnails/test/browser_thumbnails_bug818225.js
@@ -17,7 +17,7 @@ add_task(async function thumbnails_bg_bug818225() {
gBrowser,
url: URL,
},
- async browser => {
+ async () => {
gBrowserThumbnails.clearTopSiteURLCache();
await whenFileExists(URL);
}
diff --git a/toolkit/components/thumbnails/test/browser_thumbnails_redirect.js b/toolkit/components/thumbnails/test/browser_thumbnails_redirect.js
index b77b4011c3..ba06b9e779 100644
--- a/toolkit/components/thumbnails/test/browser_thumbnails_redirect.js
+++ b/toolkit/components/thumbnails/test/browser_thumbnails_redirect.js
@@ -21,7 +21,7 @@ add_task(async function thumbnails_redirect() {
gBrowser,
url: URL,
},
- browser => {}
+ () => {}
);
// Create a tab, redirecting to a page with a red background.
@@ -30,7 +30,7 @@ add_task(async function thumbnails_redirect() {
gBrowser,
url: URL,
},
- async browser => {
+ async () => {
await captureAndCheckColor(255, 0, 0, "we have a red thumbnail");
// Wait until the referrer's thumbnail's file has been written.
diff --git a/toolkit/components/thumbnails/test/browser_thumbnails_storage.js b/toolkit/components/thumbnails/test/browser_thumbnails_storage.js
index 6a9f1ed3f6..39d26897a2 100644
--- a/toolkit/components/thumbnails/test/browser_thumbnails_storage.js
+++ b/toolkit/components/thumbnails/test/browser_thumbnails_storage.js
@@ -89,7 +89,7 @@ async function promiseCreateThumbnail() {
gBrowser,
url: URL,
},
- async browser => {
+ async () => {
gBrowserThumbnails.clearTopSiteURLCache();
await whenFileExists(URL);
}
diff --git a/toolkit/components/thumbnails/test/browser_thumbnails_update.js b/toolkit/components/thumbnails/test/browser_thumbnails_update.js
index 004df9a01f..7a00717e2f 100644
--- a/toolkit/components/thumbnails/test/browser_thumbnails_update.js
+++ b/toolkit/components/thumbnails/test/browser_thumbnails_update.js
@@ -163,7 +163,7 @@ add_task(async function thumbnails_captureAndStore_error_response() {
gBrowser,
url: URL,
},
- async browser => {
+ async () => {
await captureAndCheckColor(0, 255, 0, "we have a green thumbnail");
}
);
@@ -176,7 +176,7 @@ add_task(async function thumbnails_captureAndStore_error_response() {
gBrowser,
url: URL,
},
- async browser => {
+ async () => {
await captureAndCheckColor(0, 255, 0, "we still have a green thumbnail");
}
);
@@ -195,7 +195,7 @@ add_task(async function thumbnails_captureAndStore_ok_response() {
gBrowser,
url: URL,
},
- async browser => {
+ async () => {
await captureAndCheckColor(0, 255, 0, "we have a green thumbnail");
}
);
@@ -208,7 +208,7 @@ add_task(async function thumbnails_captureAndStore_ok_response() {
gBrowser,
url: URL,
},
- async browser => {
+ async () => {
await captureAndCheckColor(255, 0, 0, "we now have a red thumbnail");
}
);
diff --git a/toolkit/components/timermanager/UpdateTimerManager.sys.mjs b/toolkit/components/timermanager/UpdateTimerManager.sys.mjs
index 607a3b0e71..14c34e8847 100644
--- a/toolkit/components/timermanager/UpdateTimerManager.sys.mjs
+++ b/toolkit/components/timermanager/UpdateTimerManager.sys.mjs
@@ -69,7 +69,7 @@ TimerManager.prototype = {
/**
* See nsIObserver.idl
*/
- observe: function TM_observe(aSubject, aTopic, aData) {
+ observe: function TM_observe(aSubject, aTopic) {
// Prevent setting the timer interval to a value of less than 30 seconds.
var minInterval = 30000;
// Prevent setting the first timer interval to a value of less than 10
diff --git a/toolkit/components/timermanager/tests/unit/consumerNotifications.js b/toolkit/components/timermanager/tests/unit/consumerNotifications.js
index 1e09207043..0f20e282e4 100644
--- a/toolkit/components/timermanager/tests/unit/consumerNotifications.js
+++ b/toolkit/components/timermanager/tests/unit/consumerNotifications.js
@@ -142,7 +142,7 @@ ChromeUtils.defineLazyGetter(this, "gCompReg", function () {
});
const gTest0TimerCallback = {
- notify: function T0CB_notify(aTimer) {
+ notify: function T0CB_notify() {
// This can happen when another notification fails and this timer having
// time to fire so check other timers are successful.
do_throw("gTest0TimerCallback notify method should not have been called");
@@ -157,7 +157,7 @@ const gTest0Factory = {
};
const gTest1TimerCallback = {
- notify: function T1CB_notify(aTimer) {
+ notify: function T1CB_notify() {
// This can happen when another notification fails and this timer having
// time to fire so check other timers are successful.
do_throw("gTest1TimerCallback notify method should not have been called");
@@ -172,7 +172,7 @@ const gTest1Factory = {
};
const gTest2TimerCallback = {
- notify: function T2CB_notify(aTimer) {
+ notify: function T2CB_notify() {
// This can happen when another notification fails and this timer having
// time to fire so check other timers are successful.
do_throw("gTest2TimerCallback notify method should not have been called");
@@ -197,7 +197,7 @@ const gTest3Factory = {
};
const gTest4TimerCallback = {
- notify: function T4CB_notify(aTimer) {
+ notify: function T4CB_notify() {
Services.catMan.deleteCategoryEntry(
CATEGORY_UPDATE_TIMER,
TESTS[4].desc,
@@ -216,7 +216,7 @@ const gTest4Factory = {
};
const gTest5TimerCallback = {
- notify: function T5CB_notify(aTimer) {
+ notify: function T5CB_notify() {
Services.catMan.deleteCategoryEntry(
CATEGORY_UPDATE_TIMER,
TESTS[5].desc,
@@ -235,7 +235,7 @@ const gTest5Factory = {
};
const gTest6TimerCallback = {
- notify: function T6CB_notify(aTimer) {
+ notify: function T6CB_notify() {
Services.catMan.deleteCategoryEntry(
CATEGORY_UPDATE_TIMER,
TESTS[6].desc,
@@ -254,7 +254,7 @@ const gTest6Factory = {
};
const gTest7TimerCallback = {
- notify: function T7CB_notify(aTimer) {
+ notify: function T7CB_notify() {
Services.catMan.deleteCategoryEntry(
CATEGORY_UPDATE_TIMER,
TESTS[7].desc,
@@ -273,7 +273,7 @@ const gTest7Factory = {
};
const gTest8TimerCallback = {
- notify: function T8CB_notify(aTimer) {
+ notify: function T8CB_notify() {
TESTS[8].notified = true;
TESTS[8].notifyTime = Date.now();
executeSoon(function () {
@@ -290,7 +290,7 @@ const gTest8Factory = {
};
const gTest9TimerCallback = {
- notify: function T9CB_notify(aTimer) {
+ notify: function T9CB_notify() {
TESTS[9].notified = true;
TESTS[9].notifyTime = Date.now();
executeSoon(function () {
@@ -301,7 +301,7 @@ const gTest9TimerCallback = {
};
const gTest10TimerCallback = {
- notify: function T9CB_notify(aTimer) {
+ notify: function T9CB_notify() {
// The timer should have been unregistered before this could
// be called.
do_throw("gTest10TimerCallback notify method should not have been called");
diff --git a/toolkit/components/tooltiptext/TooltipTextProvider.sys.mjs b/toolkit/components/tooltiptext/TooltipTextProvider.sys.mjs
index 96a3e8dd5f..a231f5b4cc 100644
--- a/toolkit/components/tooltiptext/TooltipTextProvider.sys.mjs
+++ b/toolkit/components/tooltiptext/TooltipTextProvider.sys.mjs
@@ -69,11 +69,7 @@ TooltipTextProvider.prototype = {
}
if (tipElement.namespaceURI == XUL_NS) {
lookingForSVGTitle = false;
- // NOTE: getAttribute behaves differently for XUL so we can't rely on
- // it returning null, see bug 232598.
- titleText = tipElement.hasAttribute("tooltiptext")
- ? tipElement.getAttribute("tooltiptext")
- : null;
+ titleText = tipElement.getAttribute("tooltiptext");
} else if (!defView.SVGElement.isInstance(tipElement)) {
lookingForSVGTitle = false;
titleText = tipElement.getAttribute("title");
diff --git a/toolkit/components/tooltiptext/tests/browser.toml b/toolkit/components/tooltiptext/tests/browser.toml
index d03716e683..42aca4a7a6 100644
--- a/toolkit/components/tooltiptext/tests/browser.toml
+++ b/toolkit/components/tooltiptext/tests/browser.toml
@@ -11,7 +11,6 @@ support-files = ["xul_tooltiptext.xhtml"]
["browser_bug581947.js"]
["browser_input_file_tooltips.js"]
-skip-if = ["os == 'win' && os_version == '10.0'"] # Permafail on Win 10 (bug 1400368)
["browser_nac_tooltip.js"]
diff --git a/toolkit/components/tooltiptext/tests/browser_bug581947.js b/toolkit/components/tooltiptext/tests/browser_bug581947.js
index 6e5eb9ea14..0971735470 100644
--- a/toolkit/components/tooltiptext/tests/browser_bug581947.js
+++ b/toolkit/components/tooltiptext/tests/browser_bug581947.js
@@ -55,7 +55,7 @@ function todo_check(aBrowser, aElementName, aBarred) {
return SpecialPowers.spawn(
aBrowser,
[[aElementName, aBarred]],
- async function ([aElementName, aBarred]) {
+ async function ([aElementName]) {
let e = content.document.createElement(aElementName);
let contentElement = content.document.getElementById("content");
contentElement.appendChild(e);
diff --git a/toolkit/components/translation/moz.build b/toolkit/components/translation/moz.build
index 75dfd04bc3..38214a2dc1 100644
--- a/toolkit/components/translation/moz.build
+++ b/toolkit/components/translation/moz.build
@@ -3,7 +3,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
with Files("**"):
- BUG_COMPONENT = ("Firefox", "Translation")
+ BUG_COMPONENT = ("Firefox", "Translations")
EXTRA_JS_MODULES.translation = [
"cld2/cld-worker.js",
diff --git a/toolkit/components/translations/actors/AboutTranslationsChild.sys.mjs b/toolkit/components/translations/actors/AboutTranslationsChild.sys.mjs
index 9d0b27a6a1..0b600bb03c 100644
--- a/toolkit/components/translations/actors/AboutTranslationsChild.sys.mjs
+++ b/toolkit/components/translations/actors/AboutTranslationsChild.sys.mjs
@@ -53,7 +53,7 @@ export class AboutTranslationsChild extends JSWindowActorChild {
receiveMessage({ name, data }) {
switch (name) {
- case "AboutTranslations:SendTranslationsPort":
+ case "AboutTranslations:SendTranslationsPort": {
const { fromLanguage, toLanguage, port } = data;
const transferables = [port];
this.contentWindow.postMessage(
@@ -67,6 +67,7 @@ export class AboutTranslationsChild extends JSWindowActorChild {
transferables
);
break;
+ }
default:
throw new Error("Unknown AboutTranslations message: " + name);
}
diff --git a/toolkit/components/translations/actors/TranslationsParent.sys.mjs b/toolkit/components/translations/actors/TranslationsParent.sys.mjs
index 70754d95c4..f262cbeab2 100644
--- a/toolkit/components/translations/actors/TranslationsParent.sys.mjs
+++ b/toolkit/components/translations/actors/TranslationsParent.sys.mjs
@@ -150,10 +150,110 @@ const VERIFY_SIGNATURES_FROM_FS = false;
*/
/**
- * The translations parent is used to orchestrate translations in Firefox. It can
- * download the wasm translation engines, and the machine learning language models.
+ * The state that is stored per a "top" ChromeWindow. This "top" ChromeWindow is the JS
+ * global associated with a browser window. Some state is unique to a browser window, and
+ * using the top ChromeWindow is a unique key that ensures the state will be unique to
+ * that browser window.
*
- * See Bug 971044 for more details of planned work.
+ * See BrowsingContext.webidl for information on the "top"
+ * See the TranslationsParent JSDoc for more information on the state management.
+ */
+class StatePerTopChromeWindow {
+ /**
+ * The storage backing for the states.
+ *
+ * @type {WeakMap<ChromeWindow, StatePerTopChromeWindow>}
+ */
+ static #states = new WeakMap();
+
+ /**
+ * When reloading the page, store the translation pair that needs translating.
+ *
+ * @type {null | TranslationPair}
+ */
+ translateOnPageReload = null;
+
+ /**
+ * The page may auto-translate due to user settings. On a page restore, always
+ * skip the page restore logic.
+ *
+ * @type {boolean}
+ */
+ isPageRestored = false;
+
+ /**
+ * Remember the detected languages on a page reload. This will keep the translations
+ * button from disappearing and reappearing, which causes the button to lose focus.
+ *
+ * @type {LangTags | null} previousDetectedLanguages
+ */
+ previousDetectedLanguages = null;
+
+ static #id = 0;
+ /**
+ * @param {ChromeWindow} topChromeWindow
+ */
+ constructor(topChromeWindow) {
+ this.id = StatePerTopChromeWindow.#id++;
+ StatePerTopChromeWindow.#states.set(topChromeWindow, this);
+ }
+
+ /**
+ * @param {ChromeWindow} topChromeWindow
+ * @returns {StatePerTopChromeWindow}
+ */
+ static getOrCreate(topChromeWindow) {
+ let state = StatePerTopChromeWindow.#states.get(topChromeWindow);
+ if (state) {
+ return state;
+ }
+ state = new StatePerTopChromeWindow(topChromeWindow);
+ StatePerTopChromeWindow.#states.set(topChromeWindow, state);
+ return state;
+ }
+}
+
+/**
+ * The TranslationsParent is used to orchestrate translations in Firefox. It can
+ * download the Wasm translation engine, and the language models. It manages the life
+ * cycle for offering and performing translations.
+ *
+ * Care must be taken for the life cycle of the state management and data caching. The
+ * following examples use a fictitious `myState` property to show how state can be stored.
+ *
+ * There is only 1 TranslationsParent static class in the parent process. At this
+ * layer it is safe to store things like translation models and general browser
+ * configuration as these don't change across browser windows. This is accessed like
+ * `TranslationsParent.myState`
+ *
+ * The next layer down are the top ChromeWindows. These map to the UI and user's conception
+ * of a browser window, such as what you would get by hitting cmd+n or ctrl+n to get a new
+ * browser window. State such as whether a page is reloaded or general navigation events
+ * must be unique per ChromeWindow. State here is stored in the `StatePerTopChromeWindow`
+ * abstraction, like `this.getWindowState().myState`. This layer also consists of a
+ * `FullPageTranslationsPanel` instance per top ChromeWindow (at least on Desktop).
+ *
+ * The final layer consists of the multiple tabs and navigation history inside of a
+ * ChromeWindow. Data for this layer is safe to store on the TranslationsParent instance,
+ * like `this.myState`.
+ *
+ * Below is an ascii diagram of this relationship.
+ *
+ * ┌─────────────────────────────────────────────────────────────────────────────┐
+ * │ static TranslationsParent │
+ * └─────────────────────────────────────────────────────────────────────────────┘
+ * | |
+ * v v
+ * ┌──────────────────────────────────────┐ ┌──────────────────────────────────────┐
+ * │ top ChromeWindow │ │ top ChromeWindow │
+ * │ (FullPageTranslationsPanel instance) │ │ (FullPageTranslationsPanel instance) │
+ * └──────────────────────────────────────┘ └──────────────────────────────────────┘
+ * | | | | | |
+ * v v v v v v
+ * ┌────────────────────┐ ┌─────┐ ┌─────┐ ┌────────────────────┐ ┌─────┐ ┌─────┐
+ * │ TranslationsParent │ │ ... │ │ ... │ │ TranslationsParent │ │ ... │ │ ... │
+ * │ (actor instance) │ │ │ │ │ │ (actor instance) │ │ │ │ │
+ * └────────────────────┘ └─────┘ └─────┘ └────────────────────┘ └─────┘ └─────┘
*/
export class TranslationsParent extends JSWindowActorParent {
/**
@@ -205,26 +305,32 @@ export class TranslationsParent extends JSWindowActorParent {
#isDestroyed = false;
/**
- * Remember the detected languages on a page reload. This will keep the translations
- * button from disappearing and reappearing, which causes the button to lose focus.
+ * There is only one static TranslationsParent for all of the top ChromeWindows.
+ * The top ChromeWindow maps to the user's conception of a window such as when you hit
+ * cmd+n or ctrl+n.
*
- * @type {LangTags | null} previousDetectedLanguages
+ * @returns {StatePerTopChromeWindow}
*/
- static #previousDetectedLanguages = null;
+ getWindowState() {
+ const state = StatePerTopChromeWindow.getOrCreate(
+ this.browsingContext.top.embedderWindowGlobal
+ );
+ return state;
+ }
actorCreated() {
this.innerWindowId = this.browsingContext.top.embedderElement.innerWindowID;
+ const windowState = this.getWindowState();
this.languageState = new TranslationsLanguageState(
this,
- TranslationsParent.#previousDetectedLanguages
+ windowState.previousDetectedLanguages
);
- TranslationsParent.#previousDetectedLanguages = null;
+ windowState.previousDetectedLanguages = null;
- if (TranslationsParent.#translateOnPageReload) {
+ if (windowState.translateOnPageReload) {
// The actor was recreated after a page reload, start the translation.
- const { fromLanguage, toLanguage } =
- TranslationsParent.#translateOnPageReload;
- TranslationsParent.#translateOnPageReload = null;
+ const { fromLanguage, toLanguage } = windowState.translateOnPageReload;
+ windowState.translateOnPageReload = null;
lazy.console.log(
`Translating on a page reload from "${fromLanguage}" to "${toLanguage}".`
@@ -261,12 +367,6 @@ export class TranslationsParent extends JSWindowActorParent {
static #translationsWasmRemoteClient = null;
/**
- * The page may auto-translate due to user settings. On a page restore, always
- * skip the page restore logic.
- */
- static #isPageRestored = false;
-
- /**
* Allows the actor's behavior to be changed when the translations engine is mocked via
* a dummy RemoteSettingsClient.
*
@@ -280,13 +380,6 @@ export class TranslationsParent extends JSWindowActorParent {
static #isTranslationsEngineSupported = null;
/**
- * When reloading the page, store the translation pair that needs translating.
- *
- * @type {null | TranslationPair}
- */
- static #translateOnPageReload = null;
-
- /**
* An ordered list of preferred languages based on:
* 1. App languages
* 2. Web requested languages
@@ -477,6 +570,24 @@ export class TranslationsParent extends JSWindowActorParent {
}
/**
+ * Retrieves the Translations actor from the current browser context.
+ *
+ * @param {object} browser - The browser object from which to get the context.
+ *
+ * @returns {object} The Translations actor for handling translation actions.
+ * @throws {Error} Throws an error if the TranslationsParent actor cannot be found.
+ */
+ static getTranslationsActor(browser) {
+ const actor =
+ browser.browsingContext.currentWindowGlobal.getActor("Translations");
+
+ if (!actor) {
+ throw new Error("Unable to get the TranslationsParent actor.");
+ }
+ return actor;
+ }
+
+ /**
* Detect if Wasm SIMD is supported, and cache the value. It's better to check
* for support before downloading large binary blobs to a user who can't even
* use the feature. This function also respects mocks and simulating unsupported
@@ -670,6 +781,42 @@ export class TranslationsParent extends JSWindowActorParent {
return TranslationsParent.#preferredLanguages;
}
+ /**
+ * Requests a new translations port.
+ *
+ * @param {number} innerWindowId - The id of the current window.
+ * @param {string} fromLanguage - The BCP-47 from-language tag.
+ * @param {string} toLanguage - The BCP-47 to-language tag.
+ *
+ * @returns {Promise<MessagePort | undefined>} The port for communication with the translation engine, or undefined on failure.
+ */
+ static async requestTranslationsPort(
+ innerWindowId,
+ fromLanguage,
+ toLanguage
+ ) {
+ let translationsEngineParent;
+ try {
+ translationsEngineParent =
+ await lazy.EngineProcess.getTranslationsEngineParent();
+ } catch (error) {
+ console.error("Failed to get the translation engine process", error);
+ return undefined;
+ }
+
+ // The MessageChannel will be used for communicating directly between the content
+ // process and the engine's process.
+ const { port1, port2 } = new MessageChannel();
+ translationsEngineParent.startTranslation(
+ fromLanguage,
+ toLanguage,
+ port1,
+ innerWindowId
+ );
+
+ return port2;
+ }
+
async receiveMessage({ name, data }) {
switch (name) {
case "Translations:ReportLangTags": {
@@ -826,10 +973,11 @@ export class TranslationsParent extends JSWindowActorParent {
* @param {LangTags} langTags
* @returns {boolean}
*/
- static #maybeAutoTranslate(langTags) {
- if (TranslationsParent.#isPageRestored) {
+ #maybeAutoTranslate(langTags) {
+ const windowState = this.getWindowState();
+ if (windowState.isPageRestored) {
// The user clicked the restore button. Respect it for one page load.
- TranslationsParent.#isPageRestored = false;
+ windowState.isPageRestored = false;
// Skip this auto-translation.
return false;
@@ -875,6 +1023,9 @@ export class TranslationsParent extends JSWindowActorParent {
}
return Array.from(languagePairMap.values());
});
+ TranslationsParent.#languagePairs.catch(() => {
+ TranslationsParent.#languagePairs = null;
+ });
}
return TranslationsParent.#languagePairs;
}
@@ -1671,7 +1822,8 @@ export class TranslationsParent extends JSWindowActorParent {
`Translation model fetched in ${duration / 1000} seconds:`,
record.fromLang,
record.toLang,
- record.fileType
+ record.fileType,
+ record.version
);
})
);
@@ -1901,7 +2053,8 @@ export class TranslationsParent extends JSWindowActorParent {
if (this.languageState.requestedTranslationPair) {
// This page has already been translated, restore it and translate it
// again once the actor has been recreated.
- TranslationsParent.#translateOnPageReload = { fromLanguage, toLanguage };
+ const windowState = this.getWindowState();
+ windowState.translateOnPageReload = { fromLanguage, toLanguage };
this.restorePage(fromLanguage);
} else {
const { docLangTag } = this.languageState.detectedLanguages;
@@ -1970,47 +2123,29 @@ export class TranslationsParent extends JSWindowActorParent {
restorePage() {
TranslationsParent.telemetry().onRestorePage();
// Skip auto-translate for one page load.
- TranslationsParent.#isPageRestored = true;
+ const windowState = this.getWindowState();
+ windowState.isPageRestored = true;
this.languageState.requestedTranslationPair = null;
- TranslationsParent.#previousDetectedLanguages =
+ windowState.previousDetectedLanguages =
this.languageState.detectedLanguages;
const browser = this.browsingContext.embedderElement;
browser.reload();
}
- /**
- * Keep track of when the location changes.
- */
- static #locationChangeId = 0;
-
static onLocationChange(browser) {
if (!lazy.translationsEnabledPref) {
// The pref isn't enabled, so don't attempt to get the actor.
return;
}
- let windowGlobal = browser.browsingContext.currentWindowGlobal;
- TranslationsParent.#locationChangeId++;
let actor;
try {
- actor = windowGlobal.getActor("Translations");
- } catch (_) {
- // The actor may not be supported on this page.
- }
- if (actor) {
- actor.languageState.locationChangeId =
- TranslationsParent.#locationChangeId;
+ actor =
+ browser.browsingContext.currentWindowGlobal.getActor("Translations");
+ } catch {
+ // The actor may not be supported on this page, which throws an error.
}
- }
-
- /**
- * Is this actor active for the current location change?
- *
- * @param {number} locationChangeId - The id sent by the "TranslationsParent:LanguageState" event.
- * @returns {boolean}
- */
- static isActiveLocation(locationChangeId) {
- return locationChangeId === TranslationsParent.#locationChangeId;
+ actor?.languageState.locationChanged();
}
async queryIdentifyLanguage() {
@@ -2046,7 +2181,7 @@ export class TranslationsParent extends JSWindowActorParent {
langTags.docLangTag &&
langTags.userLangTag &&
langTags.isDocLangTagSupported &&
- TranslationsParent.#maybeAutoTranslate(langTags) &&
+ this.#maybeAutoTranslate(langTags) &&
!TranslationsParent.shouldNeverTranslateLanguage(langTags.docLangTag) &&
!this.shouldNeverTranslateSite()
) {
@@ -2599,7 +2734,6 @@ class TranslationsLanguageState {
constructor(actor, previousDetectedLanguages = null) {
this.#actor = actor;
this.#detectedLanguages = previousDetectedLanguages;
- this.dispatch();
}
/**
@@ -2616,9 +2750,6 @@ class TranslationsLanguageState {
/** @type {LangTags | null} */
#detectedLanguages = null;
- /** @type {number} */
- #locationChangeId = -1;
-
/** @type {null | TranslationErrors} */
#error = null;
@@ -2628,11 +2759,6 @@ class TranslationsLanguageState {
* Dispatch anytime the language details change, so that any UI can react to it.
*/
dispatch() {
- if (!TranslationsParent.isActiveLocation(this.#locationChangeId)) {
- // Do not dispatch as this location is not active.
- return;
- }
-
const browser = this.#actor.browsingContext.top.embedderElement;
if (!browser) {
return;
@@ -2690,26 +2816,11 @@ class TranslationsLanguageState {
}
/**
- * This id represents the last location change that happened for this actor. This
- * allows the UI to disambiguate when there are races and out of order events that
- * are dispatched. Only the most up to date `locationChangeId` is used.
- *
- * @returns {number}
+ * When the location changes remove the previous error and dispatch a change event
+ * so that any browser chrome UI that needs to be updated can get the latest state.
*/
- get locationChangeId() {
- return this.#locationChangeId;
- }
-
- set locationChangeId(locationChangeId) {
- if (this.#locationChangeId === locationChangeId) {
- return;
- }
-
- this.#locationChangeId = locationChangeId;
-
- // When the location changes remove the previous error.
+ locationChanged() {
this.#error = null;
-
this.dispatch();
}
diff --git a/toolkit/components/translations/content/Translator.mjs b/toolkit/components/translations/content/Translator.mjs
new file mode 100644
index 0000000000..9a0de6a2c2
--- /dev/null
+++ b/toolkit/components/translations/content/Translator.mjs
@@ -0,0 +1,227 @@
+/* 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/. */
+
+/**
+ * This class manages the communications to the translations engine via MessagePort.
+ */
+export class Translator {
+ /**
+ * The port through with to communicate with the Translations Engine.
+ *
+ * @type {MessagePort}
+ */
+ #port;
+
+ /**
+ * True if the current #port is closed, otherwise false.
+ *
+ * @type {boolean}
+ */
+ #portClosed = true;
+
+ /**
+ * A promise that resolves when the Translator has successfully established communication with
+ * the translations engine, or rejects if communication was not successfully established.
+ *
+ * @type {Promise<void>}
+ */
+ #ready = Promise.reject;
+
+ /**
+ * The BCP-47 language tag for the from-language.
+ *
+ * @type {string}
+ */
+ #fromLanguage;
+
+ /**
+ * The BCP-47 language tag for the to-language.
+ *
+ * @type {string}
+ */
+ #toLanguage;
+
+ /**
+ * The callback function to request a new port, provided at construction time
+ * by the caller.
+ *
+ * @type {Function}
+ */
+ #requestTranslationsPort;
+
+ /**
+ * An id for each message sent. This is used to match up the request and response.
+ *
+ * @type {number}
+ */
+ #nextMessageId = 0;
+
+ /**
+ * Tie together a message id to a resolved response.
+ *
+ * @type {Map<number, TranslationRequest>}
+ */
+ #requests = new Map();
+
+ /**
+ * Initializes a new Translator.
+ *
+ * Prefer using the Translator.create() function.
+ *
+ * @see Translator.create
+ *
+ * @param {string} fromLanguage - The BCP-47 from-language tag.
+ * @param {string} toLanguage - The BCP-47 to-language tag.
+ * @param {Function} requestTranslationsPort - A callback function to request a new MessagePort.
+ */
+ constructor(fromLanguage, toLanguage, requestTranslationsPort) {
+ this.#fromLanguage = fromLanguage;
+ this.#toLanguage = toLanguage;
+ this.#requestTranslationsPort = requestTranslationsPort;
+ this.#createNewPortIfClosed();
+ }
+
+ /**
+ * @returns {Promise<void>} A promise that indicates if the Translator is ready to translate.
+ */
+ get ready() {
+ return this.#ready;
+ }
+
+ /**
+ * @returns {boolean} True if the translation port is closed, false otherwise.
+ */
+ get portClosed() {
+ return this.#portClosed;
+ }
+
+ /**
+ * @returns {string} The BCP-47 language tag of the from-language.
+ */
+ get fromLanguage() {
+ return this.#fromLanguage;
+ }
+
+ /**
+ * @returns {string} The BCP-47 language tag of the to-language.
+ */
+ get toLanguage() {
+ return this.#toLanguage;
+ }
+
+ /**
+ * Opens up a port and creates a new translator.
+ *
+ * @param {string} fromLanguage
+ * @param {string} toLanguage
+ * @returns {Promise<Translator>}
+ */
+ static async create(fromLanguage, toLanguage, requestTranslationsPort) {
+ if (!fromLanguage || !toLanguage || !requestTranslationsPort) {
+ return undefined;
+ }
+
+ const translator = new Translator(
+ fromLanguage,
+ toLanguage,
+ requestTranslationsPort
+ );
+ await translator.ready;
+
+ return translator;
+ }
+
+ /**
+ * Creates a new translation port if the current one is closed.
+ *
+ * @returns {Promise<void>} - Whether the Translator is ready to translate.
+ */
+ async #createNewPortIfClosed() {
+ if (!this.#portClosed) {
+ return this.#ready;
+ }
+
+ this.#port = await this.#requestTranslationsPort(
+ this.#fromLanguage,
+ this.#toLanguage
+ );
+
+ // Create a promise that will be resolved when the engine is ready.
+ const { promise, resolve, reject } = Promise.withResolvers();
+
+ // Match up a response on the port to message that was sent.
+ this.#port.onmessage = ({ data }) => {
+ switch (data.type) {
+ case "TranslationsPort:TranslationResponse": {
+ const { targetText, messageId } = data;
+ // A request may not match match a messageId if there is a race during the pausing
+ // and discarding of the queue.
+ this.#requests.get(messageId)?.resolve(targetText);
+ break;
+ }
+ case "TranslationsPort:GetEngineStatusResponse": {
+ if (data.status === "ready") {
+ this.#portClosed = false;
+ resolve();
+ } else {
+ this.#portClosed = true;
+ reject();
+ }
+ break;
+ }
+ case "TranslationsPort:EngineTerminated": {
+ this.#portClosed = true;
+ break;
+ }
+ default:
+ break;
+ }
+ };
+
+ this.#ready = promise;
+ this.#port.postMessage({ type: "TranslationsPort:GetEngineStatusRequest" });
+
+ return this.#ready;
+ }
+
+ /**
+ * Send a request to translate text to the Translations Engine. If it returns `null`
+ * then the request is stale. A rejection means there was an error in the translation.
+ * This request may be queued.
+ *
+ * @param {string} sourceText
+ * @returns {Promise<string>}
+ */
+ async translate(sourceText, isHTML = false) {
+ await this.#createNewPortIfClosed();
+ const { promise, resolve, reject } = Promise.withResolvers();
+ const messageId = this.#nextMessageId++;
+
+ // Store the "resolve" for the promise. It will be matched back up with the
+ // `messageId` in #handlePortMessage.
+ this.#requests.set(messageId, {
+ sourceText,
+ isHTML,
+ resolve,
+ reject,
+ });
+ this.#port.postMessage({
+ type: "TranslationsPort:TranslationRequest",
+ messageId,
+ sourceText,
+ isHTML,
+ });
+
+ return promise;
+ }
+
+ /**
+ * Close the port and remove any pending or queued requests.
+ */
+ destroy() {
+ this.#port.close();
+ this.#portClosed = true;
+ this.#ready = Promise.reject;
+ }
+}
diff --git a/toolkit/components/translations/content/translations.mjs b/toolkit/components/translations/content/translations.mjs
index 478f854bb5..d5541408d4 100644
--- a/toolkit/components/translations/content/translations.mjs
+++ b/toolkit/components/translations/content/translations.mjs
@@ -10,6 +10,8 @@
AT_logError, AT_createTranslationsPort, AT_isHtmlTranslation,
AT_isTranslationEngineSupported, AT_identifyLanguage */
+import { Translator } from "chrome://global/content/translations/Translator.mjs";
+
// Allow tests to override this value so that they can run faster.
// This is the delay in milliseconds.
window.DEBOUNCE_DELAY = 200;
@@ -66,7 +68,7 @@ class TranslationsState {
* The translator is only valid for a single language pair, and needs
* to be recreated if the language pair changes.
*
- * @type {null | Promise<Translator>}
+ * @type {null | Translator}
*/
translator = null;
@@ -133,42 +135,28 @@ class TranslationsState {
onDebounce: async () => {
// The contents of "this" can change between async steps, store a local variable
// binding of these values.
- const {
- fromLanguage,
- toLanguage,
- messageToTranslate,
- translator: translatorPromise,
- } = this;
+ const { fromLanguage, toLanguage, messageToTranslate, translator } = this;
if (!this.isTranslationEngineSupported) {
// Never translate when the engine isn't supported.
return;
}
- if (
- !fromLanguage ||
- !toLanguage ||
- !messageToTranslate ||
- !translatorPromise
- ) {
+ if (!fromLanguage || !toLanguage || !messageToTranslate || !translator) {
// Not everything is set for translation.
this.ui.updateTranslation("");
return;
}
- const [translator] = await Promise.all([
- // Ensure the engine is ready to go.
- translatorPromise,
- // Ensure the previous translation has finished so that only the latest
- // translation goes through.
- this.translationRequest,
- ]);
+ // Ensure the previous translation has finished so that only the latest
+ // translation goes through.
+ await this.translationRequest;
if (
// Check if the current configuration has changed and if this is stale. If so
// then skip this request, as there is already a newer request with more up to
// date information.
- this.translator !== translatorPromise ||
+ this.translator !== translator ||
this.fromLanguage !== fromLanguage ||
this.toLanguage !== toLanguage ||
this.messageToTranslate !== messageToTranslate
@@ -177,8 +165,10 @@ class TranslationsState {
}
const start = performance.now();
-
- this.translationRequest = translator.translate(messageToTranslate);
+ this.translationRequest = this.translator.translate(
+ messageToTranslate,
+ AT_isHtmlTranslation()
+ );
const translation = await this.translationRequest;
// The measure events will show up in the Firefox Profiler.
@@ -236,15 +226,40 @@ class TranslationsState {
`Creating a new translator for "${this.fromLanguage}" to "${this.toLanguage}"`
);
- this.translator = Translator.create(this.fromLanguage, this.toLanguage);
- this.maybeRequestTranslation();
+ const translationPortPromise = (fromLanguage, toLanguage) => {
+ const { promise, resolve } = Promise.withResolvers();
+
+ const getResponse = ({ data }) => {
+ if (
+ data.type == "GetTranslationsPort" &&
+ data.fromLanguage === fromLanguage &&
+ data.toLanguage === toLanguage
+ ) {
+ window.removeEventListener("message", getResponse);
+ resolve(data.port);
+ }
+ };
+
+ window.addEventListener("message", getResponse);
+ AT_createTranslationsPort(fromLanguage, toLanguage);
+
+ return promise;
+ };
try {
- await this.translator;
+ const translatorPromise = Translator.create(
+ this.fromLanguage,
+ this.toLanguage,
+ translationPortPromise
+ );
const duration = performance.now() - start;
+
// Signal to tests that the translator was created so they can exit.
window.postMessage("translator-ready");
AT_log(`Created a new Translator in ${duration / 1000} seconds`);
+
+ this.translator = await translatorPromise;
+ this.maybeRequestTranslation();
} catch (error) {
this.ui.showInfo("about-translations-engine-error");
AT_logError("Failed to get the Translations worker", error);
@@ -655,137 +670,3 @@ function debounce({ onDebounce, doEveryTime }) {
}, timeLeft);
};
}
-
-/**
- * Perform transalations over a `MessagePort`. This class manages the communications to
- * the translations engine.
- */
-class Translator {
- /**
- * @type {MessagePort}
- */
- #port;
-
- /**
- * An id for each message sent. This is used to match up the request and response.
- */
- #nextMessageId = 0;
-
- /**
- * Tie together a message id to a resolved response.
- *
- * @type {Map<number, TranslationRequest>}
- */
- #requests = new Map();
-
- engineStatus = "initializing";
-
- /**
- * @param {MessagePort} port
- */
- constructor(port) {
- this.#port = port;
-
- // Create a promise that will be resolved when the engine is ready.
- let engineLoaded;
- let engineFailed;
- this.ready = new Promise((resolve, reject) => {
- engineLoaded = resolve;
- engineFailed = reject;
- });
-
- // Match up a response on the port to message that was sent.
- port.onmessage = ({ data }) => {
- switch (data.type) {
- case "TranslationsPort:TranslationResponse": {
- const { targetText, messageId } = data;
- // A request may not match match a messageId if there is a race during the pausing
- // and discarding of the queue.
- this.#requests.get(messageId)?.resolve(targetText);
- break;
- }
- case "TranslationsPort:GetEngineStatusResponse": {
- if (data.status === "ready") {
- engineLoaded();
- } else {
- engineFailed();
- }
- break;
- }
- default:
- AT_logError("Unknown translations port message: " + data.type);
- break;
- }
- };
-
- port.postMessage({ type: "TranslationsPort:GetEngineStatusRequest" });
- }
-
- /**
- * Opens up a port and creates a new translator.
- *
- * @param {string} fromLanguage
- * @param {string} toLanguage
- * @returns {Promise<Translator>}
- */
- static create(fromLanguage, toLanguage) {
- return new Promise((resolve, reject) => {
- AT_createTranslationsPort(fromLanguage, toLanguage);
-
- function getResponse({ data }) {
- if (
- data.type == "GetTranslationsPort" &&
- fromLanguage === data.fromLanguage &&
- toLanguage === data.toLanguage
- ) {
- // The response matches, resolve the port.
- const translator = new Translator(data.port);
-
- // Resolve the translator once it is ready, or propagate the rejection
- // if it failed.
- translator.ready.then(() => resolve(translator), reject);
- window.removeEventListener("message", getResponse);
- }
- }
-
- // Listen for a response for the message port.
- window.addEventListener("message", getResponse);
- });
- }
-
- /**
- * Send a request to translate text to the Translations Engine. If it returns `null`
- * then the request is stale. A rejection means there was an error in the translation.
- * This request may be queued.
- *
- * @param {string} sourceText
- * @returns {Promise<string>}
- */
- translate(sourceText) {
- return new Promise((resolve, reject) => {
- const messageId = this.#nextMessageId++;
- // Store the "resolve" for the promise. It will be matched back up with the
- // `messageId` in #handlePortMessage.
- const isHTML = AT_isHtmlTranslation();
- this.#requests.set(messageId, {
- sourceText,
- isHTML,
- resolve,
- reject,
- });
- this.#port.postMessage({
- type: "TranslationsPort:TranslationRequest",
- messageId,
- sourceText,
- isHTML,
- });
- });
- }
-
- /**
- * Close the port and remove any pending or queued requests.
- */
- destroy() {
- this.#port.close();
- }
-}
diff --git a/toolkit/components/translations/jar.mn b/toolkit/components/translations/jar.mn
index b31f17718f..1dde76d3ff 100644
--- a/toolkit/components/translations/jar.mn
+++ b/toolkit/components/translations/jar.mn
@@ -11,6 +11,7 @@ toolkit.jar:
content/global/translations/translations.html (content/translations.html)
content/global/translations/translations.css (content/translations.css)
content/global/translations/translations.mjs (content/translations.mjs)
+ content/global/translations/Translator.mjs (content/Translator.mjs)
content/global/translations/TranslationsTelemetry.sys.mjs (TranslationsTelemetry.sys.mjs)
# Uncomment this line to test a local build of Bergamot. It will automatically be loaded in.
diff --git a/toolkit/components/translations/moz.build b/toolkit/components/translations/moz.build
index cdb0ca4e75..0e6c222126 100644
--- a/toolkit/components/translations/moz.build
+++ b/toolkit/components/translations/moz.build
@@ -7,7 +7,7 @@ SPHINX_TREES["/toolkit/components/translations"] = "docs"
JAR_MANIFESTS += ["jar.mn"]
with Files("**"):
- BUG_COMPONENT = ("Firefox", "Translation")
+ BUG_COMPONENT = ("Firefox", "Translations")
DIRS += ["actors"]
diff --git a/toolkit/components/translations/tests/browser/browser.toml b/toolkit/components/translations/tests/browser/browser.toml
index 50a1be7150..c940bea9a0 100644
--- a/toolkit/components/translations/tests/browser/browser.toml
+++ b/toolkit/components/translations/tests/browser/browser.toml
@@ -9,6 +9,7 @@ support-files = [
"translations-tester-es-2.html",
"translations-tester-fr.html",
"translations-tester-no-tag.html",
+ "translations-tester-select.html",
"translations-tester-shadow-dom-es.html",
"translations-tester-shadow-dom-mutation-es.html",
"translations-tester-shadow-dom-mutation-es-2.html",
diff --git a/toolkit/components/translations/tests/browser/browser_about_translations_dropdowns.js b/toolkit/components/translations/tests/browser/browser_about_translations_dropdowns.js
index 6aed512952..381903e5b1 100644
--- a/toolkit/components/translations/tests/browser/browser_about_translations_dropdowns.js
+++ b/toolkit/components/translations/tests/browser/browser_about_translations_dropdowns.js
@@ -13,7 +13,7 @@ add_task(async function test_about_translations_dropdowns() {
await openAboutTranslations({
languagePairs,
dataForContent: languagePairs,
- runInPage: async ({ dataForContent: languagePairs, selectors }) => {
+ runInPage: async ({ selectors }) => {
const { document } = content;
await ContentTaskUtils.waitForCondition(
diff --git a/toolkit/components/translations/tests/browser/shared-head.js b/toolkit/components/translations/tests/browser/shared-head.js
index 82b3e783a7..afa060c8a3 100644
--- a/toolkit/components/translations/tests/browser/shared-head.js
+++ b/toolkit/components/translations/tests/browser/shared-head.js
@@ -9,6 +9,9 @@
const { EngineProcess } = ChromeUtils.importESModule(
"chrome://global/content/ml/EngineProcess.sys.mjs"
);
+const { TranslationsPanelShared } = ChromeUtils.importESModule(
+ "chrome://browser/content/translations/TranslationsPanelShared.sys.mjs"
+);
// Avoid about:blank's non-standard behavior.
const BLANK_PAGE =
@@ -32,6 +35,8 @@ const NO_LANGUAGE_URL =
URL_COM_PREFIX + DIR_PATH + "translations-tester-no-tag.html";
const EMPTY_PDF_URL =
URL_COM_PREFIX + DIR_PATH + "translations-tester-empty-pdf-file.pdf";
+const SELECT_TEST_PAGE_URL =
+ URL_COM_PREFIX + DIR_PATH + "translations-tester-select.html";
const PIVOT_LANGUAGE = "en";
const LANGUAGE_PAIRS = [
@@ -349,33 +354,37 @@ function getTranslationsParent() {
/**
* Closes all open panels and menu popups related to Translations.
+ *
+ * @param {ChromeWindow} [win]
*/
-async function closeAllOpenPanelsAndMenus() {
- await closeSettingsMenuIfOpen();
- await closeFullPageTranslationsPanelIfOpen();
- await closeSelectTranslationsPanelIfOpen();
- await closeContextMenuIfOpen();
+async function closeAllOpenPanelsAndMenus(win) {
+ await closeSettingsMenuIfOpen(win);
+ await closeFullPageTranslationsPanelIfOpen(win);
+ await closeSelectTranslationsPanelIfOpen(win);
+ await closeContextMenuIfOpen(win);
}
/**
* Closes the popup element with the given Id if it is open.
*
* @param {string} popupElementId
+ * @param {ChromeWindow} [win]
*/
-async function closePopupIfOpen(popupElementId) {
+async function closePopupIfOpen(popupElementId, win = window) {
await waitForCondition(async () => {
- const contextMenu = document.getElementById(popupElementId);
- if (!contextMenu) {
+ const popupElement = win.document.getElementById(popupElementId);
+ if (!popupElement) {
return true;
}
- if (contextMenu.state === "closed") {
+ if (popupElement.state === "closed") {
return true;
}
let popuphiddenPromise = BrowserTestUtils.waitForEvent(
- contextMenu,
+ popupElement,
"popuphidden"
);
- PanelMultiView.hidePopup(contextMenu);
+ popupElement.hidePopup();
+ PanelMultiView.hidePopup(popupElement);
await popuphiddenPromise;
return false;
});
@@ -383,30 +392,41 @@ async function closePopupIfOpen(popupElementId) {
/**
* Closes the context menu if it is open.
+ *
+ * @param {ChromeWindow} [win]
*/
-async function closeContextMenuIfOpen() {
- await closePopupIfOpen("contentAreaContextMenu");
+async function closeContextMenuIfOpen(win) {
+ await closePopupIfOpen("contentAreaContextMenu", win);
}
/**
* Closes the translations panel settings menu if it is open.
+ *
+ * @param {ChromeWindow} [win]
*/
-async function closeSettingsMenuIfOpen() {
- await closePopupIfOpen("full-page-translations-panel-settings-menupopup");
+async function closeSettingsMenuIfOpen(win) {
+ await closePopupIfOpen(
+ "full-page-translations-panel-settings-menupopup",
+ win
+ );
}
/**
* Closes the translations panel if it is open.
+ *
+ * @param {ChromeWindow} [win]
*/
-async function closeFullPageTranslationsPanelIfOpen() {
- await closePopupIfOpen("full-page-translations-panel");
+async function closeFullPageTranslationsPanelIfOpen(win) {
+ await closePopupIfOpen("full-page-translations-panel", win);
}
/**
* Closes the translations panel if it is open.
+ *
+ * @param {ChromeWindow} [win]
*/
-async function closeSelectTranslationsPanelIfOpen() {
- await closePopupIfOpen("select-translations-panel");
+async function closeSelectTranslationsPanelIfOpen(win) {
+ await closePopupIfOpen("select-translations-panel", win);
}
/**
@@ -471,6 +491,7 @@ async function createAndMockRemoteSettings({
// The TranslationsParent will pull the language pair values from the JSON dump
// of Remote Settings. Clear these before mocking the translations engine.
TranslationsParent.clearCache();
+ TranslationsPanelShared.clearCache();
TranslationsParent.mockTranslationsEngine(
remoteClients.translationModels.client,
@@ -485,6 +506,7 @@ async function createAndMockRemoteSettings({
TranslationsParent.unmockTranslationsEngine();
TranslationsParent.clearCache();
+ TranslationsPanelShared.clearCache();
},
remoteClients,
};
@@ -497,38 +519,56 @@ async function loadTestPage({
prefs,
autoOffer,
permissionsUrls,
+ win = window,
}) {
info(`Loading test page starting at url: ${page}`);
- // Ensure no engine is being carried over from a previous test.
- await EngineProcess.destroyTranslationsEngine();
- Services.fog.testResetFOG();
- await SpecialPowers.pushPrefEnv({
- set: [
- // Enabled by default.
- ["browser.translations.enable", true],
- ["browser.translations.logLevel", "All"],
- ["browser.translations.panelShown", true],
- ["browser.translations.automaticallyPopup", true],
- ["browser.translations.alwaysTranslateLanguages", ""],
- ["browser.translations.neverTranslateLanguages", ""],
- ...(prefs ?? []),
- ],
- });
- await SpecialPowers.pushPermissions(
- [
- ENGLISH_PAGE_URL,
- FRENCH_PAGE_URL,
- NO_LANGUAGE_URL,
- SPANISH_PAGE_URL,
- SPANISH_PAGE_URL_2,
- SPANISH_PAGE_URL_DOT_ORG,
- ...(permissionsUrls || []),
- ].map(url => ({
- type: TRANSLATIONS_PERMISSION,
- allow: true,
- context: url,
- }))
- );
+
+ // If there are multiple windows, only do the first time setup on the main window.
+ const isFirstTimeSetup = win === window;
+
+ let remoteClients = null;
+ let removeMocks = () => {};
+
+ if (isFirstTimeSetup) {
+ // Ensure no engine is being carried over from a previous test.
+ await EngineProcess.destroyTranslationsEngine();
+
+ Services.fog.testResetFOG();
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ // Enabled by default.
+ ["browser.translations.enable", true],
+ ["browser.translations.logLevel", "All"],
+ ["browser.translations.panelShown", true],
+ ["browser.translations.automaticallyPopup", true],
+ ["browser.translations.alwaysTranslateLanguages", ""],
+ ["browser.translations.neverTranslateLanguages", ""],
+ ...(prefs ?? []),
+ ],
+ });
+ await SpecialPowers.pushPermissions(
+ [
+ ENGLISH_PAGE_URL,
+ FRENCH_PAGE_URL,
+ NO_LANGUAGE_URL,
+ SPANISH_PAGE_URL,
+ SPANISH_PAGE_URL_2,
+ SPANISH_PAGE_URL_DOT_ORG,
+ ...(permissionsUrls || []),
+ ].map(url => ({
+ type: TRANSLATIONS_PERMISSION,
+ allow: true,
+ context: url,
+ }))
+ );
+
+ const result = await createAndMockRemoteSettings({
+ languagePairs,
+ autoDownloadFromRemoteSettings,
+ });
+ remoteClients = result.remoteClients;
+ removeMocks = result.removeMocks;
+ }
if (autoOffer) {
TranslationsParent.testAutomaticPopup = true;
@@ -536,16 +576,11 @@ async function loadTestPage({
// Start the tab at a blank page.
const tab = await BrowserTestUtils.openNewForegroundTab(
- gBrowser,
+ win.gBrowser,
BLANK_PAGE,
true // waitForLoad
);
- const { remoteClients, removeMocks } = await createAndMockRemoteSettings({
- languagePairs,
- autoDownloadFromRemoteSettings,
- });
-
BrowserTestUtils.startLoadingURIString(tab.linkedBrowser, page);
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
@@ -1377,7 +1412,7 @@ async function waitForCloseDialogWindow(dialogWindow) {
// Extracted from https://searchfox.org/mozilla-central/rev/40ef22080910c2e2c27d9e2120642376b1d8b8b2/browser/components/preferences/in-content/tests/head.js#41
function promiseLoadSubDialog(aURL) {
- return new Promise((resolve, reject) => {
+ return new Promise(resolve => {
content.gSubDialog._dialogStack.addEventListener(
"dialogopen",
function dialogopen(aEvent) {
@@ -1444,3 +1479,10 @@ async function loadBlankPage() {
BrowserTestUtils.startLoadingURIString(gBrowser.selectedBrowser, BLANK_PAGE);
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
}
+
+/**
+ * Destroys the Translations Engine process.
+ */
+async function destroyTranslationsEngine() {
+ await EngineProcess.destroyTranslationsEngine();
+}
diff --git a/toolkit/components/translations/tests/browser/translations-test.mjs b/toolkit/components/translations/tests/browser/translations-test.mjs
index a740a2d1cc..3ff107a699 100644
--- a/toolkit/components/translations/tests/browser/translations-test.mjs
+++ b/toolkit/components/translations/tests/browser/translations-test.mjs
@@ -38,21 +38,36 @@ export function getSelectors() {
getHeader() {
return content.document.querySelector("header");
},
- getFirstParagraph() {
- return content.document.querySelector("p:first-of-type");
- },
getLastParagraph() {
return content.document.querySelector("p:last-of-type");
},
- getSpanishParagraph() {
- return content.document.getElementById("spanish-paragraph");
+ getFrenchSection() {
+ return content.document.getElementById("french-section");
},
- getSpanishHyperlink() {
- return content.document.getElementById("spanish-hyperlink");
+ getEnglishSection() {
+ return content.document.getElementById("english-section");
+ },
+ getSpanishSection() {
+ return content.document.getElementById("spanish-section");
+ },
+ getFrenchSentence() {
+ return content.document.getElementById("french-sentence");
+ },
+ getEnglishSentence() {
+ return content.document.getElementById("english-sentence");
+ },
+ getSpanishSentence() {
+ return content.document.getElementById("spanish-sentence");
},
getEnglishHyperlink() {
return content.document.getElementById("english-hyperlink");
},
+ getFrenchHyperlink() {
+ return content.document.getElementById("french-hyperlink");
+ },
+ getSpanishHyperlink() {
+ return content.document.getElementById("spanish-hyperlink");
+ },
};
}
diff --git a/toolkit/components/translations/tests/browser/translations-tester-es.html b/toolkit/components/translations/tests/browser/translations-tester-es.html
index f589df0649..abf2d42c62 100644
--- a/toolkit/components/translations/tests/browser/translations-tester-es.html
+++ b/toolkit/components/translations/tests/browser/translations-tester-es.html
@@ -20,7 +20,7 @@
<header lang="en">The following is an excerpt from Don Quijote de la Mancha, which is in the public domain</header>
<h1>Don Quijote de La Mancha</h1>
<h2>Capítulo VIII.</h2>
- <p id="spanish-paragraph">Del buen suceso que el valeroso don Quijote tuvo en la espantable y jamás imaginada aventura de los molinos de viento, con otros sucesos dignos de felice recordación</p>
+ <p>Del buen suceso que el valeroso don Quijote tuvo en la espantable y jamás imaginada aventura de los molinos de viento, con otros sucesos dignos de felice recordación</p>
<p>En esto, descubrieron treinta o cuarenta molinos de viento que hay en aquel campo; y, así como don Quijote los vio, dijo a su escudero:</p>
<p>— La ventura va guiando nuestras cosas mejor de lo que acertáramos a desear, porque ves allí, amigo Sancho Panza, donde se descubren treinta, o pocos más, desaforados gigantes, con quien pienso hacer batalla y quitarles a todos las vidas, con cuyos despojos comenzaremos a enriquecer; que ésta es buena guerra, y es gran servicio de Dios quitar tan mala simiente de sobre la faz de la tierra.</p>
<p>— ¿Qué gigantes? —dijo Sancho Panza.</p>
@@ -32,13 +32,5 @@
<p>Levantóse en esto un poco de viento y las grandes aspas comenzaron a moverse, lo cual visto por don Quijote, dijo:</p>
<p>— Pues, aunque mováis más brazos que los del gigante Briareo, me lo habéis de pagar.</p>
</div>
- <div>
- <header lang="en">The following is a link to another test page in Spanish.</header>
- <p><a id="spanish-hyperlink" href="https://example.org/browser/translations-tester-es.html">Otra pagina en español.</a></p>
- </div>
- <div>
- <header lang="en">The following is a link to another test page in English.</header>
- <p lang="en"><a id="english-hyperlink" href="https://example.org/browser/translations-tester-en.html">Another page in English.</a></p>
- </div>
</body>
</html>
diff --git a/toolkit/components/translations/tests/browser/translations-tester-select.html b/toolkit/components/translations/tests/browser/translations-tester-select.html
new file mode 100644
index 0000000000..034d89a95b
--- /dev/null
+++ b/toolkit/components/translations/tests/browser/translations-tester-select.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html lang="es">
+<head>
+ <meta charset="utf-8" />
+ <title>Select Translations Test</title>
+ <style>
+ div {
+ margin: 10px auto;
+ width: 600px
+ }
+ p {
+ margin: 47px 0;
+ font-size: 21px;
+ line-height: 2;
+ }
+ </style>
+</head>
+<body>
+ <div>
+ <header lang="en">The following is an excerpt from Don Quijote de la Mancha, which is in the public domain</header>
+ <div id="spanish-section">
+ <h1>Don Quijote de La Mancha</h1>
+ <h2>Capítulo VIII.</h2>
+ <p id="spanish-sentence">Del buen suceso que el valeroso don Quijote tuvo en la espantable y jamás imaginada aventura de los molinos de viento, con otros sucesos dignos de felice recordación</p>
+ <p>En esto, descubrieron treinta o cuarenta molinos de viento que hay en aquel campo; y, así como don Quijote los vio, dijo a su escudero:</p>
+ <p>— La ventura va guiando nuestras cosas mejor de lo que acertáramos a desear, porque ves allí, amigo Sancho Panza, donde se descubren treinta, o pocos más, desaforados gigantes, con quien pienso hacer batalla y quitarles a todos las vidas, con cuyos despojos comenzaremos a enriquecer; que ésta es buena guerra, y es gran servicio de Dios quitar tan mala simiente de sobre la faz de la tierra.</p>
+ <p>— ¿Qué gigantes? —dijo Sancho Panza.</p>
+ <p>— Aquellos que allí ves —respondió su amo— de los brazos largos, que los suelen tener algunos de casi dos leguas.</p>
+ <p>— Mire vuestra merced —respondió Sancho— que aquellos que allí se parecen no son gigantes, sino molinos de viento, y lo que en ellos parecen brazos son las aspas, que, volteadas del viento, hacen andar la piedra del molino.</p>
+ <p>— Bien parece —respondió don Quijote— que no estás cursado en esto de las aventuras: ellos son gigantes; y si tienes miedo, quítate de ahí, y ponte en oración en el espacio que yo voy a entrar con ellos en fiera y desigual batalla.</p>
+ <p>Y, diciendo esto, dio de espuelas a su caballo Rocinante, sin atender a las voces que su escudero Sancho le daba, advirtiéndole que, sin duda alguna, eran molinos de viento, y no gigantes, aquellos que iba a acometer. Pero él iba tan puesto en que eran gigantes, que ni oía las voces de su escudero Sancho ni echaba de ver, aunque estaba ya bien cerca, lo que eran; antes, iba diciendo en voces altas:</p>
+ <p>— Non fuyades, cobardes y viles criaturas, que un solo caballero es el que os acomete.</p>
+ <p>Levantóse en esto un poco de viento y las grandes aspas comenzaron a moverse, lo cual visto por don Quijote, dijo:</p>
+ <p>— Pues, aunque mováis más brazos que los del gigante Briareo, me lo habéis de pagar.</p>
+ </div>
+ </div>
+ <div>
+ <header lang="en">The following is an excerpt from Frankenstein, which is in the public domain</header>
+ <div id="english-section">
+ <h1>Frankenstein</h1>
+ <h2>Letter 3</h2>
+ <p>To Mrs. Saville, England.</p>
+ <p>July 7th, 17—.</p>
+ <p>My dear Sister,</p>
+ <p>I write a few lines in haste to say that I am safe—and well advanced on my voyage. This letter will reach England by a merchantman now on its homeward voyage from Archangel; more fortunate than I, who may not see my native land, perhaps, for many years. I am, however, in good spirits: my men are bold and apparently firm of purpose, nor do the floating sheets of ice that continually pass us, indicating the dangers of the region towards which we are advancing, appear to dismay them. We have already reached a very high latitude; but it is the height of summer, and although not so warm as in England, the southern gales, which blow us speedily towards those shores which I so ardently desire to attain, breathe a degree of renovating warmth which I had not expected.</p>
+ <p>No incidents have hitherto befallen us that would make a figure in a letter. One or two stiff gales and the springing of a leak are accidents which experienced navigators scarcely remember to record, and I shall be well content if nothing worse happen to us during our voyage.</p>
+ <p>Adieu, my dear Margaret. Be assured that for my own sake, as well as yours, I will not rashly encounter danger. I will be cool, persevering, and prudent.</p>
+ <p>But success shall crown my endeavours. Wherefore not? Thus far I have gone, tracing a secure way over the pathless seas, the very stars themselves being witnesses and testimonies of my triumph. Why not still proceed over the untamed yet obedient element? What can stop the determined heart and resolved will of man?</p>
+ <p id="english-sentence">My swelling heart involuntarily pours itself out thus. But I must finish. Heaven bless my beloved sister!</p>
+ </div>
+ </div>
+ <div>
+ <header lang="en">The following is an excerpt from Les Misérables, which is in the public domain</header>
+ <div id="french-section">
+ <h1>Les Misérables</h1>
+ <h2>Chapitre II</h2>
+ <p>Monsieur Myriel devient monseigneur Bienvenu</p>
+ <p id="french-sentence">Le palais épiscopal de Digne était attenant à l'hôpital.</p>
+ <p>Le palais épiscopal était un vaste et bel hôtel bâti en pierre au commencement du siècle dernier par monseigneur Henri Puget, docteur en théologie de la faculté de Paris, abbé de Simore, lequel était évêque de Digne en 1712. Ce palais était un vrai logis seigneurial. Tout y avait grand air, les appartements de l'évêque, les salons, les chambres, la cour d'honneur, fort large, avec promenoirs à arcades, selon l'ancienne mode florentine, les jardins plantés de magnifiques arbres. Dans la salle à manger, longue et superbe galerie qui était au rez-de-chaussée et s'ouvrait sur les jardins, monseigneur Henri Puget avait donné à manger en cérémonie le 29 juillet 1714 à messeigneurs Charles Brûlart de Genlis, archevêque-prince d'Embrun, Antoine de Mesgrigny, capucin, évêque de Grasse, Philippe de Vendôme, grand prieur de France, abbé de Saint-Honoré de Lérins, François de Berton de Grillon, évêque-baron de Vence, César de Sabran de Forcalquier, évêque-seigneur de Glandève, et Jean Soanen, prêtre de l'oratoire, prédicateur ordinaire du roi, évêque-seigneur de Senez. Les portraits de ces sept révérends personnages décoraient cette salle, et cette date mémorable, 29 juillet 1714, y était gravée en lettres d'or sur une table de marbre blanc.</p>
+ <p>L'hôpital était une maison étroite et basse à un seul étage avec un petit jardin. Trois jours après son arrivée, l'évêque visita l'hôpital. La visite terminée, il fit prier le directeur de vouloir bien venir jusque chez lui.</p>
+ </div>
+ </div>
+ <div>
+ <header lang="en">The following is a link to another test page in Spanish.</header>
+ <p lang="es"><a id="spanish-hyperlink" href="https://example.org/browser/translations-tester-es.html">Otra pagina en español.</a></p>
+ </div>
+ <div>
+ <header lang="en">The following is a link to another test page in French.</header>
+ <p lang="fr"><a id="french-hyperlink" href="https://example.org/browser/translations-tester-fr.html">Une autre page en français.</a></p>
+ </div>
+ <div>
+ <header lang="en">The following is a link to another test page in English.</header>
+ <p lang="en"><a id="english-hyperlink" href="https://example.org/browser/translations-tester-en.html">Another page in English.</a></p>
+ </div>
+</body>
+</html>
diff --git a/toolkit/components/translations/translations.d.ts b/toolkit/components/translations/translations.d.ts
index cc8d462a9c..9823f6a845 100644
--- a/toolkit/components/translations/translations.d.ts
+++ b/toolkit/components/translations/translations.d.ts
@@ -269,3 +269,10 @@ export interface SupportedLanguages {
}
export type TranslationErrors = "engine-load-error";
+
+export type SelectTranslationsPanelState =
+ | { phase: "closed"; }
+ | { phase: "idle"; fromLanguage: string; toLanguage: string, sourceText: string, }
+ | { phase: "translatable"; fromLanguage: string; toLanguage: string, sourceText: string, }
+ | { phase: "translating"; fromLanguage: string; toLanguage: string, sourceText: string, }
+ | { phase: "translated"; fromLanguage: string; toLanguage: string, sourceText: string, translatedText: string, }
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/Cargo.toml b/toolkit/components/uniffi-bindgen-gecko-js/Cargo.toml
index 32232c64b9..5ef165dc73 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/Cargo.toml
+++ b/toolkit/components/uniffi-bindgen-gecko-js/Cargo.toml
@@ -15,7 +15,7 @@ clap = { version = "4", default-features = false, features = ["std", "derive", "
extend = "1.1"
heck = "0.4"
uniffi = { workspace = true }
-uniffi_bindgen = "0.25"
+uniffi_bindgen = { workspace = true }
serde = "1"
toml = "0.5"
camino = "1.0.8"
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustRelevancy.sys.mjs b/toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustRelevancy.sys.mjs
new file mode 100644
index 0000000000..2a4663e1cb
--- /dev/null
+++ b/toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustRelevancy.sys.mjs
@@ -0,0 +1,1249 @@
+// This file was autogenerated by the `uniffi-bindgen-gecko-js` crate.
+// Trust me, you don't want to mess with it!
+
+import { UniFFITypeError } from "resource://gre/modules/UniFFI.sys.mjs";
+
+
+
+// Objects intended to be used in the unit tests
+export var UnitTestObjs = {};
+
+// Write/Read data to/from an ArrayBuffer
+class ArrayBufferDataStream {
+ constructor(arrayBuffer) {
+ this.dataView = new DataView(arrayBuffer);
+ this.pos = 0;
+ }
+
+ readUint8() {
+ let rv = this.dataView.getUint8(this.pos);
+ this.pos += 1;
+ return rv;
+ }
+
+ writeUint8(value) {
+ this.dataView.setUint8(this.pos, value);
+ this.pos += 1;
+ }
+
+ readUint16() {
+ let rv = this.dataView.getUint16(this.pos);
+ this.pos += 2;
+ return rv;
+ }
+
+ writeUint16(value) {
+ this.dataView.setUint16(this.pos, value);
+ this.pos += 2;
+ }
+
+ readUint32() {
+ let rv = this.dataView.getUint32(this.pos);
+ this.pos += 4;
+ return rv;
+ }
+
+ writeUint32(value) {
+ this.dataView.setUint32(this.pos, value);
+ this.pos += 4;
+ }
+
+ readUint64() {
+ let rv = this.dataView.getBigUint64(this.pos);
+ this.pos += 8;
+ return Number(rv);
+ }
+
+ writeUint64(value) {
+ this.dataView.setBigUint64(this.pos, BigInt(value));
+ this.pos += 8;
+ }
+
+
+ readInt8() {
+ let rv = this.dataView.getInt8(this.pos);
+ this.pos += 1;
+ return rv;
+ }
+
+ writeInt8(value) {
+ this.dataView.setInt8(this.pos, value);
+ this.pos += 1;
+ }
+
+ readInt16() {
+ let rv = this.dataView.getInt16(this.pos);
+ this.pos += 2;
+ return rv;
+ }
+
+ writeInt16(value) {
+ this.dataView.setInt16(this.pos, value);
+ this.pos += 2;
+ }
+
+ readInt32() {
+ let rv = this.dataView.getInt32(this.pos);
+ this.pos += 4;
+ return rv;
+ }
+
+ writeInt32(value) {
+ this.dataView.setInt32(this.pos, value);
+ this.pos += 4;
+ }
+
+ readInt64() {
+ let rv = this.dataView.getBigInt64(this.pos);
+ this.pos += 8;
+ return Number(rv);
+ }
+
+ writeInt64(value) {
+ this.dataView.setBigInt64(this.pos, BigInt(value));
+ this.pos += 8;
+ }
+
+ readFloat32() {
+ let rv = this.dataView.getFloat32(this.pos);
+ this.pos += 4;
+ return rv;
+ }
+
+ writeFloat32(value) {
+ this.dataView.setFloat32(this.pos, value);
+ this.pos += 4;
+ }
+
+ readFloat64() {
+ let rv = this.dataView.getFloat64(this.pos);
+ this.pos += 8;
+ return rv;
+ }
+
+ writeFloat64(value) {
+ this.dataView.setFloat64(this.pos, value);
+ this.pos += 8;
+ }
+
+
+ writeString(value) {
+ const encoder = new TextEncoder();
+ // Note: in order to efficiently write this data, we first write the
+ // string data, reserving 4 bytes for the size.
+ const dest = new Uint8Array(this.dataView.buffer, this.pos + 4);
+ const encodeResult = encoder.encodeInto(value, dest);
+ if (encodeResult.read != value.length) {
+ throw new UniFFIError(
+ "writeString: out of space when writing to ArrayBuffer. Did the computeSize() method returned the wrong result?"
+ );
+ }
+ const size = encodeResult.written;
+ // Next, go back and write the size before the string data
+ this.dataView.setUint32(this.pos, size);
+ // Finally, advance our position past both the size and string data
+ this.pos += size + 4;
+ }
+
+ readString() {
+ const decoder = new TextDecoder();
+ const size = this.readUint32();
+ const source = new Uint8Array(this.dataView.buffer, this.pos, size)
+ const value = decoder.decode(source);
+ this.pos += size;
+ return value;
+ }
+
+ // Reads a RelevancyStore pointer from the data stream
+ // UniFFI Pointers are **always** 8 bytes long. That is enforced
+ // by the C++ and Rust Scaffolding code.
+ readPointerRelevancyStore() {
+ const pointerId = 0; // relevancy:RelevancyStore
+ const res = UniFFIScaffolding.readPointer(pointerId, this.dataView.buffer, this.pos);
+ this.pos += 8;
+ return res;
+ }
+
+ // Writes a RelevancyStore pointer into the data stream
+ // UniFFI Pointers are **always** 8 bytes long. That is enforced
+ // by the C++ and Rust Scaffolding code.
+ writePointerRelevancyStore(value) {
+ const pointerId = 0; // relevancy:RelevancyStore
+ UniFFIScaffolding.writePointer(pointerId, value, this.dataView.buffer, this.pos);
+ this.pos += 8;
+ }
+
+}
+
+function handleRustResult(result, liftCallback, liftErrCallback) {
+ switch (result.code) {
+ case "success":
+ return liftCallback(result.data);
+
+ case "error":
+ throw liftErrCallback(result.data);
+
+ case "internal-error":
+ let message = result.internalErrorMessage;
+ if (message) {
+ throw new UniFFIInternalError(message);
+ } else {
+ throw new UniFFIInternalError("Unknown error");
+ }
+
+ default:
+ throw new UniFFIError(`Unexpected status code: ${result.code}`);
+ }
+}
+
+class UniFFIError {
+ constructor(message) {
+ this.message = message;
+ }
+
+ toString() {
+ return `UniFFIError: ${this.message}`
+ }
+}
+
+class UniFFIInternalError extends UniFFIError {}
+
+// Base class for FFI converters
+class FfiConverter {
+ // throw `UniFFITypeError` if a value to be converted has an invalid type
+ static checkType(value) {
+ if (value === undefined ) {
+ throw new UniFFITypeError(`undefined`);
+ }
+ if (value === null ) {
+ throw new UniFFITypeError(`null`);
+ }
+ }
+}
+
+// Base class for FFI converters that lift/lower by reading/writing to an ArrayBuffer
+class FfiConverterArrayBuffer extends FfiConverter {
+ static lift(buf) {
+ return this.read(new ArrayBufferDataStream(buf));
+ }
+
+ static lower(value) {
+ const buf = new ArrayBuffer(this.computeSize(value));
+ const dataStream = new ArrayBufferDataStream(buf);
+ this.write(dataStream, value);
+ return buf;
+ }
+}
+
+// Symbols that are used to ensure that Object constructors
+// can only be used with a proper UniFFI pointer
+const uniffiObjectPtr = Symbol("uniffiObjectPtr");
+const constructUniffiObject = Symbol("constructUniffiObject");
+UnitTestObjs.uniffiObjectPtr = uniffiObjectPtr;
+
+// Export the FFIConverter object to make external types work.
+export class FfiConverterU32 extends FfiConverter {
+ static checkType(value) {
+ super.checkType(value);
+ if (!Number.isInteger(value)) {
+ throw new UniFFITypeError(`${value} is not an integer`);
+ }
+ if (value < 0 || value > 4294967295) {
+ throw new UniFFITypeError(`${value} exceeds the U32 bounds`);
+ }
+ }
+ static computeSize() {
+ return 4;
+ }
+ static lift(value) {
+ return value;
+ }
+ static lower(value) {
+ return value;
+ }
+ static write(dataStream, value) {
+ dataStream.writeUint32(value)
+ }
+ static read(dataStream) {
+ return dataStream.readUint32()
+ }
+}
+
+// Export the FFIConverter object to make external types work.
+export class FfiConverterString extends FfiConverter {
+ static checkType(value) {
+ super.checkType(value);
+ if (typeof value !== "string") {
+ throw new UniFFITypeError(`${value} is not a string`);
+ }
+ }
+
+ static lift(buf) {
+ const decoder = new TextDecoder();
+ const utf8Arr = new Uint8Array(buf);
+ return decoder.decode(utf8Arr);
+ }
+ static lower(value) {
+ const encoder = new TextEncoder();
+ return encoder.encode(value).buffer;
+ }
+
+ static write(dataStream, value) {
+ dataStream.writeString(value);
+ }
+
+ static read(dataStream) {
+ return dataStream.readString();
+ }
+
+ static computeSize(value) {
+ const encoder = new TextEncoder();
+ return 4 + encoder.encode(value).length
+ }
+}
+
+export class RelevancyStore {
+ // Use `init` to instantiate this class.
+ // DO NOT USE THIS CONSTRUCTOR DIRECTLY
+ constructor(opts) {
+ if (!Object.prototype.hasOwnProperty.call(opts, constructUniffiObject)) {
+ throw new UniFFIError("Attempting to construct an object using the JavaScript constructor directly" +
+ "Please use a UDL defined constructor, or the init function for the primary constructor")
+ }
+ if (!opts[constructUniffiObject] instanceof UniFFIPointer) {
+ throw new UniFFIError("Attempting to create a UniFFI object with a pointer that is not an instance of UniFFIPointer")
+ }
+ this[uniffiObjectPtr] = opts[constructUniffiObject];
+ }
+ /**
+ * An async constructor for RelevancyStore.
+ *
+ * @returns {Promise<RelevancyStore>}: A promise that resolves
+ * to a newly constructed RelevancyStore
+ */
+ static init(dbpath) {
+ const liftResult = (result) => FfiConverterTypeRelevancyStore.lift(result);
+ const liftError = (data) => FfiConverterTypeRelevancyApiError.lift(data);
+ const functionCall = () => {
+ try {
+ FfiConverterString.checkType(dbpath)
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart("dbpath");
+ }
+ throw e;
+ }
+ return UniFFIScaffolding.callAsync(
+ 1, // relevancy:uniffi_relevancy_fn_constructor_relevancystore_new
+ FfiConverterString.lower(dbpath),
+ )
+ }
+ try {
+ return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
+ } catch (error) {
+ return Promise.reject(error)
+ }}
+
+ calculateMetrics() {
+ const liftResult = (result) => FfiConverterTypeInterestMetrics.lift(result);
+ const liftError = (data) => FfiConverterTypeRelevancyApiError.lift(data);
+ const functionCall = () => {
+ return UniFFIScaffolding.callAsync(
+ 2, // relevancy:uniffi_relevancy_fn_method_relevancystore_calculate_metrics
+ FfiConverterTypeRelevancyStore.lower(this),
+ )
+ }
+ try {
+ return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
+ } catch (error) {
+ return Promise.reject(error)
+ }
+ }
+
+ ingest(topUrls) {
+ const liftResult = (result) => undefined;
+ const liftError = (data) => FfiConverterTypeRelevancyApiError.lift(data);
+ const functionCall = () => {
+ try {
+ FfiConverterSequencestring.checkType(topUrls)
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart("topUrls");
+ }
+ throw e;
+ }
+ return UniFFIScaffolding.callAsync(
+ 3, // relevancy:uniffi_relevancy_fn_method_relevancystore_ingest
+ FfiConverterTypeRelevancyStore.lower(this),
+ FfiConverterSequencestring.lower(topUrls),
+ )
+ }
+ try {
+ return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
+ } catch (error) {
+ return Promise.reject(error)
+ }
+ }
+
+ userInterestVector() {
+ const liftResult = (result) => FfiConverterTypeInterestVector.lift(result);
+ const liftError = (data) => FfiConverterTypeRelevancyApiError.lift(data);
+ const functionCall = () => {
+ return UniFFIScaffolding.callAsync(
+ 4, // relevancy:uniffi_relevancy_fn_method_relevancystore_user_interest_vector
+ FfiConverterTypeRelevancyStore.lower(this),
+ )
+ }
+ try {
+ return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
+ } catch (error) {
+ return Promise.reject(error)
+ }
+ }
+
+}
+
+// Export the FFIConverter object to make external types work.
+export class FfiConverterTypeRelevancyStore extends FfiConverter {
+ static lift(value) {
+ const opts = {};
+ opts[constructUniffiObject] = value;
+ return new RelevancyStore(opts);
+ }
+
+ static lower(value) {
+ const ptr = value[uniffiObjectPtr];
+ if (!(ptr instanceof UniFFIPointer)) {
+ throw new UniFFITypeError("Object is not a 'RelevancyStore' instance");
+ }
+ return ptr;
+ }
+
+ static read(dataStream) {
+ return this.lift(dataStream.readPointerRelevancyStore());
+ }
+
+ static write(dataStream, value) {
+ dataStream.writePointerRelevancyStore(value[uniffiObjectPtr]);
+ }
+
+ static computeSize(value) {
+ return 8;
+ }
+}
+
+export class InterestMetrics {
+ constructor({ topSingleInterestSimilarity, top2interestSimilarity, top3interestSimilarity } = {}) {
+ try {
+ FfiConverterU32.checkType(topSingleInterestSimilarity)
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart("topSingleInterestSimilarity");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(top2interestSimilarity)
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart("top2interestSimilarity");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(top3interestSimilarity)
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart("top3interestSimilarity");
+ }
+ throw e;
+ }
+ this.topSingleInterestSimilarity = topSingleInterestSimilarity;
+ this.top2interestSimilarity = top2interestSimilarity;
+ this.top3interestSimilarity = top3interestSimilarity;
+ }
+ equals(other) {
+ return (
+ this.topSingleInterestSimilarity == other.topSingleInterestSimilarity &&
+ this.top2interestSimilarity == other.top2interestSimilarity &&
+ this.top3interestSimilarity == other.top3interestSimilarity
+ )
+ }
+}
+
+// Export the FFIConverter object to make external types work.
+export class FfiConverterTypeInterestMetrics extends FfiConverterArrayBuffer {
+ static read(dataStream) {
+ return new InterestMetrics({
+ topSingleInterestSimilarity: FfiConverterU32.read(dataStream),
+ top2interestSimilarity: FfiConverterU32.read(dataStream),
+ top3interestSimilarity: FfiConverterU32.read(dataStream),
+ });
+ }
+ static write(dataStream, value) {
+ FfiConverterU32.write(dataStream, value.topSingleInterestSimilarity);
+ FfiConverterU32.write(dataStream, value.top2interestSimilarity);
+ FfiConverterU32.write(dataStream, value.top3interestSimilarity);
+ }
+
+ static computeSize(value) {
+ let totalSize = 0;
+ totalSize += FfiConverterU32.computeSize(value.topSingleInterestSimilarity);
+ totalSize += FfiConverterU32.computeSize(value.top2interestSimilarity);
+ totalSize += FfiConverterU32.computeSize(value.top3interestSimilarity);
+ return totalSize
+ }
+
+ static checkType(value) {
+ super.checkType(value);
+ if (!(value instanceof InterestMetrics)) {
+ throw new UniFFITypeError(`Expected 'InterestMetrics', found '${typeof value}'`);
+ }
+ try {
+ FfiConverterU32.checkType(value.topSingleInterestSimilarity);
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart(".topSingleInterestSimilarity");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(value.top2interestSimilarity);
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart(".top2interestSimilarity");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(value.top3interestSimilarity);
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart(".top3interestSimilarity");
+ }
+ throw e;
+ }
+ }
+}
+
+export class InterestVector {
+ constructor({ animals, arts, autos, business, career, education, fashion, finance, food, government, health, hobbies, home, news, realEstate, society, sports, tech, travel, inconclusive } = {}) {
+ try {
+ FfiConverterU32.checkType(animals)
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart("animals");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(arts)
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart("arts");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(autos)
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart("autos");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(business)
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart("business");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(career)
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart("career");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(education)
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart("education");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(fashion)
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart("fashion");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(finance)
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart("finance");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(food)
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart("food");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(government)
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart("government");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(health)
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart("health");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(hobbies)
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart("hobbies");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(home)
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart("home");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(news)
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart("news");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(realEstate)
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart("realEstate");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(society)
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart("society");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(sports)
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart("sports");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(tech)
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart("tech");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(travel)
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart("travel");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(inconclusive)
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart("inconclusive");
+ }
+ throw e;
+ }
+ this.animals = animals;
+ this.arts = arts;
+ this.autos = autos;
+ this.business = business;
+ this.career = career;
+ this.education = education;
+ this.fashion = fashion;
+ this.finance = finance;
+ this.food = food;
+ this.government = government;
+ this.health = health;
+ this.hobbies = hobbies;
+ this.home = home;
+ this.news = news;
+ this.realEstate = realEstate;
+ this.society = society;
+ this.sports = sports;
+ this.tech = tech;
+ this.travel = travel;
+ this.inconclusive = inconclusive;
+ }
+ equals(other) {
+ return (
+ this.animals == other.animals &&
+ this.arts == other.arts &&
+ this.autos == other.autos &&
+ this.business == other.business &&
+ this.career == other.career &&
+ this.education == other.education &&
+ this.fashion == other.fashion &&
+ this.finance == other.finance &&
+ this.food == other.food &&
+ this.government == other.government &&
+ this.health == other.health &&
+ this.hobbies == other.hobbies &&
+ this.home == other.home &&
+ this.news == other.news &&
+ this.realEstate == other.realEstate &&
+ this.society == other.society &&
+ this.sports == other.sports &&
+ this.tech == other.tech &&
+ this.travel == other.travel &&
+ this.inconclusive == other.inconclusive
+ )
+ }
+}
+
+// Export the FFIConverter object to make external types work.
+export class FfiConverterTypeInterestVector extends FfiConverterArrayBuffer {
+ static read(dataStream) {
+ return new InterestVector({
+ animals: FfiConverterU32.read(dataStream),
+ arts: FfiConverterU32.read(dataStream),
+ autos: FfiConverterU32.read(dataStream),
+ business: FfiConverterU32.read(dataStream),
+ career: FfiConverterU32.read(dataStream),
+ education: FfiConverterU32.read(dataStream),
+ fashion: FfiConverterU32.read(dataStream),
+ finance: FfiConverterU32.read(dataStream),
+ food: FfiConverterU32.read(dataStream),
+ government: FfiConverterU32.read(dataStream),
+ health: FfiConverterU32.read(dataStream),
+ hobbies: FfiConverterU32.read(dataStream),
+ home: FfiConverterU32.read(dataStream),
+ news: FfiConverterU32.read(dataStream),
+ realEstate: FfiConverterU32.read(dataStream),
+ society: FfiConverterU32.read(dataStream),
+ sports: FfiConverterU32.read(dataStream),
+ tech: FfiConverterU32.read(dataStream),
+ travel: FfiConverterU32.read(dataStream),
+ inconclusive: FfiConverterU32.read(dataStream),
+ });
+ }
+ static write(dataStream, value) {
+ FfiConverterU32.write(dataStream, value.animals);
+ FfiConverterU32.write(dataStream, value.arts);
+ FfiConverterU32.write(dataStream, value.autos);
+ FfiConverterU32.write(dataStream, value.business);
+ FfiConverterU32.write(dataStream, value.career);
+ FfiConverterU32.write(dataStream, value.education);
+ FfiConverterU32.write(dataStream, value.fashion);
+ FfiConverterU32.write(dataStream, value.finance);
+ FfiConverterU32.write(dataStream, value.food);
+ FfiConverterU32.write(dataStream, value.government);
+ FfiConverterU32.write(dataStream, value.health);
+ FfiConverterU32.write(dataStream, value.hobbies);
+ FfiConverterU32.write(dataStream, value.home);
+ FfiConverterU32.write(dataStream, value.news);
+ FfiConverterU32.write(dataStream, value.realEstate);
+ FfiConverterU32.write(dataStream, value.society);
+ FfiConverterU32.write(dataStream, value.sports);
+ FfiConverterU32.write(dataStream, value.tech);
+ FfiConverterU32.write(dataStream, value.travel);
+ FfiConverterU32.write(dataStream, value.inconclusive);
+ }
+
+ static computeSize(value) {
+ let totalSize = 0;
+ totalSize += FfiConverterU32.computeSize(value.animals);
+ totalSize += FfiConverterU32.computeSize(value.arts);
+ totalSize += FfiConverterU32.computeSize(value.autos);
+ totalSize += FfiConverterU32.computeSize(value.business);
+ totalSize += FfiConverterU32.computeSize(value.career);
+ totalSize += FfiConverterU32.computeSize(value.education);
+ totalSize += FfiConverterU32.computeSize(value.fashion);
+ totalSize += FfiConverterU32.computeSize(value.finance);
+ totalSize += FfiConverterU32.computeSize(value.food);
+ totalSize += FfiConverterU32.computeSize(value.government);
+ totalSize += FfiConverterU32.computeSize(value.health);
+ totalSize += FfiConverterU32.computeSize(value.hobbies);
+ totalSize += FfiConverterU32.computeSize(value.home);
+ totalSize += FfiConverterU32.computeSize(value.news);
+ totalSize += FfiConverterU32.computeSize(value.realEstate);
+ totalSize += FfiConverterU32.computeSize(value.society);
+ totalSize += FfiConverterU32.computeSize(value.sports);
+ totalSize += FfiConverterU32.computeSize(value.tech);
+ totalSize += FfiConverterU32.computeSize(value.travel);
+ totalSize += FfiConverterU32.computeSize(value.inconclusive);
+ return totalSize
+ }
+
+ static checkType(value) {
+ super.checkType(value);
+ if (!(value instanceof InterestVector)) {
+ throw new UniFFITypeError(`Expected 'InterestVector', found '${typeof value}'`);
+ }
+ try {
+ FfiConverterU32.checkType(value.animals);
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart(".animals");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(value.arts);
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart(".arts");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(value.autos);
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart(".autos");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(value.business);
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart(".business");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(value.career);
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart(".career");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(value.education);
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart(".education");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(value.fashion);
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart(".fashion");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(value.finance);
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart(".finance");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(value.food);
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart(".food");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(value.government);
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart(".government");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(value.health);
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart(".health");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(value.hobbies);
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart(".hobbies");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(value.home);
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart(".home");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(value.news);
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart(".news");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(value.realEstate);
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart(".realEstate");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(value.society);
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart(".society");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(value.sports);
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart(".sports");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(value.tech);
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart(".tech");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(value.travel);
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart(".travel");
+ }
+ throw e;
+ }
+ try {
+ FfiConverterU32.checkType(value.inconclusive);
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart(".inconclusive");
+ }
+ throw e;
+ }
+ }
+}
+
+
+export const Interest = {
+ ANIMALS: 1,
+ ARTS: 2,
+ AUTOS: 3,
+ BUSINESS: 4,
+ CAREER: 5,
+ EDUCATION: 6,
+ FASHION: 7,
+ FINANCE: 8,
+ FOOD: 9,
+ GOVERNMENT: 10,
+ HEALTH: 11,
+ HOBBIES: 12,
+ HOME: 13,
+ NEWS: 14,
+ REAL_ESTATE: 15,
+ SOCIETY: 16,
+ SPORTS: 17,
+ TECH: 18,
+ TRAVEL: 19,
+ INCONCLUSIVE: 20,
+};
+
+Object.freeze(Interest);
+// Export the FFIConverter object to make external types work.
+export class FfiConverterTypeInterest extends FfiConverterArrayBuffer {
+ static read(dataStream) {
+ switch (dataStream.readInt32()) {
+ case 1:
+ return Interest.ANIMALS
+ case 2:
+ return Interest.ARTS
+ case 3:
+ return Interest.AUTOS
+ case 4:
+ return Interest.BUSINESS
+ case 5:
+ return Interest.CAREER
+ case 6:
+ return Interest.EDUCATION
+ case 7:
+ return Interest.FASHION
+ case 8:
+ return Interest.FINANCE
+ case 9:
+ return Interest.FOOD
+ case 10:
+ return Interest.GOVERNMENT
+ case 11:
+ return Interest.HEALTH
+ case 12:
+ return Interest.HOBBIES
+ case 13:
+ return Interest.HOME
+ case 14:
+ return Interest.NEWS
+ case 15:
+ return Interest.REAL_ESTATE
+ case 16:
+ return Interest.SOCIETY
+ case 17:
+ return Interest.SPORTS
+ case 18:
+ return Interest.TECH
+ case 19:
+ return Interest.TRAVEL
+ case 20:
+ return Interest.INCONCLUSIVE
+ default:
+ throw new UniFFITypeError("Unknown Interest variant");
+ }
+ }
+
+ static write(dataStream, value) {
+ if (value === Interest.ANIMALS) {
+ dataStream.writeInt32(1);
+ return;
+ }
+ if (value === Interest.ARTS) {
+ dataStream.writeInt32(2);
+ return;
+ }
+ if (value === Interest.AUTOS) {
+ dataStream.writeInt32(3);
+ return;
+ }
+ if (value === Interest.BUSINESS) {
+ dataStream.writeInt32(4);
+ return;
+ }
+ if (value === Interest.CAREER) {
+ dataStream.writeInt32(5);
+ return;
+ }
+ if (value === Interest.EDUCATION) {
+ dataStream.writeInt32(6);
+ return;
+ }
+ if (value === Interest.FASHION) {
+ dataStream.writeInt32(7);
+ return;
+ }
+ if (value === Interest.FINANCE) {
+ dataStream.writeInt32(8);
+ return;
+ }
+ if (value === Interest.FOOD) {
+ dataStream.writeInt32(9);
+ return;
+ }
+ if (value === Interest.GOVERNMENT) {
+ dataStream.writeInt32(10);
+ return;
+ }
+ if (value === Interest.HEALTH) {
+ dataStream.writeInt32(11);
+ return;
+ }
+ if (value === Interest.HOBBIES) {
+ dataStream.writeInt32(12);
+ return;
+ }
+ if (value === Interest.HOME) {
+ dataStream.writeInt32(13);
+ return;
+ }
+ if (value === Interest.NEWS) {
+ dataStream.writeInt32(14);
+ return;
+ }
+ if (value === Interest.REAL_ESTATE) {
+ dataStream.writeInt32(15);
+ return;
+ }
+ if (value === Interest.SOCIETY) {
+ dataStream.writeInt32(16);
+ return;
+ }
+ if (value === Interest.SPORTS) {
+ dataStream.writeInt32(17);
+ return;
+ }
+ if (value === Interest.TECH) {
+ dataStream.writeInt32(18);
+ return;
+ }
+ if (value === Interest.TRAVEL) {
+ dataStream.writeInt32(19);
+ return;
+ }
+ if (value === Interest.INCONCLUSIVE) {
+ dataStream.writeInt32(20);
+ return;
+ }
+ throw new UniFFITypeError("Unknown Interest variant");
+ }
+
+ static computeSize(value) {
+ return 4;
+ }
+
+ static checkType(value) {
+ if (!Number.isInteger(value) || value < 1 || value > 20) {
+ throw new UniFFITypeError(`${value} is not a valid value for Interest`);
+ }
+ }
+}
+
+
+
+
+
+export class RelevancyApiError extends Error {}
+
+
+export class Unexpected extends RelevancyApiError {
+
+ constructor(
+ reason,
+ ...params
+ ) {
+ super(...params);
+ this.reason = reason;
+ }
+ toString() {
+ return `Unexpected: ${super.toString()}`
+ }
+}
+
+// Export the FFIConverter object to make external types work.
+export class FfiConverterTypeRelevancyApiError extends FfiConverterArrayBuffer {
+ static read(dataStream) {
+ switch (dataStream.readInt32()) {
+ case 1:
+ return new Unexpected(
+ FfiConverterString.read(dataStream)
+ );
+ default:
+ throw new UniFFITypeError("Unknown RelevancyApiError variant");
+ }
+ }
+ static computeSize(value) {
+ // Size of the Int indicating the variant
+ let totalSize = 4;
+ if (value instanceof Unexpected) {
+ totalSize += FfiConverterString.computeSize(value.reason);
+ return totalSize;
+ }
+ throw new UniFFITypeError("Unknown RelevancyApiError variant");
+ }
+ static write(dataStream, value) {
+ if (value instanceof Unexpected) {
+ dataStream.writeInt32(1);
+ FfiConverterString.write(dataStream, value.reason);
+ return;
+ }
+ throw new UniFFITypeError("Unknown RelevancyApiError variant");
+ }
+
+ static errorClass = RelevancyApiError;
+}
+
+// Export the FFIConverter object to make external types work.
+export class FfiConverterSequencestring extends FfiConverterArrayBuffer {
+ static read(dataStream) {
+ const len = dataStream.readInt32();
+ const arr = [];
+ for (let i = 0; i < len; i++) {
+ arr.push(FfiConverterString.read(dataStream));
+ }
+ return arr;
+ }
+
+ static write(dataStream, value) {
+ dataStream.writeInt32(value.length);
+ value.forEach((innerValue) => {
+ FfiConverterString.write(dataStream, innerValue);
+ })
+ }
+
+ static computeSize(value) {
+ // The size of the length
+ let size = 4;
+ for (const innerValue of value) {
+ size += FfiConverterString.computeSize(innerValue);
+ }
+ return size;
+ }
+
+ static checkType(value) {
+ if (!Array.isArray(value)) {
+ throw new UniFFITypeError(`${value} is not an array`);
+ }
+ value.forEach((innerValue, idx) => {
+ try {
+ FfiConverterString.checkType(innerValue);
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart(`[${idx}]`);
+ }
+ throw e;
+ }
+ })
+ }
+}
+
+
+
+
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustRemoteSettings.sys.mjs b/toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustRemoteSettings.sys.mjs
index ab35fbb1e7..e839b6f17d 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustRemoteSettings.sys.mjs
+++ b/toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustRemoteSettings.sys.mjs
@@ -158,7 +158,7 @@ class ArrayBufferDataStream {
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
readPointerRemoteSettings() {
- const pointerId = 0; // remote_settings:RemoteSettings
+ const pointerId = 1; // remote_settings:RemoteSettings
const res = UniFFIScaffolding.readPointer(pointerId, this.dataView.buffer, this.pos);
this.pos += 8;
return res;
@@ -168,7 +168,7 @@ class ArrayBufferDataStream {
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
writePointerRemoteSettings(value) {
- const pointerId = 0; // remote_settings:RemoteSettings
+ const pointerId = 1; // remote_settings:RemoteSettings
UniFFIScaffolding.writePointer(pointerId, value, this.dataView.buffer, this.pos);
this.pos += 8;
}
@@ -356,7 +356,7 @@ export class RemoteSettings {
throw e;
}
return UniFFIScaffolding.callSync(
- 0, // remote_settings:uniffi_remote_settings_fn_constructor_remotesettings_new
+ 6, // remote_settings:uniffi_remote_settings_fn_constructor_remotesettings_new
FfiConverterTypeRemoteSettingsConfig.lower(remoteSettingsConfig),
)
}
@@ -383,7 +383,7 @@ export class RemoteSettings {
throw e;
}
return UniFFIScaffolding.callAsync(
- 1, // remote_settings:uniffi_remote_settings_fn_method_remotesettings_download_attachment_to_path
+ 7, // remote_settings:uniffi_remote_settings_fn_method_remotesettings_download_attachment_to_path
FfiConverterTypeRemoteSettings.lower(this),
FfiConverterString.lower(attachmentId),
FfiConverterString.lower(path),
@@ -401,7 +401,7 @@ export class RemoteSettings {
const liftError = (data) => FfiConverterTypeRemoteSettingsError.lift(data);
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 2, // remote_settings:uniffi_remote_settings_fn_method_remotesettings_get_records
+ 8, // remote_settings:uniffi_remote_settings_fn_method_remotesettings_get_records
FfiConverterTypeRemoteSettings.lower(this),
)
}
@@ -425,7 +425,7 @@ export class RemoteSettings {
throw e;
}
return UniFFIScaffolding.callAsync(
- 3, // remote_settings:uniffi_remote_settings_fn_method_remotesettings_get_records_since
+ 9, // remote_settings:uniffi_remote_settings_fn_method_remotesettings_get_records_since
FfiConverterTypeRemoteSettings.lower(this),
FfiConverterU64.lower(timestamp),
)
@@ -448,7 +448,11 @@ export class FfiConverterTypeRemoteSettings extends FfiConverter {
}
static lower(value) {
- return value[uniffiObjectPtr];
+ const ptr = value[uniffiObjectPtr];
+ if (!(ptr instanceof UniFFIPointer)) {
+ throw new UniFFITypeError("Object is not a 'RemoteSettings' instance");
+ }
+ return ptr;
}
static read(dataStream) {
@@ -555,7 +559,7 @@ export class FfiConverterTypeAttachment extends FfiConverterArrayBuffer {
static checkType(value) {
super.checkType(value);
if (!(value instanceof Attachment)) {
- throw new TypeError(`Expected 'Attachment', found '${typeof value}'`);
+ throw new UniFFITypeError(`Expected 'Attachment', found '${typeof value}'`);
}
try {
FfiConverterString.checkType(value.filename);
@@ -665,7 +669,7 @@ export class FfiConverterTypeRemoteSettingsConfig extends FfiConverterArrayBuffe
static checkType(value) {
super.checkType(value);
if (!(value instanceof RemoteSettingsConfig)) {
- throw new TypeError(`Expected 'RemoteSettingsConfig', found '${typeof value}'`);
+ throw new UniFFITypeError(`Expected 'RemoteSettingsConfig', found '${typeof value}'`);
}
try {
FfiConverterString.checkType(value.collectionName);
@@ -785,7 +789,7 @@ export class FfiConverterTypeRemoteSettingsRecord extends FfiConverterArrayBuffe
static checkType(value) {
super.checkType(value);
if (!(value instanceof RemoteSettingsRecord)) {
- throw new TypeError(`Expected 'RemoteSettingsRecord', found '${typeof value}'`);
+ throw new UniFFITypeError(`Expected 'RemoteSettingsRecord', found '${typeof value}'`);
}
try {
FfiConverterString.checkType(value.id);
@@ -882,7 +886,7 @@ export class FfiConverterTypeRemoteSettingsResponse extends FfiConverterArrayBuf
static checkType(value) {
super.checkType(value);
if (!(value instanceof RemoteSettingsResponse)) {
- throw new TypeError(`Expected 'RemoteSettingsResponse', found '${typeof value}'`);
+ throw new UniFFITypeError(`Expected 'RemoteSettingsResponse', found '${typeof value}'`);
}
try {
FfiConverterSequenceTypeRemoteSettingsRecord.checkType(value.records);
@@ -1005,7 +1009,7 @@ export class FfiConverterTypeRemoteSettingsError extends FfiConverterArrayBuffer
case 7:
return new AttachmentsUnsupportedError(FfiConverterString.read(dataStream));
default:
- throw new Error("Unknown RemoteSettingsError variant");
+ throw new UniFFITypeError("Unknown RemoteSettingsError variant");
}
}
static computeSize(value) {
@@ -1032,7 +1036,7 @@ export class FfiConverterTypeRemoteSettingsError extends FfiConverterArrayBuffer
if (value instanceof AttachmentsUnsupportedError) {
return totalSize;
}
- throw new Error("Unknown RemoteSettingsError variant");
+ throw new UniFFITypeError("Unknown RemoteSettingsError variant");
}
static write(dataStream, value) {
if (value instanceof JsonError) {
@@ -1063,7 +1067,7 @@ export class FfiConverterTypeRemoteSettingsError extends FfiConverterArrayBuffer
dataStream.writeInt32(7);
return;
}
- throw new Error("Unknown RemoteSettingsError variant");
+ throw new UniFFITypeError("Unknown RemoteSettingsError variant");
}
static errorClass = RemoteSettingsError;
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustSuggest.sys.mjs b/toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustSuggest.sys.mjs
index ff9507c520..7ab2e2d8e2 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustSuggest.sys.mjs
+++ b/toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustSuggest.sys.mjs
@@ -158,7 +158,7 @@ class ArrayBufferDataStream {
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
readPointerSuggestStore() {
- const pointerId = 1; // suggest:SuggestStore
+ const pointerId = 2; // suggest:SuggestStore
const res = UniFFIScaffolding.readPointer(pointerId, this.dataView.buffer, this.pos);
this.pos += 8;
return res;
@@ -168,7 +168,7 @@ class ArrayBufferDataStream {
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
writePointerSuggestStore(value) {
- const pointerId = 1; // suggest:SuggestStore
+ const pointerId = 2; // suggest:SuggestStore
UniFFIScaffolding.writePointer(pointerId, value, this.dataView.buffer, this.pos);
this.pos += 8;
}
@@ -178,7 +178,7 @@ class ArrayBufferDataStream {
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
readPointerSuggestStoreBuilder() {
- const pointerId = 2; // suggest:SuggestStoreBuilder
+ const pointerId = 3; // suggest:SuggestStoreBuilder
const res = UniFFIScaffolding.readPointer(pointerId, this.dataView.buffer, this.pos);
this.pos += 8;
return res;
@@ -188,7 +188,7 @@ class ArrayBufferDataStream {
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
writePointerSuggestStoreBuilder(value) {
- const pointerId = 2; // suggest:SuggestStoreBuilder
+ const pointerId = 3; // suggest:SuggestStoreBuilder
UniFFIScaffolding.writePointer(pointerId, value, this.dataView.buffer, this.pos);
this.pos += 8;
}
@@ -484,7 +484,7 @@ export class SuggestStore {
throw e;
}
return UniFFIScaffolding.callSync(
- 4, // suggest:uniffi_suggest_fn_constructor_suggeststore_new
+ 11, // suggest:uniffi_suggest_fn_constructor_suggeststore_new
FfiConverterString.lower(path),
FfiConverterOptionalTypeRemoteSettingsConfig.lower(settingsConfig),
)
@@ -496,7 +496,7 @@ export class SuggestStore {
const liftError = (data) => FfiConverterTypeSuggestApiError.lift(data);
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 5, // suggest:uniffi_suggest_fn_method_suggeststore_clear
+ 12, // suggest:uniffi_suggest_fn_method_suggeststore_clear
FfiConverterTypeSuggestStore.lower(this),
)
}
@@ -507,12 +507,53 @@ export class SuggestStore {
}
}
+ clearDismissedSuggestions() {
+ const liftResult = (result) => undefined;
+ const liftError = (data) => FfiConverterTypeSuggestApiError.lift(data);
+ const functionCall = () => {
+ return UniFFIScaffolding.callAsync(
+ 13, // suggest:uniffi_suggest_fn_method_suggeststore_clear_dismissed_suggestions
+ FfiConverterTypeSuggestStore.lower(this),
+ )
+ }
+ try {
+ return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
+ } catch (error) {
+ return Promise.reject(error)
+ }
+ }
+
+ dismissSuggestion(rawSuggestionUrl) {
+ const liftResult = (result) => undefined;
+ const liftError = (data) => FfiConverterTypeSuggestApiError.lift(data);
+ const functionCall = () => {
+ try {
+ FfiConverterString.checkType(rawSuggestionUrl)
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart("rawSuggestionUrl");
+ }
+ throw e;
+ }
+ return UniFFIScaffolding.callAsync(
+ 14, // suggest:uniffi_suggest_fn_method_suggeststore_dismiss_suggestion
+ FfiConverterTypeSuggestStore.lower(this),
+ FfiConverterString.lower(rawSuggestionUrl),
+ )
+ }
+ try {
+ return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
+ } catch (error) {
+ return Promise.reject(error)
+ }
+ }
+
fetchGlobalConfig() {
const liftResult = (result) => FfiConverterTypeSuggestGlobalConfig.lift(result);
const liftError = (data) => FfiConverterTypeSuggestApiError.lift(data);
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 6, // suggest:uniffi_suggest_fn_method_suggeststore_fetch_global_config
+ 15, // suggest:uniffi_suggest_fn_method_suggeststore_fetch_global_config
FfiConverterTypeSuggestStore.lower(this),
)
}
@@ -536,7 +577,7 @@ export class SuggestStore {
throw e;
}
return UniFFIScaffolding.callAsync(
- 7, // suggest:uniffi_suggest_fn_method_suggeststore_fetch_provider_config
+ 16, // suggest:uniffi_suggest_fn_method_suggeststore_fetch_provider_config
FfiConverterTypeSuggestStore.lower(this),
FfiConverterTypeSuggestionProvider.lower(provider),
)
@@ -561,7 +602,7 @@ export class SuggestStore {
throw e;
}
return UniFFIScaffolding.callAsync(
- 8, // suggest:uniffi_suggest_fn_method_suggeststore_ingest
+ 17, // suggest:uniffi_suggest_fn_method_suggeststore_ingest
FfiConverterTypeSuggestStore.lower(this),
FfiConverterTypeSuggestIngestionConstraints.lower(constraints),
)
@@ -578,7 +619,7 @@ export class SuggestStore {
const liftError = null;
const functionCall = () => {
return UniFFIScaffolding.callSync(
- 9, // suggest:uniffi_suggest_fn_method_suggeststore_interrupt
+ 18, // suggest:uniffi_suggest_fn_method_suggeststore_interrupt
FfiConverterTypeSuggestStore.lower(this),
)
}
@@ -598,7 +639,7 @@ export class SuggestStore {
throw e;
}
return UniFFIScaffolding.callAsync(
- 10, // suggest:uniffi_suggest_fn_method_suggeststore_query
+ 19, // suggest:uniffi_suggest_fn_method_suggeststore_query
FfiConverterTypeSuggestStore.lower(this),
FfiConverterTypeSuggestionQuery.lower(query),
)
@@ -621,7 +662,11 @@ export class FfiConverterTypeSuggestStore extends FfiConverter {
}
static lower(value) {
- return value[uniffiObjectPtr];
+ const ptr = value[uniffiObjectPtr];
+ if (!(ptr instanceof UniFFIPointer)) {
+ throw new UniFFITypeError("Object is not a 'SuggestStore' instance");
+ }
+ return ptr;
}
static read(dataStream) {
@@ -661,7 +706,7 @@ export class SuggestStoreBuilder {
const liftError = null;
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 11, // suggest:uniffi_suggest_fn_constructor_suggeststorebuilder_new
+ 21, // suggest:uniffi_suggest_fn_constructor_suggeststorebuilder_new
)
}
try {
@@ -675,7 +720,7 @@ export class SuggestStoreBuilder {
const liftError = (data) => FfiConverterTypeSuggestApiError.lift(data);
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 12, // suggest:uniffi_suggest_fn_method_suggeststorebuilder_build
+ 22, // suggest:uniffi_suggest_fn_method_suggeststorebuilder_build
FfiConverterTypeSuggestStoreBuilder.lower(this),
)
}
@@ -699,7 +744,7 @@ export class SuggestStoreBuilder {
throw e;
}
return UniFFIScaffolding.callAsync(
- 13, // suggest:uniffi_suggest_fn_method_suggeststorebuilder_cache_path
+ 23, // suggest:uniffi_suggest_fn_method_suggeststorebuilder_cache_path
FfiConverterTypeSuggestStoreBuilder.lower(this),
FfiConverterString.lower(path),
)
@@ -724,7 +769,7 @@ export class SuggestStoreBuilder {
throw e;
}
return UniFFIScaffolding.callAsync(
- 14, // suggest:uniffi_suggest_fn_method_suggeststorebuilder_data_path
+ 24, // suggest:uniffi_suggest_fn_method_suggeststorebuilder_data_path
FfiConverterTypeSuggestStoreBuilder.lower(this),
FfiConverterString.lower(path),
)
@@ -749,7 +794,7 @@ export class SuggestStoreBuilder {
throw e;
}
return UniFFIScaffolding.callAsync(
- 15, // suggest:uniffi_suggest_fn_method_suggeststorebuilder_remote_settings_config
+ 25, // suggest:uniffi_suggest_fn_method_suggeststorebuilder_remote_settings_config
FfiConverterTypeSuggestStoreBuilder.lower(this),
FfiConverterTypeRemoteSettingsConfig.lower(config),
)
@@ -772,7 +817,11 @@ export class FfiConverterTypeSuggestStoreBuilder extends FfiConverter {
}
static lower(value) {
- return value[uniffiObjectPtr];
+ const ptr = value[uniffiObjectPtr];
+ if (!(ptr instanceof UniFFIPointer)) {
+ throw new UniFFITypeError("Object is not a 'SuggestStoreBuilder' instance");
+ }
+ return ptr;
}
static read(dataStream) {
@@ -827,7 +876,7 @@ export class FfiConverterTypeSuggestGlobalConfig extends FfiConverterArrayBuffer
static checkType(value) {
super.checkType(value);
if (!(value instanceof SuggestGlobalConfig)) {
- throw new TypeError(`Expected 'SuggestGlobalConfig', found '${typeof value}'`);
+ throw new UniFFITypeError(`Expected 'SuggestGlobalConfig', found '${typeof value}'`);
}
try {
FfiConverterI32.checkType(value.showLessFrequentlyCap);
@@ -841,7 +890,7 @@ export class FfiConverterTypeSuggestGlobalConfig extends FfiConverterArrayBuffer
}
export class SuggestIngestionConstraints {
- constructor({ maxSuggestions = null } = {}) {
+ constructor({ maxSuggestions = null, providers = null } = {}) {
try {
FfiConverterOptionalu64.checkType(maxSuggestions)
} catch (e) {
@@ -850,11 +899,21 @@ export class SuggestIngestionConstraints {
}
throw e;
}
+ try {
+ FfiConverterOptionalSequenceTypeSuggestionProvider.checkType(providers)
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart("providers");
+ }
+ throw e;
+ }
this.maxSuggestions = maxSuggestions;
+ this.providers = providers;
}
equals(other) {
return (
- this.maxSuggestions == other.maxSuggestions
+ this.maxSuggestions == other.maxSuggestions &&
+ this.providers == other.providers
)
}
}
@@ -864,22 +923,25 @@ export class FfiConverterTypeSuggestIngestionConstraints extends FfiConverterArr
static read(dataStream) {
return new SuggestIngestionConstraints({
maxSuggestions: FfiConverterOptionalu64.read(dataStream),
+ providers: FfiConverterOptionalSequenceTypeSuggestionProvider.read(dataStream),
});
}
static write(dataStream, value) {
FfiConverterOptionalu64.write(dataStream, value.maxSuggestions);
+ FfiConverterOptionalSequenceTypeSuggestionProvider.write(dataStream, value.providers);
}
static computeSize(value) {
let totalSize = 0;
totalSize += FfiConverterOptionalu64.computeSize(value.maxSuggestions);
+ totalSize += FfiConverterOptionalSequenceTypeSuggestionProvider.computeSize(value.providers);
return totalSize
}
static checkType(value) {
super.checkType(value);
if (!(value instanceof SuggestIngestionConstraints)) {
- throw new TypeError(`Expected 'SuggestIngestionConstraints', found '${typeof value}'`);
+ throw new UniFFITypeError(`Expected 'SuggestIngestionConstraints', found '${typeof value}'`);
}
try {
FfiConverterOptionalu64.checkType(value.maxSuggestions);
@@ -889,6 +951,14 @@ export class FfiConverterTypeSuggestIngestionConstraints extends FfiConverterArr
}
throw e;
}
+ try {
+ FfiConverterOptionalSequenceTypeSuggestionProvider.checkType(value.providers);
+ } catch (e) {
+ if (e instanceof UniFFITypeError) {
+ e.addItemDescriptionPart(".providers");
+ }
+ throw e;
+ }
}
}
@@ -957,7 +1027,7 @@ export class FfiConverterTypeSuggestionQuery extends FfiConverterArrayBuffer {
static checkType(value) {
super.checkType(value);
if (!(value instanceof SuggestionQuery)) {
- throw new TypeError(`Expected 'SuggestionQuery', found '${typeof value}'`);
+ throw new UniFFITypeError(`Expected 'SuggestionQuery', found '${typeof value}'`);
}
try {
FfiConverterString.checkType(value.keyword);
@@ -1066,7 +1136,7 @@ export class FfiConverterTypeSuggestApiError extends FfiConverterArrayBuffer {
FfiConverterString.read(dataStream)
);
default:
- throw new Error("Unknown SuggestApiError variant");
+ throw new UniFFITypeError("Unknown SuggestApiError variant");
}
}
static computeSize(value) {
@@ -1087,7 +1157,7 @@ export class FfiConverterTypeSuggestApiError extends FfiConverterArrayBuffer {
totalSize += FfiConverterString.computeSize(value.reason);
return totalSize;
}
- throw new Error("Unknown SuggestApiError variant");
+ throw new UniFFITypeError("Unknown SuggestApiError variant");
}
static write(dataStream, value) {
if (value instanceof Interrupted) {
@@ -1109,7 +1179,7 @@ export class FfiConverterTypeSuggestApiError extends FfiConverterArrayBuffer {
FfiConverterString.write(dataStream, value.reason);
return;
}
- throw new Error("Unknown SuggestApiError variant");
+ throw new UniFFITypeError("Unknown SuggestApiError variant");
}
static errorClass = SuggestApiError;
@@ -1135,7 +1205,7 @@ export class FfiConverterTypeSuggestProviderConfig extends FfiConverterArrayBuff
FfiConverterI32.read(dataStream)
);
default:
- return new Error("Unknown SuggestProviderConfig variant");
+ throw new UniFFITypeError("Unknown SuggestProviderConfig variant");
}
}
@@ -1145,7 +1215,7 @@ export class FfiConverterTypeSuggestProviderConfig extends FfiConverterArrayBuff
FfiConverterI32.write(dataStream, value.minKeywordLength);
return;
}
- return new Error("Unknown SuggestProviderConfig variant");
+ throw new UniFFITypeError("Unknown SuggestProviderConfig variant");
}
static computeSize(value) {
@@ -1155,7 +1225,7 @@ export class FfiConverterTypeSuggestProviderConfig extends FfiConverterArrayBuff
totalSize += FfiConverterI32.computeSize(value.minKeywordLength);
return totalSize;
}
- return new Error("Unknown SuggestProviderConfig variant");
+ throw new UniFFITypeError("Unknown SuggestProviderConfig variant");
}
static checkType(value) {
@@ -1174,6 +1244,7 @@ Suggestion.Amp = class extends Suggestion{
url,
rawUrl,
icon,
+ iconMimetype,
fullKeyword,
blockId,
advertiser,
@@ -1188,6 +1259,7 @@ Suggestion.Amp = class extends Suggestion{
this.url = url;
this.rawUrl = rawUrl;
this.icon = icon;
+ this.iconMimetype = iconMimetype;
this.fullKeyword = fullKeyword;
this.blockId = blockId;
this.advertiser = advertiser;
@@ -1217,12 +1289,14 @@ Suggestion.Wikipedia = class extends Suggestion{
title,
url,
icon,
+ iconMimetype,
fullKeyword
) {
super();
this.title = title;
this.url = url;
this.icon = icon;
+ this.iconMimetype = iconMimetype;
this.fullKeyword = fullKeyword;
}
}
@@ -1253,6 +1327,7 @@ Suggestion.Yelp = class extends Suggestion{
url,
title,
icon,
+ iconMimetype,
score,
hasLocationSign,
subjectExactMatch,
@@ -1262,6 +1337,7 @@ Suggestion.Yelp = class extends Suggestion{
this.url = url;
this.title = title;
this.icon = icon;
+ this.iconMimetype = iconMimetype;
this.score = score;
this.hasLocationSign = hasLocationSign;
this.subjectExactMatch = subjectExactMatch;
@@ -1301,6 +1377,7 @@ export class FfiConverterTypeSuggestion extends FfiConverterArrayBuffer {
FfiConverterString.read(dataStream),
FfiConverterString.read(dataStream),
FfiConverterOptionalSequenceu8.read(dataStream),
+ FfiConverterOptionalstring.read(dataStream),
FfiConverterString.read(dataStream),
FfiConverterI64.read(dataStream),
FfiConverterString.read(dataStream),
@@ -1322,6 +1399,7 @@ export class FfiConverterTypeSuggestion extends FfiConverterArrayBuffer {
FfiConverterString.read(dataStream),
FfiConverterString.read(dataStream),
FfiConverterOptionalSequenceu8.read(dataStream),
+ FfiConverterOptionalstring.read(dataStream),
FfiConverterString.read(dataStream)
);
case 4:
@@ -1340,6 +1418,7 @@ export class FfiConverterTypeSuggestion extends FfiConverterArrayBuffer {
FfiConverterString.read(dataStream),
FfiConverterString.read(dataStream),
FfiConverterOptionalSequenceu8.read(dataStream),
+ FfiConverterOptionalstring.read(dataStream),
FfiConverterF64.read(dataStream),
FfiConverterBool.read(dataStream),
FfiConverterBool.read(dataStream),
@@ -1357,7 +1436,7 @@ export class FfiConverterTypeSuggestion extends FfiConverterArrayBuffer {
FfiConverterF64.read(dataStream)
);
default:
- return new Error("Unknown Suggestion variant");
+ throw new UniFFITypeError("Unknown Suggestion variant");
}
}
@@ -1368,6 +1447,7 @@ export class FfiConverterTypeSuggestion extends FfiConverterArrayBuffer {
FfiConverterString.write(dataStream, value.url);
FfiConverterString.write(dataStream, value.rawUrl);
FfiConverterOptionalSequenceu8.write(dataStream, value.icon);
+ FfiConverterOptionalstring.write(dataStream, value.iconMimetype);
FfiConverterString.write(dataStream, value.fullKeyword);
FfiConverterI64.write(dataStream, value.blockId);
FfiConverterString.write(dataStream, value.advertiser);
@@ -1391,6 +1471,7 @@ export class FfiConverterTypeSuggestion extends FfiConverterArrayBuffer {
FfiConverterString.write(dataStream, value.title);
FfiConverterString.write(dataStream, value.url);
FfiConverterOptionalSequenceu8.write(dataStream, value.icon);
+ FfiConverterOptionalstring.write(dataStream, value.iconMimetype);
FfiConverterString.write(dataStream, value.fullKeyword);
return;
}
@@ -1411,6 +1492,7 @@ export class FfiConverterTypeSuggestion extends FfiConverterArrayBuffer {
FfiConverterString.write(dataStream, value.url);
FfiConverterString.write(dataStream, value.title);
FfiConverterOptionalSequenceu8.write(dataStream, value.icon);
+ FfiConverterOptionalstring.write(dataStream, value.iconMimetype);
FfiConverterF64.write(dataStream, value.score);
FfiConverterBool.write(dataStream, value.hasLocationSign);
FfiConverterBool.write(dataStream, value.subjectExactMatch);
@@ -1430,7 +1512,7 @@ export class FfiConverterTypeSuggestion extends FfiConverterArrayBuffer {
FfiConverterF64.write(dataStream, value.score);
return;
}
- return new Error("Unknown Suggestion variant");
+ throw new UniFFITypeError("Unknown Suggestion variant");
}
static computeSize(value) {
@@ -1441,6 +1523,7 @@ export class FfiConverterTypeSuggestion extends FfiConverterArrayBuffer {
totalSize += FfiConverterString.computeSize(value.url);
totalSize += FfiConverterString.computeSize(value.rawUrl);
totalSize += FfiConverterOptionalSequenceu8.computeSize(value.icon);
+ totalSize += FfiConverterOptionalstring.computeSize(value.iconMimetype);
totalSize += FfiConverterString.computeSize(value.fullKeyword);
totalSize += FfiConverterI64.computeSize(value.blockId);
totalSize += FfiConverterString.computeSize(value.advertiser);
@@ -1462,6 +1545,7 @@ export class FfiConverterTypeSuggestion extends FfiConverterArrayBuffer {
totalSize += FfiConverterString.computeSize(value.title);
totalSize += FfiConverterString.computeSize(value.url);
totalSize += FfiConverterOptionalSequenceu8.computeSize(value.icon);
+ totalSize += FfiConverterOptionalstring.computeSize(value.iconMimetype);
totalSize += FfiConverterString.computeSize(value.fullKeyword);
return totalSize;
}
@@ -1480,6 +1564,7 @@ export class FfiConverterTypeSuggestion extends FfiConverterArrayBuffer {
totalSize += FfiConverterString.computeSize(value.url);
totalSize += FfiConverterString.computeSize(value.title);
totalSize += FfiConverterOptionalSequenceu8.computeSize(value.icon);
+ totalSize += FfiConverterOptionalstring.computeSize(value.iconMimetype);
totalSize += FfiConverterF64.computeSize(value.score);
totalSize += FfiConverterBool.computeSize(value.hasLocationSign);
totalSize += FfiConverterBool.computeSize(value.subjectExactMatch);
@@ -1497,7 +1582,7 @@ export class FfiConverterTypeSuggestion extends FfiConverterArrayBuffer {
totalSize += FfiConverterF64.computeSize(value.score);
return totalSize;
}
- return new Error("Unknown Suggestion variant");
+ throw new UniFFITypeError("Unknown Suggestion variant");
}
static checkType(value) {
@@ -1542,7 +1627,7 @@ export class FfiConverterTypeSuggestionProvider extends FfiConverterArrayBuffer
case 8:
return SuggestionProvider.AMP_MOBILE
default:
- return new Error("Unknown SuggestionProvider variant");
+ throw new UniFFITypeError("Unknown SuggestionProvider variant");
}
}
@@ -1579,7 +1664,7 @@ export class FfiConverterTypeSuggestionProvider extends FfiConverterArrayBuffer
dataStream.writeInt32(8);
return;
}
- return new Error("Unknown SuggestionProvider variant");
+ throw new UniFFITypeError("Unknown SuggestionProvider variant");
}
static computeSize(value) {
@@ -1780,6 +1865,43 @@ export class FfiConverterOptionalSequenceu8 extends FfiConverterArrayBuffer {
}
// Export the FFIConverter object to make external types work.
+export class FfiConverterOptionalSequenceTypeSuggestionProvider extends FfiConverterArrayBuffer {
+ static checkType(value) {
+ if (value !== undefined && value !== null) {
+ FfiConverterSequenceTypeSuggestionProvider.checkType(value)
+ }
+ }
+
+ static read(dataStream) {
+ const code = dataStream.readUint8(0);
+ switch (code) {
+ case 0:
+ return null
+ case 1:
+ return FfiConverterSequenceTypeSuggestionProvider.read(dataStream)
+ default:
+ throw UniFFIError(`Unexpected code: ${code}`);
+ }
+ }
+
+ static write(dataStream, value) {
+ if (value === null || value === undefined) {
+ dataStream.writeUint8(0);
+ return;
+ }
+ dataStream.writeUint8(1);
+ FfiConverterSequenceTypeSuggestionProvider.write(dataStream, value)
+ }
+
+ static computeSize(value) {
+ if (value === null || value === undefined) {
+ return 1;
+ }
+ return 1 + FfiConverterSequenceTypeSuggestionProvider.computeSize(value)
+ }
+}
+
+// Export the FFIConverter object to make external types work.
export class FfiConverterOptionalTypeRemoteSettingsConfig extends FfiConverterArrayBuffer {
static checkType(value) {
if (value !== undefined && value !== null) {
@@ -1982,7 +2104,7 @@ export function rawSuggestionUrlMatches(rawUrl,url) {
throw e;
}
return UniFFIScaffolding.callSync(
- 16, // suggest:uniffi_suggest_fn_func_raw_suggestion_url_matches
+ 26, // suggest:uniffi_suggest_fn_func_raw_suggestion_url_matches
FfiConverterString.lower(rawUrl),
FfiConverterString.lower(url),
)
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustSync15.sys.mjs b/toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustSync15.sys.mjs
index fb9b06bfa7..c02bf51235 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustSync15.sys.mjs
+++ b/toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustSync15.sys.mjs
@@ -282,7 +282,7 @@ export class FfiConverterTypeDeviceType extends FfiConverterArrayBuffer {
case 6:
return DeviceType.UNKNOWN
default:
- return new Error("Unknown DeviceType variant");
+ throw new UniFFITypeError("Unknown DeviceType variant");
}
}
@@ -311,7 +311,7 @@ export class FfiConverterTypeDeviceType extends FfiConverterArrayBuffer {
dataStream.writeInt32(6);
return;
}
- return new Error("Unknown DeviceType variant");
+ throw new UniFFITypeError("Unknown DeviceType variant");
}
static computeSize(value) {
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustTabs.sys.mjs b/toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustTabs.sys.mjs
index 614d7fb95b..4d34a46d9e 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustTabs.sys.mjs
+++ b/toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustTabs.sys.mjs
@@ -158,7 +158,7 @@ class ArrayBufferDataStream {
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
readPointerTabsBridgedEngine() {
- const pointerId = 3; // tabs:TabsBridgedEngine
+ const pointerId = 4; // tabs:TabsBridgedEngine
const res = UniFFIScaffolding.readPointer(pointerId, this.dataView.buffer, this.pos);
this.pos += 8;
return res;
@@ -168,7 +168,7 @@ class ArrayBufferDataStream {
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
writePointerTabsBridgedEngine(value) {
- const pointerId = 3; // tabs:TabsBridgedEngine
+ const pointerId = 4; // tabs:TabsBridgedEngine
UniFFIScaffolding.writePointer(pointerId, value, this.dataView.buffer, this.pos);
this.pos += 8;
}
@@ -178,7 +178,7 @@ class ArrayBufferDataStream {
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
readPointerTabsStore() {
- const pointerId = 4; // tabs:TabsStore
+ const pointerId = 5; // tabs:TabsStore
const res = UniFFIScaffolding.readPointer(pointerId, this.dataView.buffer, this.pos);
this.pos += 8;
return res;
@@ -188,7 +188,7 @@ class ArrayBufferDataStream {
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
writePointerTabsStore(value) {
- const pointerId = 4; // tabs:TabsStore
+ const pointerId = 5; // tabs:TabsStore
UniFFIScaffolding.writePointer(pointerId, value, this.dataView.buffer, this.pos);
this.pos += 8;
}
@@ -361,7 +361,7 @@ export class TabsBridgedEngine {
const liftError = (data) => FfiConverterTypeTabsApiError.lift(data);
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 17, // tabs:uniffi_tabs_fn_method_tabsbridgedengine_apply
+ 28, // tabs:uniffi_tabs_fn_method_tabsbridgedengine_apply
FfiConverterTypeTabsBridgedEngine.lower(this),
)
}
@@ -385,7 +385,7 @@ export class TabsBridgedEngine {
throw e;
}
return UniFFIScaffolding.callAsync(
- 18, // tabs:uniffi_tabs_fn_method_tabsbridgedengine_ensure_current_sync_id
+ 29, // tabs:uniffi_tabs_fn_method_tabsbridgedengine_ensure_current_sync_id
FfiConverterTypeTabsBridgedEngine.lower(this),
FfiConverterString.lower(newSyncId),
)
@@ -402,7 +402,7 @@ export class TabsBridgedEngine {
const liftError = (data) => FfiConverterTypeTabsApiError.lift(data);
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 19, // tabs:uniffi_tabs_fn_method_tabsbridgedengine_last_sync
+ 30, // tabs:uniffi_tabs_fn_method_tabsbridgedengine_last_sync
FfiConverterTypeTabsBridgedEngine.lower(this),
)
}
@@ -426,7 +426,7 @@ export class TabsBridgedEngine {
throw e;
}
return UniFFIScaffolding.callAsync(
- 20, // tabs:uniffi_tabs_fn_method_tabsbridgedengine_prepare_for_sync
+ 31, // tabs:uniffi_tabs_fn_method_tabsbridgedengine_prepare_for_sync
FfiConverterTypeTabsBridgedEngine.lower(this),
FfiConverterString.lower(clientData),
)
@@ -443,7 +443,7 @@ export class TabsBridgedEngine {
const liftError = (data) => FfiConverterTypeTabsApiError.lift(data);
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 21, // tabs:uniffi_tabs_fn_method_tabsbridgedengine_reset
+ 32, // tabs:uniffi_tabs_fn_method_tabsbridgedengine_reset
FfiConverterTypeTabsBridgedEngine.lower(this),
)
}
@@ -459,7 +459,7 @@ export class TabsBridgedEngine {
const liftError = (data) => FfiConverterTypeTabsApiError.lift(data);
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 22, // tabs:uniffi_tabs_fn_method_tabsbridgedengine_reset_sync_id
+ 33, // tabs:uniffi_tabs_fn_method_tabsbridgedengine_reset_sync_id
FfiConverterTypeTabsBridgedEngine.lower(this),
)
}
@@ -483,7 +483,7 @@ export class TabsBridgedEngine {
throw e;
}
return UniFFIScaffolding.callAsync(
- 23, // tabs:uniffi_tabs_fn_method_tabsbridgedengine_set_last_sync
+ 34, // tabs:uniffi_tabs_fn_method_tabsbridgedengine_set_last_sync
FfiConverterTypeTabsBridgedEngine.lower(this),
FfiConverterI64.lower(lastSync),
)
@@ -516,7 +516,7 @@ export class TabsBridgedEngine {
throw e;
}
return UniFFIScaffolding.callAsync(
- 24, // tabs:uniffi_tabs_fn_method_tabsbridgedengine_set_uploaded
+ 35, // tabs:uniffi_tabs_fn_method_tabsbridgedengine_set_uploaded
FfiConverterTypeTabsBridgedEngine.lower(this),
FfiConverterI64.lower(newTimestamp),
FfiConverterSequenceTypeTabsGuid.lower(uploadedIds),
@@ -542,7 +542,7 @@ export class TabsBridgedEngine {
throw e;
}
return UniFFIScaffolding.callAsync(
- 25, // tabs:uniffi_tabs_fn_method_tabsbridgedengine_store_incoming
+ 36, // tabs:uniffi_tabs_fn_method_tabsbridgedengine_store_incoming
FfiConverterTypeTabsBridgedEngine.lower(this),
FfiConverterSequencestring.lower(incomingEnvelopesAsJson),
)
@@ -559,7 +559,7 @@ export class TabsBridgedEngine {
const liftError = (data) => FfiConverterTypeTabsApiError.lift(data);
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 26, // tabs:uniffi_tabs_fn_method_tabsbridgedengine_sync_finished
+ 37, // tabs:uniffi_tabs_fn_method_tabsbridgedengine_sync_finished
FfiConverterTypeTabsBridgedEngine.lower(this),
)
}
@@ -575,7 +575,7 @@ export class TabsBridgedEngine {
const liftError = (data) => FfiConverterTypeTabsApiError.lift(data);
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 27, // tabs:uniffi_tabs_fn_method_tabsbridgedengine_sync_id
+ 38, // tabs:uniffi_tabs_fn_method_tabsbridgedengine_sync_id
FfiConverterTypeTabsBridgedEngine.lower(this),
)
}
@@ -591,7 +591,7 @@ export class TabsBridgedEngine {
const liftError = (data) => FfiConverterTypeTabsApiError.lift(data);
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 28, // tabs:uniffi_tabs_fn_method_tabsbridgedengine_sync_started
+ 39, // tabs:uniffi_tabs_fn_method_tabsbridgedengine_sync_started
FfiConverterTypeTabsBridgedEngine.lower(this),
)
}
@@ -607,7 +607,7 @@ export class TabsBridgedEngine {
const liftError = (data) => FfiConverterTypeTabsApiError.lift(data);
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 29, // tabs:uniffi_tabs_fn_method_tabsbridgedengine_wipe
+ 40, // tabs:uniffi_tabs_fn_method_tabsbridgedengine_wipe
FfiConverterTypeTabsBridgedEngine.lower(this),
)
}
@@ -629,7 +629,11 @@ export class FfiConverterTypeTabsBridgedEngine extends FfiConverter {
}
static lower(value) {
- return value[uniffiObjectPtr];
+ const ptr = value[uniffiObjectPtr];
+ if (!(ptr instanceof UniFFIPointer)) {
+ throw new UniFFITypeError("Object is not a 'TabsBridgedEngine' instance");
+ }
+ return ptr;
}
static read(dataStream) {
@@ -677,7 +681,7 @@ export class TabsStore {
throw e;
}
return UniFFIScaffolding.callAsync(
- 30, // tabs:uniffi_tabs_fn_constructor_tabsstore_new
+ 42, // tabs:uniffi_tabs_fn_constructor_tabsstore_new
FfiConverterString.lower(path),
)
}
@@ -692,7 +696,7 @@ export class TabsStore {
const liftError = null;
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 31, // tabs:uniffi_tabs_fn_method_tabsstore_bridged_engine
+ 43, // tabs:uniffi_tabs_fn_method_tabsstore_bridged_engine
FfiConverterTypeTabsStore.lower(this),
)
}
@@ -708,7 +712,7 @@ export class TabsStore {
const liftError = null;
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 32, // tabs:uniffi_tabs_fn_method_tabsstore_get_all
+ 44, // tabs:uniffi_tabs_fn_method_tabsstore_get_all
FfiConverterTypeTabsStore.lower(this),
)
}
@@ -724,7 +728,7 @@ export class TabsStore {
const liftError = null;
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 33, // tabs:uniffi_tabs_fn_method_tabsstore_register_with_sync_manager
+ 45, // tabs:uniffi_tabs_fn_method_tabsstore_register_with_sync_manager
FfiConverterTypeTabsStore.lower(this),
)
}
@@ -748,7 +752,7 @@ export class TabsStore {
throw e;
}
return UniFFIScaffolding.callAsync(
- 34, // tabs:uniffi_tabs_fn_method_tabsstore_set_local_tabs
+ 46, // tabs:uniffi_tabs_fn_method_tabsstore_set_local_tabs
FfiConverterTypeTabsStore.lower(this),
FfiConverterSequenceTypeRemoteTabRecord.lower(remoteTabs),
)
@@ -771,7 +775,11 @@ export class FfiConverterTypeTabsStore extends FfiConverter {
}
static lower(value) {
- return value[uniffiObjectPtr];
+ const ptr = value[uniffiObjectPtr];
+ if (!(ptr instanceof UniFFIPointer)) {
+ throw new UniFFITypeError("Object is not a 'TabsStore' instance");
+ }
+ return ptr;
}
static read(dataStream) {
@@ -878,7 +886,7 @@ export class FfiConverterTypeClientRemoteTabs extends FfiConverterArrayBuffer {
static checkType(value) {
super.checkType(value);
if (!(value instanceof ClientRemoteTabs)) {
- throw new TypeError(`Expected 'ClientRemoteTabs', found '${typeof value}'`);
+ throw new UniFFITypeError(`Expected 'ClientRemoteTabs', found '${typeof value}'`);
}
try {
FfiConverterString.checkType(value.clientId);
@@ -1014,7 +1022,7 @@ export class FfiConverterTypeRemoteTabRecord extends FfiConverterArrayBuffer {
static checkType(value) {
super.checkType(value);
if (!(value instanceof RemoteTabRecord)) {
- throw new TypeError(`Expected 'RemoteTabRecord', found '${typeof value}'`);
+ throw new UniFFITypeError(`Expected 'RemoteTabRecord', found '${typeof value}'`);
}
try {
FfiConverterString.checkType(value.title);
@@ -1124,7 +1132,7 @@ export class FfiConverterTypeTabsApiError extends FfiConverterArrayBuffer {
FfiConverterString.read(dataStream)
);
default:
- throw new Error("Unknown TabsApiError variant");
+ throw new UniFFITypeError("Unknown TabsApiError variant");
}
}
static computeSize(value) {
@@ -1142,7 +1150,7 @@ export class FfiConverterTypeTabsApiError extends FfiConverterArrayBuffer {
totalSize += FfiConverterString.computeSize(value.reason);
return totalSize;
}
- throw new Error("Unknown TabsApiError variant");
+ throw new UniFFITypeError("Unknown TabsApiError variant");
}
static write(dataStream, value) {
if (value instanceof SyncError) {
@@ -1160,7 +1168,7 @@ export class FfiConverterTypeTabsApiError extends FfiConverterArrayBuffer {
FfiConverterString.write(dataStream, value.reason);
return;
}
- throw new Error("Unknown TabsApiError variant");
+ throw new UniFFITypeError("Unknown TabsApiError variant");
}
static errorClass = TabsApiError;
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/components/moz.build b/toolkit/components/uniffi-bindgen-gecko-js/components/moz.build
index 107745a4b8..405711db98 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/components/moz.build
+++ b/toolkit/components/uniffi-bindgen-gecko-js/components/moz.build
@@ -5,6 +5,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXTRA_JS_MODULES += [
+ "generated/RustRelevancy.sys.mjs",
"generated/RustRemoteSettings.sys.mjs",
"generated/RustSuggest.sys.mjs",
"generated/RustSync15.sys.mjs",
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/config.toml b/toolkit/components/uniffi-bindgen-gecko-js/config.toml
index d59b2a69e1..d8404f5142 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/config.toml
+++ b/toolkit/components/uniffi-bindgen-gecko-js/config.toml
@@ -29,6 +29,14 @@ main = [
"SuggestStore.interrupt",
]
+[relevancy]
+crate_name = "relevancy"
+udl_file = "third_party/rust/relevancy/src/relevancy.udl"
+
+[relevancy.receiver_thread]
+default = "worker"
+main = []
+
[remote_settings]
crate_name = "remote_settings"
udl_file = "third_party/rust/remote_settings/src/remote_settings.udl"
@@ -64,16 +72,27 @@ crate_name = "uniffi_todolist"
udl_file = "third_party/rust/uniffi-example-todolist/src/todolist.udl"
fixture = true
-[fixture_callbacks]
-crate_name = "uniffi_fixture_callbacks"
-udl_file = "toolkit/components/uniffi-fixture-callbacks/src/callbacks.udl"
+[fixture_refcounts]
+crate_name = "uniffi_fixture_refcounts"
+udl_file = "toolkit/components/uniffi-fixture-refcounts/src/refcounts.udl"
fixture = true
-[fixture_callbacks.receiver_thread]
-default = "worker"
-main = [
- "log_even_numbers_main_thread",
-]
+[fixture_refcounts.receiver_thread]
+default = "main"
+
+# Temporarily disabled until we can re-enable callback support
+#
+# I think this can be done when we implement https://bugzilla.mozilla.org/show_bug.cgi?id=1888668
+# [fixture_callbacks]
+# crate_name = "uniffi_fixture_callbacks"
+# udl_file = "toolkit/components/uniffi-fixture-callbacks/src/callbacks.udl"
+# fixture = true
+#
+# [fixture_callbacks.receiver_thread]
+# default = "worker"
+# main = [
+# "log_even_numbers_main_thread",
+# ]
[custom_types]
crate_name = "uniffi_custom_types"
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustArithmetic.sys.mjs b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustArithmetic.sys.mjs
index 1790f0effa..62766a45a4 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustArithmetic.sys.mjs
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustArithmetic.sys.mjs
@@ -329,7 +329,7 @@ export class FfiConverterTypeArithmeticError extends FfiConverterArrayBuffer {
case 1:
return new IntegerOverflow(FfiConverterString.read(dataStream));
default:
- throw new Error("Unknown ArithmeticError variant");
+ throw new UniFFITypeError("Unknown ArithmeticError variant");
}
}
static computeSize(value) {
@@ -338,14 +338,14 @@ export class FfiConverterTypeArithmeticError extends FfiConverterArrayBuffer {
if (value instanceof IntegerOverflow) {
return totalSize;
}
- throw new Error("Unknown ArithmeticError variant");
+ throw new UniFFITypeError("Unknown ArithmeticError variant");
}
static write(dataStream, value) {
if (value instanceof IntegerOverflow) {
dataStream.writeInt32(1);
return;
}
- throw new Error("Unknown ArithmeticError variant");
+ throw new UniFFITypeError("Unknown ArithmeticError variant");
}
static errorClass = ArithmeticError;
@@ -377,7 +377,7 @@ export function add(a,b) {
throw e;
}
return UniFFIScaffolding.callAsync(
- 35, // arithmetic:uniffi_arithmetical_fn_func_add
+ 47, // arithmetic:uniffi_arithmetical_fn_func_add
FfiConverterU64.lower(a),
FfiConverterU64.lower(b),
)
@@ -411,7 +411,7 @@ export function div(dividend,divisor) {
throw e;
}
return UniFFIScaffolding.callAsync(
- 36, // arithmetic:uniffi_arithmetical_fn_func_div
+ 48, // arithmetic:uniffi_arithmetical_fn_func_div
FfiConverterU64.lower(dividend),
FfiConverterU64.lower(divisor),
)
@@ -445,7 +445,7 @@ export function equal(a,b) {
throw e;
}
return UniFFIScaffolding.callAsync(
- 37, // arithmetic:uniffi_arithmetical_fn_func_equal
+ 49, // arithmetic:uniffi_arithmetical_fn_func_equal
FfiConverterU64.lower(a),
FfiConverterU64.lower(b),
)
@@ -479,7 +479,7 @@ export function sub(a,b) {
throw e;
}
return UniFFIScaffolding.callAsync(
- 38, // arithmetic:uniffi_arithmetical_fn_func_sub
+ 50, // arithmetic:uniffi_arithmetical_fn_func_sub
FfiConverterU64.lower(a),
FfiConverterU64.lower(b),
)
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustCustomTypes.sys.mjs b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustCustomTypes.sys.mjs
index 286c046fa6..581478cc8a 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustCustomTypes.sys.mjs
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustCustomTypes.sys.mjs
@@ -331,7 +331,7 @@ export class FfiConverterTypeCustomTypesDemo extends FfiConverterArrayBuffer {
static checkType(value) {
super.checkType(value);
if (!(value instanceof CustomTypesDemo)) {
- throw new TypeError(`Expected 'CustomTypesDemo', found '${typeof value}'`);
+ throw new UniFFITypeError(`Expected 'CustomTypesDemo', found '${typeof value}'`);
}
try {
FfiConverterTypeUrl.checkType(value.url);
@@ -455,7 +455,7 @@ export function getCustomTypesDemo(demo) {
throw e;
}
return UniFFIScaffolding.callAsync(
- 39, // custom_types:uniffi_uniffi_custom_types_fn_func_get_custom_types_demo
+ 51, // custom_types:uniffi_uniffi_custom_types_fn_func_get_custom_types_demo
FfiConverterOptionalTypeCustomTypesDemo.lower(demo),
)
}
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustExternalTypes.sys.mjs b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustExternalTypes.sys.mjs
index c01b1a58b3..88c9390225 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustExternalTypes.sys.mjs
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustExternalTypes.sys.mjs
@@ -381,7 +381,7 @@ export function gradient(value) {
throw e;
}
return UniFFIScaffolding.callAsync(
- 40, // external_types:uniffi_uniffi_fixture_external_types_fn_func_gradient
+ 52, // external_types:uniffi_uniffi_fixture_external_types_fn_func_gradient
FfiConverterOptionalTypeLine.lower(value),
)
}
@@ -414,7 +414,7 @@ export function intersection(ln1,ln2) {
throw e;
}
return UniFFIScaffolding.callAsync(
- 41, // external_types:uniffi_uniffi_fixture_external_types_fn_func_intersection
+ 53, // external_types:uniffi_uniffi_fixture_external_types_fn_func_intersection
FfiConverterTypeLine.lower(ln1),
FfiConverterTypeLine.lower(ln2),
)
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustFixtureCallbacks.sys.mjs b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustFixtureCallbacks.sys.mjs
index 75fe81a458..2b6b6b26ff 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustFixtureCallbacks.sys.mjs
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustFixtureCallbacks.sys.mjs
@@ -596,7 +596,7 @@ export function logEvenNumbers(logger,items) {
throw e;
}
return UniFFIScaffolding.callAsync(
- 42, // fixture_callbacks:uniffi_uniffi_fixture_callbacks_fn_func_log_even_numbers
+ 46, // fixture_callbacks:uniffi_uniffi_fixture_callbacks_fn_func_log_even_numbers
FfiConverterTypeLogger.lower(logger),
FfiConverterSequencei32.lower(items),
)
@@ -630,7 +630,7 @@ export function logEvenNumbersMainThread(logger,items) {
throw e;
}
return UniFFIScaffolding.callSync(
- 43, // fixture_callbacks:uniffi_uniffi_fixture_callbacks_fn_func_log_even_numbers_main_thread
+ 47, // fixture_callbacks:uniffi_uniffi_fixture_callbacks_fn_func_log_even_numbers_main_thread
FfiConverterTypeLogger.lower(logger),
FfiConverterSequencei32.lower(items),
)
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustGeometry.sys.mjs b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustGeometry.sys.mjs
index 1a7ebb287a..6ff9a25fa9 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustGeometry.sys.mjs
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustGeometry.sys.mjs
@@ -325,7 +325,7 @@ export class FfiConverterTypeLine extends FfiConverterArrayBuffer {
static checkType(value) {
super.checkType(value);
if (!(value instanceof Line)) {
- throw new TypeError(`Expected 'Line', found '${typeof value}'`);
+ throw new UniFFITypeError(`Expected 'Line', found '${typeof value}'`);
}
try {
FfiConverterTypePoint.checkType(value.start);
@@ -398,7 +398,7 @@ export class FfiConverterTypePoint extends FfiConverterArrayBuffer {
static checkType(value) {
super.checkType(value);
if (!(value instanceof Point)) {
- throw new TypeError(`Expected 'Point', found '${typeof value}'`);
+ throw new UniFFITypeError(`Expected 'Point', found '${typeof value}'`);
}
try {
FfiConverterF64.checkType(value.coordX);
@@ -474,7 +474,7 @@ export function gradient(ln) {
throw e;
}
return UniFFIScaffolding.callAsync(
- 44, // geometry:uniffi_uniffi_geometry_fn_func_gradient
+ 58, // geometry:uniffi_uniffi_geometry_fn_func_gradient
FfiConverterTypeLine.lower(ln),
)
}
@@ -507,7 +507,7 @@ export function intersection(ln1,ln2) {
throw e;
}
return UniFFIScaffolding.callAsync(
- 45, // geometry:uniffi_uniffi_geometry_fn_func_intersection
+ 59, // geometry:uniffi_uniffi_geometry_fn_func_intersection
FfiConverterTypeLine.lower(ln1),
FfiConverterTypeLine.lower(ln2),
)
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustRefcounts.sys.mjs b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustRefcounts.sys.mjs
new file mode 100644
index 0000000000..d8a598d150
--- /dev/null
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustRefcounts.sys.mjs
@@ -0,0 +1,388 @@
+// This file was autogenerated by the `uniffi-bindgen-gecko-js` crate.
+// Trust me, you don't want to mess with it!
+
+import { UniFFITypeError } from "resource://gre/modules/UniFFI.sys.mjs";
+
+
+
+// Objects intended to be used in the unit tests
+export var UnitTestObjs = {};
+
+// Write/Read data to/from an ArrayBuffer
+class ArrayBufferDataStream {
+ constructor(arrayBuffer) {
+ this.dataView = new DataView(arrayBuffer);
+ this.pos = 0;
+ }
+
+ readUint8() {
+ let rv = this.dataView.getUint8(this.pos);
+ this.pos += 1;
+ return rv;
+ }
+
+ writeUint8(value) {
+ this.dataView.setUint8(this.pos, value);
+ this.pos += 1;
+ }
+
+ readUint16() {
+ let rv = this.dataView.getUint16(this.pos);
+ this.pos += 2;
+ return rv;
+ }
+
+ writeUint16(value) {
+ this.dataView.setUint16(this.pos, value);
+ this.pos += 2;
+ }
+
+ readUint32() {
+ let rv = this.dataView.getUint32(this.pos);
+ this.pos += 4;
+ return rv;
+ }
+
+ writeUint32(value) {
+ this.dataView.setUint32(this.pos, value);
+ this.pos += 4;
+ }
+
+ readUint64() {
+ let rv = this.dataView.getBigUint64(this.pos);
+ this.pos += 8;
+ return Number(rv);
+ }
+
+ writeUint64(value) {
+ this.dataView.setBigUint64(this.pos, BigInt(value));
+ this.pos += 8;
+ }
+
+
+ readInt8() {
+ let rv = this.dataView.getInt8(this.pos);
+ this.pos += 1;
+ return rv;
+ }
+
+ writeInt8(value) {
+ this.dataView.setInt8(this.pos, value);
+ this.pos += 1;
+ }
+
+ readInt16() {
+ let rv = this.dataView.getInt16(this.pos);
+ this.pos += 2;
+ return rv;
+ }
+
+ writeInt16(value) {
+ this.dataView.setInt16(this.pos, value);
+ this.pos += 2;
+ }
+
+ readInt32() {
+ let rv = this.dataView.getInt32(this.pos);
+ this.pos += 4;
+ return rv;
+ }
+
+ writeInt32(value) {
+ this.dataView.setInt32(this.pos, value);
+ this.pos += 4;
+ }
+
+ readInt64() {
+ let rv = this.dataView.getBigInt64(this.pos);
+ this.pos += 8;
+ return Number(rv);
+ }
+
+ writeInt64(value) {
+ this.dataView.setBigInt64(this.pos, BigInt(value));
+ this.pos += 8;
+ }
+
+ readFloat32() {
+ let rv = this.dataView.getFloat32(this.pos);
+ this.pos += 4;
+ return rv;
+ }
+
+ writeFloat32(value) {
+ this.dataView.setFloat32(this.pos, value);
+ this.pos += 4;
+ }
+
+ readFloat64() {
+ let rv = this.dataView.getFloat64(this.pos);
+ this.pos += 8;
+ return rv;
+ }
+
+ writeFloat64(value) {
+ this.dataView.setFloat64(this.pos, value);
+ this.pos += 8;
+ }
+
+
+ writeString(value) {
+ const encoder = new TextEncoder();
+ // Note: in order to efficiently write this data, we first write the
+ // string data, reserving 4 bytes for the size.
+ const dest = new Uint8Array(this.dataView.buffer, this.pos + 4);
+ const encodeResult = encoder.encodeInto(value, dest);
+ if (encodeResult.read != value.length) {
+ throw new UniFFIError(
+ "writeString: out of space when writing to ArrayBuffer. Did the computeSize() method returned the wrong result?"
+ );
+ }
+ const size = encodeResult.written;
+ // Next, go back and write the size before the string data
+ this.dataView.setUint32(this.pos, size);
+ // Finally, advance our position past both the size and string data
+ this.pos += size + 4;
+ }
+
+ readString() {
+ const decoder = new TextDecoder();
+ const size = this.readUint32();
+ const source = new Uint8Array(this.dataView.buffer, this.pos, size)
+ const value = decoder.decode(source);
+ this.pos += size;
+ return value;
+ }
+
+ // Reads a SingletonObject pointer from the data stream
+ // UniFFI Pointers are **always** 8 bytes long. That is enforced
+ // by the C++ and Rust Scaffolding code.
+ readPointerSingletonObject() {
+ const pointerId = 6; // refcounts:SingletonObject
+ const res = UniFFIScaffolding.readPointer(pointerId, this.dataView.buffer, this.pos);
+ this.pos += 8;
+ return res;
+ }
+
+ // Writes a SingletonObject pointer into the data stream
+ // UniFFI Pointers are **always** 8 bytes long. That is enforced
+ // by the C++ and Rust Scaffolding code.
+ writePointerSingletonObject(value) {
+ const pointerId = 6; // refcounts:SingletonObject
+ UniFFIScaffolding.writePointer(pointerId, value, this.dataView.buffer, this.pos);
+ this.pos += 8;
+ }
+
+}
+
+function handleRustResult(result, liftCallback, liftErrCallback) {
+ switch (result.code) {
+ case "success":
+ return liftCallback(result.data);
+
+ case "error":
+ throw liftErrCallback(result.data);
+
+ case "internal-error":
+ let message = result.internalErrorMessage;
+ if (message) {
+ throw new UniFFIInternalError(message);
+ } else {
+ throw new UniFFIInternalError("Unknown error");
+ }
+
+ default:
+ throw new UniFFIError(`Unexpected status code: ${result.code}`);
+ }
+}
+
+class UniFFIError {
+ constructor(message) {
+ this.message = message;
+ }
+
+ toString() {
+ return `UniFFIError: ${this.message}`
+ }
+}
+
+class UniFFIInternalError extends UniFFIError {}
+
+// Base class for FFI converters
+class FfiConverter {
+ // throw `UniFFITypeError` if a value to be converted has an invalid type
+ static checkType(value) {
+ if (value === undefined ) {
+ throw new UniFFITypeError(`undefined`);
+ }
+ if (value === null ) {
+ throw new UniFFITypeError(`null`);
+ }
+ }
+}
+
+// Base class for FFI converters that lift/lower by reading/writing to an ArrayBuffer
+class FfiConverterArrayBuffer extends FfiConverter {
+ static lift(buf) {
+ return this.read(new ArrayBufferDataStream(buf));
+ }
+
+ static lower(value) {
+ const buf = new ArrayBuffer(this.computeSize(value));
+ const dataStream = new ArrayBufferDataStream(buf);
+ this.write(dataStream, value);
+ return buf;
+ }
+}
+
+// Symbols that are used to ensure that Object constructors
+// can only be used with a proper UniFFI pointer
+const uniffiObjectPtr = Symbol("uniffiObjectPtr");
+const constructUniffiObject = Symbol("constructUniffiObject");
+UnitTestObjs.uniffiObjectPtr = uniffiObjectPtr;
+
+// Export the FFIConverter object to make external types work.
+export class FfiConverterI32 extends FfiConverter {
+ static checkType(value) {
+ super.checkType(value);
+ if (!Number.isInteger(value)) {
+ throw new UniFFITypeError(`${value} is not an integer`);
+ }
+ if (value < -2147483648 || value > 2147483647) {
+ throw new UniFFITypeError(`${value} exceeds the I32 bounds`);
+ }
+ }
+ static computeSize() {
+ return 4;
+ }
+ static lift(value) {
+ return value;
+ }
+ static lower(value) {
+ return value;
+ }
+ static write(dataStream, value) {
+ dataStream.writeInt32(value)
+ }
+ static read(dataStream) {
+ return dataStream.readInt32()
+ }
+}
+
+// Export the FFIConverter object to make external types work.
+export class FfiConverterString extends FfiConverter {
+ static checkType(value) {
+ super.checkType(value);
+ if (typeof value !== "string") {
+ throw new UniFFITypeError(`${value} is not a string`);
+ }
+ }
+
+ static lift(buf) {
+ const decoder = new TextDecoder();
+ const utf8Arr = new Uint8Array(buf);
+ return decoder.decode(utf8Arr);
+ }
+ static lower(value) {
+ const encoder = new TextEncoder();
+ return encoder.encode(value).buffer;
+ }
+
+ static write(dataStream, value) {
+ dataStream.writeString(value);
+ }
+
+ static read(dataStream) {
+ return dataStream.readString();
+ }
+
+ static computeSize(value) {
+ const encoder = new TextEncoder();
+ return 4 + encoder.encode(value).length
+ }
+}
+
+export class SingletonObject {
+ // Use `init` to instantiate this class.
+ // DO NOT USE THIS CONSTRUCTOR DIRECTLY
+ constructor(opts) {
+ if (!Object.prototype.hasOwnProperty.call(opts, constructUniffiObject)) {
+ throw new UniFFIError("Attempting to construct an object using the JavaScript constructor directly" +
+ "Please use a UDL defined constructor, or the init function for the primary constructor")
+ }
+ if (!opts[constructUniffiObject] instanceof UniFFIPointer) {
+ throw new UniFFIError("Attempting to create a UniFFI object with a pointer that is not an instance of UniFFIPointer")
+ }
+ this[uniffiObjectPtr] = opts[constructUniffiObject];
+ }
+
+ method() {
+ const liftResult = (result) => undefined;
+ const liftError = null;
+ const functionCall = () => {
+ return UniFFIScaffolding.callSync(
+ 55, // refcounts:uniffi_uniffi_fixture_refcounts_fn_method_singletonobject_method
+ FfiConverterTypeSingletonObject.lower(this),
+ )
+ }
+ return handleRustResult(functionCall(), liftResult, liftError);
+ }
+
+}
+
+// Export the FFIConverter object to make external types work.
+export class FfiConverterTypeSingletonObject extends FfiConverter {
+ static lift(value) {
+ const opts = {};
+ opts[constructUniffiObject] = value;
+ return new SingletonObject(opts);
+ }
+
+ static lower(value) {
+ const ptr = value[uniffiObjectPtr];
+ if (!(ptr instanceof UniFFIPointer)) {
+ throw new UniFFITypeError("Object is not a 'SingletonObject' instance");
+ }
+ return ptr;
+ }
+
+ static read(dataStream) {
+ return this.lift(dataStream.readPointerSingletonObject());
+ }
+
+ static write(dataStream, value) {
+ dataStream.writePointerSingletonObject(value[uniffiObjectPtr]);
+ }
+
+ static computeSize(value) {
+ return 8;
+ }
+}
+
+
+
+
+
+export function getJsRefcount() {
+
+ const liftResult = (result) => FfiConverterI32.lift(result);
+ const liftError = null;
+ const functionCall = () => {
+ return UniFFIScaffolding.callSync(
+ 56, // refcounts:uniffi_uniffi_fixture_refcounts_fn_func_get_js_refcount
+ )
+ }
+ return handleRustResult(functionCall(), liftResult, liftError);
+}
+
+export function getSingleton() {
+
+ const liftResult = (result) => FfiConverterTypeSingletonObject.lift(result);
+ const liftError = null;
+ const functionCall = () => {
+ return UniFFIScaffolding.callSync(
+ 57, // refcounts:uniffi_uniffi_fixture_refcounts_fn_func_get_singleton
+ )
+ }
+ return handleRustResult(functionCall(), liftResult, liftError);
+}
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustRondpoint.sys.mjs b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustRondpoint.sys.mjs
index 1edb7ec608..f6db36163e 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustRondpoint.sys.mjs
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustRondpoint.sys.mjs
@@ -158,7 +158,7 @@ class ArrayBufferDataStream {
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
readPointerOptionneur() {
- const pointerId = 5; // rondpoint:Optionneur
+ const pointerId = 7; // rondpoint:Optionneur
const res = UniFFIScaffolding.readPointer(pointerId, this.dataView.buffer, this.pos);
this.pos += 8;
return res;
@@ -168,7 +168,7 @@ class ArrayBufferDataStream {
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
writePointerOptionneur(value) {
- const pointerId = 5; // rondpoint:Optionneur
+ const pointerId = 7; // rondpoint:Optionneur
UniFFIScaffolding.writePointer(pointerId, value, this.dataView.buffer, this.pos);
this.pos += 8;
}
@@ -178,7 +178,7 @@ class ArrayBufferDataStream {
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
readPointerRetourneur() {
- const pointerId = 6; // rondpoint:Retourneur
+ const pointerId = 8; // rondpoint:Retourneur
const res = UniFFIScaffolding.readPointer(pointerId, this.dataView.buffer, this.pos);
this.pos += 8;
return res;
@@ -188,7 +188,7 @@ class ArrayBufferDataStream {
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
writePointerRetourneur(value) {
- const pointerId = 6; // rondpoint:Retourneur
+ const pointerId = 8; // rondpoint:Retourneur
UniFFIScaffolding.writePointer(pointerId, value, this.dataView.buffer, this.pos);
this.pos += 8;
}
@@ -198,7 +198,7 @@ class ArrayBufferDataStream {
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
readPointerStringifier() {
- const pointerId = 7; // rondpoint:Stringifier
+ const pointerId = 9; // rondpoint:Stringifier
const res = UniFFIScaffolding.readPointer(pointerId, this.dataView.buffer, this.pos);
this.pos += 8;
return res;
@@ -208,7 +208,7 @@ class ArrayBufferDataStream {
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
writePointerStringifier(value) {
- const pointerId = 7; // rondpoint:Stringifier
+ const pointerId = 9; // rondpoint:Stringifier
UniFFIScaffolding.writePointer(pointerId, value, this.dataView.buffer, this.pos);
this.pos += 8;
}
@@ -620,7 +620,7 @@ export class Optionneur {
const liftError = null;
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 46, // rondpoint:uniffi_uniffi_rondpoint_fn_constructor_optionneur_new
+ 61, // rondpoint:uniffi_uniffi_rondpoint_fn_constructor_optionneur_new
)
}
try {
@@ -642,7 +642,7 @@ export class Optionneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 47, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_boolean
+ 62, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_boolean
FfiConverterTypeOptionneur.lower(this),
FfiConverterBool.lower(value),
)
@@ -667,7 +667,7 @@ export class Optionneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 48, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_enum
+ 63, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_enum
FfiConverterTypeOptionneur.lower(this),
FfiConverterTypeEnumeration.lower(value),
)
@@ -692,7 +692,7 @@ export class Optionneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 49, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_f32
+ 64, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_f32
FfiConverterTypeOptionneur.lower(this),
FfiConverterF32.lower(value),
)
@@ -717,7 +717,7 @@ export class Optionneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 50, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_f64
+ 65, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_f64
FfiConverterTypeOptionneur.lower(this),
FfiConverterF64.lower(value),
)
@@ -742,7 +742,7 @@ export class Optionneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 51, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i16_dec
+ 66, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i16_dec
FfiConverterTypeOptionneur.lower(this),
FfiConverterI16.lower(value),
)
@@ -767,7 +767,7 @@ export class Optionneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 52, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i16_hex
+ 67, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i16_hex
FfiConverterTypeOptionneur.lower(this),
FfiConverterI16.lower(value),
)
@@ -792,7 +792,7 @@ export class Optionneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 53, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i32_dec
+ 68, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i32_dec
FfiConverterTypeOptionneur.lower(this),
FfiConverterI32.lower(value),
)
@@ -817,7 +817,7 @@ export class Optionneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 54, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i32_hex
+ 69, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i32_hex
FfiConverterTypeOptionneur.lower(this),
FfiConverterI32.lower(value),
)
@@ -842,7 +842,7 @@ export class Optionneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 55, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i64_dec
+ 70, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i64_dec
FfiConverterTypeOptionneur.lower(this),
FfiConverterI64.lower(value),
)
@@ -867,7 +867,7 @@ export class Optionneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 56, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i64_hex
+ 71, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i64_hex
FfiConverterTypeOptionneur.lower(this),
FfiConverterI64.lower(value),
)
@@ -892,7 +892,7 @@ export class Optionneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 57, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i8_dec
+ 72, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i8_dec
FfiConverterTypeOptionneur.lower(this),
FfiConverterI8.lower(value),
)
@@ -917,7 +917,7 @@ export class Optionneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 58, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i8_hex
+ 73, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i8_hex
FfiConverterTypeOptionneur.lower(this),
FfiConverterI8.lower(value),
)
@@ -942,7 +942,7 @@ export class Optionneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 59, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_null
+ 74, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_null
FfiConverterTypeOptionneur.lower(this),
FfiConverterOptionalstring.lower(value),
)
@@ -967,7 +967,7 @@ export class Optionneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 60, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_sequence
+ 75, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_sequence
FfiConverterTypeOptionneur.lower(this),
FfiConverterSequencestring.lower(value),
)
@@ -992,7 +992,7 @@ export class Optionneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 61, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_string
+ 76, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_string
FfiConverterTypeOptionneur.lower(this),
FfiConverterString.lower(value),
)
@@ -1017,7 +1017,7 @@ export class Optionneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 62, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u16_dec
+ 77, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u16_dec
FfiConverterTypeOptionneur.lower(this),
FfiConverterU16.lower(value),
)
@@ -1042,7 +1042,7 @@ export class Optionneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 63, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u16_hex
+ 78, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u16_hex
FfiConverterTypeOptionneur.lower(this),
FfiConverterU16.lower(value),
)
@@ -1067,7 +1067,7 @@ export class Optionneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 64, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_dec
+ 79, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_dec
FfiConverterTypeOptionneur.lower(this),
FfiConverterU32.lower(value),
)
@@ -1092,7 +1092,7 @@ export class Optionneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 65, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_hex
+ 80, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_hex
FfiConverterTypeOptionneur.lower(this),
FfiConverterU32.lower(value),
)
@@ -1117,7 +1117,7 @@ export class Optionneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 66, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_oct
+ 81, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_oct
FfiConverterTypeOptionneur.lower(this),
FfiConverterU32.lower(value),
)
@@ -1142,7 +1142,7 @@ export class Optionneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 67, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u64_dec
+ 82, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u64_dec
FfiConverterTypeOptionneur.lower(this),
FfiConverterU64.lower(value),
)
@@ -1167,7 +1167,7 @@ export class Optionneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 68, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u64_hex
+ 83, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u64_hex
FfiConverterTypeOptionneur.lower(this),
FfiConverterU64.lower(value),
)
@@ -1192,7 +1192,7 @@ export class Optionneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 69, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u8_dec
+ 84, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u8_dec
FfiConverterTypeOptionneur.lower(this),
FfiConverterU8.lower(value),
)
@@ -1217,7 +1217,7 @@ export class Optionneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 70, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u8_hex
+ 85, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u8_hex
FfiConverterTypeOptionneur.lower(this),
FfiConverterU8.lower(value),
)
@@ -1242,7 +1242,7 @@ export class Optionneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 71, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_zero
+ 86, // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_zero
FfiConverterTypeOptionneur.lower(this),
FfiConverterOptionali32.lower(value),
)
@@ -1265,7 +1265,11 @@ export class FfiConverterTypeOptionneur extends FfiConverter {
}
static lower(value) {
- return value[uniffiObjectPtr];
+ const ptr = value[uniffiObjectPtr];
+ if (!(ptr instanceof UniFFIPointer)) {
+ throw new UniFFITypeError("Object is not a 'Optionneur' instance");
+ }
+ return ptr;
}
static read(dataStream) {
@@ -1305,7 +1309,7 @@ export class Retourneur {
const liftError = null;
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 72, // rondpoint:uniffi_uniffi_rondpoint_fn_constructor_retourneur_new
+ 88, // rondpoint:uniffi_uniffi_rondpoint_fn_constructor_retourneur_new
)
}
try {
@@ -1327,7 +1331,7 @@ export class Retourneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 73, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_boolean
+ 89, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_boolean
FfiConverterTypeRetourneur.lower(this),
FfiConverterBool.lower(value),
)
@@ -1352,7 +1356,7 @@ export class Retourneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 74, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_double
+ 90, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_double
FfiConverterTypeRetourneur.lower(this),
FfiConverterF64.lower(value),
)
@@ -1377,7 +1381,7 @@ export class Retourneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 75, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_float
+ 91, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_float
FfiConverterTypeRetourneur.lower(this),
FfiConverterF32.lower(value),
)
@@ -1402,7 +1406,7 @@ export class Retourneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 76, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i16
+ 92, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i16
FfiConverterTypeRetourneur.lower(this),
FfiConverterI16.lower(value),
)
@@ -1427,7 +1431,7 @@ export class Retourneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 77, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i32
+ 93, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i32
FfiConverterTypeRetourneur.lower(this),
FfiConverterI32.lower(value),
)
@@ -1452,7 +1456,7 @@ export class Retourneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 78, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i64
+ 94, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i64
FfiConverterTypeRetourneur.lower(this),
FfiConverterI64.lower(value),
)
@@ -1477,7 +1481,7 @@ export class Retourneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 79, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i8
+ 95, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i8
FfiConverterTypeRetourneur.lower(this),
FfiConverterI8.lower(value),
)
@@ -1502,7 +1506,7 @@ export class Retourneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 80, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_nombres
+ 96, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_nombres
FfiConverterTypeRetourneur.lower(this),
FfiConverterTypeDictionnaireNombres.lower(value),
)
@@ -1527,7 +1531,7 @@ export class Retourneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 81, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_nombres_signes
+ 97, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_nombres_signes
FfiConverterTypeRetourneur.lower(this),
FfiConverterTypeDictionnaireNombresSignes.lower(value),
)
@@ -1552,7 +1556,7 @@ export class Retourneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 82, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_optionneur_dictionnaire
+ 98, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_optionneur_dictionnaire
FfiConverterTypeRetourneur.lower(this),
FfiConverterTypeOptionneurDictionnaire.lower(value),
)
@@ -1577,7 +1581,7 @@ export class Retourneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 83, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_string
+ 99, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_string
FfiConverterTypeRetourneur.lower(this),
FfiConverterString.lower(value),
)
@@ -1602,7 +1606,7 @@ export class Retourneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 84, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u16
+ 100, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u16
FfiConverterTypeRetourneur.lower(this),
FfiConverterU16.lower(value),
)
@@ -1627,7 +1631,7 @@ export class Retourneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 85, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u32
+ 101, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u32
FfiConverterTypeRetourneur.lower(this),
FfiConverterU32.lower(value),
)
@@ -1652,7 +1656,7 @@ export class Retourneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 86, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u64
+ 102, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u64
FfiConverterTypeRetourneur.lower(this),
FfiConverterU64.lower(value),
)
@@ -1677,7 +1681,7 @@ export class Retourneur {
throw e;
}
return UniFFIScaffolding.callAsync(
- 87, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u8
+ 103, // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u8
FfiConverterTypeRetourneur.lower(this),
FfiConverterU8.lower(value),
)
@@ -1700,7 +1704,11 @@ export class FfiConverterTypeRetourneur extends FfiConverter {
}
static lower(value) {
- return value[uniffiObjectPtr];
+ const ptr = value[uniffiObjectPtr];
+ if (!(ptr instanceof UniFFIPointer)) {
+ throw new UniFFITypeError("Object is not a 'Retourneur' instance");
+ }
+ return ptr;
}
static read(dataStream) {
@@ -1740,7 +1748,7 @@ export class Stringifier {
const liftError = null;
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 88, // rondpoint:uniffi_uniffi_rondpoint_fn_constructor_stringifier_new
+ 105, // rondpoint:uniffi_uniffi_rondpoint_fn_constructor_stringifier_new
)
}
try {
@@ -1762,7 +1770,7 @@ export class Stringifier {
throw e;
}
return UniFFIScaffolding.callAsync(
- 89, // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_boolean
+ 106, // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_boolean
FfiConverterTypeStringifier.lower(this),
FfiConverterBool.lower(value),
)
@@ -1787,7 +1795,7 @@ export class Stringifier {
throw e;
}
return UniFFIScaffolding.callAsync(
- 90, // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_double
+ 107, // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_double
FfiConverterTypeStringifier.lower(this),
FfiConverterF64.lower(value),
)
@@ -1812,7 +1820,7 @@ export class Stringifier {
throw e;
}
return UniFFIScaffolding.callAsync(
- 91, // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_float
+ 108, // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_float
FfiConverterTypeStringifier.lower(this),
FfiConverterF32.lower(value),
)
@@ -1837,7 +1845,7 @@ export class Stringifier {
throw e;
}
return UniFFIScaffolding.callAsync(
- 92, // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i16
+ 109, // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i16
FfiConverterTypeStringifier.lower(this),
FfiConverterI16.lower(value),
)
@@ -1862,7 +1870,7 @@ export class Stringifier {
throw e;
}
return UniFFIScaffolding.callAsync(
- 93, // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i32
+ 110, // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i32
FfiConverterTypeStringifier.lower(this),
FfiConverterI32.lower(value),
)
@@ -1887,7 +1895,7 @@ export class Stringifier {
throw e;
}
return UniFFIScaffolding.callAsync(
- 94, // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i64
+ 111, // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i64
FfiConverterTypeStringifier.lower(this),
FfiConverterI64.lower(value),
)
@@ -1912,7 +1920,7 @@ export class Stringifier {
throw e;
}
return UniFFIScaffolding.callAsync(
- 95, // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i8
+ 112, // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i8
FfiConverterTypeStringifier.lower(this),
FfiConverterI8.lower(value),
)
@@ -1937,7 +1945,7 @@ export class Stringifier {
throw e;
}
return UniFFIScaffolding.callAsync(
- 96, // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u16
+ 113, // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u16
FfiConverterTypeStringifier.lower(this),
FfiConverterU16.lower(value),
)
@@ -1962,7 +1970,7 @@ export class Stringifier {
throw e;
}
return UniFFIScaffolding.callAsync(
- 97, // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u32
+ 114, // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u32
FfiConverterTypeStringifier.lower(this),
FfiConverterU32.lower(value),
)
@@ -1987,7 +1995,7 @@ export class Stringifier {
throw e;
}
return UniFFIScaffolding.callAsync(
- 98, // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u64
+ 115, // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u64
FfiConverterTypeStringifier.lower(this),
FfiConverterU64.lower(value),
)
@@ -2012,7 +2020,7 @@ export class Stringifier {
throw e;
}
return UniFFIScaffolding.callAsync(
- 99, // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u8
+ 116, // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u8
FfiConverterTypeStringifier.lower(this),
FfiConverterU8.lower(value),
)
@@ -2037,7 +2045,7 @@ export class Stringifier {
throw e;
}
return UniFFIScaffolding.callAsync(
- 100, // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_well_known_string
+ 117, // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_well_known_string
FfiConverterTypeStringifier.lower(this),
FfiConverterString.lower(value),
)
@@ -2060,7 +2068,11 @@ export class FfiConverterTypeStringifier extends FfiConverter {
}
static lower(value) {
- return value[uniffiObjectPtr];
+ const ptr = value[uniffiObjectPtr];
+ if (!(ptr instanceof UniFFIPointer)) {
+ throw new UniFFITypeError("Object is not a 'Stringifier' instance");
+ }
+ return ptr;
}
static read(dataStream) {
@@ -2154,7 +2166,7 @@ export class FfiConverterTypeDictionnaire extends FfiConverterArrayBuffer {
static checkType(value) {
super.checkType(value);
if (!(value instanceof Dictionnaire)) {
- throw new TypeError(`Expected 'Dictionnaire', found '${typeof value}'`);
+ throw new UniFFITypeError(`Expected 'Dictionnaire', found '${typeof value}'`);
}
try {
FfiConverterTypeEnumeration.checkType(value.un);
@@ -2269,7 +2281,7 @@ export class FfiConverterTypeDictionnaireNombres extends FfiConverterArrayBuffer
static checkType(value) {
super.checkType(value);
if (!(value instanceof DictionnaireNombres)) {
- throw new TypeError(`Expected 'DictionnaireNombres', found '${typeof value}'`);
+ throw new UniFFITypeError(`Expected 'DictionnaireNombres', found '${typeof value}'`);
}
try {
FfiConverterU8.checkType(value.petitNombre);
@@ -2384,7 +2396,7 @@ export class FfiConverterTypeDictionnaireNombresSignes extends FfiConverterArray
static checkType(value) {
super.checkType(value);
if (!(value instanceof DictionnaireNombresSignes)) {
- throw new TypeError(`Expected 'DictionnaireNombresSignes', found '${typeof value}'`);
+ throw new UniFFITypeError(`Expected 'DictionnaireNombresSignes', found '${typeof value}'`);
}
try {
FfiConverterI8.checkType(value.petitNombre);
@@ -2642,7 +2654,7 @@ export class FfiConverterTypeOptionneurDictionnaire extends FfiConverterArrayBuf
static checkType(value) {
super.checkType(value);
if (!(value instanceof OptionneurDictionnaire)) {
- throw new TypeError(`Expected 'OptionneurDictionnaire', found '${typeof value}'`);
+ throw new UniFFITypeError(`Expected 'OptionneurDictionnaire', found '${typeof value}'`);
}
try {
FfiConverterI8.checkType(value.i8Var);
@@ -2806,7 +2818,7 @@ export class FfiConverterTypeminusculeMajusculeDict extends FfiConverterArrayBuf
static checkType(value) {
super.checkType(value);
if (!(value instanceof MinusculeMajusculeDict)) {
- throw new TypeError(`Expected 'MinusculeMajusculeDict', found '${typeof value}'`);
+ throw new UniFFITypeError(`Expected 'MinusculeMajusculeDict', found '${typeof value}'`);
}
try {
FfiConverterBool.checkType(value.minusculeMajusculeField);
@@ -2838,7 +2850,7 @@ export class FfiConverterTypeEnumeration extends FfiConverterArrayBuffer {
case 3:
return Enumeration.TROIS
default:
- return new Error("Unknown Enumeration variant");
+ throw new UniFFITypeError("Unknown Enumeration variant");
}
}
@@ -2855,7 +2867,7 @@ export class FfiConverterTypeEnumeration extends FfiConverterArrayBuffer {
dataStream.writeInt32(3);
return;
}
- return new Error("Unknown Enumeration variant");
+ throw new UniFFITypeError("Unknown Enumeration variant");
}
static computeSize(value) {
@@ -2914,7 +2926,7 @@ export class FfiConverterTypeEnumerationAvecDonnees extends FfiConverterArrayBuf
FfiConverterString.read(dataStream)
);
default:
- return new Error("Unknown EnumerationAvecDonnees variant");
+ throw new UniFFITypeError("Unknown EnumerationAvecDonnees variant");
}
}
@@ -2934,7 +2946,7 @@ export class FfiConverterTypeEnumerationAvecDonnees extends FfiConverterArrayBuf
FfiConverterString.write(dataStream, value.second);
return;
}
- return new Error("Unknown EnumerationAvecDonnees variant");
+ throw new UniFFITypeError("Unknown EnumerationAvecDonnees variant");
}
static computeSize(value) {
@@ -2952,7 +2964,7 @@ export class FfiConverterTypeEnumerationAvecDonnees extends FfiConverterArrayBuf
totalSize += FfiConverterString.computeSize(value.second);
return totalSize;
}
- return new Error("Unknown EnumerationAvecDonnees variant");
+ throw new UniFFITypeError("Unknown EnumerationAvecDonnees variant");
}
static checkType(value) {
@@ -2976,7 +2988,7 @@ export class FfiConverterTypeminusculeMajusculeEnum extends FfiConverterArrayBuf
case 1:
return MinusculeMajusculeEnum.MINUSCULE_MAJUSCULE_VARIANT
default:
- return new Error("Unknown MinusculeMajusculeEnum variant");
+ throw new UniFFITypeError("Unknown MinusculeMajusculeEnum variant");
}
}
@@ -2985,7 +2997,7 @@ export class FfiConverterTypeminusculeMajusculeEnum extends FfiConverterArrayBuf
dataStream.writeInt32(1);
return;
}
- return new Error("Unknown MinusculeMajusculeEnum variant");
+ throw new UniFFITypeError("Unknown MinusculeMajusculeEnum variant");
}
static computeSize(value) {
@@ -3272,7 +3284,7 @@ export function copieCarte(c) {
throw e;
}
return UniFFIScaffolding.callAsync(
- 101, // rondpoint:uniffi_uniffi_rondpoint_fn_func_copie_carte
+ 118, // rondpoint:uniffi_uniffi_rondpoint_fn_func_copie_carte
FfiConverterMapStringTypeEnumerationAvecDonnees.lower(c),
)
}
@@ -3297,7 +3309,7 @@ export function copieDictionnaire(d) {
throw e;
}
return UniFFIScaffolding.callAsync(
- 102, // rondpoint:uniffi_uniffi_rondpoint_fn_func_copie_dictionnaire
+ 119, // rondpoint:uniffi_uniffi_rondpoint_fn_func_copie_dictionnaire
FfiConverterTypeDictionnaire.lower(d),
)
}
@@ -3322,7 +3334,7 @@ export function copieEnumeration(e) {
throw e;
}
return UniFFIScaffolding.callAsync(
- 103, // rondpoint:uniffi_uniffi_rondpoint_fn_func_copie_enumeration
+ 120, // rondpoint:uniffi_uniffi_rondpoint_fn_func_copie_enumeration
FfiConverterTypeEnumeration.lower(e),
)
}
@@ -3347,7 +3359,7 @@ export function copieEnumerations(e) {
throw e;
}
return UniFFIScaffolding.callAsync(
- 104, // rondpoint:uniffi_uniffi_rondpoint_fn_func_copie_enumerations
+ 121, // rondpoint:uniffi_uniffi_rondpoint_fn_func_copie_enumerations
FfiConverterSequenceTypeEnumeration.lower(e),
)
}
@@ -3372,7 +3384,7 @@ export function switcheroo(b) {
throw e;
}
return UniFFIScaffolding.callAsync(
- 105, // rondpoint:uniffi_uniffi_rondpoint_fn_func_switcheroo
+ 122, // rondpoint:uniffi_uniffi_rondpoint_fn_func_switcheroo
FfiConverterBool.lower(b),
)
}
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustSprites.sys.mjs b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustSprites.sys.mjs
index b404af06b1..c25a488009 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustSprites.sys.mjs
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustSprites.sys.mjs
@@ -158,7 +158,7 @@ class ArrayBufferDataStream {
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
readPointerSprite() {
- const pointerId = 8; // sprites:Sprite
+ const pointerId = 10; // sprites:Sprite
const res = UniFFIScaffolding.readPointer(pointerId, this.dataView.buffer, this.pos);
this.pos += 8;
return res;
@@ -168,7 +168,7 @@ class ArrayBufferDataStream {
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
writePointerSprite(value) {
- const pointerId = 8; // sprites:Sprite
+ const pointerId = 10; // sprites:Sprite
UniFFIScaffolding.writePointer(pointerId, value, this.dataView.buffer, this.pos);
this.pos += 8;
}
@@ -325,7 +325,7 @@ export class Sprite {
throw e;
}
return UniFFIScaffolding.callAsync(
- 106, // sprites:uniffi_uniffi_sprites_fn_constructor_sprite_new
+ 124, // sprites:uniffi_uniffi_sprites_fn_constructor_sprite_new
FfiConverterOptionalTypePoint.lower(initialPosition),
)
}
@@ -361,7 +361,7 @@ export class Sprite {
throw e;
}
return UniFFIScaffolding.callAsync(
- 107, // sprites:uniffi_uniffi_sprites_fn_constructor_sprite_new_relative_to
+ 125, // sprites:uniffi_uniffi_sprites_fn_constructor_sprite_new_relative_to
FfiConverterTypePoint.lower(reference),
FfiConverterTypeVector.lower(direction),
)
@@ -377,7 +377,7 @@ export class Sprite {
const liftError = null;
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 108, // sprites:uniffi_uniffi_sprites_fn_method_sprite_get_position
+ 126, // sprites:uniffi_uniffi_sprites_fn_method_sprite_get_position
FfiConverterTypeSprite.lower(this),
)
}
@@ -401,7 +401,7 @@ export class Sprite {
throw e;
}
return UniFFIScaffolding.callAsync(
- 109, // sprites:uniffi_uniffi_sprites_fn_method_sprite_move_by
+ 127, // sprites:uniffi_uniffi_sprites_fn_method_sprite_move_by
FfiConverterTypeSprite.lower(this),
FfiConverterTypeVector.lower(direction),
)
@@ -426,7 +426,7 @@ export class Sprite {
throw e;
}
return UniFFIScaffolding.callAsync(
- 110, // sprites:uniffi_uniffi_sprites_fn_method_sprite_move_to
+ 128, // sprites:uniffi_uniffi_sprites_fn_method_sprite_move_to
FfiConverterTypeSprite.lower(this),
FfiConverterTypePoint.lower(position),
)
@@ -449,7 +449,11 @@ export class FfiConverterTypeSprite extends FfiConverter {
}
static lower(value) {
- return value[uniffiObjectPtr];
+ const ptr = value[uniffiObjectPtr];
+ if (!(ptr instanceof UniFFIPointer)) {
+ throw new UniFFITypeError("Object is not a 'Sprite' instance");
+ }
+ return ptr;
}
static read(dataStream) {
@@ -517,7 +521,7 @@ export class FfiConverterTypePoint extends FfiConverterArrayBuffer {
static checkType(value) {
super.checkType(value);
if (!(value instanceof Point)) {
- throw new TypeError(`Expected 'Point', found '${typeof value}'`);
+ throw new UniFFITypeError(`Expected 'Point', found '${typeof value}'`);
}
try {
FfiConverterF64.checkType(value.x);
@@ -590,7 +594,7 @@ export class FfiConverterTypeVector extends FfiConverterArrayBuffer {
static checkType(value) {
super.checkType(value);
if (!(value instanceof Vector)) {
- throw new TypeError(`Expected 'Vector', found '${typeof value}'`);
+ throw new UniFFITypeError(`Expected 'Vector', found '${typeof value}'`);
}
try {
FfiConverterF64.checkType(value.dx);
@@ -674,7 +678,7 @@ export function translate(position,direction) {
throw e;
}
return UniFFIScaffolding.callAsync(
- 111, // sprites:uniffi_uniffi_sprites_fn_func_translate
+ 129, // sprites:uniffi_uniffi_sprites_fn_func_translate
FfiConverterTypePoint.lower(position),
FfiConverterTypeVector.lower(direction),
)
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustTodolist.sys.mjs b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustTodolist.sys.mjs
index 59996b78fa..ddeccc9bf2 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustTodolist.sys.mjs
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated/RustTodolist.sys.mjs
@@ -158,7 +158,7 @@ class ArrayBufferDataStream {
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
readPointerTodoList() {
- const pointerId = 9; // todolist:TodoList
+ const pointerId = 11; // todolist:TodoList
const res = UniFFIScaffolding.readPointer(pointerId, this.dataView.buffer, this.pos);
this.pos += 8;
return res;
@@ -168,7 +168,7 @@ class ArrayBufferDataStream {
// UniFFI Pointers are **always** 8 bytes long. That is enforced
// by the C++ and Rust Scaffolding code.
writePointerTodoList(value) {
- const pointerId = 9; // todolist:TodoList
+ const pointerId = 11; // todolist:TodoList
UniFFIScaffolding.writePointer(pointerId, value, this.dataView.buffer, this.pos);
this.pos += 8;
}
@@ -298,7 +298,7 @@ export class TodoList {
const liftError = null;
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 112, // todolist:uniffi_uniffi_todolist_fn_constructor_todolist_new
+ 131, // todolist:uniffi_uniffi_todolist_fn_constructor_todolist_new
)
}
try {
@@ -320,7 +320,7 @@ export class TodoList {
throw e;
}
return UniFFIScaffolding.callAsync(
- 113, // todolist:uniffi_uniffi_todolist_fn_method_todolist_add_entries
+ 132, // todolist:uniffi_uniffi_todolist_fn_method_todolist_add_entries
FfiConverterTypeTodoList.lower(this),
FfiConverterSequenceTypeTodoEntry.lower(entries),
)
@@ -345,7 +345,7 @@ export class TodoList {
throw e;
}
return UniFFIScaffolding.callAsync(
- 114, // todolist:uniffi_uniffi_todolist_fn_method_todolist_add_entry
+ 133, // todolist:uniffi_uniffi_todolist_fn_method_todolist_add_entry
FfiConverterTypeTodoList.lower(this),
FfiConverterTypeTodoEntry.lower(entry),
)
@@ -370,7 +370,7 @@ export class TodoList {
throw e;
}
return UniFFIScaffolding.callAsync(
- 115, // todolist:uniffi_uniffi_todolist_fn_method_todolist_add_item
+ 134, // todolist:uniffi_uniffi_todolist_fn_method_todolist_add_item
FfiConverterTypeTodoList.lower(this),
FfiConverterString.lower(todo),
)
@@ -395,7 +395,7 @@ export class TodoList {
throw e;
}
return UniFFIScaffolding.callAsync(
- 116, // todolist:uniffi_uniffi_todolist_fn_method_todolist_add_items
+ 135, // todolist:uniffi_uniffi_todolist_fn_method_todolist_add_items
FfiConverterTypeTodoList.lower(this),
FfiConverterSequencestring.lower(items),
)
@@ -420,7 +420,7 @@ export class TodoList {
throw e;
}
return UniFFIScaffolding.callAsync(
- 117, // todolist:uniffi_uniffi_todolist_fn_method_todolist_clear_item
+ 136, // todolist:uniffi_uniffi_todolist_fn_method_todolist_clear_item
FfiConverterTypeTodoList.lower(this),
FfiConverterString.lower(todo),
)
@@ -437,7 +437,7 @@ export class TodoList {
const liftError = null;
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 118, // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_entries
+ 137, // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_entries
FfiConverterTypeTodoList.lower(this),
)
}
@@ -453,7 +453,7 @@ export class TodoList {
const liftError = (data) => FfiConverterTypeTodoError.lift(data);
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 119, // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_first
+ 138, // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_first
FfiConverterTypeTodoList.lower(this),
)
}
@@ -469,7 +469,7 @@ export class TodoList {
const liftError = null;
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 120, // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_items
+ 139, // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_items
FfiConverterTypeTodoList.lower(this),
)
}
@@ -485,7 +485,7 @@ export class TodoList {
const liftError = (data) => FfiConverterTypeTodoError.lift(data);
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 121, // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_last
+ 140, // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_last
FfiConverterTypeTodoList.lower(this),
)
}
@@ -501,7 +501,7 @@ export class TodoList {
const liftError = (data) => FfiConverterTypeTodoError.lift(data);
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 122, // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_last_entry
+ 141, // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_last_entry
FfiConverterTypeTodoList.lower(this),
)
}
@@ -517,7 +517,7 @@ export class TodoList {
const liftError = null;
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 123, // todolist:uniffi_uniffi_todolist_fn_method_todolist_make_default
+ 142, // todolist:uniffi_uniffi_todolist_fn_method_todolist_make_default
FfiConverterTypeTodoList.lower(this),
)
}
@@ -539,7 +539,11 @@ export class FfiConverterTypeTodoList extends FfiConverter {
}
static lower(value) {
- return value[uniffiObjectPtr];
+ const ptr = value[uniffiObjectPtr];
+ if (!(ptr instanceof UniFFIPointer)) {
+ throw new UniFFITypeError("Object is not a 'TodoList' instance");
+ }
+ return ptr;
}
static read(dataStream) {
@@ -594,7 +598,7 @@ export class FfiConverterTypeTodoEntry extends FfiConverterArrayBuffer {
static checkType(value) {
super.checkType(value);
if (!(value instanceof TodoEntry)) {
- throw new TypeError(`Expected 'TodoEntry', found '${typeof value}'`);
+ throw new UniFFITypeError(`Expected 'TodoEntry', found '${typeof value}'`);
}
try {
FfiConverterString.checkType(value.text);
@@ -683,7 +687,7 @@ export class FfiConverterTypeTodoError extends FfiConverterArrayBuffer {
case 5:
return new DeligatedError(FfiConverterString.read(dataStream));
default:
- throw new Error("Unknown TodoError variant");
+ throw new UniFFITypeError("Unknown TodoError variant");
}
}
static computeSize(value) {
@@ -704,7 +708,7 @@ export class FfiConverterTypeTodoError extends FfiConverterArrayBuffer {
if (value instanceof DeligatedError) {
return totalSize;
}
- throw new Error("Unknown TodoError variant");
+ throw new UniFFITypeError("Unknown TodoError variant");
}
static write(dataStream, value) {
if (value instanceof TodoDoesNotExist) {
@@ -727,7 +731,7 @@ export class FfiConverterTypeTodoError extends FfiConverterArrayBuffer {
dataStream.writeInt32(5);
return;
}
- throw new Error("Unknown TodoError variant");
+ throw new UniFFITypeError("Unknown TodoError variant");
}
static errorClass = TodoError;
@@ -876,7 +880,7 @@ export function createEntryWith(todo) {
throw e;
}
return UniFFIScaffolding.callAsync(
- 124, // todolist:uniffi_uniffi_todolist_fn_func_create_entry_with
+ 143, // todolist:uniffi_uniffi_todolist_fn_func_create_entry_with
FfiConverterString.lower(todo),
)
}
@@ -893,7 +897,7 @@ export function getDefaultList() {
const liftError = null;
const functionCall = () => {
return UniFFIScaffolding.callAsync(
- 125, // todolist:uniffi_uniffi_todolist_fn_func_get_default_list
+ 144, // todolist:uniffi_uniffi_todolist_fn_func_get_default_list
)
}
try {
@@ -917,7 +921,7 @@ export function setDefaultList(list) {
throw e;
}
return UniFFIScaffolding.callAsync(
- 126, // todolist:uniffi_uniffi_todolist_fn_func_set_default_list
+ 145, // todolist:uniffi_uniffi_todolist_fn_func_set_default_list
FfiConverterTypeTodoList.lower(list),
)
}
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/moz.build b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/moz.build
index d5ff2d7cb6..eed04220cb 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/moz.build
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/moz.build
@@ -12,6 +12,7 @@ components = [
"ExternalTypes",
"FixtureCallbacks",
"Geometry",
+ "Refcounts",
"Rondpoint",
"Sprites",
"Todolist",
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_external_types.js b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_external_types.js
index e2f985cde7..699dcfe6f3 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_external_types.js
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_external_types.js
@@ -6,10 +6,10 @@ const ExternalTypes = ChromeUtils.importESModule(
);
add_task(async function () {
- const line = new ExternalTypes.Line(
- new ExternalTypes.Point(0, 0, "p1"),
- new ExternalTypes.Point(2, 1, "p2")
- );
+ const line = new ExternalTypes.Line({
+ start: await new ExternalTypes.Point({ coordX: 0, coordY: 0 }),
+ end: await new ExternalTypes.Point({ coordX: 2, coordY: 1 }),
+ });
Assert.equal(await ExternalTypes.gradient(line), 0.5);
Assert.equal(await ExternalTypes.gradient(null), 0.0);
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_geometry.js b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_geometry.js
index cdb552e4ef..1028cd37c2 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_geometry.js
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_geometry.js
@@ -6,24 +6,16 @@ const Geometry = ChromeUtils.importESModule(
);
add_task(async function () {
- const ln1 = new Geometry.Line(
- new Geometry.Point({ coord_x: 0, coord_y: 0 }),
- new Geometry.Point({ coord_x: 1, coord_y: 2 })
- );
- const ln2 = new Geometry.Line(
- new Geometry.Point({ coord_x: 1, coord_y: 1 }),
- new Geometry.Point({ coord_x: 2, coord_y: 2 })
- );
- const origin = new Geometry.Point({ coord_x: 0, coord_y: 0 });
- Assert.ok(
- (await Geometry.intersection({ start: ln1, end: ln2 })).equals(origin)
- );
- Assert.deepEqual(
- await Geometry.intersection({ start: ln1, end: ln2 }),
- origin
- );
- Assert.strictEqual(
- await Geometry.intersection({ start: ln1, end: ln1 }),
- null
- );
+ const ln1 = new Geometry.Line({
+ start: new Geometry.Point({ coordX: 0, coordY: 0 }),
+ end: new Geometry.Point({ coordX: 1, coordY: 2 }),
+ });
+ const ln2 = new Geometry.Line({
+ start: new Geometry.Point({ coordX: 1, coordY: 1 }),
+ end: new Geometry.Point({ coordX: 2, coordY: 2 }),
+ });
+ const origin = new Geometry.Point({ coordX: 0, coordY: 0 });
+ Assert.ok((await Geometry.intersection(ln1, ln2)).equals(origin));
+ Assert.deepEqual(await Geometry.intersection(ln1, ln2), origin);
+ Assert.strictEqual(await Geometry.intersection(ln1, ln1), null);
});
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_refcounts.js b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_refcounts.js
new file mode 100644
index 0000000000..790d981104
--- /dev/null
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_refcounts.js
@@ -0,0 +1,57 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const { getSingleton, getJsRefcount } = ChromeUtils.importESModule(
+ "resource://gre/modules/RustRefcounts.sys.mjs"
+);
+
+// Test refcounts when we call methods.
+//
+// Each method call requires that we clone the Arc pointer on the JS side, then pass it to Rust
+// which will consumer the reference. Make sure we get this right
+
+function createObjectAndCallMethods() {
+ const obj = getSingleton();
+ obj.method();
+}
+
+add_test(() => {
+ // Create an object that we'll keep around. If the ref count ends up being low, we don't want
+ // to reduce it below 0, since the Rust code may catch that and clamp it
+ const obj = getSingleton();
+ createObjectAndCallMethods();
+ Cu.forceGC();
+ Cu.forceCC();
+ do_test_pending();
+ do_timeout(500, () => {
+ Assert.equal(getJsRefcount(), 1);
+ // Use `obj` to avoid unused warnings and try to ensure that JS doesn't destroy it early
+ obj.method();
+ do_test_finished();
+ run_next_test();
+ });
+});
+
+// Test refcounts when creating/destroying objects
+function createAndDeleteObjects() {
+ [getSingleton(), getSingleton(), getSingleton()];
+}
+
+add_test(() => {
+ const obj = getSingleton();
+ createAndDeleteObjects();
+ Cu.forceGC();
+ Cu.forceCC();
+ do_timeout(500, () => {
+ Assert.equal(getJsRefcount(), 1);
+ obj.method();
+ do_test_finished();
+ run_next_test();
+ });
+});
+
+// As we implement more UniFFI features we should probably add refcount tests for it.
+// Some features that should probably have tests:
+// - Async methods
+// - UniFFI builtin trait methods like 'to_string'
+// - Rust trait objects
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_rondpoint.js b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_rondpoint.js
index 8c673b2e92..4ad76074f9 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_rondpoint.js
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_rondpoint.js
@@ -22,7 +22,12 @@ const {
OptionneurDictionnaire,
} = Rondpoint;
add_task(async function () {
- const dico = new Dictionnaire(Enumeration.DEUX, true, 0, 1235);
+ const dico = new Dictionnaire({
+ un: Enumeration.DEUX,
+ deux: true,
+ petitNombre: 0,
+ grosNombre: 1235,
+ });
const copyDico = await copieDictionnaire(dico);
Assert.deepEqual(dico, copyDico);
@@ -127,13 +132,29 @@ add_task(async function () {
);
await affirmAllerRetour(
- [-1, 0, 1].map(n => new DictionnaireNombresSignes(n, n, n, n)),
+ [-1, 0, 1].map(
+ n =>
+ new DictionnaireNombresSignes({
+ petitNombre: n,
+ courtNombre: n,
+ nombreSimple: n,
+ grosNombre: n,
+ })
+ ),
rt.identiqueNombresSignes.bind(rt),
(a, b) => Assert.deepEqual(a, b)
);
await affirmAllerRetour(
- [0, 1].map(n => new DictionnaireNombres(n, n, n, n)),
+ [0, 1].map(
+ n =>
+ new DictionnaireNombres({
+ petitNombre: n,
+ courtNombre: n,
+ nombreSimple: n,
+ grosNombre: n,
+ })
+ ),
rt.identiqueNombres.bind(rt),
(a, b) => Assert.deepEqual(a, b)
);
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_sprites.js b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_sprites.js
index 3feb2fd34d..e1f29cd9c8 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_sprites.js
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_sprites.js
@@ -9,20 +9,26 @@ add_task(async function () {
Assert.ok(Sprites.Sprite);
const sempty = await Sprites.Sprite.init(null);
- Assert.deepEqual(await sempty.getPosition(), new Sprites.Point(0, 0));
+ Assert.deepEqual(
+ await sempty.getPosition(),
+ new Sprites.Point({ x: 0, y: 0 })
+ );
- const s = await Sprites.Sprite.init(new Sprites.Point(0, 1));
- Assert.deepEqual(await s.getPosition(), new Sprites.Point(0, 1));
+ const s = await Sprites.Sprite.init(new Sprites.Point({ x: 0, y: 1 }));
+ Assert.deepEqual(await s.getPosition(), new Sprites.Point({ x: 0, y: 1 }));
- s.moveTo(new Sprites.Point(1, 2));
- Assert.deepEqual(await s.getPosition(), new Sprites.Point(1, 2));
+ s.moveTo(new Sprites.Point({ x: 1, y: 2 }));
+ Assert.deepEqual(await s.getPosition(), new Sprites.Point({ x: 1, y: 2 }));
- s.moveBy(new Sprites.Vector(-4, 2));
- Assert.deepEqual(await s.getPosition(), new Sprites.Point(-3, 4));
+ s.moveBy(new Sprites.Vector({ dx: -4, dy: 2 }));
+ Assert.deepEqual(await s.getPosition(), new Sprites.Point({ x: -3, y: 4 }));
const srel = await Sprites.Sprite.newRelativeTo(
- new Sprites.Point(0, 1),
- new Sprites.Vector(1, 1.5)
+ new Sprites.Point({ x: 0, y: 1 }),
+ new Sprites.Vector({ dx: 1, dy: 1.5 })
+ );
+ Assert.deepEqual(
+ await srel.getPosition(),
+ new Sprites.Point({ x: 1, y: 2.5 })
);
- Assert.deepEqual(await srel.getPosition(), new Sprites.Point(1, 2.5));
});
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_todolist.js b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_todolist.js
index dac26d2be1..2cbd43304c 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_todolist.js
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_todolist.js
@@ -6,7 +6,9 @@ const { TodoList, TodoEntry, getDefaultList, setDefaultList } =
add_task(async function () {
const todo = await TodoList.init();
- const entry = new TodoEntry("Write bindings for strings in records");
+ const entry = new TodoEntry({
+ text: "Write bindings for strings in records",
+ });
await todo.addItem("Write JS bindings");
Assert.equal(await todo.getLast(), "Write JS bindings");
@@ -30,9 +32,9 @@ add_task(async function () {
"Test Ünicode hàndling without an entry can't believe I didn't test this at first 🤣"
);
- const entry2 = new TodoEntry(
- "Test Ünicode hàndling in an entry can't believe I didn't test this at first 🤣"
- );
+ const entry2 = new TodoEntry({
+ text: "Test Ünicode hàndling in an entry can't believe I didn't test this at first 🤣",
+ });
await todo.addEntry(entry2);
Assert.equal(
(await todo.getLastEntry()).text,
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_type_checking.js b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_type_checking.js
index bfeb07c82b..7a5e04cea1 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_type_checking.js
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_type_checking.js
@@ -42,6 +42,12 @@ add_task(async function testObjectPointers() {
/Bad pointer type/,
"getEntries() with wrong pointer type"
);
+
+ await Assert.rejects(
+ TodoList.setDefaultList(1), // expecting an object
+ /Object is not a 'TodoList' instance/,
+ "attempting to lift the wrong object type"
+ );
});
add_task(async function testEnumTypeCheck() {
@@ -68,18 +74,6 @@ add_task(async function testRecordTypeCheck() {
UniFFITypeError,
"gradient with non-Line object should throw"
);
-
- await Assert.rejects(
- Geometry.gradient({
- start: {
- coordX: 0.0,
- coordY: 0.0,
- },
- // missing the end field
- }),
- /ln.end/, // Ensure exception message includes the argument name
- "gradient with Line object with missing end field should throw"
- );
});
add_task(async function testOptionTypeCheck() {
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/xpcshell.toml b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/xpcshell.toml
index 76814ff199..801e95382f 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/xpcshell.toml
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/xpcshell.toml
@@ -2,18 +2,33 @@
["test_arithmetic.js"]
+# I think this can be re-enabled when we implement https://bugzilla.mozilla.org/show_bug.cgi?id=1888668
+lineno = "3"
+
["test_callbacks.js"]
+disabled = "Temporarily disabled until we can re-enable callback support"
+lineno = "8"
["test_custom_types.js"]
+lineno = "12"
["test_external_types.js"]
+lineno = "15"
["test_geometry.js"]
+lineno = "18"
+
+["test_refcounts.js"]
+lineno = "21"
["test_rondpoint.js"]
+lineno = "24"
["test_sprites.js"]
+lineno = "27"
["test_todolist.js"]
+lineno = "30"
["test_type_checking.js"]
+lineno = "33"
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/src/render/cpp.rs b/toolkit/components/uniffi-bindgen-gecko-js/src/render/cpp.rs
index 685c3c2bf3..7b63e8f3af 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/src/render/cpp.rs
+++ b/toolkit/components/uniffi-bindgen-gecko-js/src/render/cpp.rs
@@ -136,31 +136,25 @@ pub impl FfiType {
// Type for the Rust scaffolding code
fn rust_type(&self) -> String {
match self {
- FfiType::UInt8 => "uint8_t",
- FfiType::Int8 => "int8_t",
- FfiType::UInt16 => "uint16_t",
- FfiType::Int16 => "int16_t",
- FfiType::UInt32 => "uint32_t",
- FfiType::Int32 => "int32_t",
- FfiType::UInt64 => "uint64_t",
- FfiType::Int64 => "int64_t",
- FfiType::Float32 => "float",
- FfiType::Float64 => "double",
- FfiType::RustBuffer(_) => "RustBuffer",
- FfiType::RustArcPtr(_) => "void *",
- FfiType::ForeignCallback => "ForeignCallback",
+ FfiType::UInt8 => "uint8_t".to_owned(),
+ FfiType::Int8 => "int8_t".to_owned(),
+ FfiType::UInt16 => "uint16_t".to_owned(),
+ FfiType::Int16 => "int16_t".to_owned(),
+ FfiType::UInt32 => "uint32_t".to_owned(),
+ FfiType::Int32 => "int32_t".to_owned(),
+ FfiType::UInt64 => "uint64_t".to_owned(),
+ FfiType::Int64 => "int64_t".to_owned(),
+ FfiType::Float32 => "float".to_owned(),
+ FfiType::Float64 => "double".to_owned(),
+ FfiType::RustBuffer(_) => "RustBuffer".to_owned(),
+ FfiType::RustArcPtr(_) => "void *".to_owned(),
FfiType::ForeignBytes => unimplemented!("ForeignBytes not supported"),
- FfiType::ForeignExecutorHandle => unimplemented!("ForeignExecutorHandle not supported"),
- FfiType::ForeignExecutorCallback => {
- unimplemented!("ForeignExecutorCallback not supported")
- }
- FfiType::RustFutureHandle
- | FfiType::RustFutureContinuationCallback
- | FfiType::RustFutureContinuationData => {
- unimplemented!("Rust async functions not supported")
- }
+ FfiType::Handle => "uint64_t".to_owned(),
+ FfiType::RustCallStatus => "RustCallStatus".to_owned(),
+ FfiType::Callback(name) | FfiType::Struct(name) => name.to_owned(),
+ FfiType::VoidPointer => "void *".to_owned(),
+ FfiType::Reference(_) => unimplemented!("References not supported"),
}
- .to_owned()
}
}
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/src/render/js.rs b/toolkit/components/uniffi-bindgen-gecko-js/src/render/js.rs
index efd7b42456..cd9af529a7 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/src/render/js.rs
+++ b/toolkit/components/uniffi-bindgen-gecko-js/src/render/js.rs
@@ -92,7 +92,8 @@ pub impl Literal {
Literal::Enum(name, typ) => render_enum_literal(typ, name),
Literal::EmptyMap => "{}".to_string(),
Literal::EmptySequence => "[]".to_string(),
- Literal::Null => "null".to_string(),
+ Literal::Some { inner } => inner.render(),
+ Literal::None => "null".to_string(),
}
}
}
@@ -258,7 +259,6 @@ pub impl Type {
| Type::CallbackInterface { name, .. } => format!("Type{name}"),
Type::Timestamp => "Timestamp".into(),
Type::Duration => "Duration".into(),
- Type::ForeignExecutor => "ForeignExecutor".into(),
Type::Optional { inner_type } => format!("Optional{}", inner_type.canonical_name()),
Type::Sequence { inner_type } => format!("Sequence{}", inner_type.canonical_name()),
Type::Map {
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/src/templates/UniFFIScaffolding.cpp b/toolkit/components/uniffi-bindgen-gecko-js/src/templates/UniFFIScaffolding.cpp
index 5c4ed8c2f5..83aeae8086 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/src/templates/UniFFIScaffolding.cpp
+++ b/toolkit/components/uniffi-bindgen-gecko-js/src/templates/UniFFIScaffolding.cpp
@@ -37,7 +37,8 @@ extern "C" {
{%- let pointer_type = ci.pointer_type(object) %}
const static mozilla::uniffi::UniFFIPointerType {{ pointer_type }} {
"{{ "{}::{}"|format(ci.namespace(), object.name()) }}"_ns,
- {{ object.ffi_object_free().rust_name() }}
+ {{ object.ffi_object_clone().rust_name() }},
+ {{ object.ffi_object_free().rust_name() }},
};
{%- endfor %}
{%- endfor %}
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/src/templates/js/Enum.sys.mjs b/toolkit/components/uniffi-bindgen-gecko-js/src/templates/js/Enum.sys.mjs
index f7716ac6d8..cbcc256eb9 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/src/templates/js/Enum.sys.mjs
+++ b/toolkit/components/uniffi-bindgen-gecko-js/src/templates/js/Enum.sys.mjs
@@ -16,7 +16,7 @@ export class {{ ffi_converter }} extends FfiConverterArrayBuffer {
return {{ enum_.nm() }}.{{ variant.name().to_shouty_snake_case() }}
{%- endfor %}
default:
- return new Error("Unknown {{ enum_.nm() }} variant");
+ throw new UniFFITypeError("Unknown {{ enum_.nm() }} variant");
}
}
@@ -27,7 +27,7 @@ export class {{ ffi_converter }} extends FfiConverterArrayBuffer {
return;
}
{%- endfor %}
- return new Error("Unknown {{ enum_.nm() }} variant");
+ throw new UniFFITypeError("Unknown {{ enum_.nm() }} variant");
}
static computeSize(value) {
@@ -72,7 +72,7 @@ export class {{ ffi_converter }} extends FfiConverterArrayBuffer {
);
{%- endfor %}
default:
- return new Error("Unknown {{ enum_.nm() }} variant");
+ throw new UniFFITypeError("Unknown {{ enum_.nm() }} variant");
}
}
@@ -86,7 +86,7 @@ export class {{ ffi_converter }} extends FfiConverterArrayBuffer {
return;
}
{%- endfor %}
- return new Error("Unknown {{ enum_.nm() }} variant");
+ throw new UniFFITypeError("Unknown {{ enum_.nm() }} variant");
}
static computeSize(value) {
@@ -100,7 +100,7 @@ export class {{ ffi_converter }} extends FfiConverterArrayBuffer {
return totalSize;
}
{%- endfor %}
- return new Error("Unknown {{ enum_.nm() }} variant");
+ throw new UniFFITypeError("Unknown {{ enum_.nm() }} variant");
}
static checkType(value) {
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/src/templates/js/Error.sys.mjs b/toolkit/components/uniffi-bindgen-gecko-js/src/templates/js/Error.sys.mjs
index b140d908da..c472f1a27d 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/src/templates/js/Error.sys.mjs
+++ b/toolkit/components/uniffi-bindgen-gecko-js/src/templates/js/Error.sys.mjs
@@ -46,7 +46,7 @@ export class {{ ffi_converter }} extends FfiConverterArrayBuffer {
{%- endif %}
{%- endfor %}
default:
- throw new Error("Unknown {{ error.nm() }} variant");
+ throw new UniFFITypeError("Unknown {{ error.nm() }} variant");
}
}
static computeSize(value) {
@@ -60,7 +60,7 @@ export class {{ ffi_converter }} extends FfiConverterArrayBuffer {
return totalSize;
}
{%- endfor %}
- throw new Error("Unknown {{ error.nm() }} variant");
+ throw new UniFFITypeError("Unknown {{ error.nm() }} variant");
}
static write(dataStream, value) {
{%- for variant in error.variants() %}
@@ -72,7 +72,7 @@ export class {{ ffi_converter }} extends FfiConverterArrayBuffer {
return;
}
{%- endfor %}
- throw new Error("Unknown {{ error.nm() }} variant");
+ throw new UniFFITypeError("Unknown {{ error.nm() }} variant");
}
static errorClass = {{ error.nm() }};
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/src/templates/js/Object.sys.mjs b/toolkit/components/uniffi-bindgen-gecko-js/src/templates/js/Object.sys.mjs
index e03291089e..204bf752dd 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/src/templates/js/Object.sys.mjs
+++ b/toolkit/components/uniffi-bindgen-gecko-js/src/templates/js/Object.sys.mjs
@@ -51,7 +51,11 @@ export class {{ ffi_converter }} extends FfiConverter {
}
static lower(value) {
- return value[uniffiObjectPtr];
+ const ptr = value[uniffiObjectPtr];
+ if (!(ptr instanceof UniFFIPointer)) {
+ throw new UniFFITypeError("Object is not a '{{ object.nm() }}' instance");
+ }
+ return ptr;
}
static read(dataStream) {
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/src/templates/js/Record.sys.mjs b/toolkit/components/uniffi-bindgen-gecko-js/src/templates/js/Record.sys.mjs
index 2f54160b9e..638be5bee8 100644
--- a/toolkit/components/uniffi-bindgen-gecko-js/src/templates/js/Record.sys.mjs
+++ b/toolkit/components/uniffi-bindgen-gecko-js/src/templates/js/Record.sys.mjs
@@ -51,7 +51,7 @@ export class {{ ffi_converter }} extends FfiConverterArrayBuffer {
static checkType(value) {
super.checkType(value);
if (!(value instanceof {{ record.nm() }})) {
- throw new TypeError(`Expected '{{ record.nm() }}', found '${typeof value}'`);
+ throw new UniFFITypeError(`Expected '{{ record.nm() }}', found '${typeof value}'`);
}
{%- for field in record.fields() %}
try {
diff --git a/toolkit/components/uniffi-fixture-external-types/Cargo.toml b/toolkit/components/uniffi-fixture-external-types/Cargo.toml
index f9ac840c3f..46ee01662d 100644
--- a/toolkit/components/uniffi-fixture-external-types/Cargo.toml
+++ b/toolkit/components/uniffi-fixture-external-types/Cargo.toml
@@ -7,7 +7,7 @@ license = "MPL-2.0"
publish = false
[dependencies]
-uniffi-example-geometry = { git = "https://github.com/mozilla/uniffi-rs.git", rev = "afb29ebdc1d9edf15021b1c5332fc9f285bbe13b" }
+uniffi-example-geometry = { git = "https://github.com/mozilla/uniffi-rs.git", rev = "d52c5460ae42ecad1e73a5b394ac96d48f4769de" }
uniffi = { workspace = true }
thiserror = "1.0"
diff --git a/toolkit/components/uniffi-fixture-refcounts/Cargo.toml b/toolkit/components/uniffi-fixture-refcounts/Cargo.toml
new file mode 100644
index 0000000000..877e502711
--- /dev/null
+++ b/toolkit/components/uniffi-fixture-refcounts/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "uniffi-fixture-refcounts"
+edition = "2021"
+version = "0.21.0"
+license = "MPL-2.0"
+publish = false
+
+[dependencies]
+uniffi = { workspace = true }
+
+[build-dependencies]
+uniffi = { workspace = true, features = ["build"] }
diff --git a/toolkit/components/uniffi-fixture-refcounts/build.rs b/toolkit/components/uniffi-fixture-refcounts/build.rs
new file mode 100644
index 0000000000..9ea03e12de
--- /dev/null
+++ b/toolkit/components/uniffi-fixture-refcounts/build.rs
@@ -0,0 +1,7 @@
+/* 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/. */
+
+fn main() {
+ uniffi::generate_scaffolding("./src/refcounts.udl").unwrap();
+}
diff --git a/toolkit/components/uniffi-fixture-refcounts/src/lib.rs b/toolkit/components/uniffi-fixture-refcounts/src/lib.rs
new file mode 100644
index 0000000000..e453b22a4a
--- /dev/null
+++ b/toolkit/components/uniffi-fixture-refcounts/src/lib.rs
@@ -0,0 +1,28 @@
+/* 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/. */
+
+/// This crate exists to test managing the Rust Arc strong counts as JS objects are
+/// created/destroyed. See `test_refcounts.js` for how it's used.
+
+use std::sync::{Arc, Mutex};
+
+pub struct SingletonObject;
+
+impl SingletonObject {
+ pub fn method(&self) { }
+}
+
+static SINGLETON: Mutex<Option<Arc<SingletonObject>>> = Mutex::new(None);
+
+pub fn get_singleton() -> Arc<SingletonObject> {
+ Arc::clone(SINGLETON.lock().unwrap().get_or_insert_with(|| Arc::new(SingletonObject)))
+}
+
+pub fn get_js_refcount() -> i32 {
+ // Subtract 2: one for the reference in the Mutex and one for the temporary reference that
+ // we're calling Arc::strong_count on.
+ (Arc::strong_count(&get_singleton()) as i32) - 2
+}
+
+include!(concat!(env!("OUT_DIR"), "/refcounts.uniffi.rs"));
diff --git a/toolkit/components/uniffi-fixture-refcounts/src/refcounts.udl b/toolkit/components/uniffi-fixture-refcounts/src/refcounts.udl
new file mode 100644
index 0000000000..25ec83cfcc
--- /dev/null
+++ b/toolkit/components/uniffi-fixture-refcounts/src/refcounts.udl
@@ -0,0 +1,8 @@
+namespace refcounts {
+ SingletonObject get_singleton();
+ i32 get_js_refcount();
+};
+
+interface SingletonObject {
+ void method();
+};
diff --git a/toolkit/components/uniffi-js/OwnedRustBuffer.cpp b/toolkit/components/uniffi-js/OwnedRustBuffer.cpp
index 4e334a966e..f14033a213 100644
--- a/toolkit/components/uniffi-js/OwnedRustBuffer.cpp
+++ b/toolkit/components/uniffi-js/OwnedRustBuffer.cpp
@@ -27,7 +27,7 @@ Result<OwnedRustBuffer, nsCString> OwnedRustBuffer::FromArrayBuffer(
RustCallStatus status{};
RustBuffer buf = uniffi_rustbuffer_alloc(
- static_cast<int32_t>(aData.Length()), &status);
+ static_cast<uint64_t>(aData.Length()), &status);
buf.len = aData.Length();
if (status.code != 0) {
if (status.error_buf.data) {
@@ -84,7 +84,7 @@ RustBuffer OwnedRustBuffer::IntoRustBuffer() {
JSObject* OwnedRustBuffer::IntoArrayBuffer(JSContext* cx) {
JS::Rooted<JSObject*> obj(cx);
{
- int32_t len = mBuf.len;
+ auto len = mBuf.len;
void* data = mBuf.data;
auto userData = MakeUnique<OwnedRustBuffer>(std::move(*this));
UniquePtr<void, JS::BufferContentsDeleter> dataPtr{
diff --git a/toolkit/components/uniffi-js/ScaffoldingConverter.h b/toolkit/components/uniffi-js/ScaffoldingConverter.h
index ae5629e2e4..d11fd0c314 100644
--- a/toolkit/components/uniffi-js/ScaffoldingConverter.h
+++ b/toolkit/components/uniffi-js/ScaffoldingConverter.h
@@ -173,7 +173,7 @@ class ScaffoldingObjectConverter {
if (!value.IsSamePtrType(PointerType)) {
return Err("Bad pointer type"_ns);
}
- return value.GetPtr();
+ return value.ClonePtr();
}
static void* IntoRust(void* aValue) { return aValue; }
diff --git a/toolkit/components/uniffi-js/UniFFICallbacks.h b/toolkit/components/uniffi-js/UniFFICallbacks.h
index b18ba14f9e..2104e8dff2 100644
--- a/toolkit/components/uniffi-js/UniFFICallbacks.h
+++ b/toolkit/components/uniffi-js/UniFFICallbacks.h
@@ -67,7 +67,7 @@ void DeregisterCallbackHandler(uint64_t aInterfaceId, ErrorResult& aError);
* good use case for this is logging.
*/
MOZ_CAN_RUN_SCRIPT
-void QueueCallback(size_t aInterfaceId, uint64_t aHandle, uint32_t aMethod,
+void QueueCallback(uint64_t aInterfaceId, uint64_t aHandle, uint32_t aMethod,
const uint8_t* aArgsData, int32_t aArgsLen);
} // namespace mozilla::uniffi
diff --git a/toolkit/components/uniffi-js/UniFFIFixtureScaffolding.cpp b/toolkit/components/uniffi-js/UniFFIFixtureScaffolding.cpp
index c0695f227c..d8c4d0d8a0 100644
--- a/toolkit/components/uniffi-js/UniFFIFixtureScaffolding.cpp
+++ b/toolkit/components/uniffi-js/UniFFIFixtureScaffolding.cpp
@@ -31,11 +31,14 @@ extern "C" {
RustBuffer uniffi_uniffi_custom_types_fn_func_get_custom_types_demo(RustBuffer, RustCallStatus*);
double uniffi_uniffi_fixture_external_types_fn_func_gradient(RustBuffer, RustCallStatus*);
RustBuffer uniffi_uniffi_fixture_external_types_fn_func_intersection(RustBuffer, RustBuffer, RustCallStatus*);
- void uniffi_uniffi_fixture_callbacks_fn_init_callback_logger(ForeignCallback, RustCallStatus*);
- void uniffi_uniffi_fixture_callbacks_fn_func_log_even_numbers(uint64_t, RustBuffer, RustCallStatus*);
- void uniffi_uniffi_fixture_callbacks_fn_func_log_even_numbers_main_thread(uint64_t, RustBuffer, RustCallStatus*);
+ void * uniffi_uniffi_fixture_refcounts_fn_clone_singletonobject(void *, RustCallStatus*);
+ void uniffi_uniffi_fixture_refcounts_fn_free_singletonobject(void *, RustCallStatus*);
+ void uniffi_uniffi_fixture_refcounts_fn_method_singletonobject_method(void *, RustCallStatus*);
+ int32_t uniffi_uniffi_fixture_refcounts_fn_func_get_js_refcount(RustCallStatus*);
+ void * uniffi_uniffi_fixture_refcounts_fn_func_get_singleton(RustCallStatus*);
double uniffi_uniffi_geometry_fn_func_gradient(RustBuffer, RustCallStatus*);
RustBuffer uniffi_uniffi_geometry_fn_func_intersection(RustBuffer, RustBuffer, RustCallStatus*);
+ void * uniffi_uniffi_rondpoint_fn_clone_optionneur(void *, RustCallStatus*);
void uniffi_uniffi_rondpoint_fn_free_optionneur(void *, RustCallStatus*);
void * uniffi_uniffi_rondpoint_fn_constructor_optionneur_new(RustCallStatus*);
int8_t uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_boolean(void *, int8_t, RustCallStatus*);
@@ -63,6 +66,7 @@ extern "C" {
uint8_t uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u8_dec(void *, uint8_t, RustCallStatus*);
uint8_t uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u8_hex(void *, uint8_t, RustCallStatus*);
RustBuffer uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_zero(void *, RustBuffer, RustCallStatus*);
+ void * uniffi_uniffi_rondpoint_fn_clone_retourneur(void *, RustCallStatus*);
void uniffi_uniffi_rondpoint_fn_free_retourneur(void *, RustCallStatus*);
void * uniffi_uniffi_rondpoint_fn_constructor_retourneur_new(RustCallStatus*);
int8_t uniffi_uniffi_rondpoint_fn_method_retourneur_identique_boolean(void *, int8_t, RustCallStatus*);
@@ -80,6 +84,7 @@ extern "C" {
uint32_t uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u32(void *, uint32_t, RustCallStatus*);
uint64_t uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u64(void *, uint64_t, RustCallStatus*);
uint8_t uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u8(void *, uint8_t, RustCallStatus*);
+ void * uniffi_uniffi_rondpoint_fn_clone_stringifier(void *, RustCallStatus*);
void uniffi_uniffi_rondpoint_fn_free_stringifier(void *, RustCallStatus*);
void * uniffi_uniffi_rondpoint_fn_constructor_stringifier_new(RustCallStatus*);
RustBuffer uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_boolean(void *, int8_t, RustCallStatus*);
@@ -99,6 +104,7 @@ extern "C" {
RustBuffer uniffi_uniffi_rondpoint_fn_func_copie_enumeration(RustBuffer, RustCallStatus*);
RustBuffer uniffi_uniffi_rondpoint_fn_func_copie_enumerations(RustBuffer, RustCallStatus*);
int8_t uniffi_uniffi_rondpoint_fn_func_switcheroo(int8_t, RustCallStatus*);
+ void * uniffi_uniffi_sprites_fn_clone_sprite(void *, RustCallStatus*);
void uniffi_uniffi_sprites_fn_free_sprite(void *, RustCallStatus*);
void * uniffi_uniffi_sprites_fn_constructor_sprite_new(RustBuffer, RustCallStatus*);
void * uniffi_uniffi_sprites_fn_constructor_sprite_new_relative_to(RustBuffer, RustBuffer, RustCallStatus*);
@@ -106,6 +112,7 @@ extern "C" {
void uniffi_uniffi_sprites_fn_method_sprite_move_by(void *, RustBuffer, RustCallStatus*);
void uniffi_uniffi_sprites_fn_method_sprite_move_to(void *, RustBuffer, RustCallStatus*);
RustBuffer uniffi_uniffi_sprites_fn_func_translate(RustBuffer, RustBuffer, RustCallStatus*);
+ void * uniffi_uniffi_todolist_fn_clone_todolist(void *, RustCallStatus*);
void uniffi_uniffi_todolist_fn_free_todolist(void *, RustCallStatus*);
void * uniffi_uniffi_todolist_fn_constructor_todolist_new(RustCallStatus*);
void uniffi_uniffi_todolist_fn_method_todolist_add_entries(void *, RustBuffer, RustCallStatus*);
@@ -125,51 +132,42 @@ extern "C" {
}
// Define pointer types
+const static mozilla::uniffi::UniFFIPointerType kRefcountsSingletonObjectPointerType {
+ "refcounts::SingletonObject"_ns,
+ uniffi_uniffi_fixture_refcounts_fn_clone_singletonobject,
+ uniffi_uniffi_fixture_refcounts_fn_free_singletonobject,
+};
const static mozilla::uniffi::UniFFIPointerType kRondpointOptionneurPointerType {
"rondpoint::Optionneur"_ns,
- uniffi_uniffi_rondpoint_fn_free_optionneur
+ uniffi_uniffi_rondpoint_fn_clone_optionneur,
+ uniffi_uniffi_rondpoint_fn_free_optionneur,
};
const static mozilla::uniffi::UniFFIPointerType kRondpointRetourneurPointerType {
"rondpoint::Retourneur"_ns,
- uniffi_uniffi_rondpoint_fn_free_retourneur
+ uniffi_uniffi_rondpoint_fn_clone_retourneur,
+ uniffi_uniffi_rondpoint_fn_free_retourneur,
};
const static mozilla::uniffi::UniFFIPointerType kRondpointStringifierPointerType {
"rondpoint::Stringifier"_ns,
- uniffi_uniffi_rondpoint_fn_free_stringifier
+ uniffi_uniffi_rondpoint_fn_clone_stringifier,
+ uniffi_uniffi_rondpoint_fn_free_stringifier,
};
const static mozilla::uniffi::UniFFIPointerType kSpritesSpritePointerType {
"sprites::Sprite"_ns,
- uniffi_uniffi_sprites_fn_free_sprite
+ uniffi_uniffi_sprites_fn_clone_sprite,
+ uniffi_uniffi_sprites_fn_free_sprite,
};
const static mozilla::uniffi::UniFFIPointerType kTodolistTodoListPointerType {
"todolist::TodoList"_ns,
- uniffi_uniffi_todolist_fn_free_todolist
+ uniffi_uniffi_todolist_fn_clone_todolist,
+ uniffi_uniffi_todolist_fn_free_todolist,
};
// Define the data we need per-callback interface
-MOZ_CAN_RUN_SCRIPT
-extern "C" int UniFFIFixturesCallbackHandlerLogger(uint64_t aHandle, uint32_t aMethod, const uint8_t* aArgsData, int32_t aArgsLen, RustBuffer* aOutBuffer) {
- // Currently, we only support "fire-and-forget" async callbacks. These are
- // callbacks that run asynchronously without returning anything. The main
- // use case for callbacks is logging, which fits very well with this model.
- //
- // So, here we simple queue the callback and return immediately.
- mozilla::uniffi::QueueCallback(0, aHandle, aMethod, aArgsData, aArgsLen);
- return CALLBACK_INTERFACE_SUCCESS;
-}
-static StaticRefPtr<dom::UniFFICallbackHandler> JS_CALLBACK_HANDLER_LOGGER;
// Define a lookup function for our callback interface info
Maybe<CallbackInterfaceInfo> UniFFIFixturesGetCallbackInterfaceInfo(uint64_t aInterfaceId) {
switch(aInterfaceId) {
- case 0: { // fixture_callbacks:Logger
- return Some(CallbackInterfaceInfo {
- "Logger",
- &JS_CALLBACK_HANDLER_LOGGER,
- UniFFIFixturesCallbackHandlerLogger,
- uniffi_uniffi_fixture_callbacks_fn_init_callback_logger,
- });
- }
default:
return Nothing();
@@ -178,371 +176,399 @@ Maybe<CallbackInterfaceInfo> UniFFIFixturesGetCallbackInterfaceInfo(uint64_t aIn
Maybe<already_AddRefed<Promise>> UniFFIFixturesCallAsync(const GlobalObject& aGlobal, uint64_t aId, const Sequence<ScaffoldingType>& aArgs, ErrorResult& aError) {
switch (aId) {
- case 35: { // arithmetic:uniffi_arithmetical_fn_func_add
+ case 47: { // arithmetic:uniffi_arithmetical_fn_func_add
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint64_t>, ScaffoldingConverter<uint64_t>, ScaffoldingConverter<uint64_t>>;
return Some(CallHandler::CallAsync(uniffi_arithmetical_fn_func_add, aGlobal, aArgs, "uniffi_arithmetical_fn_func_add: "_ns, aError));
}
- case 36: { // arithmetic:uniffi_arithmetical_fn_func_div
+ case 48: { // arithmetic:uniffi_arithmetical_fn_func_div
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint64_t>, ScaffoldingConverter<uint64_t>, ScaffoldingConverter<uint64_t>>;
return Some(CallHandler::CallAsync(uniffi_arithmetical_fn_func_div, aGlobal, aArgs, "uniffi_arithmetical_fn_func_div: "_ns, aError));
}
- case 37: { // arithmetic:uniffi_arithmetical_fn_func_equal
+ case 49: { // arithmetic:uniffi_arithmetical_fn_func_equal
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int8_t>, ScaffoldingConverter<uint64_t>, ScaffoldingConverter<uint64_t>>;
return Some(CallHandler::CallAsync(uniffi_arithmetical_fn_func_equal, aGlobal, aArgs, "uniffi_arithmetical_fn_func_equal: "_ns, aError));
}
- case 38: { // arithmetic:uniffi_arithmetical_fn_func_sub
+ case 50: { // arithmetic:uniffi_arithmetical_fn_func_sub
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint64_t>, ScaffoldingConverter<uint64_t>, ScaffoldingConverter<uint64_t>>;
return Some(CallHandler::CallAsync(uniffi_arithmetical_fn_func_sub, aGlobal, aArgs, "uniffi_arithmetical_fn_func_sub: "_ns, aError));
}
- case 39: { // custom_types:uniffi_uniffi_custom_types_fn_func_get_custom_types_demo
+ case 51: { // custom_types:uniffi_uniffi_custom_types_fn_func_get_custom_types_demo
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_custom_types_fn_func_get_custom_types_demo, aGlobal, aArgs, "uniffi_uniffi_custom_types_fn_func_get_custom_types_demo: "_ns, aError));
}
- case 40: { // external_types:uniffi_uniffi_fixture_external_types_fn_func_gradient
+ case 52: { // external_types:uniffi_uniffi_fixture_external_types_fn_func_gradient
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<double>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_fixture_external_types_fn_func_gradient, aGlobal, aArgs, "uniffi_uniffi_fixture_external_types_fn_func_gradient: "_ns, aError));
}
- case 41: { // external_types:uniffi_uniffi_fixture_external_types_fn_func_intersection
+ case 53: { // external_types:uniffi_uniffi_fixture_external_types_fn_func_intersection
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_fixture_external_types_fn_func_intersection, aGlobal, aArgs, "uniffi_uniffi_fixture_external_types_fn_func_intersection: "_ns, aError));
}
- case 42: { // fixture_callbacks:uniffi_uniffi_fixture_callbacks_fn_func_log_even_numbers
- using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingConverter<uint64_t>, ScaffoldingConverter<RustBuffer>>;
- return Some(CallHandler::CallAsync(uniffi_uniffi_fixture_callbacks_fn_func_log_even_numbers, aGlobal, aArgs, "uniffi_uniffi_fixture_callbacks_fn_func_log_even_numbers: "_ns, aError));
+ case 54: { // refcounts:uniffi_uniffi_fixture_refcounts_fn_clone_singletonobject
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kRefcountsSingletonObjectPointerType>, ScaffoldingObjectConverter<&kRefcountsSingletonObjectPointerType>>;
+ return Some(CallHandler::CallAsync(uniffi_uniffi_fixture_refcounts_fn_clone_singletonobject, aGlobal, aArgs, "uniffi_uniffi_fixture_refcounts_fn_clone_singletonobject: "_ns, aError));
+ }
+ case 55: { // refcounts:uniffi_uniffi_fixture_refcounts_fn_method_singletonobject_method
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kRefcountsSingletonObjectPointerType>>;
+ return Some(CallHandler::CallAsync(uniffi_uniffi_fixture_refcounts_fn_method_singletonobject_method, aGlobal, aArgs, "uniffi_uniffi_fixture_refcounts_fn_method_singletonobject_method: "_ns, aError));
+ }
+ case 56: { // refcounts:uniffi_uniffi_fixture_refcounts_fn_func_get_js_refcount
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int32_t>>;
+ return Some(CallHandler::CallAsync(uniffi_uniffi_fixture_refcounts_fn_func_get_js_refcount, aGlobal, aArgs, "uniffi_uniffi_fixture_refcounts_fn_func_get_js_refcount: "_ns, aError));
}
- case 43: { // fixture_callbacks:uniffi_uniffi_fixture_callbacks_fn_func_log_even_numbers_main_thread
- using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingConverter<uint64_t>, ScaffoldingConverter<RustBuffer>>;
- return Some(CallHandler::CallAsync(uniffi_uniffi_fixture_callbacks_fn_func_log_even_numbers_main_thread, aGlobal, aArgs, "uniffi_uniffi_fixture_callbacks_fn_func_log_even_numbers_main_thread: "_ns, aError));
+ case 57: { // refcounts:uniffi_uniffi_fixture_refcounts_fn_func_get_singleton
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kRefcountsSingletonObjectPointerType>>;
+ return Some(CallHandler::CallAsync(uniffi_uniffi_fixture_refcounts_fn_func_get_singleton, aGlobal, aArgs, "uniffi_uniffi_fixture_refcounts_fn_func_get_singleton: "_ns, aError));
}
- case 44: { // geometry:uniffi_uniffi_geometry_fn_func_gradient
+ case 58: { // geometry:uniffi_uniffi_geometry_fn_func_gradient
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<double>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_geometry_fn_func_gradient, aGlobal, aArgs, "uniffi_uniffi_geometry_fn_func_gradient: "_ns, aError));
}
- case 45: { // geometry:uniffi_uniffi_geometry_fn_func_intersection
+ case 59: { // geometry:uniffi_uniffi_geometry_fn_func_intersection
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_geometry_fn_func_intersection, aGlobal, aArgs, "uniffi_uniffi_geometry_fn_func_intersection: "_ns, aError));
}
- case 46: { // rondpoint:uniffi_uniffi_rondpoint_fn_constructor_optionneur_new
+ case 60: { // rondpoint:uniffi_uniffi_rondpoint_fn_clone_optionneur
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>>;
+ return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_clone_optionneur, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_clone_optionneur: "_ns, aError));
+ }
+ case 61: { // rondpoint:uniffi_uniffi_rondpoint_fn_constructor_optionneur_new
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_constructor_optionneur_new, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_constructor_optionneur_new: "_ns, aError));
}
- case 47: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_boolean
+ case 62: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_boolean
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int8_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<int8_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_boolean, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_boolean: "_ns, aError));
}
- case 48: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_enum
+ case 63: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_enum
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_enum, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_enum: "_ns, aError));
}
- case 49: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_f32
+ case 64: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_f32
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<float>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<float>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_f32, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_f32: "_ns, aError));
}
- case 50: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_f64
+ case 65: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_f64
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<double>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<double>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_f64, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_f64: "_ns, aError));
}
- case 51: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i16_dec
+ case 66: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i16_dec
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int16_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<int16_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i16_dec, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i16_dec: "_ns, aError));
}
- case 52: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i16_hex
+ case 67: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i16_hex
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int16_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<int16_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i16_hex, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i16_hex: "_ns, aError));
}
- case 53: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i32_dec
+ case 68: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i32_dec
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int32_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<int32_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i32_dec, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i32_dec: "_ns, aError));
}
- case 54: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i32_hex
+ case 69: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i32_hex
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int32_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<int32_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i32_hex, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i32_hex: "_ns, aError));
}
- case 55: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i64_dec
+ case 70: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i64_dec
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int64_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<int64_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i64_dec, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i64_dec: "_ns, aError));
}
- case 56: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i64_hex
+ case 71: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i64_hex
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int64_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<int64_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i64_hex, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i64_hex: "_ns, aError));
}
- case 57: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i8_dec
+ case 72: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i8_dec
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int8_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<int8_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i8_dec, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i8_dec: "_ns, aError));
}
- case 58: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i8_hex
+ case 73: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i8_hex
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int8_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<int8_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i8_hex, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i8_hex: "_ns, aError));
}
- case 59: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_null
+ case 74: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_null
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_null, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_null: "_ns, aError));
}
- case 60: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_sequence
+ case 75: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_sequence
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_sequence, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_sequence: "_ns, aError));
}
- case 61: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_string
+ case 76: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_string
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_string, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_string: "_ns, aError));
}
- case 62: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u16_dec
+ case 77: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u16_dec
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint16_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<uint16_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u16_dec, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u16_dec: "_ns, aError));
}
- case 63: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u16_hex
+ case 78: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u16_hex
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint16_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<uint16_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u16_hex, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u16_hex: "_ns, aError));
}
- case 64: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_dec
+ case 79: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_dec
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint32_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<uint32_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_dec, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_dec: "_ns, aError));
}
- case 65: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_hex
+ case 80: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_hex
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint32_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<uint32_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_hex, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_hex: "_ns, aError));
}
- case 66: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_oct
+ case 81: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_oct
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint32_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<uint32_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_oct, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_oct: "_ns, aError));
}
- case 67: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u64_dec
+ case 82: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u64_dec
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint64_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<uint64_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u64_dec, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u64_dec: "_ns, aError));
}
- case 68: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u64_hex
+ case 83: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u64_hex
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint64_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<uint64_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u64_hex, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u64_hex: "_ns, aError));
}
- case 69: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u8_dec
+ case 84: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u8_dec
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint8_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<uint8_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u8_dec, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u8_dec: "_ns, aError));
}
- case 70: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u8_hex
+ case 85: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u8_hex
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint8_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<uint8_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u8_hex, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u8_hex: "_ns, aError));
}
- case 71: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_zero
+ case 86: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_zero
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_zero, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_zero: "_ns, aError));
}
- case 72: { // rondpoint:uniffi_uniffi_rondpoint_fn_constructor_retourneur_new
+ case 87: { // rondpoint:uniffi_uniffi_rondpoint_fn_clone_retourneur
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>>;
+ return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_clone_retourneur, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_clone_retourneur: "_ns, aError));
+ }
+ case 88: { // rondpoint:uniffi_uniffi_rondpoint_fn_constructor_retourneur_new
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_constructor_retourneur_new, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_constructor_retourneur_new: "_ns, aError));
}
- case 73: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_boolean
+ case 89: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_boolean
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int8_t>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<int8_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_boolean, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_boolean: "_ns, aError));
}
- case 74: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_double
+ case 90: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_double
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<double>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<double>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_double, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_double: "_ns, aError));
}
- case 75: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_float
+ case 91: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_float
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<float>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<float>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_float, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_float: "_ns, aError));
}
- case 76: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i16
+ case 92: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i16
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int16_t>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<int16_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i16, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i16: "_ns, aError));
}
- case 77: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i32
+ case 93: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i32
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int32_t>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<int32_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i32, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i32: "_ns, aError));
}
- case 78: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i64
+ case 94: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i64
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int64_t>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<int64_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i64, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i64: "_ns, aError));
}
- case 79: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i8
+ case 95: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i8
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int8_t>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<int8_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i8, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i8: "_ns, aError));
}
- case 80: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_nombres
+ case 96: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_nombres
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_nombres, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_nombres: "_ns, aError));
}
- case 81: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_nombres_signes
+ case 97: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_nombres_signes
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_nombres_signes, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_nombres_signes: "_ns, aError));
}
- case 82: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_optionneur_dictionnaire
+ case 98: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_optionneur_dictionnaire
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_optionneur_dictionnaire, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_optionneur_dictionnaire: "_ns, aError));
}
- case 83: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_string
+ case 99: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_string
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_string, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_string: "_ns, aError));
}
- case 84: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u16
+ case 100: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u16
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint16_t>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<uint16_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u16, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u16: "_ns, aError));
}
- case 85: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u32
+ case 101: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u32
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint32_t>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<uint32_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u32, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u32: "_ns, aError));
}
- case 86: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u64
+ case 102: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u64
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint64_t>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<uint64_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u64, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u64: "_ns, aError));
}
- case 87: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u8
+ case 103: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u8
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint8_t>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<uint8_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u8, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u8: "_ns, aError));
}
- case 88: { // rondpoint:uniffi_uniffi_rondpoint_fn_constructor_stringifier_new
+ case 104: { // rondpoint:uniffi_uniffi_rondpoint_fn_clone_stringifier
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kRondpointStringifierPointerType>, ScaffoldingObjectConverter<&kRondpointStringifierPointerType>>;
+ return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_clone_stringifier, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_clone_stringifier: "_ns, aError));
+ }
+ case 105: { // rondpoint:uniffi_uniffi_rondpoint_fn_constructor_stringifier_new
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kRondpointStringifierPointerType>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_constructor_stringifier_new, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_constructor_stringifier_new: "_ns, aError));
}
- case 89: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_boolean
+ case 106: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_boolean
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointStringifierPointerType>, ScaffoldingConverter<int8_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_boolean, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_boolean: "_ns, aError));
}
- case 90: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_double
+ case 107: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_double
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointStringifierPointerType>, ScaffoldingConverter<double>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_double, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_double: "_ns, aError));
}
- case 91: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_float
+ case 108: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_float
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointStringifierPointerType>, ScaffoldingConverter<float>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_float, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_float: "_ns, aError));
}
- case 92: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i16
+ case 109: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i16
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointStringifierPointerType>, ScaffoldingConverter<int16_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i16, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i16: "_ns, aError));
}
- case 93: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i32
+ case 110: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i32
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointStringifierPointerType>, ScaffoldingConverter<int32_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i32, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i32: "_ns, aError));
}
- case 94: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i64
+ case 111: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i64
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointStringifierPointerType>, ScaffoldingConverter<int64_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i64, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i64: "_ns, aError));
}
- case 95: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i8
+ case 112: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i8
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointStringifierPointerType>, ScaffoldingConverter<int8_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i8, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i8: "_ns, aError));
}
- case 96: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u16
+ case 113: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u16
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointStringifierPointerType>, ScaffoldingConverter<uint16_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u16, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u16: "_ns, aError));
}
- case 97: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u32
+ case 114: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u32
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointStringifierPointerType>, ScaffoldingConverter<uint32_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u32, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u32: "_ns, aError));
}
- case 98: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u64
+ case 115: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u64
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointStringifierPointerType>, ScaffoldingConverter<uint64_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u64, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u64: "_ns, aError));
}
- case 99: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u8
+ case 116: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u8
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointStringifierPointerType>, ScaffoldingConverter<uint8_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u8, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u8: "_ns, aError));
}
- case 100: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_well_known_string
+ case 117: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_well_known_string
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointStringifierPointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_method_stringifier_well_known_string, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_method_stringifier_well_known_string: "_ns, aError));
}
- case 101: { // rondpoint:uniffi_uniffi_rondpoint_fn_func_copie_carte
+ case 118: { // rondpoint:uniffi_uniffi_rondpoint_fn_func_copie_carte
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_func_copie_carte, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_func_copie_carte: "_ns, aError));
}
- case 102: { // rondpoint:uniffi_uniffi_rondpoint_fn_func_copie_dictionnaire
+ case 119: { // rondpoint:uniffi_uniffi_rondpoint_fn_func_copie_dictionnaire
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_func_copie_dictionnaire, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_func_copie_dictionnaire: "_ns, aError));
}
- case 103: { // rondpoint:uniffi_uniffi_rondpoint_fn_func_copie_enumeration
+ case 120: { // rondpoint:uniffi_uniffi_rondpoint_fn_func_copie_enumeration
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_func_copie_enumeration, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_func_copie_enumeration: "_ns, aError));
}
- case 104: { // rondpoint:uniffi_uniffi_rondpoint_fn_func_copie_enumerations
+ case 121: { // rondpoint:uniffi_uniffi_rondpoint_fn_func_copie_enumerations
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_func_copie_enumerations, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_func_copie_enumerations: "_ns, aError));
}
- case 105: { // rondpoint:uniffi_uniffi_rondpoint_fn_func_switcheroo
+ case 122: { // rondpoint:uniffi_uniffi_rondpoint_fn_func_switcheroo
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int8_t>, ScaffoldingConverter<int8_t>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_rondpoint_fn_func_switcheroo, aGlobal, aArgs, "uniffi_uniffi_rondpoint_fn_func_switcheroo: "_ns, aError));
}
- case 106: { // sprites:uniffi_uniffi_sprites_fn_constructor_sprite_new
+ case 123: { // sprites:uniffi_uniffi_sprites_fn_clone_sprite
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kSpritesSpritePointerType>, ScaffoldingObjectConverter<&kSpritesSpritePointerType>>;
+ return Some(CallHandler::CallAsync(uniffi_uniffi_sprites_fn_clone_sprite, aGlobal, aArgs, "uniffi_uniffi_sprites_fn_clone_sprite: "_ns, aError));
+ }
+ case 124: { // sprites:uniffi_uniffi_sprites_fn_constructor_sprite_new
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kSpritesSpritePointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_sprites_fn_constructor_sprite_new, aGlobal, aArgs, "uniffi_uniffi_sprites_fn_constructor_sprite_new: "_ns, aError));
}
- case 107: { // sprites:uniffi_uniffi_sprites_fn_constructor_sprite_new_relative_to
+ case 125: { // sprites:uniffi_uniffi_sprites_fn_constructor_sprite_new_relative_to
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kSpritesSpritePointerType>, ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_sprites_fn_constructor_sprite_new_relative_to, aGlobal, aArgs, "uniffi_uniffi_sprites_fn_constructor_sprite_new_relative_to: "_ns, aError));
}
- case 108: { // sprites:uniffi_uniffi_sprites_fn_method_sprite_get_position
+ case 126: { // sprites:uniffi_uniffi_sprites_fn_method_sprite_get_position
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kSpritesSpritePointerType>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_sprites_fn_method_sprite_get_position, aGlobal, aArgs, "uniffi_uniffi_sprites_fn_method_sprite_get_position: "_ns, aError));
}
- case 109: { // sprites:uniffi_uniffi_sprites_fn_method_sprite_move_by
+ case 127: { // sprites:uniffi_uniffi_sprites_fn_method_sprite_move_by
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kSpritesSpritePointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_sprites_fn_method_sprite_move_by, aGlobal, aArgs, "uniffi_uniffi_sprites_fn_method_sprite_move_by: "_ns, aError));
}
- case 110: { // sprites:uniffi_uniffi_sprites_fn_method_sprite_move_to
+ case 128: { // sprites:uniffi_uniffi_sprites_fn_method_sprite_move_to
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kSpritesSpritePointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_sprites_fn_method_sprite_move_to, aGlobal, aArgs, "uniffi_uniffi_sprites_fn_method_sprite_move_to: "_ns, aError));
}
- case 111: { // sprites:uniffi_uniffi_sprites_fn_func_translate
+ case 129: { // sprites:uniffi_uniffi_sprites_fn_func_translate
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_sprites_fn_func_translate, aGlobal, aArgs, "uniffi_uniffi_sprites_fn_func_translate: "_ns, aError));
}
- case 112: { // todolist:uniffi_uniffi_todolist_fn_constructor_todolist_new
+ case 130: { // todolist:uniffi_uniffi_todolist_fn_clone_todolist
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kTodolistTodoListPointerType>, ScaffoldingObjectConverter<&kTodolistTodoListPointerType>>;
+ return Some(CallHandler::CallAsync(uniffi_uniffi_todolist_fn_clone_todolist, aGlobal, aArgs, "uniffi_uniffi_todolist_fn_clone_todolist: "_ns, aError));
+ }
+ case 131: { // todolist:uniffi_uniffi_todolist_fn_constructor_todolist_new
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kTodolistTodoListPointerType>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_todolist_fn_constructor_todolist_new, aGlobal, aArgs, "uniffi_uniffi_todolist_fn_constructor_todolist_new: "_ns, aError));
}
- case 113: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_add_entries
+ case 132: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_add_entries
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTodolistTodoListPointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_todolist_fn_method_todolist_add_entries, aGlobal, aArgs, "uniffi_uniffi_todolist_fn_method_todolist_add_entries: "_ns, aError));
}
- case 114: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_add_entry
+ case 133: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_add_entry
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTodolistTodoListPointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_todolist_fn_method_todolist_add_entry, aGlobal, aArgs, "uniffi_uniffi_todolist_fn_method_todolist_add_entry: "_ns, aError));
}
- case 115: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_add_item
+ case 134: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_add_item
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTodolistTodoListPointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_todolist_fn_method_todolist_add_item, aGlobal, aArgs, "uniffi_uniffi_todolist_fn_method_todolist_add_item: "_ns, aError));
}
- case 116: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_add_items
+ case 135: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_add_items
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTodolistTodoListPointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_todolist_fn_method_todolist_add_items, aGlobal, aArgs, "uniffi_uniffi_todolist_fn_method_todolist_add_items: "_ns, aError));
}
- case 117: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_clear_item
+ case 136: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_clear_item
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTodolistTodoListPointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_todolist_fn_method_todolist_clear_item, aGlobal, aArgs, "uniffi_uniffi_todolist_fn_method_todolist_clear_item: "_ns, aError));
}
- case 118: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_entries
+ case 137: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_entries
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kTodolistTodoListPointerType>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_todolist_fn_method_todolist_get_entries, aGlobal, aArgs, "uniffi_uniffi_todolist_fn_method_todolist_get_entries: "_ns, aError));
}
- case 119: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_first
+ case 138: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_first
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kTodolistTodoListPointerType>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_todolist_fn_method_todolist_get_first, aGlobal, aArgs, "uniffi_uniffi_todolist_fn_method_todolist_get_first: "_ns, aError));
}
- case 120: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_items
+ case 139: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_items
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kTodolistTodoListPointerType>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_todolist_fn_method_todolist_get_items, aGlobal, aArgs, "uniffi_uniffi_todolist_fn_method_todolist_get_items: "_ns, aError));
}
- case 121: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_last
+ case 140: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_last
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kTodolistTodoListPointerType>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_todolist_fn_method_todolist_get_last, aGlobal, aArgs, "uniffi_uniffi_todolist_fn_method_todolist_get_last: "_ns, aError));
}
- case 122: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_last_entry
+ case 141: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_last_entry
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kTodolistTodoListPointerType>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_todolist_fn_method_todolist_get_last_entry, aGlobal, aArgs, "uniffi_uniffi_todolist_fn_method_todolist_get_last_entry: "_ns, aError));
}
- case 123: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_make_default
+ case 142: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_make_default
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTodolistTodoListPointerType>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_todolist_fn_method_todolist_make_default, aGlobal, aArgs, "uniffi_uniffi_todolist_fn_method_todolist_make_default: "_ns, aError));
}
- case 124: { // todolist:uniffi_uniffi_todolist_fn_func_create_entry_with
+ case 143: { // todolist:uniffi_uniffi_todolist_fn_func_create_entry_with
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_todolist_fn_func_create_entry_with, aGlobal, aArgs, "uniffi_uniffi_todolist_fn_func_create_entry_with: "_ns, aError));
}
- case 125: { // todolist:uniffi_uniffi_todolist_fn_func_get_default_list
+ case 144: { // todolist:uniffi_uniffi_todolist_fn_func_get_default_list
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_todolist_fn_func_get_default_list, aGlobal, aArgs, "uniffi_uniffi_todolist_fn_func_get_default_list: "_ns, aError));
}
- case 126: { // todolist:uniffi_uniffi_todolist_fn_func_set_default_list
+ case 145: { // todolist:uniffi_uniffi_todolist_fn_func_set_default_list
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTodolistTodoListPointerType>>;
return Some(CallHandler::CallAsync(uniffi_uniffi_todolist_fn_func_set_default_list, aGlobal, aArgs, "uniffi_uniffi_todolist_fn_func_set_default_list: "_ns, aError));
}
@@ -552,462 +578,497 @@ Maybe<already_AddRefed<Promise>> UniFFIFixturesCallAsync(const GlobalObject& aGl
bool UniFFIFixturesCallSync(const GlobalObject& aGlobal, uint64_t aId, const Sequence<ScaffoldingType>& aArgs, RootedDictionary<UniFFIScaffoldingCallResult>& aReturnValue, ErrorResult& aError) {
switch (aId) {
- case 35: { // arithmetic:uniffi_arithmetical_fn_func_add
+ case 47: { // arithmetic:uniffi_arithmetical_fn_func_add
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint64_t>, ScaffoldingConverter<uint64_t>, ScaffoldingConverter<uint64_t>>;
CallHandler::CallSync(uniffi_arithmetical_fn_func_add, aGlobal, aArgs, aReturnValue, "uniffi_arithmetical_fn_func_add: "_ns, aError);
return true;
}
- case 36: { // arithmetic:uniffi_arithmetical_fn_func_div
+ case 48: { // arithmetic:uniffi_arithmetical_fn_func_div
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint64_t>, ScaffoldingConverter<uint64_t>, ScaffoldingConverter<uint64_t>>;
CallHandler::CallSync(uniffi_arithmetical_fn_func_div, aGlobal, aArgs, aReturnValue, "uniffi_arithmetical_fn_func_div: "_ns, aError);
return true;
}
- case 37: { // arithmetic:uniffi_arithmetical_fn_func_equal
+ case 49: { // arithmetic:uniffi_arithmetical_fn_func_equal
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int8_t>, ScaffoldingConverter<uint64_t>, ScaffoldingConverter<uint64_t>>;
CallHandler::CallSync(uniffi_arithmetical_fn_func_equal, aGlobal, aArgs, aReturnValue, "uniffi_arithmetical_fn_func_equal: "_ns, aError);
return true;
}
- case 38: { // arithmetic:uniffi_arithmetical_fn_func_sub
+ case 50: { // arithmetic:uniffi_arithmetical_fn_func_sub
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint64_t>, ScaffoldingConverter<uint64_t>, ScaffoldingConverter<uint64_t>>;
CallHandler::CallSync(uniffi_arithmetical_fn_func_sub, aGlobal, aArgs, aReturnValue, "uniffi_arithmetical_fn_func_sub: "_ns, aError);
return true;
}
- case 39: { // custom_types:uniffi_uniffi_custom_types_fn_func_get_custom_types_demo
+ case 51: { // custom_types:uniffi_uniffi_custom_types_fn_func_get_custom_types_demo
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_custom_types_fn_func_get_custom_types_demo, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_custom_types_fn_func_get_custom_types_demo: "_ns, aError);
return true;
}
- case 40: { // external_types:uniffi_uniffi_fixture_external_types_fn_func_gradient
+ case 52: { // external_types:uniffi_uniffi_fixture_external_types_fn_func_gradient
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<double>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_fixture_external_types_fn_func_gradient, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_fixture_external_types_fn_func_gradient: "_ns, aError);
return true;
}
- case 41: { // external_types:uniffi_uniffi_fixture_external_types_fn_func_intersection
+ case 53: { // external_types:uniffi_uniffi_fixture_external_types_fn_func_intersection
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_fixture_external_types_fn_func_intersection, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_fixture_external_types_fn_func_intersection: "_ns, aError);
return true;
}
- case 42: { // fixture_callbacks:uniffi_uniffi_fixture_callbacks_fn_func_log_even_numbers
- using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingConverter<uint64_t>, ScaffoldingConverter<RustBuffer>>;
- CallHandler::CallSync(uniffi_uniffi_fixture_callbacks_fn_func_log_even_numbers, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_fixture_callbacks_fn_func_log_even_numbers: "_ns, aError);
+ case 54: { // refcounts:uniffi_uniffi_fixture_refcounts_fn_clone_singletonobject
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kRefcountsSingletonObjectPointerType>, ScaffoldingObjectConverter<&kRefcountsSingletonObjectPointerType>>;
+ CallHandler::CallSync(uniffi_uniffi_fixture_refcounts_fn_clone_singletonobject, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_fixture_refcounts_fn_clone_singletonobject: "_ns, aError);
+ return true;
+ }
+ case 55: { // refcounts:uniffi_uniffi_fixture_refcounts_fn_method_singletonobject_method
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kRefcountsSingletonObjectPointerType>>;
+ CallHandler::CallSync(uniffi_uniffi_fixture_refcounts_fn_method_singletonobject_method, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_fixture_refcounts_fn_method_singletonobject_method: "_ns, aError);
return true;
}
- case 43: { // fixture_callbacks:uniffi_uniffi_fixture_callbacks_fn_func_log_even_numbers_main_thread
- using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingConverter<uint64_t>, ScaffoldingConverter<RustBuffer>>;
- CallHandler::CallSync(uniffi_uniffi_fixture_callbacks_fn_func_log_even_numbers_main_thread, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_fixture_callbacks_fn_func_log_even_numbers_main_thread: "_ns, aError);
+ case 56: { // refcounts:uniffi_uniffi_fixture_refcounts_fn_func_get_js_refcount
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int32_t>>;
+ CallHandler::CallSync(uniffi_uniffi_fixture_refcounts_fn_func_get_js_refcount, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_fixture_refcounts_fn_func_get_js_refcount: "_ns, aError);
return true;
}
- case 44: { // geometry:uniffi_uniffi_geometry_fn_func_gradient
+ case 57: { // refcounts:uniffi_uniffi_fixture_refcounts_fn_func_get_singleton
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kRefcountsSingletonObjectPointerType>>;
+ CallHandler::CallSync(uniffi_uniffi_fixture_refcounts_fn_func_get_singleton, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_fixture_refcounts_fn_func_get_singleton: "_ns, aError);
+ return true;
+ }
+ case 58: { // geometry:uniffi_uniffi_geometry_fn_func_gradient
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<double>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_geometry_fn_func_gradient, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_geometry_fn_func_gradient: "_ns, aError);
return true;
}
- case 45: { // geometry:uniffi_uniffi_geometry_fn_func_intersection
+ case 59: { // geometry:uniffi_uniffi_geometry_fn_func_intersection
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_geometry_fn_func_intersection, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_geometry_fn_func_intersection: "_ns, aError);
return true;
}
- case 46: { // rondpoint:uniffi_uniffi_rondpoint_fn_constructor_optionneur_new
+ case 60: { // rondpoint:uniffi_uniffi_rondpoint_fn_clone_optionneur
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>>;
+ CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_clone_optionneur, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_clone_optionneur: "_ns, aError);
+ return true;
+ }
+ case 61: { // rondpoint:uniffi_uniffi_rondpoint_fn_constructor_optionneur_new
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_constructor_optionneur_new, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_constructor_optionneur_new: "_ns, aError);
return true;
}
- case 47: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_boolean
+ case 62: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_boolean
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int8_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<int8_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_boolean, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_boolean: "_ns, aError);
return true;
}
- case 48: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_enum
+ case 63: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_enum
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_enum, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_enum: "_ns, aError);
return true;
}
- case 49: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_f32
+ case 64: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_f32
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<float>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<float>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_f32, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_f32: "_ns, aError);
return true;
}
- case 50: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_f64
+ case 65: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_f64
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<double>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<double>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_f64, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_f64: "_ns, aError);
return true;
}
- case 51: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i16_dec
+ case 66: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i16_dec
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int16_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<int16_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i16_dec, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i16_dec: "_ns, aError);
return true;
}
- case 52: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i16_hex
+ case 67: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i16_hex
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int16_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<int16_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i16_hex, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i16_hex: "_ns, aError);
return true;
}
- case 53: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i32_dec
+ case 68: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i32_dec
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int32_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<int32_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i32_dec, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i32_dec: "_ns, aError);
return true;
}
- case 54: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i32_hex
+ case 69: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i32_hex
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int32_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<int32_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i32_hex, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i32_hex: "_ns, aError);
return true;
}
- case 55: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i64_dec
+ case 70: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i64_dec
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int64_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<int64_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i64_dec, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i64_dec: "_ns, aError);
return true;
}
- case 56: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i64_hex
+ case 71: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i64_hex
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int64_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<int64_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i64_hex, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i64_hex: "_ns, aError);
return true;
}
- case 57: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i8_dec
+ case 72: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i8_dec
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int8_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<int8_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i8_dec, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i8_dec: "_ns, aError);
return true;
}
- case 58: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i8_hex
+ case 73: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i8_hex
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int8_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<int8_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i8_hex, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_i8_hex: "_ns, aError);
return true;
}
- case 59: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_null
+ case 74: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_null
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_null, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_null: "_ns, aError);
return true;
}
- case 60: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_sequence
+ case 75: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_sequence
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_sequence, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_sequence: "_ns, aError);
return true;
}
- case 61: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_string
+ case 76: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_string
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_string, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_string: "_ns, aError);
return true;
}
- case 62: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u16_dec
+ case 77: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u16_dec
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint16_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<uint16_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u16_dec, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u16_dec: "_ns, aError);
return true;
}
- case 63: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u16_hex
+ case 78: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u16_hex
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint16_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<uint16_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u16_hex, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u16_hex: "_ns, aError);
return true;
}
- case 64: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_dec
+ case 79: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_dec
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint32_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<uint32_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_dec, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_dec: "_ns, aError);
return true;
}
- case 65: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_hex
+ case 80: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_hex
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint32_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<uint32_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_hex, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_hex: "_ns, aError);
return true;
}
- case 66: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_oct
+ case 81: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_oct
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint32_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<uint32_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_oct, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u32_oct: "_ns, aError);
return true;
}
- case 67: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u64_dec
+ case 82: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u64_dec
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint64_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<uint64_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u64_dec, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u64_dec: "_ns, aError);
return true;
}
- case 68: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u64_hex
+ case 83: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u64_hex
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint64_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<uint64_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u64_hex, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u64_hex: "_ns, aError);
return true;
}
- case 69: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u8_dec
+ case 84: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u8_dec
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint8_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<uint8_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u8_dec, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u8_dec: "_ns, aError);
return true;
}
- case 70: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u8_hex
+ case 85: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u8_hex
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint8_t>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<uint8_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u8_hex, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_u8_hex: "_ns, aError);
return true;
}
- case 71: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_zero
+ case 86: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_zero
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointOptionneurPointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_zero, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_optionneur_sinon_zero: "_ns, aError);
return true;
}
- case 72: { // rondpoint:uniffi_uniffi_rondpoint_fn_constructor_retourneur_new
+ case 87: { // rondpoint:uniffi_uniffi_rondpoint_fn_clone_retourneur
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>>;
+ CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_clone_retourneur, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_clone_retourneur: "_ns, aError);
+ return true;
+ }
+ case 88: { // rondpoint:uniffi_uniffi_rondpoint_fn_constructor_retourneur_new
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_constructor_retourneur_new, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_constructor_retourneur_new: "_ns, aError);
return true;
}
- case 73: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_boolean
+ case 89: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_boolean
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int8_t>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<int8_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_boolean, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_boolean: "_ns, aError);
return true;
}
- case 74: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_double
+ case 90: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_double
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<double>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<double>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_double, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_double: "_ns, aError);
return true;
}
- case 75: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_float
+ case 91: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_float
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<float>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<float>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_float, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_float: "_ns, aError);
return true;
}
- case 76: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i16
+ case 92: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i16
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int16_t>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<int16_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i16, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i16: "_ns, aError);
return true;
}
- case 77: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i32
+ case 93: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i32
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int32_t>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<int32_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i32, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i32: "_ns, aError);
return true;
}
- case 78: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i64
+ case 94: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i64
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int64_t>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<int64_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i64, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i64: "_ns, aError);
return true;
}
- case 79: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i8
+ case 95: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i8
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int8_t>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<int8_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i8, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_i8: "_ns, aError);
return true;
}
- case 80: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_nombres
+ case 96: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_nombres
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_nombres, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_nombres: "_ns, aError);
return true;
}
- case 81: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_nombres_signes
+ case 97: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_nombres_signes
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_nombres_signes, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_nombres_signes: "_ns, aError);
return true;
}
- case 82: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_optionneur_dictionnaire
+ case 98: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_optionneur_dictionnaire
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_optionneur_dictionnaire, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_optionneur_dictionnaire: "_ns, aError);
return true;
}
- case 83: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_string
+ case 99: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_string
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_string, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_string: "_ns, aError);
return true;
}
- case 84: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u16
+ case 100: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u16
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint16_t>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<uint16_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u16, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u16: "_ns, aError);
return true;
}
- case 85: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u32
+ case 101: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u32
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint32_t>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<uint32_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u32, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u32: "_ns, aError);
return true;
}
- case 86: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u64
+ case 102: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u64
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint64_t>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<uint64_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u64, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u64: "_ns, aError);
return true;
}
- case 87: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u8
+ case 103: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u8
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<uint8_t>, ScaffoldingObjectConverter<&kRondpointRetourneurPointerType>, ScaffoldingConverter<uint8_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u8, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_retourneur_identique_u8: "_ns, aError);
return true;
}
- case 88: { // rondpoint:uniffi_uniffi_rondpoint_fn_constructor_stringifier_new
+ case 104: { // rondpoint:uniffi_uniffi_rondpoint_fn_clone_stringifier
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kRondpointStringifierPointerType>, ScaffoldingObjectConverter<&kRondpointStringifierPointerType>>;
+ CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_clone_stringifier, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_clone_stringifier: "_ns, aError);
+ return true;
+ }
+ case 105: { // rondpoint:uniffi_uniffi_rondpoint_fn_constructor_stringifier_new
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kRondpointStringifierPointerType>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_constructor_stringifier_new, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_constructor_stringifier_new: "_ns, aError);
return true;
}
- case 89: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_boolean
+ case 106: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_boolean
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointStringifierPointerType>, ScaffoldingConverter<int8_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_boolean, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_boolean: "_ns, aError);
return true;
}
- case 90: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_double
+ case 107: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_double
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointStringifierPointerType>, ScaffoldingConverter<double>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_double, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_double: "_ns, aError);
return true;
}
- case 91: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_float
+ case 108: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_float
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointStringifierPointerType>, ScaffoldingConverter<float>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_float, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_float: "_ns, aError);
return true;
}
- case 92: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i16
+ case 109: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i16
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointStringifierPointerType>, ScaffoldingConverter<int16_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i16, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i16: "_ns, aError);
return true;
}
- case 93: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i32
+ case 110: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i32
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointStringifierPointerType>, ScaffoldingConverter<int32_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i32, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i32: "_ns, aError);
return true;
}
- case 94: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i64
+ case 111: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i64
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointStringifierPointerType>, ScaffoldingConverter<int64_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i64, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i64: "_ns, aError);
return true;
}
- case 95: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i8
+ case 112: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i8
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointStringifierPointerType>, ScaffoldingConverter<int8_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i8, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_i8: "_ns, aError);
return true;
}
- case 96: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u16
+ case 113: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u16
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointStringifierPointerType>, ScaffoldingConverter<uint16_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u16, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u16: "_ns, aError);
return true;
}
- case 97: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u32
+ case 114: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u32
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointStringifierPointerType>, ScaffoldingConverter<uint32_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u32, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u32: "_ns, aError);
return true;
}
- case 98: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u64
+ case 115: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u64
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointStringifierPointerType>, ScaffoldingConverter<uint64_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u64, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u64: "_ns, aError);
return true;
}
- case 99: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u8
+ case 116: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u8
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointStringifierPointerType>, ScaffoldingConverter<uint8_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u8, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_stringifier_to_string_u8: "_ns, aError);
return true;
}
- case 100: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_well_known_string
+ case 117: { // rondpoint:uniffi_uniffi_rondpoint_fn_method_stringifier_well_known_string
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRondpointStringifierPointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_method_stringifier_well_known_string, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_method_stringifier_well_known_string: "_ns, aError);
return true;
}
- case 101: { // rondpoint:uniffi_uniffi_rondpoint_fn_func_copie_carte
+ case 118: { // rondpoint:uniffi_uniffi_rondpoint_fn_func_copie_carte
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_func_copie_carte, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_func_copie_carte: "_ns, aError);
return true;
}
- case 102: { // rondpoint:uniffi_uniffi_rondpoint_fn_func_copie_dictionnaire
+ case 119: { // rondpoint:uniffi_uniffi_rondpoint_fn_func_copie_dictionnaire
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_func_copie_dictionnaire, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_func_copie_dictionnaire: "_ns, aError);
return true;
}
- case 103: { // rondpoint:uniffi_uniffi_rondpoint_fn_func_copie_enumeration
+ case 120: { // rondpoint:uniffi_uniffi_rondpoint_fn_func_copie_enumeration
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_func_copie_enumeration, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_func_copie_enumeration: "_ns, aError);
return true;
}
- case 104: { // rondpoint:uniffi_uniffi_rondpoint_fn_func_copie_enumerations
+ case 121: { // rondpoint:uniffi_uniffi_rondpoint_fn_func_copie_enumerations
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_func_copie_enumerations, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_func_copie_enumerations: "_ns, aError);
return true;
}
- case 105: { // rondpoint:uniffi_uniffi_rondpoint_fn_func_switcheroo
+ case 122: { // rondpoint:uniffi_uniffi_rondpoint_fn_func_switcheroo
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int8_t>, ScaffoldingConverter<int8_t>>;
CallHandler::CallSync(uniffi_uniffi_rondpoint_fn_func_switcheroo, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_rondpoint_fn_func_switcheroo: "_ns, aError);
return true;
}
- case 106: { // sprites:uniffi_uniffi_sprites_fn_constructor_sprite_new
+ case 123: { // sprites:uniffi_uniffi_sprites_fn_clone_sprite
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kSpritesSpritePointerType>, ScaffoldingObjectConverter<&kSpritesSpritePointerType>>;
+ CallHandler::CallSync(uniffi_uniffi_sprites_fn_clone_sprite, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_sprites_fn_clone_sprite: "_ns, aError);
+ return true;
+ }
+ case 124: { // sprites:uniffi_uniffi_sprites_fn_constructor_sprite_new
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kSpritesSpritePointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_sprites_fn_constructor_sprite_new, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_sprites_fn_constructor_sprite_new: "_ns, aError);
return true;
}
- case 107: { // sprites:uniffi_uniffi_sprites_fn_constructor_sprite_new_relative_to
+ case 125: { // sprites:uniffi_uniffi_sprites_fn_constructor_sprite_new_relative_to
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kSpritesSpritePointerType>, ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_sprites_fn_constructor_sprite_new_relative_to, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_sprites_fn_constructor_sprite_new_relative_to: "_ns, aError);
return true;
}
- case 108: { // sprites:uniffi_uniffi_sprites_fn_method_sprite_get_position
+ case 126: { // sprites:uniffi_uniffi_sprites_fn_method_sprite_get_position
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kSpritesSpritePointerType>>;
CallHandler::CallSync(uniffi_uniffi_sprites_fn_method_sprite_get_position, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_sprites_fn_method_sprite_get_position: "_ns, aError);
return true;
}
- case 109: { // sprites:uniffi_uniffi_sprites_fn_method_sprite_move_by
+ case 127: { // sprites:uniffi_uniffi_sprites_fn_method_sprite_move_by
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kSpritesSpritePointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_sprites_fn_method_sprite_move_by, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_sprites_fn_method_sprite_move_by: "_ns, aError);
return true;
}
- case 110: { // sprites:uniffi_uniffi_sprites_fn_method_sprite_move_to
+ case 128: { // sprites:uniffi_uniffi_sprites_fn_method_sprite_move_to
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kSpritesSpritePointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_sprites_fn_method_sprite_move_to, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_sprites_fn_method_sprite_move_to: "_ns, aError);
return true;
}
- case 111: { // sprites:uniffi_uniffi_sprites_fn_func_translate
+ case 129: { // sprites:uniffi_uniffi_sprites_fn_func_translate
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_sprites_fn_func_translate, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_sprites_fn_func_translate: "_ns, aError);
return true;
}
- case 112: { // todolist:uniffi_uniffi_todolist_fn_constructor_todolist_new
+ case 130: { // todolist:uniffi_uniffi_todolist_fn_clone_todolist
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kTodolistTodoListPointerType>, ScaffoldingObjectConverter<&kTodolistTodoListPointerType>>;
+ CallHandler::CallSync(uniffi_uniffi_todolist_fn_clone_todolist, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_todolist_fn_clone_todolist: "_ns, aError);
+ return true;
+ }
+ case 131: { // todolist:uniffi_uniffi_todolist_fn_constructor_todolist_new
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kTodolistTodoListPointerType>>;
CallHandler::CallSync(uniffi_uniffi_todolist_fn_constructor_todolist_new, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_todolist_fn_constructor_todolist_new: "_ns, aError);
return true;
}
- case 113: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_add_entries
+ case 132: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_add_entries
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTodolistTodoListPointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_todolist_fn_method_todolist_add_entries, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_todolist_fn_method_todolist_add_entries: "_ns, aError);
return true;
}
- case 114: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_add_entry
+ case 133: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_add_entry
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTodolistTodoListPointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_todolist_fn_method_todolist_add_entry, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_todolist_fn_method_todolist_add_entry: "_ns, aError);
return true;
}
- case 115: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_add_item
+ case 134: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_add_item
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTodolistTodoListPointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_todolist_fn_method_todolist_add_item, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_todolist_fn_method_todolist_add_item: "_ns, aError);
return true;
}
- case 116: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_add_items
+ case 135: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_add_items
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTodolistTodoListPointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_todolist_fn_method_todolist_add_items, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_todolist_fn_method_todolist_add_items: "_ns, aError);
return true;
}
- case 117: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_clear_item
+ case 136: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_clear_item
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTodolistTodoListPointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_todolist_fn_method_todolist_clear_item, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_todolist_fn_method_todolist_clear_item: "_ns, aError);
return true;
}
- case 118: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_entries
+ case 137: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_entries
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kTodolistTodoListPointerType>>;
CallHandler::CallSync(uniffi_uniffi_todolist_fn_method_todolist_get_entries, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_todolist_fn_method_todolist_get_entries: "_ns, aError);
return true;
}
- case 119: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_first
+ case 138: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_first
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kTodolistTodoListPointerType>>;
CallHandler::CallSync(uniffi_uniffi_todolist_fn_method_todolist_get_first, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_todolist_fn_method_todolist_get_first: "_ns, aError);
return true;
}
- case 120: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_items
+ case 139: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_items
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kTodolistTodoListPointerType>>;
CallHandler::CallSync(uniffi_uniffi_todolist_fn_method_todolist_get_items, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_todolist_fn_method_todolist_get_items: "_ns, aError);
return true;
}
- case 121: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_last
+ case 140: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_last
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kTodolistTodoListPointerType>>;
CallHandler::CallSync(uniffi_uniffi_todolist_fn_method_todolist_get_last, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_todolist_fn_method_todolist_get_last: "_ns, aError);
return true;
}
- case 122: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_last_entry
+ case 141: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_get_last_entry
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kTodolistTodoListPointerType>>;
CallHandler::CallSync(uniffi_uniffi_todolist_fn_method_todolist_get_last_entry, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_todolist_fn_method_todolist_get_last_entry: "_ns, aError);
return true;
}
- case 123: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_make_default
+ case 142: { // todolist:uniffi_uniffi_todolist_fn_method_todolist_make_default
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTodolistTodoListPointerType>>;
CallHandler::CallSync(uniffi_uniffi_todolist_fn_method_todolist_make_default, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_todolist_fn_method_todolist_make_default: "_ns, aError);
return true;
}
- case 124: { // todolist:uniffi_uniffi_todolist_fn_func_create_entry_with
+ case 143: { // todolist:uniffi_uniffi_todolist_fn_func_create_entry_with
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_todolist_fn_func_create_entry_with, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_todolist_fn_func_create_entry_with: "_ns, aError);
return true;
}
- case 125: { // todolist:uniffi_uniffi_todolist_fn_func_get_default_list
+ case 144: { // todolist:uniffi_uniffi_todolist_fn_func_get_default_list
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_uniffi_todolist_fn_func_get_default_list, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_todolist_fn_func_get_default_list: "_ns, aError);
return true;
}
- case 126: { // todolist:uniffi_uniffi_todolist_fn_func_set_default_list
+ case 145: { // todolist:uniffi_uniffi_todolist_fn_func_set_default_list
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTodolistTodoListPointerType>>;
CallHandler::CallSync(uniffi_uniffi_todolist_fn_func_set_default_list, aGlobal, aArgs, aReturnValue, "uniffi_uniffi_todolist_fn_func_set_default_list: "_ns, aError);
return true;
@@ -1019,23 +1080,27 @@ bool UniFFIFixturesCallSync(const GlobalObject& aGlobal, uint64_t aId, const Seq
Maybe<already_AddRefed<UniFFIPointer>> UniFFIFixturesReadPointer(const GlobalObject& aGlobal, uint64_t aId, const ArrayBuffer& aArrayBuff, long aPosition, ErrorResult& aError) {
const UniFFIPointerType* type;
switch (aId) {
- case 5: { // rondpoint:Optionneur
+ case 6: { // refcounts:SingletonObject
+ type = &kRefcountsSingletonObjectPointerType;
+ break;
+ }
+ case 7: { // rondpoint:Optionneur
type = &kRondpointOptionneurPointerType;
break;
}
- case 6: { // rondpoint:Retourneur
+ case 8: { // rondpoint:Retourneur
type = &kRondpointRetourneurPointerType;
break;
}
- case 7: { // rondpoint:Stringifier
+ case 9: { // rondpoint:Stringifier
type = &kRondpointStringifierPointerType;
break;
}
- case 8: { // sprites:Sprite
+ case 10: { // sprites:Sprite
type = &kSpritesSpritePointerType;
break;
}
- case 9: { // todolist:TodoList
+ case 11: { // todolist:TodoList
type = &kTodolistTodoListPointerType;
break;
}
@@ -1048,23 +1113,27 @@ Maybe<already_AddRefed<UniFFIPointer>> UniFFIFixturesReadPointer(const GlobalObj
bool UniFFIFixturesWritePointer(const GlobalObject& aGlobal, uint64_t aId, const UniFFIPointer& aPtr, const ArrayBuffer& aArrayBuff, long aPosition, ErrorResult& aError) {
const UniFFIPointerType* type;
switch (aId) {
- case 5: { // rondpoint:Optionneur
+ case 6: { // refcounts:SingletonObject
+ type = &kRefcountsSingletonObjectPointerType;
+ break;
+ }
+ case 7: { // rondpoint:Optionneur
type = &kRondpointOptionneurPointerType;
break;
}
- case 6: { // rondpoint:Retourneur
+ case 8: { // rondpoint:Retourneur
type = &kRondpointRetourneurPointerType;
break;
}
- case 7: { // rondpoint:Stringifier
+ case 9: { // rondpoint:Stringifier
type = &kRondpointStringifierPointerType;
break;
}
- case 8: { // sprites:Sprite
+ case 10: { // sprites:Sprite
type = &kSpritesSpritePointerType;
break;
}
- case 9: { // todolist:TodoList
+ case 11: { // todolist:TodoList
type = &kTodolistTodoListPointerType;
break;
}
diff --git a/toolkit/components/uniffi-js/UniFFIGeneratedScaffolding.cpp b/toolkit/components/uniffi-js/UniFFIGeneratedScaffolding.cpp
index c235d92986..20a581d5e2 100644
--- a/toolkit/components/uniffi-js/UniFFIGeneratedScaffolding.cpp
+++ b/toolkit/components/uniffi-js/UniFFIGeneratedScaffolding.cpp
@@ -24,19 +24,30 @@ using dom::UniFFIScaffoldingCallResult;
// Define scaffolding functions from UniFFI
extern "C" {
+ void * uniffi_relevancy_fn_clone_relevancystore(void *, RustCallStatus*);
+ void uniffi_relevancy_fn_free_relevancystore(void *, RustCallStatus*);
+ void * uniffi_relevancy_fn_constructor_relevancystore_new(RustBuffer, RustCallStatus*);
+ RustBuffer uniffi_relevancy_fn_method_relevancystore_calculate_metrics(void *, RustCallStatus*);
+ void uniffi_relevancy_fn_method_relevancystore_ingest(void *, RustBuffer, RustCallStatus*);
+ RustBuffer uniffi_relevancy_fn_method_relevancystore_user_interest_vector(void *, RustCallStatus*);
+ void * uniffi_remote_settings_fn_clone_remotesettings(void *, RustCallStatus*);
void uniffi_remote_settings_fn_free_remotesettings(void *, RustCallStatus*);
void * uniffi_remote_settings_fn_constructor_remotesettings_new(RustBuffer, RustCallStatus*);
void uniffi_remote_settings_fn_method_remotesettings_download_attachment_to_path(void *, RustBuffer, RustBuffer, RustCallStatus*);
RustBuffer uniffi_remote_settings_fn_method_remotesettings_get_records(void *, RustCallStatus*);
RustBuffer uniffi_remote_settings_fn_method_remotesettings_get_records_since(void *, uint64_t, RustCallStatus*);
+ void * uniffi_suggest_fn_clone_suggeststore(void *, RustCallStatus*);
void uniffi_suggest_fn_free_suggeststore(void *, RustCallStatus*);
void * uniffi_suggest_fn_constructor_suggeststore_new(RustBuffer, RustBuffer, RustCallStatus*);
void uniffi_suggest_fn_method_suggeststore_clear(void *, RustCallStatus*);
+ void uniffi_suggest_fn_method_suggeststore_clear_dismissed_suggestions(void *, RustCallStatus*);
+ void uniffi_suggest_fn_method_suggeststore_dismiss_suggestion(void *, RustBuffer, RustCallStatus*);
RustBuffer uniffi_suggest_fn_method_suggeststore_fetch_global_config(void *, RustCallStatus*);
RustBuffer uniffi_suggest_fn_method_suggeststore_fetch_provider_config(void *, RustBuffer, RustCallStatus*);
void uniffi_suggest_fn_method_suggeststore_ingest(void *, RustBuffer, RustCallStatus*);
void uniffi_suggest_fn_method_suggeststore_interrupt(void *, RustCallStatus*);
RustBuffer uniffi_suggest_fn_method_suggeststore_query(void *, RustBuffer, RustCallStatus*);
+ void * uniffi_suggest_fn_clone_suggeststorebuilder(void *, RustCallStatus*);
void uniffi_suggest_fn_free_suggeststorebuilder(void *, RustCallStatus*);
void * uniffi_suggest_fn_constructor_suggeststorebuilder_new(RustCallStatus*);
void * uniffi_suggest_fn_method_suggeststorebuilder_build(void *, RustCallStatus*);
@@ -44,6 +55,7 @@ extern "C" {
void * uniffi_suggest_fn_method_suggeststorebuilder_data_path(void *, RustBuffer, RustCallStatus*);
void * uniffi_suggest_fn_method_suggeststorebuilder_remote_settings_config(void *, RustBuffer, RustCallStatus*);
int8_t uniffi_suggest_fn_func_raw_suggestion_url_matches(RustBuffer, RustBuffer, RustCallStatus*);
+ void * uniffi_tabs_fn_clone_tabsbridgedengine(void *, RustCallStatus*);
void uniffi_tabs_fn_free_tabsbridgedengine(void *, RustCallStatus*);
RustBuffer uniffi_tabs_fn_method_tabsbridgedengine_apply(void *, RustCallStatus*);
RustBuffer uniffi_tabs_fn_method_tabsbridgedengine_ensure_current_sync_id(void *, RustBuffer, RustCallStatus*);
@@ -58,6 +70,7 @@ extern "C" {
RustBuffer uniffi_tabs_fn_method_tabsbridgedengine_sync_id(void *, RustCallStatus*);
void uniffi_tabs_fn_method_tabsbridgedengine_sync_started(void *, RustCallStatus*);
void uniffi_tabs_fn_method_tabsbridgedengine_wipe(void *, RustCallStatus*);
+ void * uniffi_tabs_fn_clone_tabsstore(void *, RustCallStatus*);
void uniffi_tabs_fn_free_tabsstore(void *, RustCallStatus*);
void * uniffi_tabs_fn_constructor_tabsstore_new(RustBuffer, RustCallStatus*);
void * uniffi_tabs_fn_method_tabsstore_bridged_engine(void *, RustCallStatus*);
@@ -67,25 +80,35 @@ extern "C" {
}
// Define pointer types
+const static mozilla::uniffi::UniFFIPointerType kRelevancyRelevancyStorePointerType {
+ "relevancy::RelevancyStore"_ns,
+ uniffi_relevancy_fn_clone_relevancystore,
+ uniffi_relevancy_fn_free_relevancystore,
+};
const static mozilla::uniffi::UniFFIPointerType kRemoteSettingsRemoteSettingsPointerType {
"remote_settings::RemoteSettings"_ns,
- uniffi_remote_settings_fn_free_remotesettings
+ uniffi_remote_settings_fn_clone_remotesettings,
+ uniffi_remote_settings_fn_free_remotesettings,
};
const static mozilla::uniffi::UniFFIPointerType kSuggestSuggestStorePointerType {
"suggest::SuggestStore"_ns,
- uniffi_suggest_fn_free_suggeststore
+ uniffi_suggest_fn_clone_suggeststore,
+ uniffi_suggest_fn_free_suggeststore,
};
const static mozilla::uniffi::UniFFIPointerType kSuggestSuggestStoreBuilderPointerType {
"suggest::SuggestStoreBuilder"_ns,
- uniffi_suggest_fn_free_suggeststorebuilder
+ uniffi_suggest_fn_clone_suggeststorebuilder,
+ uniffi_suggest_fn_free_suggeststorebuilder,
};
const static mozilla::uniffi::UniFFIPointerType kTabsTabsBridgedEnginePointerType {
"tabs::TabsBridgedEngine"_ns,
- uniffi_tabs_fn_free_tabsbridgedengine
+ uniffi_tabs_fn_clone_tabsbridgedengine,
+ uniffi_tabs_fn_free_tabsbridgedengine,
};
const static mozilla::uniffi::UniFFIPointerType kTabsTabsStorePointerType {
"tabs::TabsStore"_ns,
- uniffi_tabs_fn_free_tabsstore
+ uniffi_tabs_fn_clone_tabsstore,
+ uniffi_tabs_fn_free_tabsstore,
};
// Define the data we need per-callback interface
@@ -101,143 +124,191 @@ Maybe<CallbackInterfaceInfo> UniFFIGetCallbackInterfaceInfo(uint64_t aInterfaceI
Maybe<already_AddRefed<Promise>> UniFFICallAsync(const GlobalObject& aGlobal, uint64_t aId, const Sequence<ScaffoldingType>& aArgs, ErrorResult& aError) {
switch (aId) {
- case 0: { // remote_settings:uniffi_remote_settings_fn_constructor_remotesettings_new
+ case 0: { // relevancy:uniffi_relevancy_fn_clone_relevancystore
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kRelevancyRelevancyStorePointerType>, ScaffoldingObjectConverter<&kRelevancyRelevancyStorePointerType>>;
+ return Some(CallHandler::CallAsync(uniffi_relevancy_fn_clone_relevancystore, aGlobal, aArgs, "uniffi_relevancy_fn_clone_relevancystore: "_ns, aError));
+ }
+ case 1: { // relevancy:uniffi_relevancy_fn_constructor_relevancystore_new
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kRelevancyRelevancyStorePointerType>, ScaffoldingConverter<RustBuffer>>;
+ return Some(CallHandler::CallAsync(uniffi_relevancy_fn_constructor_relevancystore_new, aGlobal, aArgs, "uniffi_relevancy_fn_constructor_relevancystore_new: "_ns, aError));
+ }
+ case 2: { // relevancy:uniffi_relevancy_fn_method_relevancystore_calculate_metrics
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRelevancyRelevancyStorePointerType>>;
+ return Some(CallHandler::CallAsync(uniffi_relevancy_fn_method_relevancystore_calculate_metrics, aGlobal, aArgs, "uniffi_relevancy_fn_method_relevancystore_calculate_metrics: "_ns, aError));
+ }
+ case 3: { // relevancy:uniffi_relevancy_fn_method_relevancystore_ingest
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kRelevancyRelevancyStorePointerType>, ScaffoldingConverter<RustBuffer>>;
+ return Some(CallHandler::CallAsync(uniffi_relevancy_fn_method_relevancystore_ingest, aGlobal, aArgs, "uniffi_relevancy_fn_method_relevancystore_ingest: "_ns, aError));
+ }
+ case 4: { // relevancy:uniffi_relevancy_fn_method_relevancystore_user_interest_vector
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRelevancyRelevancyStorePointerType>>;
+ return Some(CallHandler::CallAsync(uniffi_relevancy_fn_method_relevancystore_user_interest_vector, aGlobal, aArgs, "uniffi_relevancy_fn_method_relevancystore_user_interest_vector: "_ns, aError));
+ }
+ case 5: { // remote_settings:uniffi_remote_settings_fn_clone_remotesettings
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kRemoteSettingsRemoteSettingsPointerType>, ScaffoldingObjectConverter<&kRemoteSettingsRemoteSettingsPointerType>>;
+ return Some(CallHandler::CallAsync(uniffi_remote_settings_fn_clone_remotesettings, aGlobal, aArgs, "uniffi_remote_settings_fn_clone_remotesettings: "_ns, aError));
+ }
+ case 6: { // remote_settings:uniffi_remote_settings_fn_constructor_remotesettings_new
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kRemoteSettingsRemoteSettingsPointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_remote_settings_fn_constructor_remotesettings_new, aGlobal, aArgs, "uniffi_remote_settings_fn_constructor_remotesettings_new: "_ns, aError));
}
- case 1: { // remote_settings:uniffi_remote_settings_fn_method_remotesettings_download_attachment_to_path
+ case 7: { // remote_settings:uniffi_remote_settings_fn_method_remotesettings_download_attachment_to_path
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kRemoteSettingsRemoteSettingsPointerType>, ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_remote_settings_fn_method_remotesettings_download_attachment_to_path, aGlobal, aArgs, "uniffi_remote_settings_fn_method_remotesettings_download_attachment_to_path: "_ns, aError));
}
- case 2: { // remote_settings:uniffi_remote_settings_fn_method_remotesettings_get_records
+ case 8: { // remote_settings:uniffi_remote_settings_fn_method_remotesettings_get_records
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRemoteSettingsRemoteSettingsPointerType>>;
return Some(CallHandler::CallAsync(uniffi_remote_settings_fn_method_remotesettings_get_records, aGlobal, aArgs, "uniffi_remote_settings_fn_method_remotesettings_get_records: "_ns, aError));
}
- case 3: { // remote_settings:uniffi_remote_settings_fn_method_remotesettings_get_records_since
+ case 9: { // remote_settings:uniffi_remote_settings_fn_method_remotesettings_get_records_since
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRemoteSettingsRemoteSettingsPointerType>, ScaffoldingConverter<uint64_t>>;
return Some(CallHandler::CallAsync(uniffi_remote_settings_fn_method_remotesettings_get_records_since, aGlobal, aArgs, "uniffi_remote_settings_fn_method_remotesettings_get_records_since: "_ns, aError));
}
- case 4: { // suggest:uniffi_suggest_fn_constructor_suggeststore_new
+ case 10: { // suggest:uniffi_suggest_fn_clone_suggeststore
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kSuggestSuggestStorePointerType>, ScaffoldingObjectConverter<&kSuggestSuggestStorePointerType>>;
+ return Some(CallHandler::CallAsync(uniffi_suggest_fn_clone_suggeststore, aGlobal, aArgs, "uniffi_suggest_fn_clone_suggeststore: "_ns, aError));
+ }
+ case 11: { // suggest:uniffi_suggest_fn_constructor_suggeststore_new
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kSuggestSuggestStorePointerType>, ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_suggest_fn_constructor_suggeststore_new, aGlobal, aArgs, "uniffi_suggest_fn_constructor_suggeststore_new: "_ns, aError));
}
- case 5: { // suggest:uniffi_suggest_fn_method_suggeststore_clear
+ case 12: { // suggest:uniffi_suggest_fn_method_suggeststore_clear
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kSuggestSuggestStorePointerType>>;
return Some(CallHandler::CallAsync(uniffi_suggest_fn_method_suggeststore_clear, aGlobal, aArgs, "uniffi_suggest_fn_method_suggeststore_clear: "_ns, aError));
}
- case 6: { // suggest:uniffi_suggest_fn_method_suggeststore_fetch_global_config
+ case 13: { // suggest:uniffi_suggest_fn_method_suggeststore_clear_dismissed_suggestions
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kSuggestSuggestStorePointerType>>;
+ return Some(CallHandler::CallAsync(uniffi_suggest_fn_method_suggeststore_clear_dismissed_suggestions, aGlobal, aArgs, "uniffi_suggest_fn_method_suggeststore_clear_dismissed_suggestions: "_ns, aError));
+ }
+ case 14: { // suggest:uniffi_suggest_fn_method_suggeststore_dismiss_suggestion
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kSuggestSuggestStorePointerType>, ScaffoldingConverter<RustBuffer>>;
+ return Some(CallHandler::CallAsync(uniffi_suggest_fn_method_suggeststore_dismiss_suggestion, aGlobal, aArgs, "uniffi_suggest_fn_method_suggeststore_dismiss_suggestion: "_ns, aError));
+ }
+ case 15: { // suggest:uniffi_suggest_fn_method_suggeststore_fetch_global_config
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kSuggestSuggestStorePointerType>>;
return Some(CallHandler::CallAsync(uniffi_suggest_fn_method_suggeststore_fetch_global_config, aGlobal, aArgs, "uniffi_suggest_fn_method_suggeststore_fetch_global_config: "_ns, aError));
}
- case 7: { // suggest:uniffi_suggest_fn_method_suggeststore_fetch_provider_config
+ case 16: { // suggest:uniffi_suggest_fn_method_suggeststore_fetch_provider_config
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kSuggestSuggestStorePointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_suggest_fn_method_suggeststore_fetch_provider_config, aGlobal, aArgs, "uniffi_suggest_fn_method_suggeststore_fetch_provider_config: "_ns, aError));
}
- case 8: { // suggest:uniffi_suggest_fn_method_suggeststore_ingest
+ case 17: { // suggest:uniffi_suggest_fn_method_suggeststore_ingest
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kSuggestSuggestStorePointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_suggest_fn_method_suggeststore_ingest, aGlobal, aArgs, "uniffi_suggest_fn_method_suggeststore_ingest: "_ns, aError));
}
- case 9: { // suggest:uniffi_suggest_fn_method_suggeststore_interrupt
+ case 18: { // suggest:uniffi_suggest_fn_method_suggeststore_interrupt
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kSuggestSuggestStorePointerType>>;
return Some(CallHandler::CallAsync(uniffi_suggest_fn_method_suggeststore_interrupt, aGlobal, aArgs, "uniffi_suggest_fn_method_suggeststore_interrupt: "_ns, aError));
}
- case 10: { // suggest:uniffi_suggest_fn_method_suggeststore_query
+ case 19: { // suggest:uniffi_suggest_fn_method_suggeststore_query
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kSuggestSuggestStorePointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_suggest_fn_method_suggeststore_query, aGlobal, aArgs, "uniffi_suggest_fn_method_suggeststore_query: "_ns, aError));
}
- case 11: { // suggest:uniffi_suggest_fn_constructor_suggeststorebuilder_new
+ case 20: { // suggest:uniffi_suggest_fn_clone_suggeststorebuilder
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kSuggestSuggestStoreBuilderPointerType>, ScaffoldingObjectConverter<&kSuggestSuggestStoreBuilderPointerType>>;
+ return Some(CallHandler::CallAsync(uniffi_suggest_fn_clone_suggeststorebuilder, aGlobal, aArgs, "uniffi_suggest_fn_clone_suggeststorebuilder: "_ns, aError));
+ }
+ case 21: { // suggest:uniffi_suggest_fn_constructor_suggeststorebuilder_new
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kSuggestSuggestStoreBuilderPointerType>>;
return Some(CallHandler::CallAsync(uniffi_suggest_fn_constructor_suggeststorebuilder_new, aGlobal, aArgs, "uniffi_suggest_fn_constructor_suggeststorebuilder_new: "_ns, aError));
}
- case 12: { // suggest:uniffi_suggest_fn_method_suggeststorebuilder_build
+ case 22: { // suggest:uniffi_suggest_fn_method_suggeststorebuilder_build
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kSuggestSuggestStorePointerType>, ScaffoldingObjectConverter<&kSuggestSuggestStoreBuilderPointerType>>;
return Some(CallHandler::CallAsync(uniffi_suggest_fn_method_suggeststorebuilder_build, aGlobal, aArgs, "uniffi_suggest_fn_method_suggeststorebuilder_build: "_ns, aError));
}
- case 13: { // suggest:uniffi_suggest_fn_method_suggeststorebuilder_cache_path
+ case 23: { // suggest:uniffi_suggest_fn_method_suggeststorebuilder_cache_path
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kSuggestSuggestStoreBuilderPointerType>, ScaffoldingObjectConverter<&kSuggestSuggestStoreBuilderPointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_suggest_fn_method_suggeststorebuilder_cache_path, aGlobal, aArgs, "uniffi_suggest_fn_method_suggeststorebuilder_cache_path: "_ns, aError));
}
- case 14: { // suggest:uniffi_suggest_fn_method_suggeststorebuilder_data_path
+ case 24: { // suggest:uniffi_suggest_fn_method_suggeststorebuilder_data_path
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kSuggestSuggestStoreBuilderPointerType>, ScaffoldingObjectConverter<&kSuggestSuggestStoreBuilderPointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_suggest_fn_method_suggeststorebuilder_data_path, aGlobal, aArgs, "uniffi_suggest_fn_method_suggeststorebuilder_data_path: "_ns, aError));
}
- case 15: { // suggest:uniffi_suggest_fn_method_suggeststorebuilder_remote_settings_config
+ case 25: { // suggest:uniffi_suggest_fn_method_suggeststorebuilder_remote_settings_config
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kSuggestSuggestStoreBuilderPointerType>, ScaffoldingObjectConverter<&kSuggestSuggestStoreBuilderPointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_suggest_fn_method_suggeststorebuilder_remote_settings_config, aGlobal, aArgs, "uniffi_suggest_fn_method_suggeststorebuilder_remote_settings_config: "_ns, aError));
}
- case 16: { // suggest:uniffi_suggest_fn_func_raw_suggestion_url_matches
+ case 26: { // suggest:uniffi_suggest_fn_func_raw_suggestion_url_matches
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int8_t>, ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_suggest_fn_func_raw_suggestion_url_matches, aGlobal, aArgs, "uniffi_suggest_fn_func_raw_suggestion_url_matches: "_ns, aError));
}
- case 17: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_apply
+ case 27: { // tabs:uniffi_tabs_fn_clone_tabsbridgedengine
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>>;
+ return Some(CallHandler::CallAsync(uniffi_tabs_fn_clone_tabsbridgedengine, aGlobal, aArgs, "uniffi_tabs_fn_clone_tabsbridgedengine: "_ns, aError));
+ }
+ case 28: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_apply
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>>;
return Some(CallHandler::CallAsync(uniffi_tabs_fn_method_tabsbridgedengine_apply, aGlobal, aArgs, "uniffi_tabs_fn_method_tabsbridgedengine_apply: "_ns, aError));
}
- case 18: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_ensure_current_sync_id
+ case 29: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_ensure_current_sync_id
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_tabs_fn_method_tabsbridgedengine_ensure_current_sync_id, aGlobal, aArgs, "uniffi_tabs_fn_method_tabsbridgedengine_ensure_current_sync_id: "_ns, aError));
}
- case 19: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_last_sync
+ case 30: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_last_sync
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int64_t>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>>;
return Some(CallHandler::CallAsync(uniffi_tabs_fn_method_tabsbridgedengine_last_sync, aGlobal, aArgs, "uniffi_tabs_fn_method_tabsbridgedengine_last_sync: "_ns, aError));
}
- case 20: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_prepare_for_sync
+ case 31: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_prepare_for_sync
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_tabs_fn_method_tabsbridgedengine_prepare_for_sync, aGlobal, aArgs, "uniffi_tabs_fn_method_tabsbridgedengine_prepare_for_sync: "_ns, aError));
}
- case 21: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_reset
+ case 32: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_reset
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>>;
return Some(CallHandler::CallAsync(uniffi_tabs_fn_method_tabsbridgedengine_reset, aGlobal, aArgs, "uniffi_tabs_fn_method_tabsbridgedengine_reset: "_ns, aError));
}
- case 22: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_reset_sync_id
+ case 33: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_reset_sync_id
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>>;
return Some(CallHandler::CallAsync(uniffi_tabs_fn_method_tabsbridgedengine_reset_sync_id, aGlobal, aArgs, "uniffi_tabs_fn_method_tabsbridgedengine_reset_sync_id: "_ns, aError));
}
- case 23: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_set_last_sync
+ case 34: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_set_last_sync
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>, ScaffoldingConverter<int64_t>>;
return Some(CallHandler::CallAsync(uniffi_tabs_fn_method_tabsbridgedengine_set_last_sync, aGlobal, aArgs, "uniffi_tabs_fn_method_tabsbridgedengine_set_last_sync: "_ns, aError));
}
- case 24: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_set_uploaded
+ case 35: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_set_uploaded
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>, ScaffoldingConverter<int64_t>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_tabs_fn_method_tabsbridgedengine_set_uploaded, aGlobal, aArgs, "uniffi_tabs_fn_method_tabsbridgedengine_set_uploaded: "_ns, aError));
}
- case 25: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_store_incoming
+ case 36: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_store_incoming
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_tabs_fn_method_tabsbridgedengine_store_incoming, aGlobal, aArgs, "uniffi_tabs_fn_method_tabsbridgedengine_store_incoming: "_ns, aError));
}
- case 26: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_sync_finished
+ case 37: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_sync_finished
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>>;
return Some(CallHandler::CallAsync(uniffi_tabs_fn_method_tabsbridgedengine_sync_finished, aGlobal, aArgs, "uniffi_tabs_fn_method_tabsbridgedengine_sync_finished: "_ns, aError));
}
- case 27: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_sync_id
+ case 38: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_sync_id
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>>;
return Some(CallHandler::CallAsync(uniffi_tabs_fn_method_tabsbridgedengine_sync_id, aGlobal, aArgs, "uniffi_tabs_fn_method_tabsbridgedengine_sync_id: "_ns, aError));
}
- case 28: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_sync_started
+ case 39: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_sync_started
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>>;
return Some(CallHandler::CallAsync(uniffi_tabs_fn_method_tabsbridgedengine_sync_started, aGlobal, aArgs, "uniffi_tabs_fn_method_tabsbridgedengine_sync_started: "_ns, aError));
}
- case 29: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_wipe
+ case 40: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_wipe
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>>;
return Some(CallHandler::CallAsync(uniffi_tabs_fn_method_tabsbridgedengine_wipe, aGlobal, aArgs, "uniffi_tabs_fn_method_tabsbridgedengine_wipe: "_ns, aError));
}
- case 30: { // tabs:uniffi_tabs_fn_constructor_tabsstore_new
+ case 41: { // tabs:uniffi_tabs_fn_clone_tabsstore
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kTabsTabsStorePointerType>, ScaffoldingObjectConverter<&kTabsTabsStorePointerType>>;
+ return Some(CallHandler::CallAsync(uniffi_tabs_fn_clone_tabsstore, aGlobal, aArgs, "uniffi_tabs_fn_clone_tabsstore: "_ns, aError));
+ }
+ case 42: { // tabs:uniffi_tabs_fn_constructor_tabsstore_new
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kTabsTabsStorePointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_tabs_fn_constructor_tabsstore_new, aGlobal, aArgs, "uniffi_tabs_fn_constructor_tabsstore_new: "_ns, aError));
}
- case 31: { // tabs:uniffi_tabs_fn_method_tabsstore_bridged_engine
+ case 43: { // tabs:uniffi_tabs_fn_method_tabsstore_bridged_engine
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>, ScaffoldingObjectConverter<&kTabsTabsStorePointerType>>;
return Some(CallHandler::CallAsync(uniffi_tabs_fn_method_tabsstore_bridged_engine, aGlobal, aArgs, "uniffi_tabs_fn_method_tabsstore_bridged_engine: "_ns, aError));
}
- case 32: { // tabs:uniffi_tabs_fn_method_tabsstore_get_all
+ case 44: { // tabs:uniffi_tabs_fn_method_tabsstore_get_all
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kTabsTabsStorePointerType>>;
return Some(CallHandler::CallAsync(uniffi_tabs_fn_method_tabsstore_get_all, aGlobal, aArgs, "uniffi_tabs_fn_method_tabsstore_get_all: "_ns, aError));
}
- case 33: { // tabs:uniffi_tabs_fn_method_tabsstore_register_with_sync_manager
+ case 45: { // tabs:uniffi_tabs_fn_method_tabsstore_register_with_sync_manager
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTabsTabsStorePointerType>>;
return Some(CallHandler::CallAsync(uniffi_tabs_fn_method_tabsstore_register_with_sync_manager, aGlobal, aArgs, "uniffi_tabs_fn_method_tabsstore_register_with_sync_manager: "_ns, aError));
}
- case 34: { // tabs:uniffi_tabs_fn_method_tabsstore_set_local_tabs
+ case 46: { // tabs:uniffi_tabs_fn_method_tabsstore_set_local_tabs
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTabsTabsStorePointerType>, ScaffoldingConverter<RustBuffer>>;
return Some(CallHandler::CallAsync(uniffi_tabs_fn_method_tabsstore_set_local_tabs, aGlobal, aArgs, "uniffi_tabs_fn_method_tabsstore_set_local_tabs: "_ns, aError));
}
@@ -247,177 +318,237 @@ Maybe<already_AddRefed<Promise>> UniFFICallAsync(const GlobalObject& aGlobal, ui
bool UniFFICallSync(const GlobalObject& aGlobal, uint64_t aId, const Sequence<ScaffoldingType>& aArgs, RootedDictionary<UniFFIScaffoldingCallResult>& aReturnValue, ErrorResult& aError) {
switch (aId) {
- case 0: { // remote_settings:uniffi_remote_settings_fn_constructor_remotesettings_new
+ case 0: { // relevancy:uniffi_relevancy_fn_clone_relevancystore
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kRelevancyRelevancyStorePointerType>, ScaffoldingObjectConverter<&kRelevancyRelevancyStorePointerType>>;
+ CallHandler::CallSync(uniffi_relevancy_fn_clone_relevancystore, aGlobal, aArgs, aReturnValue, "uniffi_relevancy_fn_clone_relevancystore: "_ns, aError);
+ return true;
+ }
+ case 1: { // relevancy:uniffi_relevancy_fn_constructor_relevancystore_new
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kRelevancyRelevancyStorePointerType>, ScaffoldingConverter<RustBuffer>>;
+ CallHandler::CallSync(uniffi_relevancy_fn_constructor_relevancystore_new, aGlobal, aArgs, aReturnValue, "uniffi_relevancy_fn_constructor_relevancystore_new: "_ns, aError);
+ return true;
+ }
+ case 2: { // relevancy:uniffi_relevancy_fn_method_relevancystore_calculate_metrics
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRelevancyRelevancyStorePointerType>>;
+ CallHandler::CallSync(uniffi_relevancy_fn_method_relevancystore_calculate_metrics, aGlobal, aArgs, aReturnValue, "uniffi_relevancy_fn_method_relevancystore_calculate_metrics: "_ns, aError);
+ return true;
+ }
+ case 3: { // relevancy:uniffi_relevancy_fn_method_relevancystore_ingest
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kRelevancyRelevancyStorePointerType>, ScaffoldingConverter<RustBuffer>>;
+ CallHandler::CallSync(uniffi_relevancy_fn_method_relevancystore_ingest, aGlobal, aArgs, aReturnValue, "uniffi_relevancy_fn_method_relevancystore_ingest: "_ns, aError);
+ return true;
+ }
+ case 4: { // relevancy:uniffi_relevancy_fn_method_relevancystore_user_interest_vector
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRelevancyRelevancyStorePointerType>>;
+ CallHandler::CallSync(uniffi_relevancy_fn_method_relevancystore_user_interest_vector, aGlobal, aArgs, aReturnValue, "uniffi_relevancy_fn_method_relevancystore_user_interest_vector: "_ns, aError);
+ return true;
+ }
+ case 5: { // remote_settings:uniffi_remote_settings_fn_clone_remotesettings
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kRemoteSettingsRemoteSettingsPointerType>, ScaffoldingObjectConverter<&kRemoteSettingsRemoteSettingsPointerType>>;
+ CallHandler::CallSync(uniffi_remote_settings_fn_clone_remotesettings, aGlobal, aArgs, aReturnValue, "uniffi_remote_settings_fn_clone_remotesettings: "_ns, aError);
+ return true;
+ }
+ case 6: { // remote_settings:uniffi_remote_settings_fn_constructor_remotesettings_new
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kRemoteSettingsRemoteSettingsPointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_remote_settings_fn_constructor_remotesettings_new, aGlobal, aArgs, aReturnValue, "uniffi_remote_settings_fn_constructor_remotesettings_new: "_ns, aError);
return true;
}
- case 1: { // remote_settings:uniffi_remote_settings_fn_method_remotesettings_download_attachment_to_path
+ case 7: { // remote_settings:uniffi_remote_settings_fn_method_remotesettings_download_attachment_to_path
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kRemoteSettingsRemoteSettingsPointerType>, ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_remote_settings_fn_method_remotesettings_download_attachment_to_path, aGlobal, aArgs, aReturnValue, "uniffi_remote_settings_fn_method_remotesettings_download_attachment_to_path: "_ns, aError);
return true;
}
- case 2: { // remote_settings:uniffi_remote_settings_fn_method_remotesettings_get_records
+ case 8: { // remote_settings:uniffi_remote_settings_fn_method_remotesettings_get_records
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRemoteSettingsRemoteSettingsPointerType>>;
CallHandler::CallSync(uniffi_remote_settings_fn_method_remotesettings_get_records, aGlobal, aArgs, aReturnValue, "uniffi_remote_settings_fn_method_remotesettings_get_records: "_ns, aError);
return true;
}
- case 3: { // remote_settings:uniffi_remote_settings_fn_method_remotesettings_get_records_since
+ case 9: { // remote_settings:uniffi_remote_settings_fn_method_remotesettings_get_records_since
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kRemoteSettingsRemoteSettingsPointerType>, ScaffoldingConverter<uint64_t>>;
CallHandler::CallSync(uniffi_remote_settings_fn_method_remotesettings_get_records_since, aGlobal, aArgs, aReturnValue, "uniffi_remote_settings_fn_method_remotesettings_get_records_since: "_ns, aError);
return true;
}
- case 4: { // suggest:uniffi_suggest_fn_constructor_suggeststore_new
+ case 10: { // suggest:uniffi_suggest_fn_clone_suggeststore
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kSuggestSuggestStorePointerType>, ScaffoldingObjectConverter<&kSuggestSuggestStorePointerType>>;
+ CallHandler::CallSync(uniffi_suggest_fn_clone_suggeststore, aGlobal, aArgs, aReturnValue, "uniffi_suggest_fn_clone_suggeststore: "_ns, aError);
+ return true;
+ }
+ case 11: { // suggest:uniffi_suggest_fn_constructor_suggeststore_new
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kSuggestSuggestStorePointerType>, ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_suggest_fn_constructor_suggeststore_new, aGlobal, aArgs, aReturnValue, "uniffi_suggest_fn_constructor_suggeststore_new: "_ns, aError);
return true;
}
- case 5: { // suggest:uniffi_suggest_fn_method_suggeststore_clear
+ case 12: { // suggest:uniffi_suggest_fn_method_suggeststore_clear
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kSuggestSuggestStorePointerType>>;
CallHandler::CallSync(uniffi_suggest_fn_method_suggeststore_clear, aGlobal, aArgs, aReturnValue, "uniffi_suggest_fn_method_suggeststore_clear: "_ns, aError);
return true;
}
- case 6: { // suggest:uniffi_suggest_fn_method_suggeststore_fetch_global_config
+ case 13: { // suggest:uniffi_suggest_fn_method_suggeststore_clear_dismissed_suggestions
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kSuggestSuggestStorePointerType>>;
+ CallHandler::CallSync(uniffi_suggest_fn_method_suggeststore_clear_dismissed_suggestions, aGlobal, aArgs, aReturnValue, "uniffi_suggest_fn_method_suggeststore_clear_dismissed_suggestions: "_ns, aError);
+ return true;
+ }
+ case 14: { // suggest:uniffi_suggest_fn_method_suggeststore_dismiss_suggestion
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kSuggestSuggestStorePointerType>, ScaffoldingConverter<RustBuffer>>;
+ CallHandler::CallSync(uniffi_suggest_fn_method_suggeststore_dismiss_suggestion, aGlobal, aArgs, aReturnValue, "uniffi_suggest_fn_method_suggeststore_dismiss_suggestion: "_ns, aError);
+ return true;
+ }
+ case 15: { // suggest:uniffi_suggest_fn_method_suggeststore_fetch_global_config
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kSuggestSuggestStorePointerType>>;
CallHandler::CallSync(uniffi_suggest_fn_method_suggeststore_fetch_global_config, aGlobal, aArgs, aReturnValue, "uniffi_suggest_fn_method_suggeststore_fetch_global_config: "_ns, aError);
return true;
}
- case 7: { // suggest:uniffi_suggest_fn_method_suggeststore_fetch_provider_config
+ case 16: { // suggest:uniffi_suggest_fn_method_suggeststore_fetch_provider_config
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kSuggestSuggestStorePointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_suggest_fn_method_suggeststore_fetch_provider_config, aGlobal, aArgs, aReturnValue, "uniffi_suggest_fn_method_suggeststore_fetch_provider_config: "_ns, aError);
return true;
}
- case 8: { // suggest:uniffi_suggest_fn_method_suggeststore_ingest
+ case 17: { // suggest:uniffi_suggest_fn_method_suggeststore_ingest
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kSuggestSuggestStorePointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_suggest_fn_method_suggeststore_ingest, aGlobal, aArgs, aReturnValue, "uniffi_suggest_fn_method_suggeststore_ingest: "_ns, aError);
return true;
}
- case 9: { // suggest:uniffi_suggest_fn_method_suggeststore_interrupt
+ case 18: { // suggest:uniffi_suggest_fn_method_suggeststore_interrupt
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kSuggestSuggestStorePointerType>>;
CallHandler::CallSync(uniffi_suggest_fn_method_suggeststore_interrupt, aGlobal, aArgs, aReturnValue, "uniffi_suggest_fn_method_suggeststore_interrupt: "_ns, aError);
return true;
}
- case 10: { // suggest:uniffi_suggest_fn_method_suggeststore_query
+ case 19: { // suggest:uniffi_suggest_fn_method_suggeststore_query
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kSuggestSuggestStorePointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_suggest_fn_method_suggeststore_query, aGlobal, aArgs, aReturnValue, "uniffi_suggest_fn_method_suggeststore_query: "_ns, aError);
return true;
}
- case 11: { // suggest:uniffi_suggest_fn_constructor_suggeststorebuilder_new
+ case 20: { // suggest:uniffi_suggest_fn_clone_suggeststorebuilder
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kSuggestSuggestStoreBuilderPointerType>, ScaffoldingObjectConverter<&kSuggestSuggestStoreBuilderPointerType>>;
+ CallHandler::CallSync(uniffi_suggest_fn_clone_suggeststorebuilder, aGlobal, aArgs, aReturnValue, "uniffi_suggest_fn_clone_suggeststorebuilder: "_ns, aError);
+ return true;
+ }
+ case 21: { // suggest:uniffi_suggest_fn_constructor_suggeststorebuilder_new
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kSuggestSuggestStoreBuilderPointerType>>;
CallHandler::CallSync(uniffi_suggest_fn_constructor_suggeststorebuilder_new, aGlobal, aArgs, aReturnValue, "uniffi_suggest_fn_constructor_suggeststorebuilder_new: "_ns, aError);
return true;
}
- case 12: { // suggest:uniffi_suggest_fn_method_suggeststorebuilder_build
+ case 22: { // suggest:uniffi_suggest_fn_method_suggeststorebuilder_build
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kSuggestSuggestStorePointerType>, ScaffoldingObjectConverter<&kSuggestSuggestStoreBuilderPointerType>>;
CallHandler::CallSync(uniffi_suggest_fn_method_suggeststorebuilder_build, aGlobal, aArgs, aReturnValue, "uniffi_suggest_fn_method_suggeststorebuilder_build: "_ns, aError);
return true;
}
- case 13: { // suggest:uniffi_suggest_fn_method_suggeststorebuilder_cache_path
+ case 23: { // suggest:uniffi_suggest_fn_method_suggeststorebuilder_cache_path
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kSuggestSuggestStoreBuilderPointerType>, ScaffoldingObjectConverter<&kSuggestSuggestStoreBuilderPointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_suggest_fn_method_suggeststorebuilder_cache_path, aGlobal, aArgs, aReturnValue, "uniffi_suggest_fn_method_suggeststorebuilder_cache_path: "_ns, aError);
return true;
}
- case 14: { // suggest:uniffi_suggest_fn_method_suggeststorebuilder_data_path
+ case 24: { // suggest:uniffi_suggest_fn_method_suggeststorebuilder_data_path
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kSuggestSuggestStoreBuilderPointerType>, ScaffoldingObjectConverter<&kSuggestSuggestStoreBuilderPointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_suggest_fn_method_suggeststorebuilder_data_path, aGlobal, aArgs, aReturnValue, "uniffi_suggest_fn_method_suggeststorebuilder_data_path: "_ns, aError);
return true;
}
- case 15: { // suggest:uniffi_suggest_fn_method_suggeststorebuilder_remote_settings_config
+ case 25: { // suggest:uniffi_suggest_fn_method_suggeststorebuilder_remote_settings_config
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kSuggestSuggestStoreBuilderPointerType>, ScaffoldingObjectConverter<&kSuggestSuggestStoreBuilderPointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_suggest_fn_method_suggeststorebuilder_remote_settings_config, aGlobal, aArgs, aReturnValue, "uniffi_suggest_fn_method_suggeststorebuilder_remote_settings_config: "_ns, aError);
return true;
}
- case 16: { // suggest:uniffi_suggest_fn_func_raw_suggestion_url_matches
+ case 26: { // suggest:uniffi_suggest_fn_func_raw_suggestion_url_matches
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int8_t>, ScaffoldingConverter<RustBuffer>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_suggest_fn_func_raw_suggestion_url_matches, aGlobal, aArgs, aReturnValue, "uniffi_suggest_fn_func_raw_suggestion_url_matches: "_ns, aError);
return true;
}
- case 17: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_apply
+ case 27: { // tabs:uniffi_tabs_fn_clone_tabsbridgedengine
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>>;
+ CallHandler::CallSync(uniffi_tabs_fn_clone_tabsbridgedengine, aGlobal, aArgs, aReturnValue, "uniffi_tabs_fn_clone_tabsbridgedengine: "_ns, aError);
+ return true;
+ }
+ case 28: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_apply
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>>;
CallHandler::CallSync(uniffi_tabs_fn_method_tabsbridgedengine_apply, aGlobal, aArgs, aReturnValue, "uniffi_tabs_fn_method_tabsbridgedengine_apply: "_ns, aError);
return true;
}
- case 18: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_ensure_current_sync_id
+ case 29: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_ensure_current_sync_id
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_tabs_fn_method_tabsbridgedengine_ensure_current_sync_id, aGlobal, aArgs, aReturnValue, "uniffi_tabs_fn_method_tabsbridgedengine_ensure_current_sync_id: "_ns, aError);
return true;
}
- case 19: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_last_sync
+ case 30: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_last_sync
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<int64_t>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>>;
CallHandler::CallSync(uniffi_tabs_fn_method_tabsbridgedengine_last_sync, aGlobal, aArgs, aReturnValue, "uniffi_tabs_fn_method_tabsbridgedengine_last_sync: "_ns, aError);
return true;
}
- case 20: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_prepare_for_sync
+ case 31: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_prepare_for_sync
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_tabs_fn_method_tabsbridgedengine_prepare_for_sync, aGlobal, aArgs, aReturnValue, "uniffi_tabs_fn_method_tabsbridgedengine_prepare_for_sync: "_ns, aError);
return true;
}
- case 21: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_reset
+ case 32: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_reset
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>>;
CallHandler::CallSync(uniffi_tabs_fn_method_tabsbridgedengine_reset, aGlobal, aArgs, aReturnValue, "uniffi_tabs_fn_method_tabsbridgedengine_reset: "_ns, aError);
return true;
}
- case 22: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_reset_sync_id
+ case 33: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_reset_sync_id
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>>;
CallHandler::CallSync(uniffi_tabs_fn_method_tabsbridgedengine_reset_sync_id, aGlobal, aArgs, aReturnValue, "uniffi_tabs_fn_method_tabsbridgedengine_reset_sync_id: "_ns, aError);
return true;
}
- case 23: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_set_last_sync
+ case 34: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_set_last_sync
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>, ScaffoldingConverter<int64_t>>;
CallHandler::CallSync(uniffi_tabs_fn_method_tabsbridgedengine_set_last_sync, aGlobal, aArgs, aReturnValue, "uniffi_tabs_fn_method_tabsbridgedengine_set_last_sync: "_ns, aError);
return true;
}
- case 24: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_set_uploaded
+ case 35: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_set_uploaded
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>, ScaffoldingConverter<int64_t>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_tabs_fn_method_tabsbridgedengine_set_uploaded, aGlobal, aArgs, aReturnValue, "uniffi_tabs_fn_method_tabsbridgedengine_set_uploaded: "_ns, aError);
return true;
}
- case 25: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_store_incoming
+ case 36: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_store_incoming
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_tabs_fn_method_tabsbridgedengine_store_incoming, aGlobal, aArgs, aReturnValue, "uniffi_tabs_fn_method_tabsbridgedengine_store_incoming: "_ns, aError);
return true;
}
- case 26: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_sync_finished
+ case 37: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_sync_finished
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>>;
CallHandler::CallSync(uniffi_tabs_fn_method_tabsbridgedengine_sync_finished, aGlobal, aArgs, aReturnValue, "uniffi_tabs_fn_method_tabsbridgedengine_sync_finished: "_ns, aError);
return true;
}
- case 27: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_sync_id
+ case 38: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_sync_id
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>>;
CallHandler::CallSync(uniffi_tabs_fn_method_tabsbridgedengine_sync_id, aGlobal, aArgs, aReturnValue, "uniffi_tabs_fn_method_tabsbridgedengine_sync_id: "_ns, aError);
return true;
}
- case 28: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_sync_started
+ case 39: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_sync_started
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>>;
CallHandler::CallSync(uniffi_tabs_fn_method_tabsbridgedengine_sync_started, aGlobal, aArgs, aReturnValue, "uniffi_tabs_fn_method_tabsbridgedengine_sync_started: "_ns, aError);
return true;
}
- case 29: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_wipe
+ case 40: { // tabs:uniffi_tabs_fn_method_tabsbridgedengine_wipe
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>>;
CallHandler::CallSync(uniffi_tabs_fn_method_tabsbridgedengine_wipe, aGlobal, aArgs, aReturnValue, "uniffi_tabs_fn_method_tabsbridgedengine_wipe: "_ns, aError);
return true;
}
- case 30: { // tabs:uniffi_tabs_fn_constructor_tabsstore_new
+ case 41: { // tabs:uniffi_tabs_fn_clone_tabsstore
+ using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kTabsTabsStorePointerType>, ScaffoldingObjectConverter<&kTabsTabsStorePointerType>>;
+ CallHandler::CallSync(uniffi_tabs_fn_clone_tabsstore, aGlobal, aArgs, aReturnValue, "uniffi_tabs_fn_clone_tabsstore: "_ns, aError);
+ return true;
+ }
+ case 42: { // tabs:uniffi_tabs_fn_constructor_tabsstore_new
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kTabsTabsStorePointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_tabs_fn_constructor_tabsstore_new, aGlobal, aArgs, aReturnValue, "uniffi_tabs_fn_constructor_tabsstore_new: "_ns, aError);
return true;
}
- case 31: { // tabs:uniffi_tabs_fn_method_tabsstore_bridged_engine
+ case 43: { // tabs:uniffi_tabs_fn_method_tabsstore_bridged_engine
using CallHandler = ScaffoldingCallHandler<ScaffoldingObjectConverter<&kTabsTabsBridgedEnginePointerType>, ScaffoldingObjectConverter<&kTabsTabsStorePointerType>>;
CallHandler::CallSync(uniffi_tabs_fn_method_tabsstore_bridged_engine, aGlobal, aArgs, aReturnValue, "uniffi_tabs_fn_method_tabsstore_bridged_engine: "_ns, aError);
return true;
}
- case 32: { // tabs:uniffi_tabs_fn_method_tabsstore_get_all
+ case 44: { // tabs:uniffi_tabs_fn_method_tabsstore_get_all
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<RustBuffer>, ScaffoldingObjectConverter<&kTabsTabsStorePointerType>>;
CallHandler::CallSync(uniffi_tabs_fn_method_tabsstore_get_all, aGlobal, aArgs, aReturnValue, "uniffi_tabs_fn_method_tabsstore_get_all: "_ns, aError);
return true;
}
- case 33: { // tabs:uniffi_tabs_fn_method_tabsstore_register_with_sync_manager
+ case 45: { // tabs:uniffi_tabs_fn_method_tabsstore_register_with_sync_manager
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTabsTabsStorePointerType>>;
CallHandler::CallSync(uniffi_tabs_fn_method_tabsstore_register_with_sync_manager, aGlobal, aArgs, aReturnValue, "uniffi_tabs_fn_method_tabsstore_register_with_sync_manager: "_ns, aError);
return true;
}
- case 34: { // tabs:uniffi_tabs_fn_method_tabsstore_set_local_tabs
+ case 46: { // tabs:uniffi_tabs_fn_method_tabsstore_set_local_tabs
using CallHandler = ScaffoldingCallHandler<ScaffoldingConverter<void>, ScaffoldingObjectConverter<&kTabsTabsStorePointerType>, ScaffoldingConverter<RustBuffer>>;
CallHandler::CallSync(uniffi_tabs_fn_method_tabsstore_set_local_tabs, aGlobal, aArgs, aReturnValue, "uniffi_tabs_fn_method_tabsstore_set_local_tabs: "_ns, aError);
return true;
@@ -429,23 +560,27 @@ bool UniFFICallSync(const GlobalObject& aGlobal, uint64_t aId, const Sequence<Sc
Maybe<already_AddRefed<UniFFIPointer>> UniFFIReadPointer(const GlobalObject& aGlobal, uint64_t aId, const ArrayBuffer& aArrayBuff, long aPosition, ErrorResult& aError) {
const UniFFIPointerType* type;
switch (aId) {
- case 0: { // remote_settings:RemoteSettings
+ case 0: { // relevancy:RelevancyStore
+ type = &kRelevancyRelevancyStorePointerType;
+ break;
+ }
+ case 1: { // remote_settings:RemoteSettings
type = &kRemoteSettingsRemoteSettingsPointerType;
break;
}
- case 1: { // suggest:SuggestStore
+ case 2: { // suggest:SuggestStore
type = &kSuggestSuggestStorePointerType;
break;
}
- case 2: { // suggest:SuggestStoreBuilder
+ case 3: { // suggest:SuggestStoreBuilder
type = &kSuggestSuggestStoreBuilderPointerType;
break;
}
- case 3: { // tabs:TabsBridgedEngine
+ case 4: { // tabs:TabsBridgedEngine
type = &kTabsTabsBridgedEnginePointerType;
break;
}
- case 4: { // tabs:TabsStore
+ case 5: { // tabs:TabsStore
type = &kTabsTabsStorePointerType;
break;
}
@@ -458,23 +593,27 @@ Maybe<already_AddRefed<UniFFIPointer>> UniFFIReadPointer(const GlobalObject& aGl
bool UniFFIWritePointer(const GlobalObject& aGlobal, uint64_t aId, const UniFFIPointer& aPtr, const ArrayBuffer& aArrayBuff, long aPosition, ErrorResult& aError) {
const UniFFIPointerType* type;
switch (aId) {
- case 0: { // remote_settings:RemoteSettings
+ case 0: { // relevancy:RelevancyStore
+ type = &kRelevancyRelevancyStorePointerType;
+ break;
+ }
+ case 1: { // remote_settings:RemoteSettings
type = &kRemoteSettingsRemoteSettingsPointerType;
break;
}
- case 1: { // suggest:SuggestStore
+ case 2: { // suggest:SuggestStore
type = &kSuggestSuggestStorePointerType;
break;
}
- case 2: { // suggest:SuggestStoreBuilder
+ case 3: { // suggest:SuggestStoreBuilder
type = &kSuggestSuggestStoreBuilderPointerType;
break;
}
- case 3: { // tabs:TabsBridgedEngine
+ case 4: { // tabs:TabsBridgedEngine
type = &kTabsTabsBridgedEnginePointerType;
break;
}
- case 4: { // tabs:TabsStore
+ case 5: { // tabs:TabsStore
type = &kTabsTabsStorePointerType;
break;
}
diff --git a/toolkit/components/uniffi-js/UniFFIPointer.cpp b/toolkit/components/uniffi-js/UniFFIPointer.cpp
index c3a1eba93d..8e79bac0db 100644
--- a/toolkit/components/uniffi-js/UniFFIPointer.cpp
+++ b/toolkit/components/uniffi-js/UniFFIPointer.cpp
@@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsPrintfCString.h"
+#include "js/GCAPI.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/dom/UniFFIPointer.h"
#include "mozilla/dom/UniFFIBinding.h"
@@ -81,8 +82,14 @@ void UniFFIPointer::Write(const ArrayBuffer& aArrayBuff, uint32_t aPosition,
// in Rust and Read(), a u64 is read as BigEndian and then converted to
// a pointer we do the reverse here
const auto& data_ptr = aData.Subspan(aPosition, 8);
+ // The hazard checker assumes all calls to a function pointer may result
+ // in a GC call and `ClonePtr` calls mType->clone. However, we know that
+ // mtype->clone won't make a GC call since it's essentially just a call
+ // to Rust's `Arc::clone()`. Use AutoSuppressGCAnalysis to tell the
+ // hazard checker to ignore the call.
+ JS::AutoSuppressGCAnalysis suppress;
mozilla::BigEndian::writeUint64(data_ptr.Elements(),
- (uint64_t)GetPtr());
+ (uint64_t)ClonePtr());
return true;
})) {
aError.ThrowRangeError("position is out of range");
@@ -100,10 +107,14 @@ JSObject* UniFFIPointer::WrapObject(JSContext* aCx,
return dom::UniFFIPointer_Binding::Wrap(aCx, this, aGivenProto);
}
-void* UniFFIPointer::GetPtr() const {
+void* UniFFIPointer::ClonePtr() const {
MOZ_LOG(sUniFFIPointerLogger, LogLevel::Info,
- ("[UniFFI] Getting raw pointer"));
- return this->mPtr;
+ ("[UniFFI] Cloning raw pointer"));
+ RustCallStatus status{};
+ auto cloned = this->mType->clone(this->mPtr, &status);
+ MOZ_DIAGNOSTIC_ASSERT(status.code == RUST_CALL_SUCCESS,
+ "UniFFI clone call returned a non-success result");
+ return cloned;
}
bool UniFFIPointer::IsSamePtrType(const UniFFIPointerType* aType) const {
diff --git a/toolkit/components/uniffi-js/UniFFIPointer.h b/toolkit/components/uniffi-js/UniFFIPointer.h
index 59acc0ca66..f9a95c309e 100644
--- a/toolkit/components/uniffi-js/UniFFIPointer.h
+++ b/toolkit/components/uniffi-js/UniFFIPointer.h
@@ -36,15 +36,13 @@ class UniFFIPointer final : public nsISupports, public nsWrapperCache {
nsISupports* GetParentObject() { return nullptr; }
/**
- * returns the raw pointer `UniFFIPointer` holds
- * This is safe because:
- * - The pointer was allocated in Rust as a reference counted `Arc<T>`
- * - Rust cloned the pointer without destructing it when passed into C++
- * - Eventually, when the destructor of `UniFFIPointer` runs, we return
- * ownership to Rust, which then decrements the count and deallocates the
- * memory the pointer points to.
+ * Clone the raw pointer that `UniFFIPointer` holds
+ *
+ * Use this when lowering the pointer to pass it across the FFI, for example:
+ * - When calling a method
+ * - When passing the object as an argument to a function
*/
- void* GetPtr() const;
+ void* ClonePtr() const;
/**
* Returns true if the pointer type `this` holds is the same as the argument
diff --git a/toolkit/components/uniffi-js/UniFFIPointerType.h b/toolkit/components/uniffi-js/UniFFIPointerType.h
index 7236e50cb7..25294cfc9d 100644
--- a/toolkit/components/uniffi-js/UniFFIPointerType.h
+++ b/toolkit/components/uniffi-js/UniFFIPointerType.h
@@ -21,7 +21,9 @@ namespace mozilla::uniffi {
**/
struct UniFFIPointerType {
nsLiteralCString typeName;
- // The Rust destructor for the pointer, this gives back ownership to Rust
+ // Rust FFI function to clone for the pointer
+ void* (*clone)(void*, RustCallStatus*);
+ // Rust FFI function to destroy for the pointer
void (*destructor)(void*, RustCallStatus*);
};
} // namespace mozilla::uniffi
diff --git a/toolkit/components/uniffi-js/UniFFIRust.h b/toolkit/components/uniffi-js/UniFFIRust.h
index 2907af8f51..608b8063ae 100644
--- a/toolkit/components/uniffi-js/UniFFIRust.h
+++ b/toolkit/components/uniffi-js/UniFFIRust.h
@@ -28,8 +28,8 @@ constexpr int8_t CALLBACK_INTERFACE_UNEXPECTED_ERROR = 2;
// structs/functions from UniFFI
extern "C" {
struct RustBuffer {
- int32_t capacity;
- int32_t len;
+ uint64_t capacity;
+ uint64_t len;
uint8_t* data;
};
@@ -42,7 +42,7 @@ typedef int (*ForeignCallback)(uint64_t handle, uint32_t method,
const uint8_t* argsData, int32_t argsLen,
RustBuffer* buf_ptr);
-RustBuffer uniffi_rustbuffer_alloc(int32_t size, RustCallStatus* call_status);
+RustBuffer uniffi_rustbuffer_alloc(uint64_t size, RustCallStatus* call_status);
void uniffi_rustbuffer_free(RustBuffer buf, RustCallStatus* call_status);
}
diff --git a/toolkit/components/url-classifier/nsUrlClassifierUtils.cpp b/toolkit/components/url-classifier/nsUrlClassifierUtils.cpp
index da5e9cd451..6a329830d6 100644
--- a/toolkit/components/url-classifier/nsUrlClassifierUtils.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierUtils.cpp
@@ -293,7 +293,12 @@ nsUrlClassifierUtils::GetKeyForURI(nsIURI* uri, nsACString& _retval) {
rv = innerURI->GetQuery(query);
NS_ENSURE_SUCCESS(rv, rv);
- _retval.AppendPrintf("?%s", query.get());
+ // We have to canonicalize the query too based on
+ // https://developers.google.com/safe-browsing/v4/urls-hashing?hl=en#canonicalization
+ rv = CanonicalizeQuery(query, temp);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ _retval.Append(temp);
}
return NS_OK;
@@ -917,6 +922,25 @@ nsresult nsUrlClassifierUtils::CanonicalizePath(const nsACString& path,
return NS_OK;
}
+nsresult nsUrlClassifierUtils::CanonicalizeQuery(const nsACString& query,
+ nsACString& _retval) {
+ _retval.Truncate();
+ _retval.Append('?');
+
+ // Unescape the query
+ nsAutoCString unescaped;
+ if (!NS_UnescapeURL(PromiseFlatCString(query).get(),
+ PromiseFlatCString(query).Length(), 0, unescaped)) {
+ unescaped.Assign(query);
+ }
+
+ // slash folding does not apply to the query parameters, but we need to
+ // percent-escape all characters that are <= ASCII 32, >= 127, "#", or "%"
+ SpecialEncode(unescaped, false, _retval);
+
+ return NS_OK;
+}
+
void nsUrlClassifierUtils::CleanupHostname(const nsACString& hostname,
nsACString& _retval) {
_retval.Truncate();
diff --git a/toolkit/components/url-classifier/nsUrlClassifierUtils.h b/toolkit/components/url-classifier/nsUrlClassifierUtils.h
index 5ff9d97fdc..bcced1a76a 100644
--- a/toolkit/components/url-classifier/nsUrlClassifierUtils.h
+++ b/toolkit/components/url-classifier/nsUrlClassifierUtils.h
@@ -29,6 +29,8 @@ class nsUrlClassifierUtils final : public nsIUrlClassifierUtils,
nsACString& _retval);
nsresult CanonicalizePath(const nsACString& url, nsACString& _retval);
+ nsresult CanonicalizeQuery(const nsACString& query, nsACString& _retval);
+
// This function will encode all "special" characters in typical url encoding,
// that is %hh where h is a valid hex digit. The characters which are encoded
// by this function are any ascii characters under 32(control characters and
diff --git a/toolkit/components/url-classifier/tests/mochitest/classifierCommon.js b/toolkit/components/url-classifier/tests/mochitest/classifierCommon.js
index 00a4e8d08b..bf3d35047f 100644
--- a/toolkit/components/url-classifier/tests/mochitest/classifierCommon.js
+++ b/toolkit/components/url-classifier/tests/mochitest/classifierCommon.js
@@ -63,7 +63,7 @@ function doReload() {
}
}
-// SafeBrowsing.jsm is initialized after mozEntries are added. Add observer
+// SafeBrowsing.sys.mjs is initialized after mozEntries are added. Add observer
// to receive "finished" event. For the case when this function is called
// after the event had already been notified, we lookup entries to see if
// they are already added to database.
diff --git a/toolkit/components/url-classifier/tests/mochitest/classifierHelper.js b/toolkit/components/url-classifier/tests/mochitest/classifierHelper.js
index b07bfc7fc5..55870b462c 100644
--- a/toolkit/components/url-classifier/tests/mochitest/classifierHelper.js
+++ b/toolkit/components/url-classifier/tests/mochitest/classifierHelper.js
@@ -24,7 +24,7 @@ classifierHelper._updatesToCleanup = [];
classifierHelper._initsCB = [];
-// This function return a Promise, promise is resolved when SafeBrowsing.jsm
+// This function return a Promise, promise is resolved when SafeBrowsing.sys.mjs
// is initialized.
classifierHelper.waitForInit = function () {
return new Promise(function (resolve) {
diff --git a/toolkit/components/url-classifier/tests/unit/test_canonicalization.js b/toolkit/components/url-classifier/tests/unit/test_canonicalization.js
index e26bb5d84a..a9fdf71315 100644
--- a/toolkit/components/url-classifier/tests/unit/test_canonicalization.js
+++ b/toolkit/components/url-classifier/tests/unit/test_canonicalization.js
@@ -80,4 +80,14 @@ function run_test() {
canonicalize("http://host.com//twoslashes?more//slashes"),
"http://host.com/twoslashes?more//slashes"
);
+ equal(
+ canonicalize("http://host.com/path?query%3Awith%3Acolons"),
+ "http://host.com/path?query:with:colons"
+ );
+ equal(
+ canonicalize(
+ "https://wiki.mozilla.org/index.php?title=MozillaWiki%3AHelp&action=history"
+ ),
+ "https://wiki.mozilla.org/index.php?title=MozillaWiki:Help&action=history"
+ );
}
diff --git a/toolkit/components/utils/ClientEnvironment.sys.mjs b/toolkit/components/utils/ClientEnvironment.sys.mjs
index 1c6bbfbaba..64fb6da9e1 100644
--- a/toolkit/components/utils/ClientEnvironment.sys.mjs
+++ b/toolkit/components/utils/ClientEnvironment.sys.mjs
@@ -76,7 +76,7 @@ export class ClientEnvironmentBase {
}
return new Proxy(target, {
- get(target, prop, receiver) {
+ get(target, prop) {
if (prop == "main") {
return target.main;
}
diff --git a/toolkit/components/utils/SimpleServices.sys.mjs b/toolkit/components/utils/SimpleServices.sys.mjs
index a69388435c..7c872cc7a6 100644
--- a/toolkit/components/utils/SimpleServices.sys.mjs
+++ b/toolkit/components/utils/SimpleServices.sys.mjs
@@ -103,7 +103,7 @@ AddonLocalizationConverter.prototype = {
this.listener = aListener;
},
- onStartRequest(aRequest) {
+ onStartRequest() {
this.parts = [];
this.decoder = new TextDecoder();
},
diff --git a/toolkit/components/viewsource/content/viewSourceUtils.js b/toolkit/components/viewsource/content/viewSourceUtils.js
index 69d3eff287..bc85bb3c1b 100644
--- a/toolkit/components/viewsource/content/viewSourceUtils.js
+++ b/toolkit/components/viewsource/content/viewSourceUtils.js
@@ -62,8 +62,8 @@ var gViewSourceUtils = {
}
// Try existing browsers first.
let browserWin = Services.wm.getMostRecentWindow("navigator:browser");
- if (browserWin && browserWin.BrowserViewSourceOfDocument) {
- browserWin.BrowserViewSourceOfDocument(aArgs);
+ if (browserWin && browserWin.BrowserCommands.viewSourceOfDocument) {
+ browserWin.BrowserCommands.viewSourceOfDocument(aArgs);
return;
}
// No browser window created yet, try to create one.
diff --git a/toolkit/components/viewsource/test/browser/browser_contextmenu.js b/toolkit/components/viewsource/test/browser/browser_contextmenu.js
index afc8ab8c84..12244f7589 100644
--- a/toolkit/components/viewsource/test/browser/browser_contextmenu.js
+++ b/toolkit/components/viewsource/test/browser/browser_contextmenu.js
@@ -42,7 +42,7 @@ async function onViewSourceWindowOpen(aWindow) {
gCopyEmailMenuItem = aWindow.document.getElementById("context-copyemail");
let browser = gBrowser.selectedBrowser;
- await SpecialPowers.spawn(browser, [], async function (arg) {
+ await SpecialPowers.spawn(browser, [], async function () {
let tags = content.document.querySelectorAll("a[href]");
Assert.equal(
tags[0].href,
diff --git a/toolkit/components/viewsource/test/browser/browser_viewsource_newwindow.js b/toolkit/components/viewsource/test/browser/browser_viewsource_newwindow.js
index 8a38d94719..e141e94150 100644
--- a/toolkit/components/viewsource/test/browser/browser_viewsource_newwindow.js
+++ b/toolkit/components/viewsource/test/browser/browser_viewsource_newwindow.js
@@ -33,7 +33,7 @@ add_task(async function () {
},
async browser => {
let winPromise = waitForNewViewSourceWindow("view-source:" + PAGE);
- BrowserViewSource(browser);
+ BrowserCommands.viewSource(browser);
let win = await winPromise;
ok(win, "View Source opened up in a new window.");
@@ -58,14 +58,14 @@ add_task(async function () {
url: "data:text/html," + source,
gBrowser,
},
- async browser => {
+ async () => {
let winPromise = waitForNewViewSourceWindow(
"view-source:data:text/html;charset=utf-8,%3Cp%3E%EF%B7%90test%EF%B7%AF%3C%2Fp%3E"
);
await SpecialPowers.spawn(
gBrowser.selectedBrowser,
[],
- async function (arg) {
+ async function () {
let element = content.document.querySelector("p");
content.getSelection().selectAllChildren(element);
}
diff --git a/toolkit/components/viewsource/test/browser/head.js b/toolkit/components/viewsource/test/browser/head.js
index a75c6a8658..083c169c0a 100644
--- a/toolkit/components/viewsource/test/browser/head.js
+++ b/toolkit/components/viewsource/test/browser/head.js
@@ -43,7 +43,7 @@ async function waitForViewSourceTab(open) {
*/
function openViewSourceForBrowser(browser) {
return waitForViewSourceTab(() => {
- window.BrowserViewSource(browser);
+ window.BrowserCommands.viewSource(browser);
});
}
diff --git a/toolkit/components/windowcreator/test/320x240.ogv b/toolkit/components/windowcreator/test/320x240.ogv
deleted file mode 100644
index 093158432a..0000000000
--- a/toolkit/components/windowcreator/test/320x240.ogv
+++ /dev/null
Binary files differ
diff --git a/toolkit/components/windowcreator/test/320x240.webm b/toolkit/components/windowcreator/test/320x240.webm
new file mode 100644
index 0000000000..16ecdbf688
--- /dev/null
+++ b/toolkit/components/windowcreator/test/320x240.webm
Binary files differ
diff --git a/toolkit/components/windowcreator/test/bug449141_page.html b/toolkit/components/windowcreator/test/bug449141_page.html
index 3b8e3f550a..4c8e46dd10 100644
--- a/toolkit/components/windowcreator/test/bug449141_page.html
+++ b/toolkit/components/windowcreator/test/bug449141_page.html
@@ -6,6 +6,6 @@
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
- <video src='320x240.ogv'></video>
+ <video src='320x240.webm'></video>
</body>
</html>
diff --git a/toolkit/components/windowcreator/test/chrome.toml b/toolkit/components/windowcreator/test/chrome.toml
index 0fdc8250e0..8afd9acbe5 100644
--- a/toolkit/components/windowcreator/test/chrome.toml
+++ b/toolkit/components/windowcreator/test/chrome.toml
@@ -1,6 +1,6 @@
[DEFAULT]
support-files = [
- "320x240.ogv",
+ "320x240.webm",
"bug449141_page.html",
"bug1170334_iframe.xml",
"bug1170334_style.css",
diff --git a/toolkit/components/windowcreator/test/test_bug449141.html b/toolkit/components/windowcreator/test/test_bug449141.html
index f178689599..24963295a0 100644
--- a/toolkit/components/windowcreator/test/test_bug449141.html
+++ b/toolkit/components/windowcreator/test/test_bug449141.html
@@ -35,7 +35,7 @@ var progressListener = {
onProgressChange() {
/* Ignore progress callback */
},
- onStateChange(aProgress, aRequest, aStateFlag, aStatus) {
+ onStateChange(aProgress, aRequest, aStateFlag) {
if (aStateFlag & STATE_STOP) {
var dirExists = false;
var videoExists = false;
@@ -43,7 +43,7 @@ var progressListener = {
var videoFile = getTempDir();
videoFile.append(this.dirName);
dirExists = videoFile.exists();
- videoFile.append("320x240.ogv");
+ videoFile.append("320x240.webm");
videoExists = videoFile.exists();
this.folder.remove(true);
this.file.remove(false);
diff --git a/toolkit/components/windowwatcher/nsWindowWatcher.cpp b/toolkit/components/windowwatcher/nsWindowWatcher.cpp
index 209af157a3..b482214d31 100644
--- a/toolkit/components/windowwatcher/nsWindowWatcher.cpp
+++ b/toolkit/components/windowwatcher/nsWindowWatcher.cpp
@@ -1431,7 +1431,7 @@ nsresult nsWindowWatcher::OpenWindowInternal(
}
if (parentWidget &&
((!newWindowShouldBeModal && parentIsModal) || isAppModal)) {
- parentWidget->SetFakeModal(true);
+ parentWidget->SetModal(true);
} else {
// Reset popup state while opening a modal dialog, and firing
// events about the dialog, to prevent the current state from
diff --git a/toolkit/components/windowwatcher/test/browser_new_content_window_chromeflags.js b/toolkit/components/windowwatcher/test/browser_new_content_window_chromeflags.js
index 4eff3b46f0..d14175c088 100644
--- a/toolkit/components/windowwatcher/test/browser_new_content_window_chromeflags.js
+++ b/toolkit/components/windowwatcher/test/browser_new_content_window_chromeflags.js
@@ -134,7 +134,7 @@ function getContentChromeFlags(win) {
* @param chromeFlags (int)
* Some chromeFlags to check.
*/
-function assertContentFlags(chromeFlags, isPopup) {
+function assertContentFlags(chromeFlags) {
for (let feature in DISALLOWED) {
let flag = DISALLOWED[feature].flag;
Assert.ok(flag, "Expected flag to be a non-zeroish value");
@@ -183,7 +183,7 @@ add_task(async function test_disallowed_flags() {
gBrowser,
url: SCRIPT_PAGE,
},
- async function (browser) {
+ async function () {
let win = await newWinPromise;
let parentChromeFlags = getParentChromeFlags(win);
assertContentFlags(parentChromeFlags);
@@ -230,7 +230,7 @@ add_task(async function test_disallowed_flags() {
gBrowser,
url: SCRIPT_PAGE_FOR_CHROME_ALL,
},
- async function (browser) {
+ async function () {
let win = await newWinPromise;
let parentChromeFlags = getParentChromeFlags(win);
Assert.notEqual(
@@ -257,7 +257,7 @@ add_task(async function test_scrollbars_flag() {
gBrowser,
url: SCRIPT_PAGE,
},
- async function (browser) {
+ async function () {
let win = await newWinPromise;
let parentChromeFlags = getParentChromeFlags(win);
diff --git a/toolkit/components/windowwatcher/test/browser_new_remote_window_flags.js b/toolkit/components/windowwatcher/test/browser_new_remote_window_flags.js
index 06a15d123c..ae2bdc41b3 100644
--- a/toolkit/components/windowwatcher/test/browser_new_remote_window_flags.js
+++ b/toolkit/components/windowwatcher/test/browser_new_remote_window_flags.js
@@ -61,7 +61,7 @@ add_task(async function test_new_remote_window_flags_window_open() {
gBrowser,
url: SCRIPT_PAGE,
},
- async function (browser) {
+ async function () {
let win = await newWinPromise;
assertFlags(win);
await BrowserTestUtils.closeWindow(win);
diff --git a/toolkit/components/windowwatcher/test/browser_new_sized_window.js b/toolkit/components/windowwatcher/test/browser_new_sized_window.js
index 0a1e63a8c8..acb73f027e 100644
--- a/toolkit/components/windowwatcher/test/browser_new_sized_window.js
+++ b/toolkit/components/windowwatcher/test/browser_new_sized_window.js
@@ -33,7 +33,7 @@ function test_dimensions({ width, height }) {
gBrowser,
url: SCRIPT_PAGE,
},
- async function (browser) {
+ async function () {
let win = await newWinPromise;
let rect = win.gBrowser.selectedBrowser.getBoundingClientRect();
diff --git a/toolkit/components/windowwatcher/test/browser_non_popup_from_popup.js b/toolkit/components/windowwatcher/test/browser_non_popup_from_popup.js
index 9c905bacc1..b6e7f2b131 100644
--- a/toolkit/components/windowwatcher/test/browser_non_popup_from_popup.js
+++ b/toolkit/components/windowwatcher/test/browser_non_popup_from_popup.js
@@ -28,7 +28,7 @@ add_task(async function test_non_popup_from_popup() {
gBrowser,
url: BLANK_PAGE,
},
- async function (browser) {
+ async function () {
// Wait for a popup opened by POPUP_OPENER.
const newPopupPromise = BrowserTestUtils.waitForNewWindow();
diff --git a/toolkit/components/windowwatcher/test/head.js b/toolkit/components/windowwatcher/test/head.js
index f72344bcd0..d126856244 100644
--- a/toolkit/components/windowwatcher/test/head.js
+++ b/toolkit/components/windowwatcher/test/head.js
@@ -125,7 +125,7 @@ async function testPopupPatterns(nonPopup) {
gBrowser,
url: BLANK_PAGE,
},
- async function (browser) {
+ async function () {
const newWinPromise = BrowserTestUtils.waitForNewWindow();
BrowserTestUtils.startLoadingURIString(gBrowser, SCRIPT_PAGE);
@@ -159,7 +159,7 @@ async function testPopupPatterns(nonPopup) {
gBrowser,
url: BLANK_PAGE,
},
- async function (browser) {
+ async function () {
const newTabPromise = BrowserTestUtils.waitForNewTab(
gBrowser,
OPEN_PAGE
diff --git a/toolkit/components/workerloader/tests/worker_test_loading.js b/toolkit/components/workerloader/tests/worker_test_loading.js
index 551c56d7d3..d13fbed684 100644
--- a/toolkit/components/workerloader/tests/worker_test_loading.js
+++ b/toolkit/components/workerloader/tests/worker_test_loading.js
@@ -152,7 +152,7 @@ add_test(function test_load() {
is(J.foo, "foo", "Module J exported value foo");
});
-self.onmessage = function (message) {
+self.onmessage = function () {
for (let test of tests) {
info("Entering " + test.name);
try {
diff --git a/toolkit/components/xulstore/XULStore.sys.mjs b/toolkit/components/xulstore/XULStore.sys.mjs
index eb1965ec6d..f84cd57712 100644
--- a/toolkit/components/xulstore/XULStore.sys.mjs
+++ b/toolkit/components/xulstore/XULStore.sys.mjs
@@ -89,7 +89,7 @@ XULStore.prototype = {
this.readFile();
},
- observe(subject, topic, data) {
+ observe(subject, topic) {
this.writeFile();
if (topic == "profile-before-change") {
this._saveAllowed = false;
@@ -153,7 +153,7 @@ XULStore.prototype = {
}
const uri = node.ownerDocument.documentURI;
- const value = node.getAttribute(attr);
+ const value = node.getAttribute(attr) || "";
if (node.localName == "window") {
this.log("Persisting attributes to windows is handled by AppWindow.");
diff --git a/toolkit/components/xulstore/nsIXULStore.idl b/toolkit/components/xulstore/nsIXULStore.idl
index 832c07dc90..488a99c50b 100644
--- a/toolkit/components/xulstore/nsIXULStore.idl
+++ b/toolkit/components/xulstore/nsIXULStore.idl
@@ -46,7 +46,7 @@ interface nsIXULStore: nsISupports
* @param id - identifier of the node
* @param attr - attribute
*/
- bool hasValue(in AString doc, in AString id, in AString attr);
+ boolean hasValue(in AString doc, in AString id, in AString attr);
/**
* Retrieves a value in the store, or an empty string if it does not exist.