From 2aa4a82499d4becd2284cdb482213d541b8804dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 16:29:10 +0200 Subject: Adding upstream version 86.0.1. Signed-off-by: Daniel Baumann --- browser/extensions/doh-rollout/manifest.json | 15 + browser/extensions/doh-rollout/moz.build | 14 + browser/extensions/formautofill/.eslintrc.js | 100 + browser/extensions/formautofill/FormAutofill.jsm | 156 + .../extensions/formautofill/FormAutofillChild.jsm | 198 + .../formautofill/FormAutofillContent.jsm | 915 +++++ .../formautofill/FormAutofillDoorhanger.jsm | 446 +++ .../formautofill/FormAutofillHandler.jsm | 1478 +++++++ .../formautofill/FormAutofillHeuristics.jsm | 1280 ++++++ .../formautofill/FormAutofillNameUtils.jsm | 410 ++ .../extensions/formautofill/FormAutofillParent.jsm | 878 +++++ .../formautofill/FormAutofillPreferences.jsm | 403 ++ .../formautofill/FormAutofillStorage.jsm | 2203 +++++++++++ .../extensions/formautofill/FormAutofillSync.jsm | 391 ++ .../extensions/formautofill/FormAutofillUtils.jsm | 1125 ++++++ .../formautofill/ProfileAutoCompleteResult.jsm | 516 +++ .../addressmetadata/addressReferences.js | 2456 ++++++++++++ .../addressmetadata/addressReferencesExt.js | 29 + browser/extensions/formautofill/api.js | 235 ++ browser/extensions/formautofill/background.js | 15 + .../formautofill/content/autofillEditForms.js | 648 +++ .../formautofill/content/customElements.js | 423 ++ .../formautofill/content/editAddress.xhtml | 126 + .../formautofill/content/editCreditCard.xhtml | 110 + .../extensions/formautofill/content/editDialog.js | 224 ++ .../formautofill/content/formautofill.css | 53 + .../formautofill/content/formfill-anchor.svg | 8 + .../formautofill/content/heuristicsRegexp.js | 583 +++ .../formautofill/content/icon-address-save.svg | 6 + .../formautofill/content/icon-address-update.svg | 6 + .../content/icon-credit-card-generic.svg | 8 + .../formautofill/content/icon-credit-card.svg | 8 + browser/extensions/formautofill/content/l10n.js | 61 + .../formautofill/content/manageAddresses.xhtml | 41 + .../formautofill/content/manageCreditCards.xhtml | 42 + .../formautofill/content/manageDialog.css | 126 + .../formautofill/content/manageDialog.js | 486 +++ .../content/third-party/cc-logo-amex.png | Bin 0 -> 1306 bytes .../content/third-party/cc-logo-amex@2x.png | Bin 0 -> 2311 bytes .../content/third-party/cc-logo-cartebancaire.png | Bin 0 -> 1240 bytes .../third-party/cc-logo-cartebancaire@2x.png | Bin 0 -> 3111 bytes .../content/third-party/cc-logo-diners.svg | 1 + .../content/third-party/cc-logo-discover.png | Bin 0 -> 1117 bytes .../content/third-party/cc-logo-discover@2x.png | Bin 0 -> 2471 bytes .../content/third-party/cc-logo-jcb.svg | 1 + .../content/third-party/cc-logo-mastercard.svg | 1 + .../content/third-party/cc-logo-mir.svg | 1 + .../content/third-party/cc-logo-unionpay.svg | 1 + .../content/third-party/cc-logo-visa.svg | 1 + .../extensions/formautofill/docs/heuristics.rst | 37 + browser/extensions/formautofill/docs/index.rst | 31 + browser/extensions/formautofill/jar.mn | 11 + .../locales/en-US/formautofill.properties | 230 ++ browser/extensions/formautofill/locales/jar.mn | 8 + browser/extensions/formautofill/locales/moz.build | 7 + browser/extensions/formautofill/manifest.json | 26 + browser/extensions/formautofill/moz.build | 53 + .../formautofill/phonenumberutils/PhoneNumber.jsm | 478 +++ .../phonenumberutils/PhoneNumberMetaData.jsm | 230 ++ .../phonenumberutils/PhoneNumberNormalizer.jsm | 71 + browser/extensions/formautofill/schema.json | 1 + .../formautofill/skin/linux/autocomplete-item.css | 10 + .../formautofill/skin/linux/editDialog.css | 8 + .../formautofill/skin/osx/autocomplete-item.css | 9 + .../formautofill/skin/osx/editDialog.css | 5 + .../skin/shared/autocomplete-item-shared.css | 188 + .../formautofill/skin/shared/editAddress.css | 122 + .../formautofill/skin/shared/editCreditCard.css | 57 + .../formautofill/skin/shared/editDialog-shared.css | 110 + .../skin/windows/autocomplete-item.css | 21 + .../formautofill/skin/windows/editDialog.css | 12 + .../formautofill/test/browser/browser.ini | 29 + .../test/browser/browser_autocomplete_footer.js | 124 + .../browser_autocomplete_marked_back_forward.js | 67 + .../browser_autocomplete_marked_detached_tab.js | 60 + .../test/browser/browser_check_installed.js | 12 + .../test/browser/browser_dropdown_layout.js | 49 + .../test/browser/browser_editAddressDialog.js | 925 +++++ .../browser/browser_first_time_use_doorhanger.js | 140 + .../test/browser/browser_manageAddressesDialog.js | 107 + .../test/browser/browser_remoteiframe.js | 145 + .../browser/browser_submission_in_private_mode.js | 33 + .../test/browser/browser_update_doorhanger.js | 187 + .../test/browser/creditCard/browser.ini | 32 + .../creditCard/browser_anti_clickjacking.js | 119 + .../creditCard/browser_creditCard_doorhanger.js | 951 +++++ .../browser_creditCard_dropdown_layout.js | 53 + .../browser_creditCard_fill_cancel_login.js | 33 + .../creditCard/browser_creditCard_telemetry.js | 758 ++++ .../creditCard/browser_editCreditCardDialog.js | 450 +++ .../browser/creditCard/browser_insecure_form.js | 134 + .../creditCard/browser_manageCreditCardsDialog.js | 291 ++ .../creditCard/browser_privacyPreferences.js | 277 ++ .../test/browser/creditCard/head_cc.js | 1 + .../test/browser/focus-leak/browser.ini | 10 + .../browser_iframe_typecontent_input_focus.js | 55 + .../doc_iframe_typecontent_input_focus.xhtml | 7 + .../doc_iframe_typecontent_input_focus_frame.html | 6 + .../extensions/formautofill/test/browser/head.js | 527 +++ .../test/fixtures/autocomplete_basic.html | 52 + .../fixtures/autocomplete_creditcard_basic.html | 29 + .../fixtures/autocomplete_creditcard_iframe.html | 12 + .../test/fixtures/autocomplete_iframe.html | 13 + .../test/fixtures/autocomplete_simple_basic.html | 19 + .../test/fixtures/heuristics_cc_exp.html | 73 + .../test/fixtures/heuristics_de_fields.html | 66 + .../test/fixtures/multiple_section.html | 84 + .../third_party/BestBuy/Checkout_Payment.html | 283 ++ .../BestBuy/Checkout_ShippingAddress.html | 326 ++ .../test/fixtures/third_party/BestBuy/SignIn.html | 21 + .../CDW/Checkout_BillingPaymentInfo.html | 469 +++ .../fixtures/third_party/CDW/Checkout_Logon.html | 118 + .../third_party/CDW/Checkout_ShippingInfo.html | 376 ++ .../test/fixtures/third_party/CostCo/Payment.html | 892 +++++ .../third_party/CostCo/ShippingAddress.html | 527 +++ .../test/fixtures/third_party/CostCo/SignIn.html | 374 ++ .../HomeDepot/Checkout_ShippingPayment.html | 381 ++ .../fixtures/third_party/HomeDepot/SignIn.html | 83 + .../test/fixtures/third_party/Lush/index.html | 421 ++ .../third_party/Macys/Checkout_Payment.html | 474 +++ .../Macys/Checkout_ShippingAddress.html | 439 +++ .../test/fixtures/third_party/Macys/SignIn.html | 208 + .../fixtures/third_party/NewEgg/BillingInfo.html | 1074 +++++ .../test/fixtures/third_party/NewEgg/Login.html | 156 + .../fixtures/third_party/NewEgg/ShippingInfo.html | 270 ++ .../fixtures/third_party/OfficeDepot/Payment.html | 672 ++++ .../third_party/OfficeDepot/ShippingAddress.html | 347 ++ .../fixtures/third_party/OfficeDepot/SignIn.html | 44 + .../fixtures/third_party/QVC/PaymentMethod.html | 527 +++ .../test/fixtures/third_party/QVC/SignIn.html | 80 + .../fixtures/third_party/QVC/YourInformation.html | 522 +++ .../formautofill/test/fixtures/third_party/README | 4 + .../fixtures/third_party/Sears/PaymentOptions.html | 566 +++ .../third_party/Sears/ShippingAddress.html | 447 +++ .../test/fixtures/third_party/Staples/Basic.html | 117 + .../fixtures/third_party/Staples/Basic_ac_on.html | 117 + .../third_party/Staples/PaymentBilling.html | 99 + .../third_party/Staples/PaymentBilling_ac_on.html | 98 + .../fixtures/third_party/Walmart/Checkout.html | 243 ++ .../test/fixtures/third_party/Walmart/Payment.html | 235 ++ .../fixtures/third_party/Walmart/Shipping.html | 234 ++ .../test/mochitest/creditCard/mochitest.ini | 20 + .../test_basic_creditcard_autocomplete_form.html | 253 ++ .../test/mochitest/creditCard/test_clear_form.html | 196 + .../test_creditcard_autocomplete_off.html | 96 + .../test/mochitest/formautofill_common.js | 452 +++ .../test/mochitest/formautofill_parent_utils.js | 309 ++ .../formautofill/test/mochitest/mochitest.ini | 19 + .../mochitest/test_address_level_1_submission.html | 101 + .../mochitest/test_autofill_and_ordinal_forms.html | 116 + .../test/mochitest/test_autofocus_form.html | 69 + .../mochitest/test_basic_autocomplete_form.html | 220 ++ .../test/mochitest/test_form_changes.html | 106 + .../test_formautofill_preview_highlight.html | 102 + .../test_multi_locale_CA_address_form.html | 274 ++ .../test/mochitest/test_multiple_forms.html | 67 + .../test/mochitest/test_on_address_submission.html | 164 + browser/extensions/formautofill/test/unit/head.js | 345 ++ .../test/unit/heuristics/test_basic.js | 180 + .../test/unit/heuristics/test_cc_exp.js | 112 + .../test/unit/heuristics/test_de_fields.js | 48 + .../test/unit/heuristics/test_known_strings.js | 145 + .../test/unit/heuristics/test_multiple_section.js | 280 ++ .../unit/heuristics/third_party/test_BestBuy.js | 163 + .../test/unit/heuristics/third_party/test_CDW.js | 169 + .../unit/heuristics/third_party/test_CostCo.js | 448 +++ .../unit/heuristics/third_party/test_HomeDepot.js | 110 + .../test/unit/heuristics/third_party/test_Lush.js | 56 + .../test/unit/heuristics/third_party/test_Macys.js | 190 + .../unit/heuristics/third_party/test_NewEgg.js | 232 ++ .../heuristics/third_party/test_OfficeDepot.js | 219 ++ .../test/unit/heuristics/third_party/test_QVC.js | 143 + .../test/unit/heuristics/third_party/test_Sears.js | 196 + .../unit/heuristics/third_party/test_Staples.js | 109 + .../unit/heuristics/third_party/test_Walmart.js | 167 + .../formautofill/test/unit/test_activeStatus.js | 176 + .../test/unit/test_addressDataLoader.js | 102 + .../formautofill/test/unit/test_addressRecords.js | 848 ++++ .../test/unit/test_autofillFormFields.js | 625 +++ .../test/unit/test_collectFormFields.js | 1129 ++++++ .../formautofill/test/unit/test_createRecords.js | 477 +++ .../test/unit/test_creditCardRecords.js | 892 +++++ .../test/unit/test_extractLabelStrings.js | 80 + .../test/unit/test_findLabelElements.js | 103 + .../test/unit/test_getAdaptedProfiles.js | 1194 ++++++ .../test/unit/test_getCategoriesFromFieldNames.js | 95 + .../test/unit/test_getFormInputDetails.js | 205 + .../formautofill/test/unit/test_getInfo.js | 326 ++ .../formautofill/test/unit/test_getRecords.js | 258 ++ .../formautofill/test/unit/test_isAvailable.js | 37 + .../formautofill/test/unit/test_isCJKName.js | 80 + .../test/unit/test_isFieldEligibleForAutofill.js | 88 + .../test/unit/test_markAsAutofillField.js | 87 + .../formautofill/test/unit/test_migrateRecords.js | 321 ++ .../formautofill/test/unit/test_nameUtils.js | 289 ++ .../formautofill/test/unit/test_onFormSubmitted.js | 686 ++++ .../test/unit/test_parseAddressFormat.js | 66 + .../formautofill/test/unit/test_phoneNumber.js | 399 ++ .../test/unit/test_profileAutocompleteResult.js | 450 +++ .../formautofill/test/unit/test_reconcile.js | 1136 ++++++ .../formautofill/test/unit/test_savedFieldNames.js | 106 + .../formautofill/test/unit/test_storage_remove.js | 89 + .../test/unit/test_storage_syncfields.js | 497 +++ .../test/unit/test_storage_tombstones.js | 191 + .../extensions/formautofill/test/unit/test_sync.js | 924 +++++ .../test/unit/test_toOneLineAddress.js | 64 + .../formautofill/test/unit/test_transformFields.js | 973 +++++ .../extensions/formautofill/test/unit/xpcshell.ini | 83 + browser/extensions/moz.build | 7 + browser/extensions/report-site-issue/.eslintrc.js | 76 + browser/extensions/report-site-issue/background.js | 215 + .../experimentalAPIs/aboutConfigPrefs.js | 41 + .../experimentalAPIs/aboutConfigPrefs.json | 35 + .../experimentalAPIs/actors/tabExtrasActor.jsm | 156 + .../experimentalAPIs/browserInfo.js | 74 + .../experimentalAPIs/browserInfo.json | 57 + .../report-site-issue/experimentalAPIs/l10n.js | 57 + .../report-site-issue/experimentalAPIs/l10n.json | 19 + .../experimentalAPIs/pageActionExtras.js | 43 + .../experimentalAPIs/pageActionExtras.json | 41 + .../experimentalAPIs/tabExtras.js | 97 + .../experimentalAPIs/tabExtras.json | 19 + .../report-site-issue/icons/lightbulb.svg | 6 + .../locales/en-US/webcompat.properties | 10 + .../extensions/report-site-issue/locales/jar.mn | 8 + .../extensions/report-site-issue/locales/moz.build | 7 + browser/extensions/report-site-issue/manifest.json | 78 + browser/extensions/report-site-issue/moz.build | 41 + .../report-site-issue/test/browser/.eslintrc.js | 5 + .../report-site-issue/test/browser/browser.ini | 11 + .../test/browser/browser_button_state.js | 93 + .../test/browser/browser_disabled_cleanup.js | 53 + .../test/browser/browser_report_site_issue.js | 291 ++ .../report-site-issue/test/browser/fastclick.html | 11 + .../report-site-issue/test/browser/frameworks.html | 8 + .../report-site-issue/test/browser/head.js | 204 + .../report-site-issue/test/browser/test.html | 39 + .../report-site-issue/test/browser/webcompat.html | 85 + .../screenshots/assertIsBlankDocument.js | 16 + browser/extensions/screenshots/assertIsTrusted.js | 24 + .../extensions/screenshots/background/analytics.js | 367 ++ browser/extensions/screenshots/background/auth.js | 224 ++ .../screenshots/background/communication.js | 53 + .../screenshots/background/deviceInfo.js | 38 + browser/extensions/screenshots/background/main.js | 291 ++ .../screenshots/background/selectorLoader.js | 134 + .../extensions/screenshots/background/senderror.js | 154 + .../screenshots/background/startBackground.js | 135 + .../extensions/screenshots/background/takeshot.js | 179 + browser/extensions/screenshots/blank.html | 5 + browser/extensions/screenshots/blobConverters.js | 48 + .../extensions/screenshots/build/buildSettings.js | 15 + .../screenshots/build/inlineSelectionCss.js | 666 ++++ browser/extensions/screenshots/build/raven.js | 4115 ++++++++++++++++++++ browser/extensions/screenshots/build/selection.js | 125 + browser/extensions/screenshots/build/shot.js | 754 ++++ .../screenshots/build/thumbnailGenerator.js | 154 + browser/extensions/screenshots/catcher.js | 101 + browser/extensions/screenshots/clipboard.js | 58 + browser/extensions/screenshots/domainFromUrl.js | 33 + .../screenshots/experiments/screenshots/api.js | 47 + .../experiments/screenshots/schema.json | 29 + browser/extensions/screenshots/icons/cancel.svg | 4 + browser/extensions/screenshots/icons/cloud.svg | 4 + .../screenshots/icons/copied-notification.svg | 4 + browser/extensions/screenshots/icons/copy.svg | 4 + .../screenshots/icons/download-white.svg | 4 + browser/extensions/screenshots/icons/download.svg | 4 + browser/extensions/screenshots/icons/help-16.svg | 4 + .../screenshots/icons/icon-highlight-32-v2.svg | 4 + browser/extensions/screenshots/icons/icon-v2.svg | 4 + .../icons/icon-welcome-face-without-eyes.svg | 4 + .../extensions/screenshots/icons/menu-fullpage.svg | 4 + .../screenshots/icons/menu-myshot-white.svg | 4 + .../extensions/screenshots/icons/menu-myshot.svg | 4 + .../extensions/screenshots/icons/menu-visible.svg | 4 + browser/extensions/screenshots/log.js | 52 + browser/extensions/screenshots/makeUuid.js | 23 + browser/extensions/screenshots/manifest.json | 91 + browser/extensions/screenshots/moz.build | 81 + browser/extensions/screenshots/randomString.js | 18 + .../screenshots/selector/callBackground.js | 31 + .../screenshots/selector/documentMetadata.js | 91 + browser/extensions/screenshots/selector/shooter.js | 147 + browser/extensions/screenshots/selector/ui.js | 828 ++++ .../extensions/screenshots/selector/uicontrol.js | 901 +++++ browser/extensions/screenshots/selector/util.js | 107 + browser/extensions/screenshots/sitehelper.js | 94 + .../screenshots/test/browser/.eslintrc.yml | 5 + .../screenshots/test/browser/browser.ini | 4 + .../test/browser/browser_screenshots_injection.js | 72 + .../test/browser/browser_screenshots_ui_check.js | 94 + .../screenshots/test/browser/injection-page.html | 24 + .../webcompat/about-compat/AboutCompat.jsm | 36 + .../webcompat/about-compat/aboutCompat.css | 187 + .../webcompat/about-compat/aboutCompat.html | 37 + .../webcompat/about-compat/aboutCompat.js | 171 + .../extensions/webcompat/about-compat/aboutPage.js | 48 + .../webcompat/about-compat/aboutPage.json | 6 + .../about-compat/aboutPageProcessScript.js | 32 + browser/extensions/webcompat/components.conf | 17 + browser/extensions/webcompat/data/injections.js | 467 +++ .../webcompat/data/picture_in_picture_overrides.js | 60 + browser/extensions/webcompat/data/shims.js | 253 ++ browser/extensions/webcompat/data/ua_overrides.js | 686 ++++ .../webcompat/experiment-apis/aboutConfigPrefs.js | 57 + .../experiment-apis/aboutConfigPrefs.json | 72 + .../webcompat/experiment-apis/appConstants.js | 32 + .../webcompat/experiment-apis/appConstants.json | 15 + .../webcompat/experiment-apis/experiments.js | 34 + .../webcompat/experiment-apis/experiments.json | 21 + .../webcompat/experiment-apis/matchPatterns.js | 30 + .../webcompat/experiment-apis/matchPatterns.json | 29 + .../webcompat/experiment-apis/pictureInPicture.js | 90 + .../experiment-apis/pictureInPicture.json | 51 + .../webcompat/experiment-apis/sharedPreferences.js | 33 + .../experiment-apis/sharedPreferences.json | 44 + .../experiment-apis/systemManufacturer.js | 27 + .../experiment-apis/systemManufacturer.json | 20 + .../experiment-apis/trackingProtection.js | 168 + .../experiment-apis/trackingProtection.json | 75 + .../css/bug0000000-testbed-css-injection.css | 3 + ...-mail.google.com-allow-horizontal-scrolling.css | 12 + .../bug1570119-teamcoco.com-scrollbar-width.css | 11 + ...1570328-developer-apple.com-transform-scale.css | 17 + ...00-apply.lloydsbank.co.uk-radio-buttons-fix.css | 11 + .../bug1605611-maps.google.com-directions-time.css | 24 + .../bug1610016-gaana.com-input-position-fix.css | 13 + ...344-directv.com.co-hide-unsupported-message.css | 13 + ...missingmail.usps.com-checkboxes-not-visible.css | 13 + .../bug1645064-s-kanava.fi-invisible-charts.css | 12 + ...651917-teletrader.com.body-transform-origin.css | 14 + .../bug1653075-livescience.com-scrollbar-width.css | 14 + .../css/bug1654865-sports.ndtv.com-float-fix.css | 13 + .../bug1654877-preev.com-moz-appearance-fix.css | 15 + .../bug1654907-reactine.ca-hide-unsupported.css | 12 + .../bug1655049-dev.to-unclickable-button-fix.css | 12 + .../css/bug1666771-zilow-map-overdraw.css | 17 + .../js/bug0000000-testbed-js-injection.js | 11 + ...452707-window.controllers-shim-ib.absa.co.za.js | 29 + .../js/bug1457335-histography.io-ua-change.js | 34 + .../js/bug1472075-bankofamerica.com-ua-change.js | 48 + .../js/bug1570856-medium.com-menu-isTier1.js | 34 + ...bug1579159-m.tailieu.vn-pdfjs-worker-disable.js | 28 + .../bug1605611-maps.google.com-directions-time.js | 82 + .../js/bug1610358-pcloud.com-appVersion-change.js | 25 + .../bug1631811-datastudio.google.com-indexedDB.js | 18 + .../js/bug1665035-dckids.com-cookieEnabled.js | 30 + .../bug1677442-store.hp.com-disable-indexeddb.js | 20 + .../js/bug1682238-gamearter.com-ua-change.js | 27 + .../webcompat/lib/about_compat_broker.js | 123 + .../extensions/webcompat/lib/custom_functions.js | 96 + browser/extensions/webcompat/lib/injections.js | 163 + .../webcompat/lib/intervention_helpers.js | 233 ++ .../extensions/webcompat/lib/messaging_helper.js | 36 + browser/extensions/webcompat/lib/module_shim.js | 24 + .../webcompat/lib/picture_in_picture_overrides.js | 74 + .../webcompat/lib/shim_messaging_helper.js | 65 + browser/extensions/webcompat/lib/shims.js | 415 ++ browser/extensions/webcompat/lib/ua_overrides.js | 265 ++ browser/extensions/webcompat/manifest.json | 144 + browser/extensions/webcompat/moz.build | 125 + browser/extensions/webcompat/run.js | 24 + .../webcompat/shims/adsafeprotected-ima.js | 69 + browser/extensions/webcompat/shims/bmauth.js | 21 + browser/extensions/webcompat/shims/eluminate.js | 68 + browser/extensions/webcompat/shims/empty-script.js | 5 + browser/extensions/webcompat/shims/facebook-sdk.js | 198 + .../shims/google-analytics-ecommerce-plugin.js | 13 + .../webcompat/shims/google-analytics-legacy.js | 133 + .../shims/google-analytics-tag-manager.js | 24 + .../extensions/webcompat/shims/google-analytics.js | 45 + .../webcompat/shims/google-publisher-tags.js | 163 + .../extensions/webcompat/shims/live-test-shim.js | 84 + .../extensions/webcompat/shims/mochitest-shim-1.js | 89 + .../extensions/webcompat/shims/mochitest-shim-2.js | 87 + .../extensions/webcompat/shims/mochitest-shim-3.js | 7 + .../webcompat/shims/rambler-authenticator.js | 86 + .../extensions/webcompat/shims/rich-relevance.js | 30 + .../extensions/webcompat/tests/browser/browser.ini | 13 + .../webcompat/tests/browser/browser_shims.js | 73 + browser/extensions/webcompat/tests/browser/head.js | 139 + .../webcompat/tests/browser/iframe_test.html | 17 + .../webcompat/tests/browser/shims_test.html | 18 + .../webcompat/tests/browser/shims_test.js | 11 + .../webcompat/tests/browser/shims_test_2.html | 18 + .../webcompat/tests/browser/shims_test_2.js | 11 + .../webcompat/tests/browser/shims_test_3.html | 18 + .../webcompat/tests/browser/shims_test_3.js | 7 + 389 files changed, 73094 insertions(+) create mode 100644 browser/extensions/doh-rollout/manifest.json create mode 100644 browser/extensions/doh-rollout/moz.build create mode 100644 browser/extensions/formautofill/.eslintrc.js create mode 100644 browser/extensions/formautofill/FormAutofill.jsm create mode 100644 browser/extensions/formautofill/FormAutofillChild.jsm create mode 100644 browser/extensions/formautofill/FormAutofillContent.jsm create mode 100644 browser/extensions/formautofill/FormAutofillDoorhanger.jsm create mode 100644 browser/extensions/formautofill/FormAutofillHandler.jsm create mode 100644 browser/extensions/formautofill/FormAutofillHeuristics.jsm create mode 100644 browser/extensions/formautofill/FormAutofillNameUtils.jsm create mode 100644 browser/extensions/formautofill/FormAutofillParent.jsm create mode 100644 browser/extensions/formautofill/FormAutofillPreferences.jsm create mode 100644 browser/extensions/formautofill/FormAutofillStorage.jsm create mode 100644 browser/extensions/formautofill/FormAutofillSync.jsm create mode 100644 browser/extensions/formautofill/FormAutofillUtils.jsm create mode 100644 browser/extensions/formautofill/ProfileAutoCompleteResult.jsm create mode 100644 browser/extensions/formautofill/addressmetadata/addressReferences.js create mode 100644 browser/extensions/formautofill/addressmetadata/addressReferencesExt.js create mode 100644 browser/extensions/formautofill/api.js create mode 100644 browser/extensions/formautofill/background.js create mode 100644 browser/extensions/formautofill/content/autofillEditForms.js create mode 100644 browser/extensions/formautofill/content/customElements.js create mode 100644 browser/extensions/formautofill/content/editAddress.xhtml create mode 100644 browser/extensions/formautofill/content/editCreditCard.xhtml create mode 100644 browser/extensions/formautofill/content/editDialog.js create mode 100644 browser/extensions/formautofill/content/formautofill.css create mode 100644 browser/extensions/formautofill/content/formfill-anchor.svg create mode 100644 browser/extensions/formautofill/content/heuristicsRegexp.js create mode 100644 browser/extensions/formautofill/content/icon-address-save.svg create mode 100644 browser/extensions/formautofill/content/icon-address-update.svg create mode 100644 browser/extensions/formautofill/content/icon-credit-card-generic.svg create mode 100644 browser/extensions/formautofill/content/icon-credit-card.svg create mode 100644 browser/extensions/formautofill/content/l10n.js create mode 100644 browser/extensions/formautofill/content/manageAddresses.xhtml create mode 100644 browser/extensions/formautofill/content/manageCreditCards.xhtml create mode 100644 browser/extensions/formautofill/content/manageDialog.css create mode 100644 browser/extensions/formautofill/content/manageDialog.js create mode 100644 browser/extensions/formautofill/content/third-party/cc-logo-amex.png create mode 100644 browser/extensions/formautofill/content/third-party/cc-logo-amex@2x.png create mode 100644 browser/extensions/formautofill/content/third-party/cc-logo-cartebancaire.png create mode 100644 browser/extensions/formautofill/content/third-party/cc-logo-cartebancaire@2x.png create mode 100644 browser/extensions/formautofill/content/third-party/cc-logo-diners.svg create mode 100644 browser/extensions/formautofill/content/third-party/cc-logo-discover.png create mode 100644 browser/extensions/formautofill/content/third-party/cc-logo-discover@2x.png create mode 100644 browser/extensions/formautofill/content/third-party/cc-logo-jcb.svg create mode 100644 browser/extensions/formautofill/content/third-party/cc-logo-mastercard.svg create mode 100644 browser/extensions/formautofill/content/third-party/cc-logo-mir.svg create mode 100644 browser/extensions/formautofill/content/third-party/cc-logo-unionpay.svg create mode 100644 browser/extensions/formautofill/content/third-party/cc-logo-visa.svg create mode 100644 browser/extensions/formautofill/docs/heuristics.rst create mode 100644 browser/extensions/formautofill/docs/index.rst create mode 100644 browser/extensions/formautofill/jar.mn create mode 100644 browser/extensions/formautofill/locales/en-US/formautofill.properties create mode 100644 browser/extensions/formautofill/locales/jar.mn create mode 100644 browser/extensions/formautofill/locales/moz.build create mode 100644 browser/extensions/formautofill/manifest.json create mode 100644 browser/extensions/formautofill/moz.build create mode 100644 browser/extensions/formautofill/phonenumberutils/PhoneNumber.jsm create mode 100644 browser/extensions/formautofill/phonenumberutils/PhoneNumberMetaData.jsm create mode 100644 browser/extensions/formautofill/phonenumberutils/PhoneNumberNormalizer.jsm create mode 100644 browser/extensions/formautofill/schema.json create mode 100644 browser/extensions/formautofill/skin/linux/autocomplete-item.css create mode 100644 browser/extensions/formautofill/skin/linux/editDialog.css create mode 100644 browser/extensions/formautofill/skin/osx/autocomplete-item.css create mode 100644 browser/extensions/formautofill/skin/osx/editDialog.css create mode 100644 browser/extensions/formautofill/skin/shared/autocomplete-item-shared.css create mode 100644 browser/extensions/formautofill/skin/shared/editAddress.css create mode 100644 browser/extensions/formautofill/skin/shared/editCreditCard.css create mode 100644 browser/extensions/formautofill/skin/shared/editDialog-shared.css create mode 100644 browser/extensions/formautofill/skin/windows/autocomplete-item.css create mode 100644 browser/extensions/formautofill/skin/windows/editDialog.css create mode 100644 browser/extensions/formautofill/test/browser/browser.ini create mode 100644 browser/extensions/formautofill/test/browser/browser_autocomplete_footer.js create mode 100644 browser/extensions/formautofill/test/browser/browser_autocomplete_marked_back_forward.js create mode 100644 browser/extensions/formautofill/test/browser/browser_autocomplete_marked_detached_tab.js create mode 100644 browser/extensions/formautofill/test/browser/browser_check_installed.js create mode 100644 browser/extensions/formautofill/test/browser/browser_dropdown_layout.js create mode 100644 browser/extensions/formautofill/test/browser/browser_editAddressDialog.js create mode 100644 browser/extensions/formautofill/test/browser/browser_first_time_use_doorhanger.js create mode 100644 browser/extensions/formautofill/test/browser/browser_manageAddressesDialog.js create mode 100644 browser/extensions/formautofill/test/browser/browser_remoteiframe.js create mode 100644 browser/extensions/formautofill/test/browser/browser_submission_in_private_mode.js create mode 100644 browser/extensions/formautofill/test/browser/browser_update_doorhanger.js create mode 100644 browser/extensions/formautofill/test/browser/creditCard/browser.ini create mode 100644 browser/extensions/formautofill/test/browser/creditCard/browser_anti_clickjacking.js create mode 100644 browser/extensions/formautofill/test/browser/creditCard/browser_creditCard_doorhanger.js create mode 100644 browser/extensions/formautofill/test/browser/creditCard/browser_creditCard_dropdown_layout.js create mode 100644 browser/extensions/formautofill/test/browser/creditCard/browser_creditCard_fill_cancel_login.js create mode 100644 browser/extensions/formautofill/test/browser/creditCard/browser_creditCard_telemetry.js create mode 100644 browser/extensions/formautofill/test/browser/creditCard/browser_editCreditCardDialog.js create mode 100644 browser/extensions/formautofill/test/browser/creditCard/browser_insecure_form.js create mode 100644 browser/extensions/formautofill/test/browser/creditCard/browser_manageCreditCardsDialog.js create mode 100644 browser/extensions/formautofill/test/browser/creditCard/browser_privacyPreferences.js create mode 100644 browser/extensions/formautofill/test/browser/creditCard/head_cc.js create mode 100644 browser/extensions/formautofill/test/browser/focus-leak/browser.ini create mode 100644 browser/extensions/formautofill/test/browser/focus-leak/browser_iframe_typecontent_input_focus.js create mode 100644 browser/extensions/formautofill/test/browser/focus-leak/doc_iframe_typecontent_input_focus.xhtml create mode 100644 browser/extensions/formautofill/test/browser/focus-leak/doc_iframe_typecontent_input_focus_frame.html create mode 100644 browser/extensions/formautofill/test/browser/head.js create mode 100644 browser/extensions/formautofill/test/fixtures/autocomplete_basic.html create mode 100644 browser/extensions/formautofill/test/fixtures/autocomplete_creditcard_basic.html create mode 100644 browser/extensions/formautofill/test/fixtures/autocomplete_creditcard_iframe.html create mode 100644 browser/extensions/formautofill/test/fixtures/autocomplete_iframe.html create mode 100644 browser/extensions/formautofill/test/fixtures/autocomplete_simple_basic.html create mode 100644 browser/extensions/formautofill/test/fixtures/heuristics_cc_exp.html create mode 100644 browser/extensions/formautofill/test/fixtures/heuristics_de_fields.html create mode 100644 browser/extensions/formautofill/test/fixtures/multiple_section.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/BestBuy/Checkout_Payment.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/BestBuy/Checkout_ShippingAddress.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/BestBuy/SignIn.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/CDW/Checkout_BillingPaymentInfo.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/CDW/Checkout_Logon.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/CDW/Checkout_ShippingInfo.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/CostCo/Payment.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/CostCo/ShippingAddress.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/CostCo/SignIn.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/HomeDepot/Checkout_ShippingPayment.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/HomeDepot/SignIn.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/Lush/index.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/Macys/Checkout_Payment.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/Macys/Checkout_ShippingAddress.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/Macys/SignIn.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/NewEgg/BillingInfo.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/NewEgg/Login.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/NewEgg/ShippingInfo.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/OfficeDepot/Payment.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/OfficeDepot/ShippingAddress.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/OfficeDepot/SignIn.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/QVC/PaymentMethod.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/QVC/SignIn.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/QVC/YourInformation.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/README create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/Sears/PaymentOptions.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/Sears/ShippingAddress.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/Staples/Basic.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/Staples/Basic_ac_on.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/Staples/PaymentBilling.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/Staples/PaymentBilling_ac_on.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/Walmart/Checkout.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/Walmart/Payment.html create mode 100644 browser/extensions/formautofill/test/fixtures/third_party/Walmart/Shipping.html create mode 100644 browser/extensions/formautofill/test/mochitest/creditCard/mochitest.ini create mode 100644 browser/extensions/formautofill/test/mochitest/creditCard/test_basic_creditcard_autocomplete_form.html create mode 100644 browser/extensions/formautofill/test/mochitest/creditCard/test_clear_form.html create mode 100644 browser/extensions/formautofill/test/mochitest/creditCard/test_creditcard_autocomplete_off.html create mode 100644 browser/extensions/formautofill/test/mochitest/formautofill_common.js create mode 100644 browser/extensions/formautofill/test/mochitest/formautofill_parent_utils.js create mode 100644 browser/extensions/formautofill/test/mochitest/mochitest.ini create mode 100644 browser/extensions/formautofill/test/mochitest/test_address_level_1_submission.html create mode 100644 browser/extensions/formautofill/test/mochitest/test_autofill_and_ordinal_forms.html create mode 100644 browser/extensions/formautofill/test/mochitest/test_autofocus_form.html create mode 100644 browser/extensions/formautofill/test/mochitest/test_basic_autocomplete_form.html create mode 100644 browser/extensions/formautofill/test/mochitest/test_form_changes.html create mode 100644 browser/extensions/formautofill/test/mochitest/test_formautofill_preview_highlight.html create mode 100644 browser/extensions/formautofill/test/mochitest/test_multi_locale_CA_address_form.html create mode 100644 browser/extensions/formautofill/test/mochitest/test_multiple_forms.html create mode 100644 browser/extensions/formautofill/test/mochitest/test_on_address_submission.html create mode 100644 browser/extensions/formautofill/test/unit/head.js create mode 100644 browser/extensions/formautofill/test/unit/heuristics/test_basic.js create mode 100644 browser/extensions/formautofill/test/unit/heuristics/test_cc_exp.js create mode 100644 browser/extensions/formautofill/test/unit/heuristics/test_de_fields.js create mode 100644 browser/extensions/formautofill/test/unit/heuristics/test_known_strings.js create mode 100644 browser/extensions/formautofill/test/unit/heuristics/test_multiple_section.js create mode 100644 browser/extensions/formautofill/test/unit/heuristics/third_party/test_BestBuy.js create mode 100644 browser/extensions/formautofill/test/unit/heuristics/third_party/test_CDW.js create mode 100644 browser/extensions/formautofill/test/unit/heuristics/third_party/test_CostCo.js create mode 100644 browser/extensions/formautofill/test/unit/heuristics/third_party/test_HomeDepot.js create mode 100644 browser/extensions/formautofill/test/unit/heuristics/third_party/test_Lush.js create mode 100644 browser/extensions/formautofill/test/unit/heuristics/third_party/test_Macys.js create mode 100644 browser/extensions/formautofill/test/unit/heuristics/third_party/test_NewEgg.js create mode 100644 browser/extensions/formautofill/test/unit/heuristics/third_party/test_OfficeDepot.js create mode 100644 browser/extensions/formautofill/test/unit/heuristics/third_party/test_QVC.js create mode 100644 browser/extensions/formautofill/test/unit/heuristics/third_party/test_Sears.js create mode 100644 browser/extensions/formautofill/test/unit/heuristics/third_party/test_Staples.js create mode 100644 browser/extensions/formautofill/test/unit/heuristics/third_party/test_Walmart.js create mode 100644 browser/extensions/formautofill/test/unit/test_activeStatus.js create mode 100644 browser/extensions/formautofill/test/unit/test_addressDataLoader.js create mode 100644 browser/extensions/formautofill/test/unit/test_addressRecords.js create mode 100644 browser/extensions/formautofill/test/unit/test_autofillFormFields.js create mode 100644 browser/extensions/formautofill/test/unit/test_collectFormFields.js create mode 100644 browser/extensions/formautofill/test/unit/test_createRecords.js create mode 100644 browser/extensions/formautofill/test/unit/test_creditCardRecords.js create mode 100644 browser/extensions/formautofill/test/unit/test_extractLabelStrings.js create mode 100644 browser/extensions/formautofill/test/unit/test_findLabelElements.js create mode 100644 browser/extensions/formautofill/test/unit/test_getAdaptedProfiles.js create mode 100644 browser/extensions/formautofill/test/unit/test_getCategoriesFromFieldNames.js create mode 100644 browser/extensions/formautofill/test/unit/test_getFormInputDetails.js create mode 100644 browser/extensions/formautofill/test/unit/test_getInfo.js create mode 100644 browser/extensions/formautofill/test/unit/test_getRecords.js create mode 100644 browser/extensions/formautofill/test/unit/test_isAvailable.js create mode 100644 browser/extensions/formautofill/test/unit/test_isCJKName.js create mode 100644 browser/extensions/formautofill/test/unit/test_isFieldEligibleForAutofill.js create mode 100644 browser/extensions/formautofill/test/unit/test_markAsAutofillField.js create mode 100644 browser/extensions/formautofill/test/unit/test_migrateRecords.js create mode 100644 browser/extensions/formautofill/test/unit/test_nameUtils.js create mode 100644 browser/extensions/formautofill/test/unit/test_onFormSubmitted.js create mode 100644 browser/extensions/formautofill/test/unit/test_parseAddressFormat.js create mode 100644 browser/extensions/formautofill/test/unit/test_phoneNumber.js create mode 100644 browser/extensions/formautofill/test/unit/test_profileAutocompleteResult.js create mode 100644 browser/extensions/formautofill/test/unit/test_reconcile.js create mode 100644 browser/extensions/formautofill/test/unit/test_savedFieldNames.js create mode 100644 browser/extensions/formautofill/test/unit/test_storage_remove.js create mode 100644 browser/extensions/formautofill/test/unit/test_storage_syncfields.js create mode 100644 browser/extensions/formautofill/test/unit/test_storage_tombstones.js create mode 100644 browser/extensions/formautofill/test/unit/test_sync.js create mode 100644 browser/extensions/formautofill/test/unit/test_toOneLineAddress.js create mode 100644 browser/extensions/formautofill/test/unit/test_transformFields.js create mode 100644 browser/extensions/formautofill/test/unit/xpcshell.ini create mode 100644 browser/extensions/moz.build create mode 100644 browser/extensions/report-site-issue/.eslintrc.js create mode 100644 browser/extensions/report-site-issue/background.js create mode 100644 browser/extensions/report-site-issue/experimentalAPIs/aboutConfigPrefs.js create mode 100644 browser/extensions/report-site-issue/experimentalAPIs/aboutConfigPrefs.json create mode 100644 browser/extensions/report-site-issue/experimentalAPIs/actors/tabExtrasActor.jsm create mode 100644 browser/extensions/report-site-issue/experimentalAPIs/browserInfo.js create mode 100644 browser/extensions/report-site-issue/experimentalAPIs/browserInfo.json create mode 100644 browser/extensions/report-site-issue/experimentalAPIs/l10n.js create mode 100644 browser/extensions/report-site-issue/experimentalAPIs/l10n.json create mode 100644 browser/extensions/report-site-issue/experimentalAPIs/pageActionExtras.js create mode 100644 browser/extensions/report-site-issue/experimentalAPIs/pageActionExtras.json create mode 100644 browser/extensions/report-site-issue/experimentalAPIs/tabExtras.js create mode 100644 browser/extensions/report-site-issue/experimentalAPIs/tabExtras.json create mode 100644 browser/extensions/report-site-issue/icons/lightbulb.svg create mode 100644 browser/extensions/report-site-issue/locales/en-US/webcompat.properties create mode 100644 browser/extensions/report-site-issue/locales/jar.mn create mode 100644 browser/extensions/report-site-issue/locales/moz.build create mode 100644 browser/extensions/report-site-issue/manifest.json create mode 100644 browser/extensions/report-site-issue/moz.build create mode 100644 browser/extensions/report-site-issue/test/browser/.eslintrc.js create mode 100644 browser/extensions/report-site-issue/test/browser/browser.ini create mode 100644 browser/extensions/report-site-issue/test/browser/browser_button_state.js create mode 100644 browser/extensions/report-site-issue/test/browser/browser_disabled_cleanup.js create mode 100644 browser/extensions/report-site-issue/test/browser/browser_report_site_issue.js create mode 100644 browser/extensions/report-site-issue/test/browser/fastclick.html create mode 100644 browser/extensions/report-site-issue/test/browser/frameworks.html create mode 100644 browser/extensions/report-site-issue/test/browser/head.js create mode 100644 browser/extensions/report-site-issue/test/browser/test.html create mode 100644 browser/extensions/report-site-issue/test/browser/webcompat.html create mode 100644 browser/extensions/screenshots/assertIsBlankDocument.js create mode 100644 browser/extensions/screenshots/assertIsTrusted.js create mode 100644 browser/extensions/screenshots/background/analytics.js create mode 100644 browser/extensions/screenshots/background/auth.js create mode 100644 browser/extensions/screenshots/background/communication.js create mode 100644 browser/extensions/screenshots/background/deviceInfo.js create mode 100644 browser/extensions/screenshots/background/main.js create mode 100644 browser/extensions/screenshots/background/selectorLoader.js create mode 100644 browser/extensions/screenshots/background/senderror.js create mode 100644 browser/extensions/screenshots/background/startBackground.js create mode 100644 browser/extensions/screenshots/background/takeshot.js create mode 100644 browser/extensions/screenshots/blank.html create mode 100644 browser/extensions/screenshots/blobConverters.js create mode 100644 browser/extensions/screenshots/build/buildSettings.js create mode 100644 browser/extensions/screenshots/build/inlineSelectionCss.js create mode 100644 browser/extensions/screenshots/build/raven.js create mode 100644 browser/extensions/screenshots/build/selection.js create mode 100644 browser/extensions/screenshots/build/shot.js create mode 100644 browser/extensions/screenshots/build/thumbnailGenerator.js create mode 100644 browser/extensions/screenshots/catcher.js create mode 100644 browser/extensions/screenshots/clipboard.js create mode 100644 browser/extensions/screenshots/domainFromUrl.js create mode 100644 browser/extensions/screenshots/experiments/screenshots/api.js create mode 100644 browser/extensions/screenshots/experiments/screenshots/schema.json create mode 100644 browser/extensions/screenshots/icons/cancel.svg create mode 100644 browser/extensions/screenshots/icons/cloud.svg create mode 100644 browser/extensions/screenshots/icons/copied-notification.svg create mode 100644 browser/extensions/screenshots/icons/copy.svg create mode 100644 browser/extensions/screenshots/icons/download-white.svg create mode 100644 browser/extensions/screenshots/icons/download.svg create mode 100644 browser/extensions/screenshots/icons/help-16.svg create mode 100644 browser/extensions/screenshots/icons/icon-highlight-32-v2.svg create mode 100644 browser/extensions/screenshots/icons/icon-v2.svg create mode 100644 browser/extensions/screenshots/icons/icon-welcome-face-without-eyes.svg create mode 100644 browser/extensions/screenshots/icons/menu-fullpage.svg create mode 100644 browser/extensions/screenshots/icons/menu-myshot-white.svg create mode 100644 browser/extensions/screenshots/icons/menu-myshot.svg create mode 100644 browser/extensions/screenshots/icons/menu-visible.svg create mode 100644 browser/extensions/screenshots/log.js create mode 100644 browser/extensions/screenshots/makeUuid.js create mode 100644 browser/extensions/screenshots/manifest.json create mode 100644 browser/extensions/screenshots/moz.build create mode 100644 browser/extensions/screenshots/randomString.js create mode 100644 browser/extensions/screenshots/selector/callBackground.js create mode 100644 browser/extensions/screenshots/selector/documentMetadata.js create mode 100644 browser/extensions/screenshots/selector/shooter.js create mode 100644 browser/extensions/screenshots/selector/ui.js create mode 100644 browser/extensions/screenshots/selector/uicontrol.js create mode 100644 browser/extensions/screenshots/selector/util.js create mode 100644 browser/extensions/screenshots/sitehelper.js create mode 100644 browser/extensions/screenshots/test/browser/.eslintrc.yml create mode 100644 browser/extensions/screenshots/test/browser/browser.ini create mode 100644 browser/extensions/screenshots/test/browser/browser_screenshots_injection.js create mode 100644 browser/extensions/screenshots/test/browser/browser_screenshots_ui_check.js create mode 100644 browser/extensions/screenshots/test/browser/injection-page.html create mode 100644 browser/extensions/webcompat/about-compat/AboutCompat.jsm create mode 100644 browser/extensions/webcompat/about-compat/aboutCompat.css create mode 100644 browser/extensions/webcompat/about-compat/aboutCompat.html create mode 100644 browser/extensions/webcompat/about-compat/aboutCompat.js create mode 100644 browser/extensions/webcompat/about-compat/aboutPage.js create mode 100644 browser/extensions/webcompat/about-compat/aboutPage.json create mode 100644 browser/extensions/webcompat/about-compat/aboutPageProcessScript.js create mode 100644 browser/extensions/webcompat/components.conf create mode 100644 browser/extensions/webcompat/data/injections.js create mode 100644 browser/extensions/webcompat/data/picture_in_picture_overrides.js create mode 100644 browser/extensions/webcompat/data/shims.js create mode 100644 browser/extensions/webcompat/data/ua_overrides.js create mode 100644 browser/extensions/webcompat/experiment-apis/aboutConfigPrefs.js create mode 100644 browser/extensions/webcompat/experiment-apis/aboutConfigPrefs.json create mode 100644 browser/extensions/webcompat/experiment-apis/appConstants.js create mode 100644 browser/extensions/webcompat/experiment-apis/appConstants.json create mode 100644 browser/extensions/webcompat/experiment-apis/experiments.js create mode 100644 browser/extensions/webcompat/experiment-apis/experiments.json create mode 100644 browser/extensions/webcompat/experiment-apis/matchPatterns.js create mode 100644 browser/extensions/webcompat/experiment-apis/matchPatterns.json create mode 100644 browser/extensions/webcompat/experiment-apis/pictureInPicture.js create mode 100644 browser/extensions/webcompat/experiment-apis/pictureInPicture.json create mode 100644 browser/extensions/webcompat/experiment-apis/sharedPreferences.js create mode 100644 browser/extensions/webcompat/experiment-apis/sharedPreferences.json create mode 100644 browser/extensions/webcompat/experiment-apis/systemManufacturer.js create mode 100644 browser/extensions/webcompat/experiment-apis/systemManufacturer.json create mode 100644 browser/extensions/webcompat/experiment-apis/trackingProtection.js create mode 100644 browser/extensions/webcompat/experiment-apis/trackingProtection.json create mode 100644 browser/extensions/webcompat/injections/css/bug0000000-testbed-css-injection.css create mode 100644 browser/extensions/webcompat/injections/css/bug1561371-mail.google.com-allow-horizontal-scrolling.css create mode 100644 browser/extensions/webcompat/injections/css/bug1570119-teamcoco.com-scrollbar-width.css create mode 100644 browser/extensions/webcompat/injections/css/bug1570328-developer-apple.com-transform-scale.css create mode 100644 browser/extensions/webcompat/injections/css/bug1575000-apply.lloydsbank.co.uk-radio-buttons-fix.css create mode 100644 browser/extensions/webcompat/injections/css/bug1605611-maps.google.com-directions-time.css create mode 100644 browser/extensions/webcompat/injections/css/bug1610016-gaana.com-input-position-fix.css create mode 100644 browser/extensions/webcompat/injections/css/bug1610344-directv.com.co-hide-unsupported-message.css create mode 100644 browser/extensions/webcompat/injections/css/bug1644830-missingmail.usps.com-checkboxes-not-visible.css create mode 100644 browser/extensions/webcompat/injections/css/bug1645064-s-kanava.fi-invisible-charts.css create mode 100644 browser/extensions/webcompat/injections/css/bug1651917-teletrader.com.body-transform-origin.css create mode 100644 browser/extensions/webcompat/injections/css/bug1653075-livescience.com-scrollbar-width.css create mode 100644 browser/extensions/webcompat/injections/css/bug1654865-sports.ndtv.com-float-fix.css create mode 100644 browser/extensions/webcompat/injections/css/bug1654877-preev.com-moz-appearance-fix.css create mode 100644 browser/extensions/webcompat/injections/css/bug1654907-reactine.ca-hide-unsupported.css create mode 100644 browser/extensions/webcompat/injections/css/bug1655049-dev.to-unclickable-button-fix.css create mode 100644 browser/extensions/webcompat/injections/css/bug1666771-zilow-map-overdraw.css create mode 100644 browser/extensions/webcompat/injections/js/bug0000000-testbed-js-injection.js create mode 100644 browser/extensions/webcompat/injections/js/bug1452707-window.controllers-shim-ib.absa.co.za.js create mode 100644 browser/extensions/webcompat/injections/js/bug1457335-histography.io-ua-change.js create mode 100644 browser/extensions/webcompat/injections/js/bug1472075-bankofamerica.com-ua-change.js create mode 100644 browser/extensions/webcompat/injections/js/bug1570856-medium.com-menu-isTier1.js create mode 100644 browser/extensions/webcompat/injections/js/bug1579159-m.tailieu.vn-pdfjs-worker-disable.js create mode 100644 browser/extensions/webcompat/injections/js/bug1605611-maps.google.com-directions-time.js create mode 100644 browser/extensions/webcompat/injections/js/bug1610358-pcloud.com-appVersion-change.js create mode 100644 browser/extensions/webcompat/injections/js/bug1631811-datastudio.google.com-indexedDB.js create mode 100644 browser/extensions/webcompat/injections/js/bug1665035-dckids.com-cookieEnabled.js create mode 100644 browser/extensions/webcompat/injections/js/bug1677442-store.hp.com-disable-indexeddb.js create mode 100644 browser/extensions/webcompat/injections/js/bug1682238-gamearter.com-ua-change.js create mode 100644 browser/extensions/webcompat/lib/about_compat_broker.js create mode 100644 browser/extensions/webcompat/lib/custom_functions.js create mode 100644 browser/extensions/webcompat/lib/injections.js create mode 100644 browser/extensions/webcompat/lib/intervention_helpers.js create mode 100644 browser/extensions/webcompat/lib/messaging_helper.js create mode 100644 browser/extensions/webcompat/lib/module_shim.js create mode 100644 browser/extensions/webcompat/lib/picture_in_picture_overrides.js create mode 100644 browser/extensions/webcompat/lib/shim_messaging_helper.js create mode 100644 browser/extensions/webcompat/lib/shims.js create mode 100644 browser/extensions/webcompat/lib/ua_overrides.js create mode 100644 browser/extensions/webcompat/manifest.json create mode 100644 browser/extensions/webcompat/moz.build create mode 100644 browser/extensions/webcompat/run.js create mode 100644 browser/extensions/webcompat/shims/adsafeprotected-ima.js create mode 100644 browser/extensions/webcompat/shims/bmauth.js create mode 100644 browser/extensions/webcompat/shims/eluminate.js create mode 100644 browser/extensions/webcompat/shims/empty-script.js create mode 100644 browser/extensions/webcompat/shims/facebook-sdk.js create mode 100644 browser/extensions/webcompat/shims/google-analytics-ecommerce-plugin.js create mode 100644 browser/extensions/webcompat/shims/google-analytics-legacy.js create mode 100644 browser/extensions/webcompat/shims/google-analytics-tag-manager.js create mode 100644 browser/extensions/webcompat/shims/google-analytics.js create mode 100644 browser/extensions/webcompat/shims/google-publisher-tags.js create mode 100644 browser/extensions/webcompat/shims/live-test-shim.js create mode 100644 browser/extensions/webcompat/shims/mochitest-shim-1.js create mode 100644 browser/extensions/webcompat/shims/mochitest-shim-2.js create mode 100644 browser/extensions/webcompat/shims/mochitest-shim-3.js create mode 100644 browser/extensions/webcompat/shims/rambler-authenticator.js create mode 100644 browser/extensions/webcompat/shims/rich-relevance.js create mode 100644 browser/extensions/webcompat/tests/browser/browser.ini create mode 100644 browser/extensions/webcompat/tests/browser/browser_shims.js create mode 100644 browser/extensions/webcompat/tests/browser/head.js create mode 100644 browser/extensions/webcompat/tests/browser/iframe_test.html create mode 100644 browser/extensions/webcompat/tests/browser/shims_test.html create mode 100644 browser/extensions/webcompat/tests/browser/shims_test.js create mode 100644 browser/extensions/webcompat/tests/browser/shims_test_2.html create mode 100644 browser/extensions/webcompat/tests/browser/shims_test_2.js create mode 100644 browser/extensions/webcompat/tests/browser/shims_test_3.html create mode 100644 browser/extensions/webcompat/tests/browser/shims_test_3.js (limited to 'browser/extensions') diff --git a/browser/extensions/doh-rollout/manifest.json b/browser/extensions/doh-rollout/manifest.json new file mode 100644 index 0000000000..d41945d991 --- /dev/null +++ b/browser/extensions/doh-rollout/manifest.json @@ -0,0 +1,15 @@ +{ + "manifest_version": 2, + "name": "DoH Roll-Out", + "description": "This used to be a Mozilla add-on that supported the roll-out of DoH, but now only exists as a stub to enable migrations.", + "version": "2.0.0", + + "hidden": true, + + "applications": { + "gecko": { + "id": "doh-rollout@mozilla.org", + "strict_min_version": "72.0a1" + } + } +} diff --git a/browser/extensions/doh-rollout/moz.build b/browser/extensions/doh-rollout/moz.build new file mode 100644 index 0000000000..bce8283117 --- /dev/null +++ b/browser/extensions/doh-rollout/moz.build @@ -0,0 +1,14 @@ +# -*- 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/. + +DEFINES["MOZ_APP_VERSION"] = CONFIG["MOZ_APP_VERSION"] +DEFINES["MOZ_APP_MAXVERSION"] = CONFIG["MOZ_APP_MAXVERSION"] + + +FINAL_TARGET_FILES.features["doh-rollout@mozilla.org"] += ["manifest.json"] + +with Files("**"): + BUG_COMPONENT = ("Firefox", "Security") diff --git a/browser/extensions/formautofill/.eslintrc.js b/browser/extensions/formautofill/.eslintrc.js new file mode 100644 index 0000000000..0187f91da3 --- /dev/null +++ b/browser/extensions/formautofill/.eslintrc.js @@ -0,0 +1,100 @@ +/* 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 = { + rules: { + // Rules from the mozilla plugin + "mozilla/balanced-listeners": "error", + "mozilla/no-aArgs": "error", + "mozilla/var-only-at-top-level": "error", + + "valid-jsdoc": [ + "error", + { + prefer: { + return: "returns", + }, + preferType: { + Boolean: "boolean", + Number: "number", + String: "string", + bool: "boolean", + }, + requireParamDescription: false, + requireReturn: false, + requireReturnDescription: false, + }, + ], + + // No expressions where a statement is expected + "no-unused-expressions": "error", + + // No declaring variables that are never used + "no-unused-vars": [ + "error", + { + args: "none", + vars: "all", + }, + ], + + // No using variables before defined + "no-use-before-define": "error", + + // Disallow using variables outside the blocks they are defined (especially + // since only let and const are used, see "no-var"). + "block-scoped-var": "error", + + // Warn about cyclomatic complexity in functions. + complexity: ["error", { max: 26 }], + + // Maximum depth callbacks can be nested. + "max-nested-callbacks": ["error", 4], + + // Disallow using the console API. + "no-console": "error", + + // Disallow fallthrough of case statements, except if there is a comment. + "no-fallthrough": "error", + + // Disallow use of multiline strings (use template strings instead). + "no-multi-str": "error", + + // Disallow usage of __proto__ property. + "no-proto": "error", + + // Disallow use of assignment in return statement. It is preferable for a + // single line of code to have only one easily predictable effect. + "no-return-assign": "error", + + // Require use of the second argument for parseInt(). + radix: "error", + + // Require "use strict" to be defined globally in the script. + strict: ["error", "global"], + + // Disallow Yoda conditions (where literal value comes first). + yoda: "error", + + // Disallow function or variable declarations in nested blocks + "no-inner-declarations": "error", + }, + + overrides: [ + { + files: "test/unit/head.js", + rules: { + "no-unused-vars": [ + "error", + { + args: "none", + vars: "local", + }, + ], + }, + }, + ], +}; diff --git a/browser/extensions/formautofill/FormAutofill.jsm b/browser/extensions/formautofill/FormAutofill.jsm new file mode 100644 index 0000000000..513f82d22e --- /dev/null +++ b/browser/extensions/formautofill/FormAutofill.jsm @@ -0,0 +1,156 @@ +/* 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"; + +var EXPORTED_SYMBOLS = ["FormAutofill"]; + +const { XPCOMUtils } = ChromeUtils.import( + "resource://gre/modules/XPCOMUtils.jsm" +); +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); + +XPCOMUtils.defineLazyModuleGetters(this, { + Region: "resource://gre/modules/Region.jsm", +}); + +const ADDRESSES_FIRST_TIME_USE_PREF = "extensions.formautofill.firstTimeUse"; +const AUTOFILL_CREDITCARDS_AVAILABLE_PREF = + "extensions.formautofill.creditCards.available"; +const CREDITCARDS_USED_STATUS_PREF = "extensions.formautofill.creditCards.used"; +const ENABLED_AUTOFILL_ADDRESSES_PREF = + "extensions.formautofill.addresses.enabled"; +const ENABLED_AUTOFILL_ADDRESSES_CAPTURE_PREF = + "extensions.formautofill.addresses.capture.enabled"; +const ENABLED_AUTOFILL_CREDITCARDS_PREF = + "extensions.formautofill.creditCards.enabled"; +const ENABLED_AUTOFILL_CREDITCARDS_REAUTH_PREF = + "extensions.formautofill.reauth.enabled"; +const AUTOFILL_CREDITCARDS_HIDE_UI_PREF = + "extensions.formautofill.creditCards.hideui"; +const SUPPORTED_COUNTRIES_PREF = "extensions.formautofill.supportedCountries"; + +XPCOMUtils.defineLazyPreferenceGetter( + this, + "logLevel", + "extensions.formautofill.loglevel", + "Warn" +); + +// A logging helper for debug logging to avoid creating Console objects +// or triggering expensive JS -> C++ calls when debug logging is not +// enabled. +// +// Console objects, even natively-implemented ones, can consume a lot of +// memory, and since this code may run in every content process, that +// memory can add up quickly. And, even when debug-level messages are +// being ignored, console.debug() calls can be expensive. +// +// This helper avoids both of those problems by never touching the +// console object unless debug logging is enabled. +function debug() { + if (logLevel.toLowerCase() == "debug") { + this.log.debug(...arguments); + } +} + +var FormAutofill = { + ENABLED_AUTOFILL_ADDRESSES_PREF, + ENABLED_AUTOFILL_ADDRESSES_CAPTURE_PREF, + ENABLED_AUTOFILL_CREDITCARDS_PREF, + ENABLED_AUTOFILL_CREDITCARDS_REAUTH_PREF, + ADDRESSES_FIRST_TIME_USE_PREF, + CREDITCARDS_USED_STATUS_PREF, + + get DEFAULT_REGION() { + return Region.home || "US"; + }, + get isAutofillEnabled() { + return ( + FormAutofill.isAutofillAddressesEnabled || + this.isAutofillCreditCardsEnabled + ); + }, + get isAutofillCreditCardsEnabled() { + return ( + FormAutofill.isAutofillCreditCardsAvailable && + FormAutofill._isAutofillCreditCardsEnabled + ); + }, + + defineLazyLogGetter(scope, logPrefix) { + scope.debug = debug; + + XPCOMUtils.defineLazyGetter(scope, "log", () => { + let ConsoleAPI = ChromeUtils.import( + "resource://gre/modules/Console.jsm", + {} + ).ConsoleAPI; + return new ConsoleAPI({ + maxLogLevelPref: "extensions.formautofill.loglevel", + prefix: logPrefix, + }); + }); + }, +}; + +XPCOMUtils.defineLazyPreferenceGetter( + FormAutofill, + "isAutofillAddressesEnabled", + ENABLED_AUTOFILL_ADDRESSES_PREF +); +XPCOMUtils.defineLazyPreferenceGetter( + FormAutofill, + "isAutofillAddressesCaptureEnabled", + ENABLED_AUTOFILL_ADDRESSES_CAPTURE_PREF +); +XPCOMUtils.defineLazyPreferenceGetter( + FormAutofill, + "isAutofillCreditCardsAvailable", + AUTOFILL_CREDITCARDS_AVAILABLE_PREF +); +XPCOMUtils.defineLazyPreferenceGetter( + FormAutofill, + "_isAutofillCreditCardsEnabled", + ENABLED_AUTOFILL_CREDITCARDS_PREF +); +XPCOMUtils.defineLazyPreferenceGetter( + FormAutofill, + "isAutofillCreditCardsHideUI", + AUTOFILL_CREDITCARDS_HIDE_UI_PREF +); +XPCOMUtils.defineLazyPreferenceGetter( + FormAutofill, + "isAutofillAddressesFirstTimeUse", + ADDRESSES_FIRST_TIME_USE_PREF +); +XPCOMUtils.defineLazyPreferenceGetter( + FormAutofill, + "AutofillCreditCardsUsedStatus", + CREDITCARDS_USED_STATUS_PREF +); +XPCOMUtils.defineLazyPreferenceGetter( + FormAutofill, + "supportedCountries", + SUPPORTED_COUNTRIES_PREF, + null, + null, + val => val.split(",") +); + +// XXX: This should be invalidated on intl:app-locales-changed. +XPCOMUtils.defineLazyGetter(FormAutofill, "countries", () => { + let availableRegionCodes = Services.intl.getAvailableLocaleDisplayNames( + "region" + ); + let displayNames = Services.intl.getRegionDisplayNames( + undefined, + availableRegionCodes + ); + let result = new Map(); + for (let i = 0; i < availableRegionCodes.length; i++) { + result.set(availableRegionCodes[i].toUpperCase(), displayNames[i]); + } + return result; +}); diff --git a/browser/extensions/formautofill/FormAutofillChild.jsm b/browser/extensions/formautofill/FormAutofillChild.jsm new file mode 100644 index 0000000000..58aa1c4a91 --- /dev/null +++ b/browser/extensions/formautofill/FormAutofillChild.jsm @@ -0,0 +1,198 @@ +/* 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"; + +var EXPORTED_SYMBOLS = ["FormAutofillChild"]; + +var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); +ChromeUtils.defineModuleGetter( + this, + "setTimeout", + "resource://gre/modules/Timer.jsm" +); +ChromeUtils.defineModuleGetter( + this, + "FormAutofill", + "resource://formautofill/FormAutofill.jsm" +); +ChromeUtils.defineModuleGetter( + this, + "FormAutofillContent", + "resource://formautofill/FormAutofillContent.jsm" +); +ChromeUtils.defineModuleGetter( + this, + "FormAutofillUtils", + "resource://formautofill/FormAutofillUtils.jsm" +); +ChromeUtils.defineModuleGetter( + this, + "AutoCompleteChild", + "resource://gre/actors/AutoCompleteChild.jsm" +); + +/** + * Handles content's interactions for the frame. + */ +class FormAutofillChild extends JSWindowActorChild { + constructor() { + super(); + + this._nextHandleElement = null; + this._alreadyDOMContentLoaded = false; + this._hasDOMContentLoadedHandler = false; + this._hasPendingTask = false; + this.testListener = null; + + AutoCompleteChild.addPopupStateListener(this); + } + + didDestroy() { + AutoCompleteChild.removePopupStateListener(this); + } + + popupStateChanged(messageName, data, target) { + let docShell; + try { + docShell = this.docShell; + } catch (ex) { + AutoCompleteChild.removePopupStateListener(this); + return; + } + + if (!FormAutofill.isAutofillEnabled) { + return; + } + + const { chromeEventHandler } = docShell; + + switch (messageName) { + case "FormAutoComplete:PopupClosed": { + FormAutofillContent.onPopupClosed(data.selectedRowStyle); + Services.tm.dispatchToMainThread(() => { + chromeEventHandler.removeEventListener( + "keydown", + FormAutofillContent._onKeyDown, + true + ); + }); + + break; + } + case "FormAutoComplete:PopupOpened": { + FormAutofillContent.onPopupOpened(); + chromeEventHandler.addEventListener( + "keydown", + FormAutofillContent._onKeyDown, + true + ); + break; + } + } + } + + _doIdentifyAutofillFields() { + if (this._hasPendingTask) { + return; + } + this._hasPendingTask = true; + + setTimeout(() => { + FormAutofillContent.identifyAutofillFields(this._nextHandleElement); + this._hasPendingTask = false; + this._nextHandleElement = null; + // This is for testing purpose only which sends a notification to indicate that the + // form has been identified, and ready to open popup. + this.sendAsyncMessage("FormAutofill:FieldsIdentified"); + FormAutofillContent.updateActiveInput(); + }); + } + + handleEvent(evt) { + if (!evt.isTrusted) { + return; + } + + switch (evt.type) { + case "focusin": { + if (FormAutofill.isAutofillEnabled) { + this.onFocusIn(evt); + } + break; + } + case "DOMFormBeforeSubmit": { + if (FormAutofill.isAutofillEnabled) { + this.onDOMFormBeforeSubmit(evt); + } + break; + } + + default: { + throw new Error("Unexpected event type"); + } + } + } + + onFocusIn(evt) { + FormAutofillContent.updateActiveInput(); + + let element = evt.target; + if (!FormAutofillUtils.isFieldEligibleForAutofill(element)) { + return; + } + this._nextHandleElement = element; + + if (!this._alreadyDOMContentLoaded) { + let doc = element.ownerDocument; + if (doc.readyState === "loading") { + if (!this._hasDOMContentLoadedHandler) { + this._hasDOMContentLoadedHandler = true; + doc.addEventListener( + "DOMContentLoaded", + () => this._doIdentifyAutofillFields(), + { once: true } + ); + } + return; + } + this._alreadyDOMContentLoaded = true; + } + + this._doIdentifyAutofillFields(); + } + + /** + * Handle the DOMFormBeforeSubmit event. + * @param {Event} evt + */ + onDOMFormBeforeSubmit(evt) { + let formElement = evt.target; + + if (!FormAutofill.isAutofillEnabled) { + return; + } + + FormAutofillContent.formSubmitted(formElement); + } + + receiveMessage(message) { + if (!FormAutofill.isAutofillEnabled) { + return; + } + + const doc = this.document; + + switch (message.name) { + case "FormAutofill:PreviewProfile": { + FormAutofillContent.previewProfile(doc); + break; + } + case "FormAutofill:ClearForm": { + FormAutofillContent.clearForm(); + break; + } + } + } +} diff --git a/browser/extensions/formautofill/FormAutofillContent.jsm b/browser/extensions/formautofill/FormAutofillContent.jsm new file mode 100644 index 0000000000..ff7ebd06de --- /dev/null +++ b/browser/extensions/formautofill/FormAutofillContent.jsm @@ -0,0 +1,915 @@ +/* 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/. */ + +/** + * Form Autofill content process module. + */ + +/* eslint-disable no-use-before-define */ + +"use strict"; + +var EXPORTED_SYMBOLS = ["FormAutofillContent"]; + +const Cm = Components.manager; + +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); +const { XPCOMUtils } = ChromeUtils.import( + "resource://gre/modules/XPCOMUtils.jsm" +); + +ChromeUtils.defineModuleGetter( + this, + "AddressResult", + "resource://formautofill/ProfileAutoCompleteResult.jsm" +); +ChromeUtils.defineModuleGetter( + this, + "ComponentUtils", + "resource://gre/modules/ComponentUtils.jsm" +); +ChromeUtils.defineModuleGetter( + this, + "CreditCardResult", + "resource://formautofill/ProfileAutoCompleteResult.jsm" +); +ChromeUtils.defineModuleGetter( + this, + "FormAutofill", + "resource://formautofill/FormAutofill.jsm" +); +ChromeUtils.defineModuleGetter( + this, + "FormAutofillHandler", + "resource://formautofill/FormAutofillHandler.jsm" +); +ChromeUtils.defineModuleGetter( + this, + "FormAutofillUtils", + "resource://formautofill/FormAutofillUtils.jsm" +); +ChromeUtils.defineModuleGetter( + this, + "FormLikeFactory", + "resource://gre/modules/FormLikeFactory.jsm" +); +ChromeUtils.defineModuleGetter( + this, + "InsecurePasswordUtils", + "resource://gre/modules/InsecurePasswordUtils.jsm" +); +ChromeUtils.defineModuleGetter( + this, + "PrivateBrowsingUtils", + "resource://gre/modules/PrivateBrowsingUtils.jsm" +); + +const formFillController = Cc[ + "@mozilla.org/satchel/form-fill-controller;1" +].getService(Ci.nsIFormFillController); +const autocompleteController = Cc[ + "@mozilla.org/autocomplete/controller;1" +].getService(Ci.nsIAutoCompleteController); + +XPCOMUtils.defineLazyGetter( + this, + "ADDRESSES_COLLECTION_NAME", + () => FormAutofillUtils.ADDRESSES_COLLECTION_NAME +); +XPCOMUtils.defineLazyGetter( + this, + "CREDITCARDS_COLLECTION_NAME", + () => FormAutofillUtils.CREDITCARDS_COLLECTION_NAME +); +XPCOMUtils.defineLazyGetter( + this, + "FIELD_STATES", + () => FormAutofillUtils.FIELD_STATES +); + +function getActorFromWindow(contentWindow, name = "FormAutofill") { + // In unit tests, contentWindow isn't a real window. + if (!contentWindow) { + return null; + } + + return contentWindow.windowGlobalChild + ? contentWindow.windowGlobalChild.getActor(name) + : null; +} + +// Register/unregister a constructor as a factory. +function AutocompleteFactory() {} +AutocompleteFactory.prototype = { + register(targetConstructor) { + let proto = targetConstructor.prototype; + this._classID = proto.classID; + + let factory = ComponentUtils._getFactory(targetConstructor); + this._factory = factory; + + let registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); + registrar.registerFactory( + proto.classID, + proto.classDescription, + proto.contractID, + factory + ); + + if (proto.classID2) { + this._classID2 = proto.classID2; + registrar.registerFactory( + proto.classID2, + proto.classDescription, + proto.contractID2, + factory + ); + } + }, + + unregister() { + let registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); + registrar.unregisterFactory(this._classID, this._factory); + if (this._classID2) { + registrar.unregisterFactory(this._classID2, this._factory); + } + this._factory = null; + }, +}; + +/** + * @constructor + * + * @implements {nsIAutoCompleteSearch} + */ +function AutofillProfileAutoCompleteSearch() { + FormAutofill.defineLazyLogGetter(this, "AutofillProfileAutoCompleteSearch"); +} +AutofillProfileAutoCompleteSearch.prototype = { + classID: Components.ID("4f9f1e4c-7f2c-439e-9c9e-566b68bc187d"), + contractID: "@mozilla.org/autocomplete/search;1?name=autofill-profiles", + classDescription: "AutofillProfileAutoCompleteSearch", + QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteSearch"]), + + // Begin nsIAutoCompleteSearch implementation + + /** + * Searches for a given string and notifies a listener (either synchronously + * or asynchronously) of the result + * + * @param {string} searchString the string to search for + * @param {string} searchParam + * @param {Object} previousResult a previous result to use for faster searchinig + * @param {Object} listener the listener to notify when the search is complete + */ + startSearch(searchString, searchParam, previousResult, listener) { + let { + activeInput, + activeSection, + activeFieldDetail, + savedFieldNames, + } = FormAutofillContent; + this.forceStop = false; + + this.debug("startSearch: for", searchString, "with input", activeInput); + + let isAddressField = FormAutofillUtils.isAddressField( + activeFieldDetail.fieldName + ); + const isCreditCardField = FormAutofillUtils.isCreditCardField( + activeFieldDetail.fieldName + ); + let isInputAutofilled = activeFieldDetail.state == FIELD_STATES.AUTO_FILLED; + let allFieldNames = activeSection.allFieldNames; + let filledRecordGUID = activeSection.filledRecordGUID; + + let creditCardsEnabledAndVisible = + FormAutofill.isAutofillCreditCardsEnabled && + !FormAutofill.isAutofillCreditCardsHideUI; + let searchPermitted = isAddressField + ? FormAutofill.isAutofillAddressesEnabled + : creditCardsEnabledAndVisible; + let AutocompleteResult = isAddressField ? AddressResult : CreditCardResult; + let isFormAutofillSearch = true; + let pendingSearchResult = null; + + ProfileAutocomplete.lastProfileAutoCompleteFocusedInput = activeInput; + // Fallback to form-history if ... + // - specified autofill feature is pref off. + // - no profile can fill the currently-focused input. + // - the current form has already been populated and the field is not + // an empty credit card field. + // - (address only) less than 3 inputs are covered by all saved fields in the storage. + if ( + !searchPermitted || + !savedFieldNames.has(activeFieldDetail.fieldName) || + (!isInputAutofilled && + filledRecordGUID && + !(isCreditCardField && activeInput.value === "")) || + (isAddressField && + allFieldNames.filter(field => savedFieldNames.has(field)).length < + FormAutofillUtils.AUTOFILL_FIELDS_THRESHOLD) + ) { + isFormAutofillSearch = false; + if (activeInput.autocomplete == "off") { + // Create a dummy result as an empty search result. + pendingSearchResult = new AutocompleteResult("", "", [], [], {}); + } else { + pendingSearchResult = new Promise(resolve => { + let formHistory = Cc[ + "@mozilla.org/autocomplete/search;1?name=form-history" + ].createInstance(Ci.nsIAutoCompleteSearch); + formHistory.startSearch(searchString, searchParam, previousResult, { + onSearchResult: (_, result) => resolve(result), + }); + }); + } + } else if (isInputAutofilled) { + pendingSearchResult = new AutocompleteResult(searchString, "", [], [], { + isInputAutofilled, + }); + } else { + let infoWithoutElement = { ...activeFieldDetail }; + delete infoWithoutElement.elementWeakRef; + + let data = { + collectionName: isAddressField + ? ADDRESSES_COLLECTION_NAME + : CREDITCARDS_COLLECTION_NAME, + info: infoWithoutElement, + searchString, + }; + + pendingSearchResult = this._getRecords(activeInput, data).then( + records => { + if (this.forceStop) { + return null; + } + // Sort addresses by timeLastUsed for showing the lastest used address at top. + records.sort((a, b) => b.timeLastUsed - a.timeLastUsed); + + let adaptedRecords = activeSection.getAdaptedProfiles(records); + let handler = FormAutofillContent.activeHandler; + let isSecure = InsecurePasswordUtils.isFormSecure(handler.form); + + return new AutocompleteResult( + searchString, + activeFieldDetail.fieldName, + allFieldNames, + adaptedRecords, + { isSecure, isInputAutofilled } + ); + } + ); + } + + Promise.resolve(pendingSearchResult).then(result => { + listener.onSearchResult(this, result); + // Don't save cache results or reset state when returning non-autofill results such as the + // form history fallback above. + if (isFormAutofillSearch) { + ProfileAutocomplete.lastProfileAutoCompleteResult = result; + // Reset AutoCompleteController's state at the end of startSearch to ensure that + // none of form autofill result will be cached in other places and make the + // result out of sync. + autocompleteController.resetInternalState(); + } else { + // Clear the cache so that we don't try to autofill from it after falling + // back to form history. + ProfileAutocomplete.lastProfileAutoCompleteResult = null; + } + }); + }, + + /** + * Stops an asynchronous search that is in progress + */ + stopSearch() { + ProfileAutocomplete.lastProfileAutoCompleteResult = null; + this.forceStop = true; + }, + + /** + * Get the records from parent process for AutoComplete result. + * + * @private + * @param {Object} input + * 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. + * @returns {Promise} + * Promise that resolves when addresses returned from parent process. + */ + _getRecords(input, data) { + this.debug("_getRecords with data:", data); + if (!input) { + return []; + } + + let actor = getActorFromWindow(input.ownerGlobal); + return actor.sendQuery("FormAutofill:GetRecords", data); + }, +}; + +let ProfileAutocomplete = { + QueryInterface: ChromeUtils.generateQI(["nsIObserver"]), + + lastProfileAutoCompleteResult: null, + lastProfileAutoCompleteFocusedInput: null, + _registered: false, + _factory: null, + + ensureRegistered() { + if (this._registered) { + return; + } + + FormAutofill.defineLazyLogGetter(this, "ProfileAutocomplete"); + this.debug("ensureRegistered"); + this._factory = new AutocompleteFactory(); + this._factory.register(AutofillProfileAutoCompleteSearch); + this._registered = true; + + Services.obs.addObserver(this, "autocomplete-will-enter-text"); + + this.debug( + "ensureRegistered. Finished with _registered:", + this._registered + ); + }, + + ensureUnregistered() { + if (!this._registered) { + return; + } + + this.debug("ensureUnregistered"); + this._factory.unregister(); + this._factory = null; + this._registered = false; + this._lastAutoCompleteResult = null; + + Services.obs.removeObserver(this, "autocomplete-will-enter-text"); + }, + + async observe(subject, topic, data) { + switch (topic) { + case "autocomplete-will-enter-text": { + if (!FormAutofillContent.activeInput) { + // The observer notification is for autocomplete in a different process. + break; + } + FormAutofillContent.autofillPending = true; + Services.obs.notifyObservers(null, "autofill-fill-starting"); + await this._fillFromAutocompleteRow(FormAutofillContent.activeInput); + Services.obs.notifyObservers(null, "autofill-fill-complete"); + FormAutofillContent.autofillPending = false; + break; + } + } + }, + + _getSelectedIndex(contentWindow) { + let actor = getActorFromWindow(contentWindow, "AutoComplete"); + if (!actor) { + throw new Error("Invalid autocomplete selectedIndex"); + } + + return actor.selectedIndex; + }, + + async _fillFromAutocompleteRow(focusedInput) { + this.debug("_fillFromAutocompleteRow:", focusedInput); + let formDetails = FormAutofillContent.activeFormDetails; + if (!formDetails) { + // The observer notification is for a different frame. + return; + } + + let selectedIndex = this._getSelectedIndex(focusedInput.ownerGlobal); + if ( + selectedIndex == -1 || + !this.lastProfileAutoCompleteResult || + this.lastProfileAutoCompleteResult.getStyleAt(selectedIndex) != + "autofill-profile" + ) { + return; + } + + let profile = JSON.parse( + this.lastProfileAutoCompleteResult.getCommentAt(selectedIndex) + ); + + await FormAutofillContent.activeHandler.autofillFormFields(profile); + }, + + _clearProfilePreview() { + if ( + !this.lastProfileAutoCompleteFocusedInput || + !FormAutofillContent.activeSection + ) { + return; + } + + FormAutofillContent.activeSection.clearPreviewedFormFields(); + }, + + _previewSelectedProfile(selectedIndex) { + if ( + !FormAutofillContent.activeInput || + !FormAutofillContent.activeFormDetails + ) { + // The observer notification is for a different process/frame. + return; + } + + if ( + !this.lastProfileAutoCompleteResult || + this.lastProfileAutoCompleteResult.getStyleAt(selectedIndex) != + "autofill-profile" + ) { + return; + } + + let profile = JSON.parse( + this.lastProfileAutoCompleteResult.getCommentAt(selectedIndex) + ); + FormAutofillContent.activeSection.previewFormFields(profile); + }, +}; + +/** + * Handles content's interactions for the process. + * + * NOTE: Declares it by "var" to make it accessible in unit tests. + */ +var FormAutofillContent = { + /** + * @type {WeakMap} mapping FormLike root HTML elements to FormAutofillHandler objects. + */ + _formsDetails: new WeakMap(), + + /** + * @type {Set} Set of the fields with usable values in any saved profile. + */ + get savedFieldNames() { + return Services.cpmm.sharedData.get("FormAutofill:savedFieldNames"); + }, + + /** + * @type {Object} The object where to store the active items, e.g. element, + * handler, section, and field detail. + */ + _activeItems: {}, + + /** + * @type {boolean} Flag indicating whether a focus action requiring + * the popup to be active is pending. + */ + _popupPending: false, + + /** + * @type {boolean} Flag indicating whether the form is waiting to be + * filled by Autofill. + */ + _autofillPending: false, + + init() { + FormAutofill.defineLazyLogGetter(this, "FormAutofillContent"); + this.debug("init"); + + // eslint-disable-next-line mozilla/balanced-listeners + Services.cpmm.sharedData.addEventListener("change", this); + + let autofillEnabled = Services.cpmm.sharedData.get("FormAutofill:enabled"); + // If storage hasn't be initialized yet autofillEnabled is undefined but we need to ensure + // autocomplete is registered before the focusin so register it in this case as long as the + // pref is true. + let shouldEnableAutofill = + autofillEnabled === undefined && + (FormAutofill.isAutofillAddressesEnabled || + FormAutofill.isAutofillCreditCardsEnabled); + if (autofillEnabled || shouldEnableAutofill) { + ProfileAutocomplete.ensureRegistered(); + } + }, + + /** + * Send the profile to parent for doorhanger and storage saving/updating. + * + * @param {Object} profile Submitted form's address/creditcard guid and record. + * @param {Object} domWin Current content window. + * @param {int} timeStartedFillingMS Time of form filling started. + */ + _onFormSubmit(profile, domWin, timeStartedFillingMS) { + let actor = getActorFromWindow(domWin); + actor.sendAsyncMessage("FormAutofill:OnFormSubmit", { + profile, + timeStartedFillingMS, + }); + }, + + /** + * Handle a form submission and early return when: + * 1. In private browsing mode. + * 2. Could not map any autofill handler by form element. + * 3. Number of filled fields is less than autofill threshold + * + * @param {HTMLElement} formElement Root element which receives submit event. + * @param {Window} domWin Content window; passed for unit tests and when + * invoked by the FormAutofillSection + * @param {Object} handler FormAutofillHander, if known by caller + */ + formSubmitted( + formElement, + domWin = formElement.ownerGlobal, + handler = undefined + ) { + this.debug("Handling form submission"); + + if (!FormAutofill.isAutofillEnabled) { + this.debug("Form Autofill is disabled"); + return; + } + + // The `domWin` truthiness test is used by unit tests to bypass this check. + if (domWin && PrivateBrowsingUtils.isContentWindowPrivate(domWin)) { + this.debug("Ignoring submission in a private window"); + return; + } + + handler = handler ?? this._formsDetails.get(formElement); + if (!handler) { + this.debug("Form element could not map to an existing handler"); + return; + } + + let records = handler.createRecords(); + if (!Object.values(records).some(typeRecords => typeRecords.length)) { + return; + } + + records.creditCard.forEach(record => { + let extra = { + // Fields which have been filled manually. + fields_not_auto: "0", + // Fields which have been autofilled. + fields_auto: "0", + // Fields which have been autofilled and then modified. + fields_modified: "0", + }; + + if (record.guid !== null) { + // If the `guid` is not null, it means we're editing an existing record. + // In that case, all fields in the record are autofilled, and fields in + // `untouchedFields` are unmodified. + let totalCount = handler.form.elements.length; + let autofilledCount = Object.keys(record.record).length; + let unmodifiedCount = record.untouchedFields.length; + + extra.fields_not_auto = (totalCount - autofilledCount).toString(); + extra.fields_auto = autofilledCount.toString(); + extra.fields_modified = (autofilledCount - unmodifiedCount).toString(); + } else { + // If the `guid` is null, we're filling a new form. + // In that case, all not-null fields are manually filled. + extra.fields_not_auto = Array.from(handler.form.elements) + .filter(element => !!element.value.trim().length) + .length.toString(); + } + + Services.telemetry.recordEvent( + "creditcard", + "submitted", + "cc_form", + record.flowId, + extra + ); + }); + if (records.creditCard.length) { + Services.telemetry.scalarAdd( + "formautofill.creditCards.submitted_sections_count", + records.creditCard.length + ); + } + + this._onFormSubmit(records, domWin, handler.timeStartedFillingMS); + }, + + handleEvent(evt) { + switch (evt.type) { + case "change": { + if (!evt.changedKeys.includes("FormAutofill:enabled")) { + return; + } + if (Services.cpmm.sharedData.get("FormAutofill:enabled")) { + ProfileAutocomplete.ensureRegistered(); + if (this._popupPending) { + this._popupPending = false; + this.debug("handleEvent: Opening deferred popup"); + formFillController.showPopup(); + } + } else { + ProfileAutocomplete.ensureUnregistered(); + } + break; + } + } + }, + + /** + * Get the form's handler from cache which is created after page identified. + * + * @param {HTMLInputElement} element Focused input which triggered profile searching + * @returns {Array|null} + * Return target form's handler from content cache + * (or return null if the information is not found in the cache). + * + */ + _getFormHandler(element) { + if (!element) { + return null; + } + let rootElement = FormLikeFactory.findRootForField(element); + return this._formsDetails.get(rootElement); + }, + + /** + * Get the active form's information from cache which is created after page + * identified. + * + * @returns {Array|null} + * Return target form's information from content cache + * (or return null if the information is not found in the cache). + * + */ + get activeFormDetails() { + let formHandler = this.activeHandler; + return formHandler ? formHandler.fieldDetails : null; + }, + + /** + * All active items should be updated according the active element of + * `formFillController.focusedInput`. All of them including element, + * handler, section, and field detail, can be retrieved by their own getters. + * + * @param {HTMLElement|null} element The active item should be updated based + * on this or `formFillController.focusedInput` will be taken. + */ + updateActiveInput(element) { + element = element || formFillController.focusedInput; + if (!element) { + this.debug("updateActiveElement: no element selected"); + this._activeItems = {}; + return; + } + this._activeItems = { + elementWeakRef: Cu.getWeakReference(element), + fieldDetail: null, + }; + + this.debug("updateActiveElement: checking for popup-on-focus"); + // We know this element just received focus. If it's a credit card field, + // open its popup. + if (this._autofillPending) { + this.debug("updateActiveElement: skipping check; autofill is imminent"); + } else if (element.value?.length !== 0) { + this.debug( + "updateActiveElement: Not opening popup because field is " + + `not empty: element.value = "${element.value}"` + ); + } else { + this.debug( + "updateActiveElement: checking if empty field is cc-*: ", + this.activeFieldDetail?.fieldName + ); + if (this.activeFieldDetail?.fieldName?.startsWith("cc-")) { + if (Services.cpmm.sharedData.get("FormAutofill:enabled")) { + this.debug("updateActiveElement: opening pop up"); + formFillController.showPopup(); + } else { + this.debug( + "updateActiveElement: Deferring pop-up until Autofill is ready" + ); + this._popupPending = true; + } + } + } + }, + + get activeInput() { + let elementWeakRef = this._activeItems.elementWeakRef; + return elementWeakRef ? elementWeakRef.get() : null; + }, + + get activeHandler() { + const activeInput = this.activeInput; + if (!activeInput) { + return null; + } + + // XXX: We are recomputing the activeHandler every time to avoid keeping a + // reference on the active element. This might be called quite frequently + // so if _getFormHandler/findRootForField become more costly, we should + // look into caching this result (eg by adding a weakmap). + let handler = this._getFormHandler(activeInput); + if (handler) { + handler.focusedInput = activeInput; + } + return handler; + }, + + get activeSection() { + let formHandler = this.activeHandler; + return formHandler ? formHandler.activeSection : null; + }, + + /** + * Get the active input's information from cache which is created after page + * identified. + * + * @returns {Object|null} + * Return the active input's information that cloned from content cache + * (or return null if the information is not found in the cache). + */ + get activeFieldDetail() { + if (!this._activeItems.fieldDetail) { + let formDetails = this.activeFormDetails; + if (!formDetails) { + return null; + } + for (let detail of formDetails) { + let detailElement = detail.elementWeakRef.get(); + if (detailElement && this.activeInput == detailElement) { + this._activeItems.fieldDetail = detail; + break; + } + } + } + return this._activeItems.fieldDetail; + }, + + set autofillPending(flag) { + this.debug("Setting autofillPending to", flag); + this._autofillPending = flag; + }, + + identifyAutofillFields(element) { + this.debug( + "identifyAutofillFields:", + String(element.ownerDocument.location) + ); + + if (!this.savedFieldNames) { + this.debug("identifyAutofillFields: savedFieldNames are not known yet"); + let actor = getActorFromWindow(element.ownerGlobal); + if (actor) { + actor.sendAsyncMessage("FormAutofill:InitStorage"); + } + } + + let formHandler = this._getFormHandler(element); + if (!formHandler) { + let formLike = FormLikeFactory.createFromField(element); + formHandler = new FormAutofillHandler( + formLike, + this.formSubmitted.bind(this) + ); + } else if (!formHandler.updateFormIfNeeded(element)) { + this.debug("No control is removed or inserted since last collection."); + return; + } + + let validDetails = formHandler.collectFormFields(); + + this._formsDetails.set(formHandler.form.rootElement, formHandler); + this.debug("Adding form handler to _formsDetails:", formHandler); + + validDetails.forEach(detail => + this._markAsAutofillField(detail.elementWeakRef.get()) + ); + }, + + clearForm() { + let focusedInput = + this.activeInput || ProfileAutocomplete._lastAutoCompleteFocusedInput; + if (!focusedInput) { + return; + } + + this.activeSection.clearPopulatedForm(); + }, + + previewProfile(doc) { + let docWin = doc.ownerGlobal; + let selectedIndex = ProfileAutocomplete._getSelectedIndex(docWin); + let lastAutoCompleteResult = + ProfileAutocomplete.lastProfileAutoCompleteResult; + let focusedInput = this.activeInput; + let actor = getActorFromWindow(docWin); + + if ( + selectedIndex === -1 || + !focusedInput || + !lastAutoCompleteResult || + lastAutoCompleteResult.getStyleAt(selectedIndex) != "autofill-profile" + ) { + actor.sendAsyncMessage("FormAutofill:UpdateWarningMessage", {}); + + ProfileAutocomplete._clearProfilePreview(); + } else { + let focusedInputDetails = this.activeFieldDetail; + let profile = JSON.parse( + lastAutoCompleteResult.getCommentAt(selectedIndex) + ); + let allFieldNames = FormAutofillContent.activeSection.allFieldNames; + let profileFields = allFieldNames.filter( + fieldName => !!profile[fieldName] + ); + + let focusedCategory = FormAutofillUtils.getCategoryFromFieldName( + focusedInputDetails.fieldName + ); + let categories = FormAutofillUtils.getCategoriesFromFieldNames( + profileFields + ); + actor.sendAsyncMessage("FormAutofill:UpdateWarningMessage", { + focusedCategory, + categories, + }); + + ProfileAutocomplete._previewSelectedProfile(selectedIndex); + } + }, + + onPopupClosed(selectedRowStyle) { + this.debug("Popup has closed."); + ProfileAutocomplete._clearProfilePreview(); + + let lastAutoCompleteResult = + ProfileAutocomplete.lastProfileAutoCompleteResult; + let focusedInput = FormAutofillContent.activeInput; + if ( + lastAutoCompleteResult && + FormAutofillContent._keyDownEnterForInput && + focusedInput === FormAutofillContent._keyDownEnterForInput && + focusedInput === ProfileAutocomplete.lastProfileAutoCompleteFocusedInput + ) { + if (selectedRowStyle == "autofill-footer") { + let actor = getActorFromWindow(focusedInput.ownerGlobal); + actor.sendAsyncMessage("FormAutofill:OpenPreferences"); + } else if (selectedRowStyle == "autofill-clear-button") { + FormAutofillContent.clearForm(); + } + } + }, + + onPopupOpened() { + this.debug( + "Popup has opened, automatic =", + formFillController.passwordPopupAutomaticallyOpened + ); + + Services.telemetry.recordEvent( + "creditcard", + "popup_shown", + "cc_form", + this.activeSection.flowId + ); + }, + + _markAsAutofillField(field) { + // Since Form Autofill popup is only for input element, any non-Input + // element should be excluded here. + if (!field || ChromeUtils.getClassName(field) !== "HTMLInputElement") { + return; + } + + formFillController.markAsAutofillField(field); + }, + + _onKeyDown(e) { + delete FormAutofillContent._keyDownEnterForInput; + let lastAutoCompleteResult = + ProfileAutocomplete.lastProfileAutoCompleteResult; + let focusedInput = FormAutofillContent.activeInput; + if ( + e.keyCode != e.DOM_VK_RETURN || + !lastAutoCompleteResult || + !focusedInput || + focusedInput != ProfileAutocomplete.lastProfileAutoCompleteFocusedInput + ) { + return; + } + FormAutofillContent._keyDownEnterForInput = focusedInput; + }, +}; + +FormAutofillContent.init(); diff --git a/browser/extensions/formautofill/FormAutofillDoorhanger.jsm b/browser/extensions/formautofill/FormAutofillDoorhanger.jsm new file mode 100644 index 0000000000..cf27bf56dc --- /dev/null +++ b/browser/extensions/formautofill/FormAutofillDoorhanger.jsm @@ -0,0 +1,446 @@ +/* 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/. */ + +/* + * Implements doorhanger singleton that wraps up the PopupNotifications and handles + * the doorhager UI for formautofill related features. + */ + +"use strict"; + +var EXPORTED_SYMBOLS = ["FormAutofillDoorhanger"]; + +const { AppConstants } = ChromeUtils.import( + "resource://gre/modules/AppConstants.jsm" +); +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); +const { FormAutofill } = ChromeUtils.import( + "resource://formautofill/FormAutofill.jsm" +); +const { FormAutofillUtils } = ChromeUtils.import( + "resource://formautofill/FormAutofillUtils.jsm" +); + +this.log = null; +FormAutofill.defineLazyLogGetter(this, EXPORTED_SYMBOLS[0]); + +const GetStringFromName = FormAutofillUtils.stringBundle.GetStringFromName; +const formatStringFromName = + FormAutofillUtils.stringBundle.formatStringFromName; +const brandShortName = FormAutofillUtils.brandBundle.GetStringFromName( + "brandShortName" +); +let changeAutofillOptsKey = "changeAutofillOptions"; +let autofillOptsKey = "autofillOptionsLink"; +if (AppConstants.platform == "macosx") { + changeAutofillOptsKey += "OSX"; + autofillOptsKey += "OSX"; +} + +const CONTENT = { + firstTimeUse: { + notificationId: "autofill-address", + message: formatStringFromName("saveAddressesMessage", [brandShortName]), + anchor: { + id: "autofill-address-notification-icon", + URL: "chrome://formautofill/content/formfill-anchor.svg", + tooltiptext: GetStringFromName("openAutofillMessagePanel"), + }, + mainAction: { + label: GetStringFromName(changeAutofillOptsKey), + accessKey: GetStringFromName("changeAutofillOptionsAccessKey"), + callbackState: "open-pref", + disableHighlight: true, + }, + options: { + persistWhileVisible: true, + popupIconURL: "chrome://formautofill/content/icon-address-save.svg", + checkbox: { + get checked() { + return Services.prefs.getBoolPref("services.sync.engine.addresses"); + }, + get label() { + // If sync account is not set, return null label to hide checkbox + return Services.prefs.prefHasUserValue("services.sync.username") + ? GetStringFromName("addressesSyncCheckbox") + : null; + }, + callback(event) { + let checked = event.target.checked; + Services.prefs.setBoolPref("services.sync.engine.addresses", checked); + log.debug("Set addresses sync to", checked); + }, + }, + hideClose: true, + }, + }, + updateAddress: { + notificationId: "autofill-address", + message: GetStringFromName("updateAddressMessage"), + descriptionLabel: GetStringFromName("updateAddressDescriptionLabel"), + descriptionIcon: false, + linkMessage: GetStringFromName(autofillOptsKey), + spotlightURL: "about:preferences#privacy-address-autofill", + anchor: { + id: "autofill-address-notification-icon", + URL: "chrome://formautofill/content/formfill-anchor.svg", + tooltiptext: GetStringFromName("openAutofillMessagePanel"), + }, + mainAction: { + label: GetStringFromName("updateAddressLabel"), + accessKey: GetStringFromName("updateAddressAccessKey"), + callbackState: "update", + }, + secondaryActions: [ + { + label: GetStringFromName("createAddressLabel"), + accessKey: GetStringFromName("createAddressAccessKey"), + callbackState: "create", + }, + ], + options: { + persistWhileVisible: true, + popupIconURL: "chrome://formautofill/content/icon-address-update.svg", + hideClose: true, + }, + }, + addCreditCard: { + notificationId: "autofill-credit-card", + message: formatStringFromName("saveCreditCardMessage", [brandShortName]), + descriptionLabel: GetStringFromName("saveCreditCardDescriptionLabel"), + descriptionIcon: true, + linkMessage: GetStringFromName(autofillOptsKey), + spotlightURL: "about:preferences#privacy-credit-card-autofill", + anchor: { + id: "autofill-credit-card-notification-icon", + URL: "chrome://formautofill/content/formfill-anchor.svg", + tooltiptext: GetStringFromName("openAutofillMessagePanel"), + }, + mainAction: { + label: GetStringFromName("saveCreditCardLabel"), + accessKey: GetStringFromName("saveCreditCardAccessKey"), + callbackState: "save", + }, + secondaryActions: [ + { + label: GetStringFromName("cancelCreditCardLabel"), + accessKey: GetStringFromName("cancelCreditCardAccessKey"), + callbackState: "cancel", + }, + { + label: GetStringFromName("neverSaveCreditCardLabel"), + accessKey: GetStringFromName("neverSaveCreditCardAccessKey"), + callbackState: "disable", + }, + ], + options: { + persistWhileVisible: true, + popupIconURL: "chrome://formautofill/content/icon-credit-card.svg", + hideClose: true, + checkbox: { + get checked() { + return Services.prefs.getBoolPref("services.sync.engine.creditcards"); + }, + get label() { + // Only set the label when the fallowing conditions existed: + // - sync account is set + // - credit card sync is disabled + // - credit card sync is available + // otherwise return null label to hide checkbox. + return Services.prefs.prefHasUserValue("services.sync.username") && + !Services.prefs.getBoolPref("services.sync.engine.creditcards") && + Services.prefs.getBoolPref( + "services.sync.engine.creditcards.available" + ) + ? GetStringFromName("creditCardsSyncCheckbox") + : null; + }, + callback(event) { + let { + secondaryButton, + menubutton, + } = event.target.parentNode.parentNode.parentNode; + let checked = event.target.checked; + Services.prefs.setBoolPref( + "services.sync.engine.creditcards", + checked + ); + secondaryButton.disabled = checked; + menubutton.disabled = checked; + log.debug("Set creditCard sync to", checked); + }, + }, + }, + }, + updateCreditCard: { + notificationId: "autofill-credit-card", + message: GetStringFromName("updateCreditCardMessage"), + descriptionLabel: GetStringFromName("updateCreditCardDescriptionLabel"), + descriptionIcon: true, + linkMessage: GetStringFromName(autofillOptsKey), + spotlightURL: "about:preferences#privacy-credit-card-autofill", + anchor: { + id: "autofill-credit-card-notification-icon", + URL: "chrome://formautofill/content/formfill-anchor.svg", + tooltiptext: GetStringFromName("openAutofillMessagePanel"), + }, + mainAction: { + label: GetStringFromName("updateCreditCardLabel"), + accessKey: GetStringFromName("updateCreditCardAccessKey"), + callbackState: "update", + }, + secondaryActions: [ + { + label: GetStringFromName("createCreditCardLabel"), + accessKey: GetStringFromName("createCreditCardAccessKey"), + callbackState: "create", + }, + ], + options: { + persistWhileVisible: true, + popupIconURL: "chrome://formautofill/content/icon-credit-card.svg", + hideClose: true, + }, + }, +}; + +let FormAutofillDoorhanger = { + /** + * Generate the main action and secondary actions from content parameters and + * promise resolve. + * + * @private + * @param {Object} mainActionParams + * Parameters for main action. + * @param {Array} secondaryActionParams + * Array of the parameters for secondary actions. + * @param {Function} resolve Should be called in action callback. + * @returns {Array} + Return the mainAction and secondary actions in an array for showing doorhanger + */ + _createActions(mainActionParams, secondaryActionParams, resolve) { + if (!mainActionParams) { + return [null, null]; + } + + let { + label, + accessKey, + disableHighlight, + callbackState, + } = mainActionParams; + let callback = resolve.bind(null, callbackState); + let mainAction = { label, accessKey, callback, disableHighlight }; + + if (!secondaryActionParams) { + return [mainAction, null]; + } + + let secondaryActions = []; + for (let params of secondaryActionParams) { + let cb = resolve.bind(null, params.callbackState); + secondaryActions.push({ + label: params.label, + accessKey: params.accessKey, + callback: cb, + }); + } + + return [mainAction, secondaryActions]; + }, + _getNotificationElm(browser, id) { + let notificationId = id + "-notification"; + let chromeDoc = browser.ownerDocument; + return chromeDoc.getElementById(notificationId); + }, + /** + * Append the link label element to the popupnotificationcontent. + * @param {XULElement} content + * popupnotificationcontent + * @param {string} message + * The localized string for link title. + * @param {string} link + * Makes it possible to open and highlight a section in preferences + */ + _appendPrivacyPanelLink(content, message, link) { + let chromeDoc = content.ownerDocument; + let privacyLinkElement = chromeDoc.createXULElement("label", { + is: "text-link", + }); + privacyLinkElement.setAttribute("useoriginprincipal", true); + privacyLinkElement.setAttribute( + "href", + link || "about:preferences#privacy-form-autofill" + ); + privacyLinkElement.setAttribute("value", message); + content.appendChild(privacyLinkElement); + }, + + /** + * Append the description section to the popupnotificationcontent. + * @param {XULElement} content + * popupnotificationcontent + * @param {string} descriptionLabel + * The label showing above description. + * @param {string} descriptionIcon + * The src of description icon. + */ + _appendDescription(content, descriptionLabel, descriptionIcon) { + let chromeDoc = content.ownerDocument; + let docFragment = chromeDoc.createDocumentFragment(); + + let descriptionLabelElement = chromeDoc.createXULElement("label"); + descriptionLabelElement.setAttribute("value", descriptionLabel); + docFragment.appendChild(descriptionLabelElement); + + let descriptionWrapper = chromeDoc.createXULElement("hbox"); + descriptionWrapper.className = "desc-message-box"; + + if (descriptionIcon) { + let descriptionIconElement = chromeDoc.createXULElement("image"); + descriptionWrapper.appendChild(descriptionIconElement); + } + + let descriptionElement = chromeDoc.createXULElement("description"); + descriptionWrapper.appendChild(descriptionElement); + docFragment.appendChild(descriptionWrapper); + + content.appendChild(docFragment); + }, + + _updateDescription(content, description) { + content.querySelector("description").textContent = description; + }, + + /** + * Create an image element for notification anchor if it doesn't already exist. + * @param {XULElement} browser + * Target browser element for showing doorhanger. + * @param {Object} anchor + * Anchor options for setting the anchor element. + * @param {string} anchor.id + * ID of the anchor element. + * @param {string} anchor.URL + * Path of the icon asset. + * @param {string} anchor.tooltiptext + * Tooltip string for the anchor. + */ + _setAnchor(browser, anchor) { + let chromeDoc = browser.ownerDocument; + let { id, URL, tooltiptext } = anchor; + let anchorEt = chromeDoc.getElementById(id); + if (!anchorEt) { + let notificationPopupBox = chromeDoc.getElementById( + "notification-popup-box" + ); + // Icon shown on URL bar + let anchorElement = chromeDoc.createXULElement("image"); + anchorElement.id = id; + anchorElement.setAttribute("src", URL); + anchorElement.classList.add("notification-anchor-icon"); + anchorElement.setAttribute("role", "button"); + anchorElement.setAttribute("tooltiptext", tooltiptext); + notificationPopupBox.appendChild(anchorElement); + } + }, + _addCheckboxListener(browser, { notificationId, options }) { + if (!options.checkbox) { + return; + } + let { checkbox } = this._getNotificationElm(browser, notificationId); + + if (checkbox && !checkbox.hidden) { + checkbox.addEventListener("command", options.checkbox.callback); + } + }, + _removeCheckboxListener(browser, { notificationId, options }) { + if (!options.checkbox) { + return; + } + let { checkbox } = this._getNotificationElm(browser, notificationId); + + if (checkbox && !checkbox.hidden) { + checkbox.removeEventListener("command", options.checkbox.callback); + } + }, + /** + * Show different types of doorhanger by leveraging PopupNotifications. + * @param {XULElement} browser + * Target browser element for showing doorhanger. + * @param {string} type + * The type of the doorhanger. There will have first time use/update/credit card. + * @param {string} description + * The message that provides more information on doorhanger. + * @returns {Promise} + Resolved with action type when action callback is triggered. + */ + async show(browser, type, description) { + log.debug("show doorhanger with type:", type); + return new Promise(resolve => { + let { + notificationId, + message, + descriptionLabel, + descriptionIcon, + linkMessage, + spotlightURL, + anchor, + mainAction, + secondaryActions, + options, + } = CONTENT[type]; + + const { ownerGlobal: chromeWin, ownerDocument: chromeDoc } = browser; + options.eventCallback = topic => { + log.debug("eventCallback:", topic); + + if (topic == "removed" || topic == "dismissed") { + this._removeCheckboxListener(browser, { notificationId, options }); + return; + } + + // The doorhanger is customizable only when notification box is shown + if (topic != "shown") { + return; + } + this._addCheckboxListener(browser, { notificationId, options }); + + // There's no preferences link or other customization in first time use doorhanger. + if (type == "firstTimeUse") { + return; + } + + const notificationElementId = notificationId + "-notification"; + const notification = chromeDoc.getElementById(notificationElementId); + const notificationContent = + notification.querySelector("popupnotificationcontent") || + chromeDoc.createXULElement("popupnotificationcontent"); + if (!notification.contains(notificationContent)) { + notificationContent.setAttribute("orient", "vertical"); + this._appendDescription( + notificationContent, + descriptionLabel, + descriptionIcon + ); + this._appendPrivacyPanelLink( + notificationContent, + linkMessage, + spotlightURL + ); + notification.appendNotificationContent(notificationContent); + } + this._updateDescription(notificationContent, description); + }; + this._setAnchor(browser, anchor); + chromeWin.PopupNotifications.show( + browser, + notificationId, + message, + anchor.id, + ...this._createActions(mainAction, secondaryActions, resolve), + options + ); + }); + }, +}; diff --git a/browser/extensions/formautofill/FormAutofillHandler.jsm b/browser/extensions/formautofill/FormAutofillHandler.jsm new file mode 100644 index 0000000000..0bf6dfa496 --- /dev/null +++ b/browser/extensions/formautofill/FormAutofillHandler.jsm @@ -0,0 +1,1478 @@ +/* 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/. */ + +/* + * Defines a handler object to represent forms that autofill can handle. + */ + +"use strict"; + +var EXPORTED_SYMBOLS = ["FormAutofillHandler"]; + +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); + +const { AppConstants } = ChromeUtils.import( + "resource://gre/modules/AppConstants.jsm" +); +const { XPCOMUtils } = ChromeUtils.import( + "resource://gre/modules/XPCOMUtils.jsm" +); +const { FormAutofill } = ChromeUtils.import( + "resource://formautofill/FormAutofill.jsm" +); + +ChromeUtils.defineModuleGetter( + this, + "FormAutofillUtils", + "resource://formautofill/FormAutofillUtils.jsm" +); +ChromeUtils.defineModuleGetter( + this, + "FormAutofillHeuristics", + "resource://formautofill/FormAutofillHeuristics.jsm" +); +ChromeUtils.defineModuleGetter( + this, + "FormLikeFactory", + "resource://gre/modules/FormLikeFactory.jsm" +); + +const formFillController = Cc[ + "@mozilla.org/satchel/form-fill-controller;1" +].getService(Ci.nsIFormFillController); + +XPCOMUtils.defineLazyGetter(this, "reauthPasswordPromptMessage", () => { + const brandShortName = FormAutofillUtils.brandBundle.GetStringFromName( + "brandShortName" + ); + // The string name for Mac is changed because the value needed updating. + const platform = AppConstants.platform.replace("macosx", "macos"); + return FormAutofillUtils.stringBundle.formatStringFromName( + `useCreditCardPasswordPrompt.${platform}`, + [brandShortName] + ); +}); + +XPCOMUtils.defineLazyModuleGetters(this, { + CreditCard: "resource://gre/modules/CreditCard.jsm", +}); + +XPCOMUtils.defineLazyServiceGetters(this, { + gUUIDGenerator: ["@mozilla.org/uuid-generator;1", "nsIUUIDGenerator"], +}); + +this.log = null; +FormAutofill.defineLazyLogGetter(this, EXPORTED_SYMBOLS[0]); + +const { FIELD_STATES } = FormAutofillUtils; + +class FormAutofillSection { + constructor(fieldDetails, winUtils) { + this.fieldDetails = fieldDetails; + this.filledRecordGUID = null; + this.winUtils = winUtils; + + /** + * Enum for form autofill MANUALLY_MANAGED_STATES values + */ + this._FIELD_STATE_ENUM = { + // not themed + [FIELD_STATES.NORMAL]: null, + // highlighted + [FIELD_STATES.AUTO_FILLED]: "autofill", + // highlighted && grey color text + [FIELD_STATES.PREVIEW]: "-moz-autofill-preview", + }; + + if (!this.isValidSection()) { + this.fieldDetails = []; + log.debug( + `Ignoring ${this.constructor.name} related fields since it is an invalid section` + ); + } + + this._cacheValue = { + allFieldNames: null, + matchingSelectOption: null, + }; + } + + /* + * Examine the section is a valid section or not based on its fieldDetails or + * other information. This method must be overrided. + * + * @returns {boolean} True for a valid section, otherwise false + * + */ + isValidSection() { + throw new TypeError("isValidSection method must be overrided"); + } + + /* + * Examine the section is an enabled section type or not based on its + * preferences. This method must be overrided. + * + * @returns {boolean} True for an enabled section type, otherwise false + * + */ + isEnabled() { + throw new TypeError("isEnabled method must be overrided"); + } + + /* + * Examine the section is createable for storing the profile. This method + * must be overrided. + * + * @param {Object} record The record for examining createable + * @returns {boolean} True for the record is createable, otherwise false + * + */ + isRecordCreatable(record) { + throw new TypeError("isRecordCreatable method must be overrided"); + } + + /** + * Override this method if the profile is needed to apply some transformers. + * + * @param {Object} profile + * A profile should be converted based on the specific requirement. + */ + applyTransformers(profile) {} + + /** + * Override this method if the profile is needed to be customized for + * previewing values. + * + * @param {Object} profile + * A profile for pre-processing before previewing values. + */ + preparePreviewProfile(profile) {} + + /** + * Override this method if the profile is needed to be customized for filling + * values. + * + * @param {Object} profile + * A profile for pre-processing before filling values. + * @returns {boolean} Whether the profile should be filled. + */ + async prepareFillingProfile(profile) { + return true; + } + + /* + * Override this methid if any data for `createRecord` is needed to be + * normailized before submitting the record. + * + * @param {Object} profile + * A record for normalization. + */ + normalizeCreatingRecord(data) {} + + /* + * Override this method if there is any field value needs to compute for a + * specific case. Return the original value in the default case. + * @param {String} value + * The original field value. + * @param {Object} fieldDetail + * A fieldDetail of the related element. + * @param {HTMLElement} element + * A element for checking converting value. + * + * @returns {String} + * A string of the converted value. + */ + computeFillingValue(value, fieldName, element) { + return value; + } + + set focusedInput(element) { + this._focusedDetail = this.getFieldDetailByElement(element); + } + + getFieldDetailByElement(element) { + return this.fieldDetails.find( + detail => detail.elementWeakRef.get() == element + ); + } + + get allFieldNames() { + if (!this._cacheValue.allFieldNames) { + this._cacheValue.allFieldNames = this.fieldDetails.map( + record => record.fieldName + ); + } + return this._cacheValue.allFieldNames; + } + + getFieldDetailByName(fieldName) { + return this.fieldDetails.find(detail => detail.fieldName == fieldName); + } + + matchSelectOptions(profile) { + if (!this._cacheValue.matchingSelectOption) { + this._cacheValue.matchingSelectOption = new WeakMap(); + } + + for (let fieldName in profile) { + let fieldDetail = this.getFieldDetailByName(fieldName); + if (!fieldDetail) { + continue; + } + + let element = fieldDetail.elementWeakRef.get(); + if (ChromeUtils.getClassName(element) !== "HTMLSelectElement") { + continue; + } + + let cache = this._cacheValue.matchingSelectOption.get(element) || {}; + let value = profile[fieldName]; + if (cache[value] && cache[value].get()) { + continue; + } + + let option = FormAutofillUtils.findSelectOption( + element, + profile, + fieldName + ); + if (option) { + cache[value] = Cu.getWeakReference(option); + this._cacheValue.matchingSelectOption.set(element, cache); + } else { + if (cache[value]) { + delete cache[value]; + this._cacheValue.matchingSelectOption.set(element, cache); + } + // Delete the field so the phishing hint won't treat it as a "also fill" + // field. + delete profile[fieldName]; + } + } + } + + adaptFieldMaxLength(profile) { + for (let key in profile) { + let detail = this.getFieldDetailByName(key); + if (!detail) { + continue; + } + + let element = detail.elementWeakRef.get(); + if (!element) { + continue; + } + + let maxLength = element.maxLength; + if ( + maxLength === undefined || + maxLength < 0 || + profile[key].toString().length <= maxLength + ) { + continue; + } + + if (maxLength) { + switch (typeof profile[key]) { + case "string": + // If this is an expiration field and our previous + // adaptations haven't resulted in a string that is + // short enough to satisfy the field length, and the + // field is constrained to a length of 5, then we + // assume it is intended to hold an expiration of the + // form "MM/YY". + if (key == "cc-exp" && maxLength == 5) { + const month2Digits = ( + "0" + profile["cc-exp-month"].toString() + ).slice(-2); + const year2Digits = profile["cc-exp-year"].toString().slice(-2); + profile[key] = `${month2Digits}/${year2Digits}`; + } else { + profile[key] = profile[key].substr(0, maxLength); + } + break; + case "number": + // There's no way to truncate a number smaller than a + // single digit. + if (maxLength < 1) { + maxLength = 1; + } + // The only numbers we store are expiration month/year, + // and if they truncate, we want the final digits, not + // the initial ones. + profile[key] = profile[key] % Math.pow(10, maxLength); + break; + default: + log.warn( + "adaptFieldMaxLength: Don't know how to truncate", + typeof profile[key], + profile[key] + ); + } + } else { + delete profile[key]; + } + } + } + + getAdaptedProfiles(originalProfiles) { + for (let profile of originalProfiles) { + this.applyTransformers(profile); + } + return originalProfiles; + } + + /** + * Processes form fields that can be autofilled, and populates them with the + * profile provided by backend. + * + * @param {Object} profile + * A profile to be filled in. + * @returns {boolean} + * True if successful, false if failed + */ + async autofillFields(profile) { + let focusedDetail = this._focusedDetail; + if (!focusedDetail) { + throw new Error("No fieldDetail for the focused input."); + } + + if (!(await this.prepareFillingProfile(profile))) { + log.debug("profile cannot be filled", profile); + return false; + } + log.debug("profile in autofillFields:", profile); + + let focusedInput = focusedDetail.elementWeakRef.get(); + + this.filledRecordGUID = profile.guid; + for (let fieldDetail of this.fieldDetails) { + // Avoid filling field value in the following cases: + // 1. a non-empty input field for an unfocused input + // 2. the invalid value set + // 3. value already chosen in select element + + let element = fieldDetail.elementWeakRef.get(); + if (!element) { + continue; + } + + element.previewValue = ""; + let value = profile[fieldDetail.fieldName]; + + if (ChromeUtils.getClassName(element) === "HTMLInputElement" && value) { + // For the focused input element, it will be filled with a valid value + // anyway. + // For the others, the fields should be only filled when their values + // are empty or are the result of an earlier auto-fill. + if ( + element == focusedInput || + (element != focusedInput && !element.value) || + fieldDetail.state == FIELD_STATES.AUTO_FILLED + ) { + element.focus({ preventScroll: true }); + element.setUserInput(value); + this._changeFieldState(fieldDetail, FIELD_STATES.AUTO_FILLED); + } + } else if (ChromeUtils.getClassName(element) === "HTMLSelectElement") { + let cache = this._cacheValue.matchingSelectOption.get(element) || {}; + let option = cache[value] && cache[value].get(); + if (!option) { + continue; + } + // Do not change value or dispatch events if the option is already selected. + // Use case for multiple select is not considered here. + if (!option.selected) { + option.selected = true; + element.focus({ preventScroll: true }); + element.dispatchEvent( + new element.ownerGlobal.Event("input", { bubbles: true }) + ); + element.dispatchEvent( + new element.ownerGlobal.Event("change", { bubbles: true }) + ); + } + // Autofill highlight appears regardless if value is changed or not + this._changeFieldState(fieldDetail, FIELD_STATES.AUTO_FILLED); + } + } + focusedInput.focus({ preventScroll: true }); + return true; + } + + /** + * Populates result to the preview layers with given profile. + * + * @param {Object} profile + * A profile to be previewed with + */ + previewFormFields(profile) { + log.debug("preview profile: ", profile); + + this.preparePreviewProfile(profile); + + for (let fieldDetail of this.fieldDetails) { + let element = fieldDetail.elementWeakRef.get(); + let value = profile[fieldDetail.fieldName] || ""; + + // Skip the field that is null + if (!element) { + continue; + } + + if (ChromeUtils.getClassName(element) === "HTMLSelectElement") { + // Unlike text input, select element is always previewed even if + // the option is already selected. + if (value) { + let cache = this._cacheValue.matchingSelectOption.get(element) || {}; + let option = cache[value] && cache[value].get(); + if (option) { + value = option.text || ""; + } else { + value = ""; + } + } + } else if (element.value) { + // Skip the field if it already has text entered. + continue; + } + element.previewValue = value; + this._changeFieldState( + fieldDetail, + value ? FIELD_STATES.PREVIEW : FIELD_STATES.NORMAL + ); + } + } + + /** + * Clear preview text and background highlight of all fields. + */ + clearPreviewedFormFields() { + log.debug("clear previewed fields in:", this.form); + + for (let fieldDetail of this.fieldDetails) { + let element = fieldDetail.elementWeakRef.get(); + if (!element) { + log.warn(fieldDetail.fieldName, "is unreachable"); + continue; + } + + element.previewValue = ""; + + // We keep the state if this field has + // already been auto-filled. + if (fieldDetail.state == FIELD_STATES.AUTO_FILLED) { + continue; + } + + this._changeFieldState(fieldDetail, FIELD_STATES.NORMAL); + } + } + + /** + * Clear value and highlight style of all filled fields. + */ + clearPopulatedForm() { + for (let fieldDetail of this.fieldDetails) { + let element = fieldDetail.elementWeakRef.get(); + if (!element) { + log.warn(fieldDetail.fieldName, "is unreachable"); + continue; + } + + // Only reset value for input element. + if ( + fieldDetail.state == FIELD_STATES.AUTO_FILLED && + ChromeUtils.getClassName(element) === "HTMLInputElement" + ) { + element.setUserInput(""); + } + } + } + + /** + * Change the state of a field to correspond with different presentations. + * + * @param {Object} fieldDetail + * A fieldDetail of which its element is about to update the state. + * @param {string} nextState + * Used to determine the next state + */ + _changeFieldState(fieldDetail, nextState) { + let element = fieldDetail.elementWeakRef.get(); + + if (!element) { + log.warn(fieldDetail.fieldName, "is unreachable while changing state"); + return; + } + if (!(nextState in this._FIELD_STATE_ENUM)) { + log.warn( + fieldDetail.fieldName, + "is trying to change to an invalid state" + ); + return; + } + if (fieldDetail.state == nextState) { + return; + } + + for (let [state, mmStateValue] of Object.entries(this._FIELD_STATE_ENUM)) { + // The NORMAL state is simply the absence of other manually + // managed states so we never need to add or remove it. + if (!mmStateValue) { + continue; + } + + if (state == nextState) { + this.winUtils.addManuallyManagedState(element, mmStateValue); + } else { + this.winUtils.removeManuallyManagedState(element, mmStateValue); + } + } + + if (nextState == FIELD_STATES.AUTO_FILLED) { + element.addEventListener("input", this, { mozSystemGroup: true }); + } + + fieldDetail.state = nextState; + } + + resetFieldStates() { + for (let fieldDetail of this.fieldDetails) { + const element = fieldDetail.elementWeakRef.get(); + element.removeEventListener("input", this, { mozSystemGroup: true }); + this._changeFieldState(fieldDetail, FIELD_STATES.NORMAL); + } + this.filledRecordGUID = null; + } + + isFilled() { + return !!this.filledRecordGUID; + } + + /** + * Return the record that is converted from `fieldDetails` and only valid + * form record is included. + * + * @returns {Object|null} + * A record object consists of three properties: + * - guid: The id of the previously-filled profile or null if omitted. + * - record: A valid record converted from details with trimmed result. + * - untouchedFields: Fields that aren't touched after autofilling. + * Return `null` for any uncreatable or invalid record. + */ + createRecord() { + let details = this.fieldDetails; + if (!this.isEnabled() || !details || !details.length) { + return null; + } + + let data = { + guid: this.filledRecordGUID, + record: {}, + untouchedFields: [], + }; + if (this.flowId) { + data.flowId = this.flowId; + } + + details.forEach(detail => { + let element = detail.elementWeakRef.get(); + // Remove the unnecessary spaces + let value = element && element.value.trim(); + value = this.computeFillingValue(value, detail, element); + + if (!value || value.length > FormAutofillUtils.MAX_FIELD_VALUE_LENGTH) { + // Keep the property and preserve more information for updating + data.record[detail.fieldName] = ""; + return; + } + + data.record[detail.fieldName] = value; + + if (detail.state == FIELD_STATES.AUTO_FILLED) { + data.untouchedFields.push(detail.fieldName); + } + }); + + this.normalizeCreatingRecord(data); + + if (!this.isRecordCreatable(data.record)) { + return null; + } + + return data; + } + + handleEvent(event) { + switch (event.type) { + case "input": { + if (!event.isTrusted) { + return; + } + const target = event.target; + const targetFieldDetail = this.getFieldDetailByElement(target); + const isCreditCardField = FormAutofillUtils.isCreditCardField( + targetFieldDetail.fieldName + ); + + // If the user manually blanks a credit card field, then + // we want the popup to be activated. + if ( + ChromeUtils.getClassName(target) !== "HTMLSelectElement" && + isCreditCardField && + target.value === "" + ) { + formFillController.showPopup(); + } + + if (targetFieldDetail.state == FIELD_STATES.NORMAL) { + return; + } + + this._changeFieldState(targetFieldDetail, FIELD_STATES.NORMAL); + + if (isCreditCardField) { + Services.telemetry.recordEvent( + "creditcard", + "filled_modified", + "cc_form", + this.flowId, + { + field_name: targetFieldDetail.fieldName, + } + ); + } + + let isAutofilled = false; + let dimFieldDetails = []; + for (const fieldDetail of this.fieldDetails) { + const element = fieldDetail.elementWeakRef.get(); + + if (ChromeUtils.getClassName(element) === "HTMLSelectElement") { + // Dim fields are those we don't attempt to revert their value + // when clear the target set, such as + + + + + + +