diff --git a/.eslintignore b/.eslintignore index 3a749f2574..ab54c23dc6 100644 --- a/.eslintignore +++ b/.eslintignore @@ -3,25 +3,24 @@ # Always ignore crashtests - specially crafted files that originally caused a # crash. -**/crashtests/** +**/crashtests/ # Also ignore reftest - specially crafted to produce expected output. -**/reftest/** -**/reftests/** +**/reftest/ +**/reftests/ # Exclude expected objdirs. -obj*/** +obj*/ # dom/ exclusions which should be removed (aka ESLint enabled) -dom/base/*.* -dom/media/test/** +dom/media/test/ !dom/media/test/marionette/yttest/*.js -dom/xhr/** +dom/xhr/ # build/ third-party code -build/pgo/js-input/** +build/pgo/js-input/ # browser/ exclusions -browser/app/** +browser/app/ browser/branding/**/firefox-branding.js # Gzipped test file. browser/base/content/test/general/gZipOfflineChild.html @@ -34,27 +33,26 @@ browser/components/sessionstore/test/unit/data/sessionstore_invalid.js # code as a .jsm (schema.jsm) browser/components/enterprisepolicies/schemas/schema.jsm # generated & special files in cld2 -browser/components/translation/cld2/** +browser/components/translation/cld2/ # Screenshots is imported as a system add-on and has # their own lint rules currently. -browser/extensions/screenshots/** -browser/extensions/pdfjs/content/build** -browser/extensions/pdfjs/content/web** +browser/extensions/screenshots/ +browser/extensions/pdfjs/content/build +browser/extensions/pdfjs/content/web # generated or library files in pocket browser/components/pocket/content/panels/js/tmpl.js -browser/components/pocket/content/panels/js/vendor/** +browser/components/pocket/content/panels/js/vendor/ # Ignore newtab files # Kept in sync with browser/components/newtab/.eslintignore browser/components/newtab/data/ browser/components/newtab/logs/ -browser/components/newtab/prerendered/ browser/components/newtab/vendor/ # The only file in browser/locales/ is pre-processed. -browser/locales/** +browser/locales/ # imported from chromium -browser/extensions/mortar/** +browser/extensions/mortar/ # Generated data files browser/extensions/formautofill/phonenumberutils/PhoneNumberMetaData.jsm @@ -80,63 +78,61 @@ devtools/client/shared/webpack/shims/test/test_clipboard.html devtools/shared/qrcode/tests/mochitest/test_decode.html devtools/shared/tests/mochitest/*.html devtools/shared/webconsole/test/test_*.html +devtools/server/tests/mochitest/test_inspector-inactive-property-helper.html # Ignore devtools debugger files # Keep in sync with devtools/client/debugger/.eslintignore devtools/client/debugger/assets/* -devtools/client/debugger/src/test/examples/** -devtools/client/debugger/src/test/integration/** -devtools/client/debugger/src/test/unit-sources/** -devtools/client/debugger/src/**/fixtures/** -devtools/client/debugger/src/test/mochitest/** +devtools/client/debugger/src/test/examples/ +devtools/client/debugger/src/test/integration/ +devtools/client/debugger/src/test/unit-sources/ +devtools/client/debugger/src/**/fixtures/ +devtools/client/debugger/src/test/mochitest/ devtools/client/debugger/bin/ -devtools/client/debugger/packages/**/fixtures/** +devtools/client/debugger/packages/**/fixtures/ devtools/client/debugger/node_modules devtools/client/debugger/out # Ignore devtools debugger files which aren't intended for linting, and also # aren't included in any .eslintignore or .prettierignore file. # See https://github.com/firefox-devtools/debugger/blob/master/package.json#L24 -devtools/client/debugger/configs/** -devtools/client/debugger/dist/** -devtools/client/debugger/flow-typed/** -devtools/client/debugger/images/** -devtools/client/debugger/test/** +devtools/client/debugger/configs/ +devtools/client/debugger/dist/ +devtools/client/debugger/flow-typed/ +devtools/client/debugger/images/ +devtools/client/debugger/test/ devtools/client/debugger/index.html # Ignore devtools imported repositories -devtools/client/shared/components/reps/** +devtools/client/shared/components/reps/ # Ignore devtools preferences files -devtools/client/preferences/** -devtools/client/webide/preferences/** -devtools/shared/preferences/** -devtools/startup/preferences/devtools-startup.js +devtools/client/preferences/ # Ignore devtools generated code devtools/shared/css/generated/properties-db.js -devtools/client/webconsole/test/fixtures/stubs/*.js -!devtools/client/webconsole/test/fixtures/stubs/index.js +devtools/client/webconsole/test/node/fixtures/stubs/*.js +!devtools/client/webconsole/test/node/fixtures/stubs/index.js # Ignore devtools third-party libs -devtools/shared/jsbeautify/* -devtools/shared/acorn/* -devtools/shared/node-properties/* -devtools/shared/pretty-fast/* -devtools/shared/sourcemap/* -devtools/shared/sprintfjs/* -devtools/shared/qrcode/decoder/* -devtools/shared/qrcode/encoder/* +devtools/shared/jsbeautify/ +devtools/shared/acorn/ +devtools/shared/node-properties/ +devtools/shared/pretty-fast/ +devtools/shared/sourcemap/ +devtools/shared/sprintfjs/ +devtools/shared/qrcode/decoder/ +devtools/shared/qrcode/encoder/ devtools/client/inspector/markup/test/lib_* devtools/client/jsonview/lib/require.js devtools/client/shared/demangle.js -devtools/client/shared/source-map/* -devtools/client/shared/vendor/* +devtools/client/shared/source-map/ +devtools/client/shared/vendor/ devtools/client/shared/sourceeditor/codemirror/*.js devtools/client/shared/sourceeditor/codemirror/**/*.js -devtools/client/shared/sourceeditor/tern/* +devtools/client/shared/sourceeditor/tern/ devtools/client/shared/sourceeditor/test/cm_mode_ruby.js -devtools/client/shared/sourceeditor/test/codemirror/* +devtools/client/shared/sourceeditor/test/codemirror/ devtools/server/actors/utils/automation-timeline.js # Ignore devtools files testing sourcemaps / code style @@ -144,20 +140,23 @@ devtools/client/debugger/test/mochitest/code_*.js devtools/client/framework/test/code_* devtools/client/inspector/markup/test/events_bundle.js devtools/client/netmonitor/test/xhr_bundle.js -devtools/client/webconsole/test/mochitest/code_bundle_nosource.js -devtools/client/webconsole/test/mochitest/code_bundle_invalidmap.js +devtools/client/webconsole/test/browser/code_bundle_nosource.js +devtools/client/webconsole/test/browser/code_bundle_invalidmap.js devtools/server/tests/unit/babel_and_browserify_script_with_source_map.js devtools/server/tests/unit/setBreakpoint* devtools/server/tests/unit/sourcemapped.js +# Testing syntax error +devtools/client/webconsole/test/browser/test-syntaxerror-worklet.js + # devtools specific format test file devtools/server/tests/unit/xpcshell_debugging_script.js # Third-party -dom/canvas/test/webgl-conf/** -dom/imptests/** -dom/media/webaudio/test/blink/** -dom/media/webvtt/** +dom/canvas/test/webgl-conf/ +dom/imptests/ +dom/media/webaudio/test/blink/ +dom/media/webvtt/ dom/svg/test/test_nonAnimStrings.xhtml dom/svg/test/test_SVG_namespace_ids.html @@ -193,39 +192,39 @@ dom/workers/test/invalid.js dom/workers/test/threadErrors_worker1.js # Third-party -editor/libeditor/tests/browserscope/** +editor/libeditor/tests/browserscope/ # Third-party -gfx/ots/** -gfx/skia/** -gfx/wr/** +gfx/ots/ +gfx/skia/ +gfx/wr/ # intl/ exclusions -intl/icu/** +intl/icu/ # Bug 1527075: This directory is linted in github repository -intl/l10n/** +intl/l10n/ # Third-party -layout/mathml/imptests/** +layout/mathml/imptests/ # Exclude everything but self-hosted JS -js/ductwork/** -js/examples/** -js/ipc/** -js/public/** -js/xpconnect/** -js/src/devtools/** -js/src/octane/** -js/src/jit-test/** -js/src/jsapi-tests/binast/** -js/src/tests/** +js/ductwork/ +js/examples/ +js/ipc/ +js/public/ +js/xpconnect/ +js/src/devtools/ +js/src/octane/ +js/src/jit-test/ +js/src/jsapi-tests/binast/ +js/src/tests/ js/src/Y.js # Third-party -media/webrtc/trunk/** +media/webrtc/trunk/ # mobile/android/ exclusions -mobile/android/tests/browser/chrome/tp5/** +mobile/android/tests/browser/chrome/tp5/ # Uses `#filter substitution` mobile/android/app/mobile.js @@ -241,10 +240,10 @@ mobile/android/locales/ # Pre-processed/pref files modules/libpref/greprefs.js modules/libpref/init/all.js -modules/libpref/test/unit/*data/** +modules/libpref/test/unit/*data/ # Only contains non-standard test files. -python/** +python/ # Remote agent remote/Protocol.jsm @@ -256,7 +255,7 @@ remote/test/browser/chrome-remote-interface.js remote/test/demo.js # NSS / taskcluster only. -security/nss/** +security/nss/ # services/ exclusions @@ -271,7 +270,7 @@ services/fxaccounts/FxAccountsPairingChannel.js services/sync/modules/constants.js # Servo is imported. -servo/** +servo/ # Remote protocol exclusions testing/marionette/atom.js @@ -282,35 +281,37 @@ testing/marionette/harness # other testing/ exclusions # third party modules -testing/mochitest/tests/Harness_sanity/** -testing/mochitest/MochiKit/** -testing/mochitest/tests/MochiKit-1.4.2/** -testing/mochitest/tests/SimpleTest/** +testing/mochitest/tests/Harness_sanity/ +testing/mochitest/MochiKit/ +testing/mochitest/tests/MochiKit-1.4.2/ +testing/mochitest/tests/SimpleTest/ testing/modules/ajv-4.1.1.js testing/modules/sinon-7.2.7.js # octothorpe used for pref file comment causes parsing error testing/mozbase/mozprofile/tests/files/prefs_with_comments.js + +# Mozproxy third party +testing/mozbase/mozproxy/mozproxy/backends/mitm/scripts/catapult/ + testing/talos/talos/scripts/jszip.min.js testing/talos/talos/startup_test/sessionrestore/profile/sessionstore.js testing/talos/talos/startup_test/sessionrestore/profile-manywindows/sessionstore.js -testing/talos/talos/tests/devtools/addon/content/pages/** -testing/talos/talos/tests/dromaeo/** -testing/talos/talos/tests/v8_7/** -testing/talos/talos/tests/kraken/** +testing/talos/talos/tests/devtools/addon/content/pages/ +testing/talos/talos/tests/dromaeo/ +testing/talos/talos/tests/v8_7/ +testing/talos/talos/tests/kraken/ # Runing Talos may extract data here, see bug 1435677. -testing/talos/talos/tests/tp5n/** -# Raptor third party -testing/raptor/raptor/playback/scripts/catapult/** +testing/talos/talos/tests/tp5n/ -testing/web-platform/** -testing/xpcshell/moz-http2/** -testing/xpcshell/node-http2/** -testing/xpcshell/dns-packet/** -testing/xpcshell/node-ip/** +testing/web-platform/ +testing/xpcshell/moz-http2/ +testing/xpcshell/node-http2/ +testing/xpcshell/dns-packet/ +testing/xpcshell/node-ip/ # Third party. -third_party/** +third_party/ # toolkit/ exclusions @@ -338,7 +339,7 @@ toolkit/modules/AppConstants.jsm toolkit/modules/tests/xpcshell/test_task.js # Third party -toolkit/modules/third_party/** +toolkit/modules/third_party/ tools/tryselect/selectors/chooser/templates/chooser.html diff --git a/.eslintrc.js b/.eslintrc.js index 205c3837d6..fcb8b911c2 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -194,23 +194,18 @@ module.exports = { "mozilla/reject-importGlobalProperties": "off", "mozilla/no-arbitrary-setTimeout": "off", "mozilla/no-define-cc-etc": "off", - "mozilla/no-useless-parameters": "off", - "mozilla/no-useless-run-test": "off", "mozilla/use-chromeutils-generateqi": "off", - "mozilla/use-chromeutils-import": "off", "mozilla/use-default-preference-values": "off", "mozilla/use-includes-instead-of-indexOf": "off", "mozilla/use-services": "off", "mozilla/use-ownerGlobal": "off", "complexity": "off", "consistent-return": "off", - "dot-notation": "off", "no-array-constructor": "off", "no-caller": "off", "no-cond-assign": "off", "no-extra-boolean-cast": "off", "no-eval": "off", - "no-else-return": "off", "no-func-assign": "off", "no-global-assign": "off", "no-implied-eval": "off", @@ -326,5 +321,49 @@ module.exports = { "no-useless-return": "off", "no-with": "off", } + }, { + "files": [ + "browser/components/extensions/ExtensionControlledPopup.jsm", + "browser/components/extensions/test/browser/browser_ext_devtools_network.js", + "browser/components/extensions/test/browser/browser_ext_tabs_zoom.js", + "browser/components/places/tests/browser/browser_bookmarksProperties.js", + "browser/components/preferences/in-content/tests/browser_extension_controlled.js", + "browser/extensions/formautofill/FormAutofillParent.jsm", + "browser/tools/mozscreenshots/head.js", + "devtools/client/aboutdebugging/test/browser/helper-addons.js", + "devtools/client/inspector/animation/animation.js", + "devtools/client/inspector/changes/ChangesView.js", + "devtools/client/inspector/markup/test/helper_screenshot_node.js", + "devtools/client/performance/modules/widgets/graphs.js", + "devtools/client/scratchpad/scratchpad.js", + "devtools/client/webconsole/webconsole-wrapper.js", + "devtools/server/tests/unit/test_breakpoint-17.js", + "devtools/shared/adb/adb-process.js", + "devtools/shared/fronts/webconsole.js", + "dom/l10n/tests/mochitest/document_l10n/non-system-principal/test.html", + "dom/payments/test/test_basiccard.html", + "dom/payments/test/test_bug1478740.html", + "dom/payments/test/test_canMakePayment.html", + "dom/payments/test/test_closePayment.html", + "dom/payments/test/test_showPayment.html", + "dom/tests/browser/browser_persist_cookies.js", + "dom/tests/browser/browser_persist_mixed_content_image.js", + "netwerk/test/unit/test_http2-proxy.js", + "toolkit/components/contentprefs/ContentPrefService2.jsm", + "toolkit/components/extensions/ExtensionShortcuts.jsm", + "toolkit/components/extensions/ExtensionTestCommon.jsm", + "toolkit/components/extensions/test/browser/browser_ext_themes_dynamic_getCurrent.js", + "toolkit/components/extensions/test/browser/browser_ext_themes_warnings.js", + "toolkit/components/passwordmgr/test/browser/browser_autocomplete_footer.js", + "toolkit/components/remotebrowserutils/tests/browser/browser_httpResponseProcessSelection.js", + "toolkit/components/satchel/FormHistory.jsm", + "toolkit/content/tests/browser/browser_findbar.js", + "toolkit/modules/NewTabUtils.jsm", + "toolkit/mozapps/extensions/test/browser/browser_CTP_plugins.js", + "toolkit/mozapps/extensions/test/browser/head.js", + ], + "rules": { + "no-async-promise-executor": "off", + } }] }; diff --git a/.gitignore b/.gitignore index 305459caa0..96ec746368 100644 --- a/.gitignore +++ b/.gitignore @@ -44,8 +44,6 @@ security/manager/.nss.checkout /gecko.log # Ignore newtab component build assets -browser/components/newtab/bin/prerender.js -browser/components/newtab/bin/prerender.js.map browser/components/newtab/data/locales.json browser/components/newtab/logs/ diff --git a/.prettierignore b/.prettierignore index cf1d22586e..57a9b5d24c 100644 --- a/.prettierignore +++ b/.prettierignore @@ -15,10 +15,6 @@ browser/branding/nightly/pref/firefox-branding.js browser/branding/official/pref/firefox-branding.js browser/branding/unofficial/pref/firefox-branding.js devtools/client/preferences/debugger.js -devtools/client/preferences/devtools-client.js -devtools/client/webide/preferences/webide.js -devtools/shared/preferences/devtools-shared.js -devtools/startup/preferences/devtools-startup.js extensions/pref/autoconfig/test/unit/autoconfig.js mobile/android/app/geckoview-prefs.js mobile/android/app/mobile.js diff --git a/Cargo.lock b/Cargo.lock index ea51ecc9b4..efe5d73b74 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,15 +1,5 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -[[package]] -name = "Inflector" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" -dependencies = [ - "lazy_static", - "regex", -] - [[package]] name = "adler32" version = "1.0.3" @@ -25,15 +15,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "ansi_term" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -dependencies = [ - "winapi 0.3.7", -] - [[package]] name = "app_units" version = "0.7.0" @@ -212,19 +193,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88ceb0d16c4fd0e42876e298d7d3ce3780dd9ebdcbe4199816a32c77e08597ff" -[[package]] -name = "binast" -version = "0.2.0" -dependencies = [ - "Inflector", - "binjs_meta", - "clap", - "env_logger", - "itertools", - "log", - "yaml-rust", -] - [[package]] name = "bincode" version = "1.2.1" @@ -255,18 +223,6 @@ dependencies = [ "shlex", ] -[[package]] -name = "binjs_meta" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d535cc5246fd9035268770420afd76c05f87e68b83ebed0ac94e8258e88fc353" -dependencies = [ - "Inflector", - "itertools", - "log", - "weedle", -] - [[package]] name = "bit-vec" version = "0.5.1" @@ -516,14 +472,11 @@ version = "2.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f16b89cbb9ee36d87483dc939fe9f1e13c05898d56d7b230a0d4dff033a536" dependencies = [ - "ansi_term", - "atty", "bitflags", "strsim", "term_size", "textwrap", "unicode-width", - "vec_map", ] [[package]] @@ -750,9 +703,9 @@ dependencies = [ [[package]] name = "cssparser" -version = "0.27.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" +checksum = "809d22aba9ffd53e9028f2d37261f1826ef613d0e96b1a5ddeefa97cde82bcca" dependencies = [ "cssparser-macros", "dtoa-short", @@ -1726,12 +1679,6 @@ dependencies = [ "safemem", ] -[[package]] -name = "linked-hash-map" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70fb39025bc7cdd76305867c4eccf2f2dcf6e9a57f5b21a93e1c2d86cd03ec9e" - [[package]] name = "lmdb-rkv" version = "0.14.0" @@ -2476,6 +2423,7 @@ dependencies = [ "goblin", "memmap", "object", + "rustc-demangle", "thin-vec", "uuid", ] @@ -3733,12 +3681,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e0a7d8bed3178a8fb112199d466eeca9ed09a14ba8ad67718179b4fd5487d0b" -[[package]] -name = "vec_map" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c" - [[package]] name = "version_check" version = "0.9.1" @@ -3922,15 +3864,6 @@ dependencies = [ "url", ] -[[package]] -name = "weedle" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7d4f9feb723a800d8f7b74edc9fa44ff35cb0b2ec64886714362f423427f37" -dependencies = [ - "nom", -] - [[package]] name = "winapi" version = "0.2.8" @@ -4086,15 +4019,6 @@ dependencies = [ "xpcom", ] -[[package]] -name = "yaml-rust" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95acf0db5515d07da9965ec0e0ba6cc2d825e2caeb7303b66ca441729801254e" -dependencies = [ - "linked-hash-map", -] - [[package]] name = "zip" version = "0.4.2" diff --git a/Cargo.toml b/Cargo.toml index a21e00fda0..062b1f1259 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,6 @@ members = [ "js/src/rust", "js/src/wasm/cranelift", "js/rust", - "js/src/frontend/binast", # Code generator. "testing/geckodriver", "toolkit/crashreporter/rust", "toolkit/library/gtest/rust", diff --git a/README.md b/README.md index 02a8c8bbee..29601686a9 100644 --- a/README.md +++ b/README.md @@ -35,3 +35,10 @@ Then report like for site 'x' is missing feature 'y' **YOU MAY DONATE** But only by crypto [Look here](https://github.com/Feodor2/Mypal68/issues/84) + + +**MY MIRROR PAGES** + +https://codeberg.org/Theodor2/Mypal68 +https://notabug.org/Theodor/Mypal68 + diff --git a/accessible/base/EventQueue.cpp b/accessible/base/EventQueue.cpp index a169686d49..26486e1aa8 100644 --- a/accessible/base/EventQueue.cpp +++ b/accessible/base/EventQueue.cpp @@ -267,8 +267,7 @@ void EventQueue::CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent, void EventQueue::ProcessEventQueue() { // Process only currently queued events. - nsTArray > events; - events.SwapElements(mEvents); + const nsTArray > events = std::move(mEvents); uint32_t eventCount = events.Length(); #ifdef A11Y_LOG diff --git a/accessible/generic/Accessible.cpp b/accessible/generic/Accessible.cpp index 1ff146a7bb..632a4bc2c2 100644 --- a/accessible/generic/Accessible.cpp +++ b/accessible/generic/Accessible.cpp @@ -830,7 +830,7 @@ nsresult Accessible::HandleAccEvent(AccEvent* aEvent) { nsAutoCString strMarker; strMarker.AppendLiteral("A11y Event - "); strMarker.Append(strEventType); - profiler_add_marker(strMarker.get(), JS::ProfilingCategoryPair::OTHER); + PROFILER_ADD_MARKER(strMarker.get(), OTHER); } #endif diff --git a/accessible/ipc/other/DocAccessibleChild.cpp b/accessible/ipc/other/DocAccessibleChild.cpp index a430c5dac8..fe8f1a859c 100644 --- a/accessible/ipc/other/DocAccessibleChild.cpp +++ b/accessible/ipc/other/DocAccessibleChild.cpp @@ -173,7 +173,7 @@ static void AddRelation(Accessible* aAcc, RelationType aType, if (!targets.IsEmpty()) { RelationTargets* newRelation = aTargets->AppendElement( RelationTargets(static_cast(aType), nsTArray())); - newRelation->Targets().SwapElements(targets); + newRelation->Targets() = std::move(targets); } } diff --git a/browser/actors/ContextMenuChild.jsm b/browser/actors/ContextMenuChild.jsm index 999dfe968f..ffb43e5e4e 100644 --- a/browser/actors/ContextMenuChild.jsm +++ b/browser/actors/ContextMenuChild.jsm @@ -586,9 +586,17 @@ class ContextMenuChild extends ActorChild { let referrerInfo = Cc["@mozilla.org/referrer-info;1"].createInstance( Ci.nsIReferrerInfo ); - referrerInfo.initWithElement( - context.onLink ? context.link : aEvent.composedTarget - ); + referrerInfo.initWithElement(aEvent.composedTarget); + + // In the case "onLink" we may have to send link referrerInfo to use in + // _openLinkInParameters + let linkReferrerInfo = null; + if (context.onLink) { + linkReferrerInfo = Cc["@mozilla.org/referrer-info;1"].createInstance( + Ci.nsIReferrerInfo + ); + linkReferrerInfo.initWithElement(context.link); + } let targetAsCPOW = context.target; if (targetAsCPOW) { @@ -652,17 +660,12 @@ class ContextMenuChild extends ActorChild { ); } - // In the case "onLink" we may have to send target referrerInfo. This object - // may be used to in saveMedia function. - if (context.onLink) { - let targetReferrerInfo = Cc[ - "@mozilla.org/referrer-info;1" - ].createInstance(Ci.nsIReferrerInfo); - - targetReferrerInfo.initWithElement(aEvent.composedTarget); - data.targetReferrerInfo = E10SUtils.serializeReferrerInfo( - targetReferrerInfo - ); + if (linkReferrerInfo) { + if (isRemote) { + data.linkReferrerInfo = E10SUtils.serializeReferrerInfo(linkReferrerInfo); + } else { + data.linkReferrerInfo = linkReferrerInfo; + } } Services.obs.notifyObservers( diff --git a/browser/app/profile/mypal.js b/browser/app/profile/mypal.js index 275b4a4f05..5827c0bc07 100644 --- a/browser/app/profile/mypal.js +++ b/browser/app/profile/mypal.js @@ -1715,3 +1715,413 @@ pref("identity.fxaccounts.toolbar.accessed", false); #else pref("corroborator.enabled", true); #endif + +// Disable WebIDE and ConnectPage by default (Bug 1539451) +pref("devtools.webide.enabled", false); +pref("devtools.connectpage.enabled", false); + +// Toolbox preferences +pref("devtools.toolbox.footer.height", 250); +pref("devtools.toolbox.sidebar.width", 500); +pref("devtools.toolbox.host", "bottom"); +pref("devtools.toolbox.previousHost", "right"); +pref("devtools.toolbox.selectedTool", "inspector"); +pref("devtools.toolbox.sideEnabled", true); +pref("devtools.toolbox.zoomValue", "1"); +pref("devtools.toolbox.splitconsoleEnabled", false); +pref("devtools.toolbox.splitconsoleHeight", 100); +pref("devtools.toolbox.tabsOrder", ""); + +// Toolbox Button preferences +pref("devtools.command-button-pick.enabled", true); +pref("devtools.command-button-frames.enabled", true); +pref("devtools.command-button-splitconsole.enabled", true); +pref("devtools.command-button-paintflashing.enabled", false); +pref("devtools.command-button-scratchpad.enabled", false); +pref("devtools.command-button-responsive.enabled", true); +pref("devtools.command-button-screenshot.enabled", false); +pref("devtools.command-button-rulers.enabled", false); +pref("devtools.command-button-measure.enabled", false); +pref("devtools.command-button-noautohide.enabled", false); + +// Inspector preferences +// Enable the Inspector +pref("devtools.inspector.enabled", true); +// What was the last active sidebar in the inspector +pref("devtools.inspector.activeSidebar", "layoutview"); +pref("devtools.inspector.remote", false); + +// Enable the 3 pane mode in the inspector +pref("devtools.inspector.three-pane-enabled", true); +// Enable the 3 pane mode in the chrome inspector +pref("devtools.inspector.chrome.three-pane-enabled", false); +// Collapse pseudo-elements by default in the rule-view +pref("devtools.inspector.show_pseudo_elements", false); +// The default size for image preview tooltips in the rule-view/computed-view/markup-view +pref("devtools.inspector.imagePreviewTooltipSize", 300); +// Enable user agent style inspection in rule-view +pref("devtools.inspector.showUserAgentStyles", false); +// Show all native anonymous content +pref("devtools.inspector.showAllAnonymousContent", false); +// Show user agent shadow roots +pref("devtools.inspector.showUserAgentShadowRoots", false); +// Enable the new Rules View +pref("devtools.inspector.new-rulesview.enabled", false); + +// Grid highlighter preferences +pref("devtools.gridinspector.gridOutlineMaxColumns", 50); +pref("devtools.gridinspector.gridOutlineMaxRows", 50); +pref("devtools.gridinspector.showGridAreas", false); +pref("devtools.gridinspector.showGridLineNumbers", false); +pref("devtools.gridinspector.showInfiniteLines", false); +// Max number of grid highlighters that can be displayed +pref("devtools.gridinspector.maxHighlighters", 3); + +// Whether or not the box model panel is opened in the layout view +pref("devtools.layout.boxmodel.opened", true); +// Whether or not the flexbox panel is opened in the layout view +pref("devtools.layout.flexbox.opened", true); +// Whether or not the grid inspector panel is opened in the layout view +pref("devtools.layout.grid.opened", true); + +// Enable hovering Box Model values and jumping to their source CSS rule in the +// rule-view. +#if defined(NIGHTLY_BUILD) + pref("devtools.layout.boxmodel.highlightProperty", true); +#else + pref("devtools.layout.boxmodel.highlightProperty", false); +#endif + +// By how many times eyedropper will magnify pixels +pref("devtools.eyedropper.zoom", 6); + +// Enable to collapse attributes that are too long. +pref("devtools.markup.collapseAttributes", true); +// Length to collapse attributes +pref("devtools.markup.collapseAttributeLength", 120); +// Whether to auto-beautify the HTML on copy. +pref("devtools.markup.beautifyOnCopy", false); +// Whether or not the DOM mutation breakpoints context menu are enabled in the +// markup view. +pref("devtools.markup.mutationBreakpoints.enabled", true); + +// DevTools default color unit +pref("devtools.defaultColorUnit", "authored"); + +// Enable the Memory tools +pref("devtools.memory.enabled", true); + +pref("devtools.memory.custom-census-displays", "{}"); +pref("devtools.memory.custom-label-displays", "{}"); +pref("devtools.memory.custom-tree-map-displays", "{}"); + +pref("devtools.memory.max-individuals", 1000); +pref("devtools.memory.max-retaining-paths", 10); + +// Enable the Performance tools +pref("devtools.performance.enabled", true); + +// The default Performance UI settings +pref("devtools.performance.memory.sample-probability", "0.05"); +// Can't go higher than this without causing internal allocation overflows while +// serializing the allocations data over the RDP. +pref("devtools.performance.memory.max-log-length", 125000); +pref("devtools.performance.timeline.hidden-markers", + "[\"Composite\",\"CompositeForwardTransaction\"]"); +pref("devtools.performance.profiler.buffer-size", 10000000); +pref("devtools.performance.profiler.sample-frequency-hz", 1000); +pref("devtools.performance.ui.invert-call-tree", true); +pref("devtools.performance.ui.invert-flame-graph", false); +pref("devtools.performance.ui.flatten-tree-recursion", true); +pref("devtools.performance.ui.show-platform-data", false); +pref("devtools.performance.ui.show-idle-blocks", true); +pref("devtools.performance.ui.enable-memory", false); +pref("devtools.performance.ui.enable-allocations", false); +pref("devtools.performance.ui.enable-framerate", true); +pref("devtools.performance.ui.show-jit-optimizations", false); +pref("devtools.performance.ui.show-triggers-for-gc-types", + "TOO_MUCH_MALLOC ALLOC_TRIGGER LAST_DITCH EAGER_ALLOC_TRIGGER"); + +// Temporary pref disabling memory flame views +// TODO remove once we have flame charts via bug 1148663 +pref("devtools.performance.ui.enable-memory-flame", false); + +// Enable experimental options in the UI only in Nightly +#if defined(NIGHTLY_BUILD) + pref("devtools.performance.ui.experimental", true); +#else + pref("devtools.performance.ui.experimental", false); +#endif + +// Preferences for the new performance panel. This pref configures the base URL +// for the profiler.firefox.com instance to use. This is useful so that a +// developer can change it while working on profiler.firefox.com, or in tests. +// This isn't exposed directly to the user. +pref("devtools.performance.recording.ui-base-url", "https://profiler.firefox.com"); + +// A JSON array of strings, where each string is a file path to an objdir on +// the host machine. This is used in order to look up symbol information from +// build artifacts of local builds. +pref("devtools.performance.recording.objdirs", "[]"); + +// The default cache UI setting +pref("devtools.cache.disabled", false); + +// The default service workers UI setting +pref("devtools.serviceWorkers.testing.enabled", false); + +// Enable the Network Monitor +pref("devtools.netmonitor.enabled", true); + +// Enable the Application panel +pref("devtools.application.enabled", false); + +// The default Network Monitor UI settings +pref("devtools.netmonitor.panes-network-details-width", 550); +pref("devtools.netmonitor.panes-network-details-height", 450); +pref("devtools.netmonitor.filters", "[\"all\"]"); +pref("devtools.netmonitor.visibleColumns", + "[\"status\",\"method\",\"domain\",\"file\",\"cause\",\"type\",\"transferred\",\"contentSize\",\"waterfall\"]" +); +pref("devtools.netmonitor.columnsData", + '[{"name":"status","minWidth":30,"width":5}, {"name":"method","minWidth":30,"width":5}, {"name":"domain","minWidth":30,"width":10}, {"name":"file","minWidth":30,"width":25}, {"name":"url","minWidth":30,"width":25}, {"name":"cause","minWidth":30,"width":10},{"name":"type","minWidth":30,"width":5},{"name":"transferred","minWidth":30,"width":10},{"name":"contentSize","minWidth":30,"width":5},{"name":"waterfall","minWidth":150,"width":25}]'); +pref("devtools.netmonitor.ws.payload-preview-height", 128); +pref("devtools.netmonitor.ws.visibleColumns", + '["data", "time"]' +); +pref("devtools.netmonitor.ws.displayed-frames.limit", 500); + +pref("devtools.netmonitor.response.ui.limit", 10240); + +// Save request/response bodies yes/no. +pref("devtools.netmonitor.saveRequestAndResponseBodies", true); + +// The default Network monitor HAR export setting +pref("devtools.netmonitor.har.defaultLogDir", ""); +pref("devtools.netmonitor.har.defaultFileName", "%hostname_Archive [%date]"); +pref("devtools.netmonitor.har.jsonp", false); +pref("devtools.netmonitor.har.jsonpCallback", ""); +pref("devtools.netmonitor.har.includeResponseBodies", true); +pref("devtools.netmonitor.har.compress", false); +pref("devtools.netmonitor.har.forceExport", false); +pref("devtools.netmonitor.har.pageLoadedTimeout", 1500); +pref("devtools.netmonitor.har.enableAutoExportToFile", false); + +// Enable WebSocket monitoring in Nightly builds. +#if defined(NIGHTLY_BUILD) + pref("devtools.netmonitor.features.webSockets", true); +#else + pref("devtools.netmonitor.features.webSockets", false); +#endif + +// Scratchpad settings +// - recentFileMax: The maximum number of recently-opened files +// stored. Setting this preference to 0 will not +// clear any recent files, but rather hide the +// 'Open Recent'-menu. +// - lineNumbers: Whether to show line numbers or not. +// - wrapText: Whether to wrap text or not. +// - showTrailingSpace: Whether to highlight trailing space or not. +// - editorFontSize: Editor font size configuration. +// - enableAutocompletion: Whether to enable JavaScript autocompletion. +pref("devtools.scratchpad.recentFilesMax", 10); +pref("devtools.scratchpad.lineNumbers", true); +pref("devtools.scratchpad.wrapText", false); +pref("devtools.scratchpad.showTrailingSpace", false); +pref("devtools.scratchpad.editorFontSize", 12); +pref("devtools.scratchpad.enableAutocompletion", true); + +// Enable the Storage Inspector +pref("devtools.storage.enabled", true); + +// Enable the Style Editor. +pref("devtools.styleeditor.enabled", true); +pref("devtools.styleeditor.autocompletion-enabled", true); +pref("devtools.styleeditor.showMediaSidebar", true); +pref("devtools.styleeditor.mediaSidebarWidth", 238); +pref("devtools.styleeditor.navSidebarWidth", 245); +pref("devtools.styleeditor.transitions", true); + +// Screenshot Option Settings. +pref("devtools.screenshot.clipboard.enabled", false); +pref("devtools.screenshot.audio.enabled", true); + +// Enable Scratchpad +pref("devtools.scratchpad.enabled", false); + +// Make sure the DOM panel is hidden by default +pref("devtools.dom.enabled", false); + +// Enable the Accessibility panel. +pref("devtools.accessibility.enabled", true); + +// Web console filters +pref("devtools.webconsole.filter.error", true); +pref("devtools.webconsole.filter.warn", true); +pref("devtools.webconsole.filter.info", true); +pref("devtools.webconsole.filter.log", true); +pref("devtools.webconsole.filter.debug", true); +pref("devtools.webconsole.filter.css", false); +pref("devtools.webconsole.filter.net", false); +pref("devtools.webconsole.filter.netxhr", false); + +// Webconsole autocomplete preference +pref("devtools.webconsole.input.autocomplete",true); + +// Browser console filters +pref("devtools.browserconsole.filter.error", true); +pref("devtools.browserconsole.filter.warn", true); +pref("devtools.browserconsole.filter.info", true); +pref("devtools.browserconsole.filter.log", true); +pref("devtools.browserconsole.filter.debug", true); +pref("devtools.browserconsole.filter.css", false); +pref("devtools.browserconsole.filter.net", false); +pref("devtools.browserconsole.filter.netxhr", false); + +// Max number of inputs to store in web console history. +pref("devtools.webconsole.inputHistoryCount", 300); + +// Persistent logging: |true| if you want the relevant tool to keep all of the +// logged messages after reloading the page, |false| if you want the output to +// be cleared each time page navigation happens. +pref("devtools.webconsole.persistlog", false); +pref("devtools.netmonitor.persistlog", false); + +// Web Console timestamp: |true| if you want the logs and instructions +// in the Web Console to display a timestamp, or |false| to not display +// any timestamps. +pref("devtools.webconsole.timestampMessages", false); + +// Enable the webconsole sidebar toggle in Nightly builds. +#if defined(NIGHTLY_BUILD) + pref("devtools.webconsole.sidebarToggle", true); +#else + pref("devtools.webconsole.sidebarToggle", false); +#endif + +// Enable editor mode in the console in Nightly builds. +#if defined(NIGHTLY_BUILD) + pref("devtools.webconsole.features.editor", true); +#else + pref("devtools.webconsole.features.editor", false); +#endif + +// Saved editor mode state in the console. +pref("devtools.webconsole.input.editor", false); + +// Editor width for webconsole and browserconsole +pref("devtools.webconsole.input.editorWidth", 0); +pref("devtools.browserconsole.input.editorWidth", 0); + +// Disable the new performance recording panel by default +pref("devtools.performance.new-panel-enabled", false); + +// Enable message grouping in the console, true by default +pref("devtools.webconsole.groupWarningMessages", true); + +// Saved state of the Display content messages checkbox in the browser console. +pref("devtools.browserconsole.contentMessages", false); + +// Enable client-side mapping service for source maps +pref("devtools.source-map.client-service.enabled", true); + +// The number of lines that are displayed in the web console. +pref("devtools.hud.loglimit", 10000); + +// The developer tools editor configuration: +// - tabsize: how many spaces to use when a Tab character is displayed. +// - expandtab: expand Tab characters to spaces. +// - keymap: which keymap to use (can be 'default', 'emacs' or 'vim') +// - autoclosebrackets: whether to permit automatic bracket/quote closing. +// - detectindentation: whether to detect the indentation from the file +// - enableCodeFolding: Whether to enable code folding or not. +pref("devtools.editor.tabsize", 2); +pref("devtools.editor.expandtab", true); +pref("devtools.editor.keymap", "default"); +pref("devtools.editor.autoclosebrackets", true); +pref("devtools.editor.detectindentation", true); +pref("devtools.editor.enableCodeFolding", true); +pref("devtools.editor.autocomplete", true); + +// The angle of the viewport. +pref("devtools.responsive.viewport.angle", 0); +// The width of the viewport. +pref("devtools.responsive.viewport.width", 320); +// The height of the viewport. +pref("devtools.responsive.viewport.height", 480); +// The pixel ratio of the viewport. +pref("devtools.responsive.viewport.pixelRatio", 0); +// Whether or not the viewports are left aligned. +pref("devtools.responsive.leftAlignViewport.enabled", false); +// Whether to reload when touch simulation is toggled +pref("devtools.responsive.reloadConditions.touchSimulation", false); +// Whether to reload when user agent is changed +pref("devtools.responsive.reloadConditions.userAgent", false); +// Whether to show the notification about reloading to apply emulation +pref("devtools.responsive.reloadNotification.enabled", true); +// Whether or not touch simulation is enabled. +pref("devtools.responsive.touchSimulation.enabled", false); +// Whether or not meta viewport is enabled, if and only if touchSimulation +// is also enabled. +pref("devtools.responsive.metaViewport.enabled", false); +// The user agent of the viewport. +pref("devtools.responsive.userAgent", ""); + +// Whether to show the settings onboarding tooltip only in release or beta +// builds. +#if defined(RELEASE_OR_BETA) + pref("devtools.responsive.show-setting-tooltip", true); +#else + pref("devtools.responsive.show-setting-tooltip", false); +#endif +// Show the custom user agent input in Nightly builds. +#if defined(NIGHTLY_BUILD) + pref("devtools.responsive.showUserAgentInput", true); +#else + pref("devtools.responsive.showUserAgentInput", false); +#endif + +// Show tab debug targets for This Firefox (on by default for local builds). +#ifdef MOZILLA_OFFICIAL + pref("devtools.aboutdebugging.local-tab-debugging", false); +#else + pref("devtools.aboutdebugging.local-tab-debugging", true); +#endif + +// Show process debug targets. +pref("devtools.aboutdebugging.process-debugging", true); +// Stringified array of network locations that users can connect to. +pref("devtools.aboutdebugging.network-locations", "[]"); +// Debug target pane collapse/expand settings. +pref("devtools.aboutdebugging.collapsibilities.installedExtension", false); +pref("devtools.aboutdebugging.collapsibilities.otherWorker", false); +pref("devtools.aboutdebugging.collapsibilities.serviceWorker", false); +pref("devtools.aboutdebugging.collapsibilities.sharedWorker", false); +pref("devtools.aboutdebugging.collapsibilities.tab", false); +pref("devtools.aboutdebugging.collapsibilities.temporaryExtension", false); + +// about:debugging: only show system and hidden extensions in local builds by +// default. +#ifdef MOZILLA_OFFICIAL + pref("devtools.aboutdebugging.showHiddenAddons", false); +#else + pref("devtools.aboutdebugging.showHiddenAddons", true); +#endif + +// Map top-level await expressions in the console +pref("devtools.debugger.features.map-await-expression", true); + +// Disable autohide for DevTools popups and tooltips. +// This is currently not exposed by any UI to avoid making +// about:devtools-toolbox tabs unusable by mistake. +pref("devtools.popup.disable_autohide", false); + +pref("devtools.webide.templatesURL", "https://code.cdn.mozilla.net/templates/list.json"); +pref("devtools.webide.autoinstallADBExtension", true); +pref("devtools.webide.autoConnectRuntime", true); +pref("devtools.webide.restoreLastProject", true); +pref("devtools.webide.enableLocalRuntime", false); +pref("devtools.webide.lastConnectedRuntime", ""); +pref("devtools.webide.lastSelectedProject", ""); +pref("devtools.webide.zoom", "1"); +pref("devtools.webide.busyTimeout", 10000); diff --git a/browser/app/winlauncher/test/moz.build b/browser/app/winlauncher/test/moz.build index abd93ad82b..a2fc1706cd 100644 --- a/browser/app/winlauncher/test/moz.build +++ b/browser/app/winlauncher/test/moz.build @@ -25,6 +25,3 @@ if CONFIG['CC_TYPE'] in ('gcc', 'clang'): LDFLAGS += [ '-municode', ] - -if CONFIG['CC_TYPE'] == 'clang-cl': - AllowCompilerWarnings() # workaround for bug 1090497 diff --git a/browser/base/content/aboutRestartRequired.js b/browser/base/content/aboutRestartRequired.js index 753664f427..d9ad24f6dd 100644 --- a/browser/base/content/aboutRestartRequired.js +++ b/browser/base/content/aboutRestartRequired.js @@ -32,3 +32,8 @@ var AboutRestartRequired = { }; AboutRestartRequired.init(); + +let restartButton = document.getElementById("restart"); +restartButton.onclick = function() { + AboutRestartRequired.restart(); +}; diff --git a/browser/base/content/aboutRestartRequired.xhtml b/browser/base/content/aboutRestartRequired.xhtml index 02e2296d75..6db21758a1 100644 --- a/browser/base/content/aboutRestartRequired.xhtml +++ b/browser/base/content/aboutRestartRequired.xhtml @@ -6,6 +6,7 @@ + @@ -33,8 +34,7 @@
- +
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index a3d95d2704..b03d2f10b1 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -3381,6 +3381,7 @@ function losslessDecodeURI(aURI) { // This includes all bidirectional formatting characters. // (RFC 3987 sections 3.2 and 4.1 paragraph 6) value = value.replace( + // eslint-disable-next-line no-misleading-character-class /[\u00ad\u034f\u061c\u115f-\u1160\u17b4-\u17b5\u180b-\u180d\u200b\u200e-\u200f\u202a-\u202e\u2060-\u206f\u3164\ufe00-\ufe0f\ufeff\uffa0\ufff0-\ufff8]|\ud834[\udd73-\udd7a]|[\udb40-\udb43][\udc00-\udfff]/g, encodeURIComponent ); @@ -9276,7 +9277,7 @@ var RestoreLastSessionObserver = { ) { Services.obs.addObserver(this, "sessionstore-last-session-cleared", true); goSetCommandEnabled("Browser:RestoreLastSession", true); - } else if (SessionStartup.isAutomaticRestoreEnabled()) { + } else if (SessionStore.willAutoRestore) { document .getElementById("Browser:RestoreLastSession") .setAttribute("hidden", true); diff --git a/browser/base/content/nsContextMenu.js b/browser/base/content/nsContextMenu.js index 292c489b1c..42f28da13e 100644 --- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -44,7 +44,7 @@ function openContextMenu(aMessage) { let browser = aMessage.target; let spellInfo = data.spellInfo; let frameReferrerInfo = data.frameReferrerInfo; - let targetReferrerInfo = data.targetReferrerInfo; + let linkReferrerInfo = data.linkReferrerInfo; // ContextMenu.jsm sends us the target as a CPOW only so that // we can send that CPOW back down to the content process and @@ -68,8 +68,8 @@ function openContextMenu(aMessage) { E10SUtils.deserializeReferrerInfo(frameReferrerInfo); } - if (targetReferrerInfo) { - targetReferrerInfo = E10SUtils.deserializeReferrerInfo(targetReferrerInfo); + if (linkReferrerInfo && data.isRemote) { + linkReferrerInfo = E10SUtils.deserializeReferrerInfo(linkReferrerInfo); } gContextMenuContentData = { @@ -86,7 +86,7 @@ function openContextMenu(aMessage) { charSet: data.charSet, referrerInfo: E10SUtils.deserializeReferrerInfo(data.referrerInfo), frameReferrerInfo, - targetReferrerInfo, + linkReferrerInfo, contentType: data.contentType, contentDisposition: data.contentDisposition, frameOuterWindowID: data.frameOuterWindowID, @@ -1094,7 +1094,9 @@ nsContextMenu.prototype = { params.frameOuterWindowID = this.frameOuterWindowID; } - let referrerInfo = gContextMenuContentData.referrerInfo; + let referrerInfo = this.onLink + ? gContextMenuContentData.linkReferrerInfo + : gContextMenuContentData.referrerInfo; // If we want to change userContextId, we must be sure that we don't // propagate the referrer. if ( @@ -1371,7 +1373,7 @@ nsContextMenu.prototype = { ); // Cache this because we fetch the data async - let { targetReferrerInfo } = gContextMenuContentData; + let referrerInfo = gContextMenuContentData.referrerInfo; let onMessage = message => { mm.removeMessageListener( @@ -1386,7 +1388,7 @@ nsContextMenu.prototype = { "SaveImageTitle", true, // bypass cache false, // don't skip prompt for where to save - targetReferrerInfo, // referrer info + referrerInfo, // referrer info null, // document null, // content type null, // content disposition @@ -1672,6 +1674,10 @@ nsContextMenu.prototype = { // Save URL of clicked-on link. saveLink() { + let referrerInfo = this.onLink + ? gContextMenuContentData.linkReferrerInfo + : gContextMenuContentData.referrerInfo; + let isContentWindowPrivate = this.isRemote ? this.ownerDoc.isPrivate : undefined; @@ -1681,7 +1687,7 @@ nsContextMenu.prototype = { null, true, this.ownerDoc, - gContextMenuContentData.referrerInfo, + referrerInfo, this.frameOuterWindowID, this.linkDownload, isContentWindowPrivate @@ -1701,7 +1707,7 @@ nsContextMenu.prototype = { let isContentWindowPrivate = this.isRemote ? this.ownerDoc.isPrivate : undefined; - let referrerInfo = gContextMenuContentData.targetReferrerInfo; + let referrerInfo = gContextMenuContentData.referrerInfo; let isPrivate = PrivateBrowsingUtils.isBrowserPrivate(this.browser); if (this.onCanvas) { // Bypass cache, since it's a data: URL. diff --git a/browser/base/content/test/plugins/head.js b/browser/base/content/test/plugins/head.js index b8de7f2d0b..ba667d350d 100644 --- a/browser/base/content/test/plugins/head.js +++ b/browser/base/content/test/plugins/head.js @@ -19,7 +19,7 @@ XPCOMUtils.defineLazyServiceGetters(this, { // Various tests in this directory may define gTestBrowser, to use as the // default browser under test in some of the functions below. -/* global gTestBrowser */ +/* global gTestBrowser:true */ /** * Waits a specified number of miliseconds. diff --git a/browser/base/content/test/static/browser_all_files_referenced.js b/browser/base/content/test/static/browser_all_files_referenced.js index b021c2c78f..0eaa393c36 100644 --- a/browser/base/content/test/static/browser_all_files_referenced.js +++ b/browser/base/content/test/static/browser_all_files_referenced.js @@ -32,8 +32,6 @@ var gExceptionPaths = [ // https://github.com/mozilla/activity-stream/issues/3053 "resource://activity-stream/data/content/tippytop/images/", - // https://github.com/mozilla/activity-stream/issues/3758 - "resource://activity-stream/prerendered/", // browser/extensions/pdfjs/content/build/pdf.js#1999 "resource://pdf.js/web/images/", @@ -266,6 +264,11 @@ if (!isDevtools) { ]) { whitelist.add("resource://services-sync/engines/" + module); } + // resource://devtools/shared/worker/loader.js, + // resource://devtools/shared/builtin-modules.js + if (!AppConstants.ENABLE_REMOTE_AGENT) { + whitelist.add("resource://gre/modules/jsdebugger.jsm"); + } } if (AppConstants.MOZ_CODE_COVERAGE) { diff --git a/browser/base/content/test/static/browser_misused_characters_in_strings.js b/browser/base/content/test/static/browser_misused_characters_in_strings.js index a9667b4feb..6bbefe0c74 100644 --- a/browser/base/content/test/static/browser_misused_characters_in_strings.js +++ b/browser/base/content/test/static/browser_misused_characters_in_strings.js @@ -281,13 +281,13 @@ add_task(async function checkAllTheFluents() { domParser.forceEnableDTD(); for (let uri of uris) { let rawContents = await fetchFile(uri.spec); - let resource = FluentResource.fromString(rawContents); + let resource = new FluentResource(rawContents); if (!resource) { return; } - for (let [key, val] of resource) { - CheckError(domParser, uri, key, val); + for (let message of resource.body) { + CheckError(domParser, uri, message.id, message); } } }); diff --git a/browser/components/BrowserGlue.jsm b/browser/components/BrowserGlue.jsm index 002ed6ab03..1834d11b1f 100644 --- a/browser/components/BrowserGlue.jsm +++ b/browser/components/BrowserGlue.jsm @@ -482,9 +482,6 @@ XPCOMUtils.defineLazyModuleGetters(this, { RemotePrompt: "resource:///modules/RemotePrompt.jsm", }); -/* global ContentPrefServiceParent:false, ContentSearch:false, - UpdateListener:false, webrtcUI:false */ - /** * IF YOU ADD OR REMOVE FROM THIS LIST, PLEASE UPDATE THE LIST ABOVE AS WELL. * XXX Bug 1325373 is for making eslint detect these automatically. diff --git a/browser/components/downloads/DownloadsViewUI.jsm b/browser/components/downloads/DownloadsViewUI.jsm index 24da156a4d..10d7f2fb28 100644 --- a/browser/components/downloads/DownloadsViewUI.jsm +++ b/browser/components/downloads/DownloadsViewUI.jsm @@ -327,8 +327,14 @@ this.DownloadsViewUI.DownloadElementShell.prototype = { * Downloads View. */ showStatusWithDetails(stateLabel, hoverStatus) { + let referrer = + this.download.source.referrerInfo && + this.download.source.referrerInfo.originalReferrer + ? this.download.source.referrerInfo.originalReferrer.spec + : null; + let [displayHost] = DownloadUtils.getURIHost( - this.download.source.referrer || this.download.source.url + referrer || this.download.source.url ); let [displayDate] = DownloadUtils.getReadableDates( new Date(this.download.endTime) diff --git a/browser/components/downloads/test/browser/browser.ini b/browser/components/downloads/test/browser/browser.ini index 13a1ff88cd..98497a2943 100644 --- a/browser/components/downloads/test/browser/browser.ini +++ b/browser/components/downloads/test/browser/browser.ini @@ -16,3 +16,4 @@ skip-if = (os == 'win' && os_version == '10.0' && ccov) # Bug 1306510 skip-if = true # Bug 1352792 [browser_downloads_panel_height.js] [browser_downloads_autohide.js] +[browser_go_to_download_page.js] diff --git a/browser/components/downloads/test/browser/browser_go_to_download_page.js b/browser/components/downloads/test/browser/browser_go_to_download_page.js new file mode 100644 index 0000000000..6a42a885a0 --- /dev/null +++ b/browser/components/downloads/test/browser/browser_go_to_download_page.js @@ -0,0 +1,93 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const ReferrerInfo = Components.Constructor( + "@mozilla.org/referrer-info;1", + "nsIReferrerInfo", + "init" +); + +const TEST_REFERRER = "https://example.com/"; + +registerCleanupFunction(async function() { + await task_resetState(); + await PlacesUtils.history.clear(); +}); + +async function addDownload(referrerInfo) { + let startTimeMs = Date.now(); + + let publicList = await Downloads.getList(Downloads.PUBLIC); + let downloadData = { + source: { + url: "http://www.example.com/test-download.txt", + referrerInfo, + }, + target: { + path: gTestTargetFile.path, + }, + startTime: new Date(startTimeMs++), + }; + let download = await Downloads.createDownload(downloadData); + await publicList.add(download); + await download.start(); +} + +/** + * Make sure "Go To Download Page" is enabled and works as expected. + */ +add_task(async function test_go_to_download_page() { + let referrerInfo = new ReferrerInfo( + Ci.nsIReferrerInfo.NO_REFERRER, + true, + NetUtil.newURI(TEST_REFERRER) + ); + + let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, TEST_REFERRER); + + // Wait for focus first + await promiseFocus(); + + // Ensure that state is reset in case previous tests didn't finish. + await task_resetState(); + + // Populate the downloads database with the data required by this test. + await addDownload(referrerInfo); + + // Open the user interface and wait for data to be fully loaded. + await task_openPanel(); + + let win = await openLibrary("Downloads"); + registerCleanupFunction(function() { + win.close(); + }); + + let listbox = win.document.getElementById("downloadsRichListBox"); + ok(listbox, "download list box present"); + + // Select one of the downloads. + listbox.itemChildren[0].click(); + + let contextMenu = win.document.getElementById("downloadsContextMenu"); + + let popupShownPromise = BrowserTestUtils.waitForEvent( + contextMenu, + "popupshown" + ); + EventUtils.synthesizeMouseAtCenter( + listbox.itemChildren[0], + { type: "contextmenu", button: 2 }, + win + ); + await popupShownPromise; + + // Find and click "Go To Download Page" + let goToDownloadButton = [...contextMenu.children].find( + child => child.command == "downloadsCmd_openReferrer" + ); + goToDownloadButton.click(); + + let newTab = await tabPromise; + ok(newTab, "Go To Download Page opened a new tab"); + gBrowser.removeTab(newTab); +}); diff --git a/browser/components/enterprisepolicies/content/aboutPolicies.js b/browser/components/enterprisepolicies/content/aboutPolicies.js index 9ae8620447..87648c6f25 100644 --- a/browser/components/enterprisepolicies/content/aboutPolicies.js +++ b/browser/components/enterprisepolicies/content/aboutPolicies.js @@ -337,7 +337,7 @@ function generateDocumentation() { } let gInited = false; -function init() { +window.onload = function() { if (gInited) { return; } @@ -371,7 +371,7 @@ function init() { sectionButton.click(); } }); -} +}; function show(button) { let current_tab = document.querySelector(".active"); diff --git a/browser/components/enterprisepolicies/content/aboutPolicies.xhtml b/browser/components/enterprisepolicies/content/aboutPolicies.xhtml index 4d69c44fce..ea3599d9b1 100644 --- a/browser/components/enterprisepolicies/content/aboutPolicies.xhtml +++ b/browser/components/enterprisepolicies/content/aboutPolicies.xhtml @@ -8,6 +8,7 @@ + <link rel="stylesheet" href="chrome://browser/content/policies/aboutPolicies.css" type="text/css" /> <link rel="localization" href="branding/brand.ftl"/> @@ -16,7 +17,7 @@ <link rel="localization" href="browser/policies/policies-descriptions.ftl"/> <script src="chrome://browser/content/policies/aboutPolicies.js" /> </head> - <body id="body" onload="init()"> + <body id="body"> <div id="categories"> <div class="category" selected="true" id="category-active"> <img class="category-icon" src="chrome://browser/content/policies/policies-active.svg"></img> diff --git a/browser/components/extensions/parent/ext-browsingData.js b/browser/components/extensions/parent/ext-browsingData.js index 4018ed55fa..439b6eee85 100644 --- a/browser/components/extensions/parent/ext-browsingData.js +++ b/browser/components/extensions/parent/ext-browsingData.js @@ -166,7 +166,7 @@ const clearLocalStorage = async function(options) { Services.obs.notifyObservers(null, "extension:purge-localStorage"); } - if (Services.lsm.nextGenLocalStorageEnabled) { + if (Services.domStorageManager.nextGenLocalStorageEnabled) { // Ideally we could reuse the logic in Sanitizer.jsm or nsIClearDataService, // but this API exposes an ability to wipe data at a much finger granularity // than those APIs. So custom logic is used here to wipe only the QM diff --git a/browser/components/extensions/test/browser/browser_ext_addon_debugging_netmonitor.js b/browser/components/extensions/test/browser/browser_ext_addon_debugging_netmonitor.js index 9d4d889d30..2c8459987b 100644 --- a/browser/components/extensions/test/browser/browser_ext_addon_debugging_netmonitor.js +++ b/browser/components/extensions/test/browser/browser_ext_addon_debugging_netmonitor.js @@ -3,7 +3,7 @@ const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm"); const { DebuggerClient } = require("devtools/shared/client/debugger-client"); -const { DebuggerServer } = require("devtools/server/main"); +const { DebuggerServer } = require("devtools/server/debugger-server"); const { gDevTools } = require("devtools/client/framework/devtools"); const { Toolbox } = require("devtools/client/framework/toolbox"); @@ -28,9 +28,7 @@ async function setupToolboxTest(extensionId) { } } - const console = await toolbox.selectTool("webconsole"); - const { hud } = console; - const { jsterm } = hud; + const consoleFront = await toolbox.target.getFront("console"); const netmonitor = await toolbox.selectTool("netmonitor"); @@ -38,7 +36,7 @@ async function setupToolboxTest(extensionId) { // Call a function defined in the target extension to make it // fetch from an expected http url. - await jsterm.execute(`doFetchHTTPRequest("${expectedURL}");`); + await consoleFront.evaluateJSAsync(`doFetchHTTPRequest("${expectedURL}");`); await waitFor(() => { return !netmonitor.panelWin.document.querySelector( @@ -68,7 +66,7 @@ async function setupToolboxTest(extensionId) { // Call a function defined in the target extension to make assertions // on the network requests collected by the netmonitor panel. - await jsterm.execute( + await consoleFront.evaluateJSAsync( `testNetworkRequestReceived(${JSON.stringify(requests)});` ); diff --git a/browser/components/extensions/test/browser/browser_ext_contextMenus.js b/browser/components/extensions/test/browser/browser_ext_contextMenus.js index 25e62e1ef9..c1b40e37d7 100644 --- a/browser/components/extensions/test/browser/browser_ext_contextMenus.js +++ b/browser/components/extensions/test/browser/browser_ext_contextMenus.js @@ -4,8 +4,7 @@ Services.scriptloader.loadSubScript( "chrome://mochitests/content/browser/browser/components/places/tests/browser/head.js", this ); -/* globals withSidebarTree, synthesizeClickOnSelectedTreeCell, - * promiseLibrary, promiseLibraryClosed +/* globals withSidebarTree, synthesizeClickOnSelectedTreeCell, promiseLibrary, promiseLibraryClosed */ const PAGE = diff --git a/browser/components/extensions/test/browser/browser_ext_pageAction_popup_resize.js b/browser/components/extensions/test/browser/browser_ext_pageAction_popup_resize.js index 1178bc0d0e..06a4623083 100644 --- a/browser/components/extensions/test/browser/browser_ext_pageAction_popup_resize.js +++ b/browser/components/extensions/test/browser/browser_ext_pageAction_popup_resize.js @@ -11,7 +11,6 @@ add_task(async function testPageActionPopupResize() { }, }, background: function() { - /* global browser */ browser.tabs.query({ active: true, currentWindow: true }, tabs => { const tabId = tabs[0].id; diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_discard.js b/browser/components/extensions/test/browser/browser_ext_tabs_discard.js index 7bcf2e6fe3..4f4f6d15c4 100644 --- a/browser/components/extensions/test/browser/browser_ext_tabs_discard.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_discard.js @@ -1,4 +1,4 @@ -/* global gBrowser SessionStore */ +/* global gBrowser */ "use strict"; add_task(async function test_discarded() { diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_highlight.js b/browser/components/extensions/test/browser/browser_ext_tabs_highlight.js index 298a006480..d40c6833c1 100644 --- a/browser/components/extensions/test/browser/browser_ext_tabs_highlight.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_highlight.js @@ -1,4 +1,4 @@ -/* global gBrowser SessionStore */ +/* global gBrowser */ "use strict"; add_task(async function test_highlighted() { diff --git a/browser/components/newtab/.eslintignore b/browser/components/newtab/.eslintignore index f7f27c26af..1620218633 100644 --- a/browser/components/newtab/.eslintignore +++ b/browser/components/newtab/.eslintignore @@ -1,4 +1,3 @@ data/ logs/ -prerendered/ vendor/ diff --git a/browser/components/newtab/.eslintrc.js b/browser/components/newtab/.eslintrc.js index dbaea8769a..0d6baa5f3a 100644 --- a/browser/components/newtab/.eslintrc.js +++ b/browser/components/newtab/.eslintrc.js @@ -19,7 +19,7 @@ module.exports = { // Temporarily disabled since they aren't vendored into in mozilla central yet // "react-hooks", // require("react-hooks") - // "fetch-options", // require("eslint-plugin-fetch-options") + "fetch-options", // require("eslint-plugin-fetch-options") ], "settings": { "react": { @@ -76,7 +76,7 @@ module.exports = { "rules": { // "react-hooks/rules-of-hooks": 2, - // "fetch-options/no-fetch-credentials": 2, + "fetch-options/no-fetch-credentials": 2, "react/jsx-boolean-value": [2, "always"], "react/jsx-closing-bracket-location": [2, "after-props"], diff --git a/browser/components/newtab/AboutNewTabService.jsm b/browser/components/newtab/AboutNewTabService.jsm index 29d7822ec0..682dd596fd 100644 --- a/browser/components/newtab/AboutNewTabService.jsm +++ b/browser/components/newtab/AboutNewTabService.jsm @@ -14,13 +14,8 @@ ChromeUtils.defineModuleGetter(this, "AboutNewTab", "resource:///modules/AboutNewTab.jsm"); const TOPIC_APP_QUIT = "quit-application-granted"; -const TOPIC_LOCALES_CHANGE = "intl:app-locales-changed"; const TOPIC_CONTENT_DOCUMENT_INTERACTIVE = "content-document-interactive"; -// Automated tests ensure packaged locales are in this list. Copied output of: -// https://github.com/mozilla/activity-stream/blob/master/bin/render-activity-stream-html.js -const ACTIVITY_STREAM_BCP47 = "en-US".split(" "); - const ABOUT_URL = "about:newtab"; const BASE_URL = "resource://activity-stream/"; const ACTIVITY_STREAM_PAGES = new Set(["home", "newtab", "welcome"]); @@ -35,7 +30,6 @@ const PREF_ACTIVITY_STREAM_DEBUG = "browser.newtabpage.activity-stream.debug"; function AboutNewTabService() { Services.obs.addObserver(this, TOPIC_APP_QUIT); - Services.obs.addObserver(this, TOPIC_LOCALES_CHANGE); Services.prefs.addObserver(PREF_SEPARATE_PRIVILEGED_CONTENT_PROCESS, this); if (!IS_RELEASE_OR_BETA) { Services.prefs.addObserver(PREF_ACTIVITY_STREAM_DEBUG, this); @@ -71,7 +65,7 @@ function AboutNewTabService() { * * When the URL loaded is about:newtab, the default behavior, or when entered in the * URL bar, the redirector is hit. The service is then called to return the - * appropriate activity stream url based on prefs and locales. + * appropriate activity stream url based on prefs. * * NOTE: "about:newtab" will always result in a default newtab page, and never an overridden URL. * @@ -88,7 +82,6 @@ AboutNewTabService.prototype = { _newTabURL: ABOUT_URL, _activityStreamEnabled: false, - _activityStreamPath: "", _activityStreamDebug: false, _privilegedContentProcess: false, _overridden: false, @@ -105,11 +98,9 @@ AboutNewTabService.prototype = { case "nsPref:changed": if (data === PREF_SEPARATE_PRIVILEGED_CONTENT_PROCESS) { this._privilegedContentProcess = Services.prefs.getBoolPref(PREF_SEPARATE_PRIVILEGED_CONTENT_PROCESS); - this.updatePrerenderedPath(); this.notifyChange(); } else if (!IS_RELEASE_OR_BETA && data === PREF_ACTIVITY_STREAM_DEBUG) { this._activityStreamDebug = Services.prefs.getBoolPref(PREF_ACTIVITY_STREAM_DEBUG, false); - this.updatePrerenderedPath(); this.notifyChange(); } break; @@ -145,10 +136,8 @@ AboutNewTabService.prototype = { `${BASE_URL}vendor/react${debugString}.js`, `${BASE_URL}vendor/react-dom${debugString}.js`, `${BASE_URL}vendor/prop-types.js`, - `${BASE_URL}vendor/react-intl.js`, `${BASE_URL}vendor/redux.js`, `${BASE_URL}vendor/react-redux.js`, - `${BASE_URL}prerendered/${this.activityStreamLocale}/activity-stream-strings.js`, `${BASE_URL}data/content/activity-stream.bundle.js`, ]; @@ -175,10 +164,6 @@ AboutNewTabService.prototype = { Services.obs.removeObserver(this, TOPIC_CONTENT_DOCUMENT_INTERACTIVE); } break; - case TOPIC_LOCALES_CHANGE: - this.updatePrerenderedPath(); - this.notifyChange(); - break; } }, @@ -210,34 +195,23 @@ AboutNewTabService.prototype = { if (!IS_RELEASE_OR_BETA) { this._activityStreamDebug = Services.prefs.getBoolPref(PREF_ACTIVITY_STREAM_DEBUG, false); } - this.updatePrerenderedPath(); this._newtabURL = ABOUT_URL; return true; }, - /** - * Figure out what path under prerendered to use based on current state. - */ - updatePrerenderedPath() { - // Debug files are specially packaged in a non-localized directory, but with - // dynamic script loading, localized debug is supported. - this._activityStreamPath = `${this._activityStreamDebug && - !this._privilegedContentProcess ? "static" : this.activityStreamLocale}/`; - }, - /* * Returns the default URL. * - * This URL depends on various activity stream prefs and locales. Overriding + * This URL depends on various activity stream prefs. Overriding * the newtab page has no effect on the result of this function. */ get defaultURL() { // Generate the desired activity stream resource depending on state, e.g., - // resource://activity-stream/prerendered/ar/activity-stream.html - // resource://activity-stream/prerendered/static/activity-stream-debug.html + // "resource://activity-stream/prerendered/activity-stream.html" + // "resource://activity-stream/prerendered/activity-stream-debug.html" + // "resource://activity-stream/prerendered/activity-stream-noscripts.html" return [ "resource://activity-stream/prerendered/", - this._activityStreamPath, "activity-stream", // Debug version loads dev scripts but noscripts separately loads scripts this._activityStreamDebug && !this._privilegedContentProcess ? "-debug" : "", @@ -287,21 +261,6 @@ AboutNewTabService.prototype = { return this._activityStreamDebug; }, - get activityStreamLocale() { - // Pick the best available locale to match the app locales - return Services.locale.negotiateLanguages( - // Fix up incorrect BCP47 that are actually lang tags as a workaround for - // bug 1479606 returning the wrong values in the content process - Services.locale.appLocalesAsBCP47.map(l => l.replace(/^(ja-JP-mac)$/, "$1os")), - ACTIVITY_STREAM_BCP47, - // defaultLocale's strings aren't necessarily packaged, but en-US' are - "en-US", - Services.locale.langNegStrategyLookup - // Convert the BCP47 to lang tag, which is what is used in our paths, as a - // workaround for bug 1478930 negotiating incorrectly with lang tags - )[0].replace(/^(ja-JP-mac)os$/, "$1"); - }, - resetNewTabURL() { this._overridden = false; this._newTabURL = ABOUT_URL; @@ -328,7 +287,6 @@ AboutNewTabService.prototype = { return; } Services.obs.removeObserver(this, TOPIC_APP_QUIT); - Services.obs.removeObserver(this, TOPIC_LOCALES_CHANGE); Services.prefs.removeObserver(PREF_SEPARATE_PRIVILEGED_CONTENT_PROCESS, this); if (!IS_RELEASE_OR_BETA) { Services.prefs.removeObserver(PREF_ACTIVITY_STREAM_DEBUG, this); diff --git a/browser/components/newtab/bin/render-activity-stream-html.js b/browser/components/newtab/bin/render-activity-stream-html.js index f67d62ea59..b96c67176d 100644 --- a/browser/components/newtab/bin/render-activity-stream-html.js +++ b/browser/components/newtab/bin/render-activity-stream-html.js @@ -3,8 +3,6 @@ const fs = require("fs"); const {mkdir} = require("shelljs"); const path = require("path"); -const {CENTRAL_LOCALES, DEFAULT_LOCALE} = require("./locales"); - // Note: DEFAULT_OPTIONS.baseUrl should match BASE_URL in aboutNewTabService.js // in mozilla-central. const DEFAULT_OPTIONS = { @@ -12,52 +10,11 @@ const DEFAULT_OPTIONS = { baseUrl: "resource://activity-stream/", }; -// Locales that should be displayed RTL -const RTL_LIST = ["ar", "he", "fa", "ur"]; - -/** - * Get the language part of the locale. - */ -function getLanguage(locale) { - return locale.split("-")[0]; -} - -/** - * Get the best strings for a single provided locale using similar locales and - * DEFAULT_LOCALE as fallbacks. - */ -function getStrings(locale, allStrings) { - const availableLocales = Object.keys(allStrings); - - const language = getLanguage(locale); - const similarLocales = availableLocales.filter(other => - other !== locale && getLanguage(other) === language); - - // Rank locales from least desired to most desired - const localeFallbacks = [DEFAULT_LOCALE, ...similarLocales, locale]; - - // Get strings from each locale replacing with those from more desired ones - const desired = Object.assign({}, ...localeFallbacks.map(l => allStrings[l])); - - // Only include strings that are currently used (defined by default locale) - return Object.assign({}, ...Object.keys(allStrings[DEFAULT_LOCALE]).map( - key => ({[key]: desired[key]}))); -} - -/** - * Get the text direction of the locale. - */ -function getTextDirection(locale) { - return RTL_LIST.includes(locale.split("-")[0]) ? "rtl" : "ltr"; -} - /** * templateHTML - Generates HTML for activity stream, given some options and * prerendered HTML if necessary. * * @param {obj} options - * {str} options.locale The locale to render in lang="" attribute - * {str} options.direction The language direction to render in dir="" attribute * {str} options.baseUrl The base URL for all local assets * {bool} options.debug Should we use dev versions of JS libraries? * {bool} options.noscripts Should we include scripts in the prerendered files? @@ -71,10 +28,8 @@ function templateHTML(options) { `${options.baseUrl}vendor/react${debugString}.js`, `${options.baseUrl}vendor/react-dom${debugString}.js`, `${options.baseUrl}vendor/prop-types.js`, - `${options.baseUrl}vendor/react-intl.js`, `${options.baseUrl}vendor/redux.js`, `${options.baseUrl}vendor/react-redux.js`, - `${options.baseUrl}prerendered/${options.locale}/activity-stream-strings.js`, `${options.baseUrl}data/content/activity-stream.bundle.js`, ]; @@ -82,7 +37,7 @@ function templateHTML(options) { const scriptRender = `\n${scripts.map(script => ` <script src="${script}"></script>`).join("\n")}`; return `<!doctype html> -<html lang="${options.locale}" dir="${options.direction}"> +<html> <head> <meta charset="utf-8"> <meta http-equiv="Content-Security-Policy" content="default-src 'none'; object-src 'none'; script-src resource: chrome:; connect-src https:; img-src https: data: blob:; style-src 'unsafe-inline';"> @@ -102,49 +57,24 @@ function templateHTML(options) { `; } -/** - * templateJs - Generates a js file that passes the initial state of the prerendered - * DOM to the React version. This is necessary to ensure the checksum matches when - * React mounts so that it can attach to the prerendered elements instead of blowing - * them away. - * - * Note that this may no longer be necessary in React 16 and we should review whether - * it is still necessary. - * - * @param {string} name The name of the global to expose - * @param {string} desc Extra description to include in a js comment - * @param {obj} state The data to expose as a window global - * @return {str} The js file as a string - */ -function templateJs(name, desc, state) { - return `// Note - this is a generated ${desc} file. -window.${name} = ${JSON.stringify(state, null, 2)}; -`; -} - /** * writeFiles - Writes to the desired files the result of a template given * various prerendered data and options. * - * @param {string} name Something to identify in the console * @param {string} destPath Path to write the files to * @param {Map} filesMap Mapping of a string file name to templater * @param {Object} options Various options for the templater */ -function writeFiles(name, destPath, filesMap, options) { +function writeFiles(destPath, filesMap, options) { for (const [file, templater] of filesMap) { + console.log("\x1b[32m", `✓ ${file}`, "\x1b[0m"); fs.writeFileSync(path.join(destPath, file), templater({options})); } - console.log("\x1b[32m", `✓ ${name}`, "\x1b[0m"); } const STATIC_FILES = new Map([ - ["activity-stream-debug.html", ({options}) => templateHTML(options)], -]); - -const LOCALIZED_FILES = new Map([ - ["activity-stream-strings.js", ({options: {locale, strings}}) => templateJs("gActivityStreamStrings", locale, strings)], ["activity-stream.html", ({options}) => templateHTML(options)], + ["activity-stream-debug.html", ({options}) => templateHTML(Object.assign({}, options, {debug: true}))], ["activity-stream-noscripts.html", ({options}) => templateHTML(Object.assign({}, options, {noscripts: true}))], ]); @@ -163,74 +93,13 @@ function main() { // eslint-disable-line max-statements }, }); - const baseOptions = Object.assign({debug: false}, DEFAULT_OPTIONS, args || {}); - const addonPath = path.resolve(__dirname, baseOptions.addonPath); - const allStrings = require(`${baseOptions.addonPath}/data/locales.json`); - const extraLocales = Object.keys(allStrings).filter(locale => - locale !== DEFAULT_LOCALE && !CENTRAL_LOCALES.includes(locale)); - + const options = Object.assign({debug: false}, DEFAULT_OPTIONS, args || {}); + const addonPath = path.resolve(__dirname, options.addonPath); const prerenderedPath = path.join(addonPath, "prerendered"); - console.log(`Writing prerendered files to individual directories under ${prerenderedPath}:`); + console.log(`Writing prerendered files to ${prerenderedPath}:`); - // Save default locale's strings to compare against other locales' strings - let defaultStrings; - let langStrings; - const isSubset = (strings, existing) => existing && - Object.keys(strings).every(key => strings[key] === existing[key]); - - // Process the default locale first then all the ones from mozilla-central - const localizedLocales = []; - const skippedLocales = []; - for (const locale of [DEFAULT_LOCALE, ...CENTRAL_LOCALES]) { - // Skip the locale if it would have resulted in duplicate packaged files - const strings = getStrings(locale, allStrings); - if (isSubset(strings, defaultStrings) || isSubset(strings, langStrings)) { - skippedLocales.push(locale); - continue; - } - - const options = Object.assign({}, baseOptions, { - direction: getTextDirection(locale), - locale, - strings, - }); - - // Put locale-specific files in their own directory - const localePath = path.join(prerenderedPath, "locales", locale); - mkdir("-p", localePath); - writeFiles(locale, localePath, LOCALIZED_FILES, options); - - // Only write static files once for the default locale - if (locale === DEFAULT_LOCALE) { - const staticPath = path.join(prerenderedPath, "static"); - mkdir("-p", staticPath); - writeFiles(`${locale} (static)`, staticPath, STATIC_FILES, - Object.assign({}, options, {debug: true})); - - // Save the default strings to compare against other locales' strings - defaultStrings = strings; - } - - // Save the language's strings to maybe reuse for the next similar locales - if (getLanguage(locale) === locale) { - langStrings = strings; - } - - localizedLocales.push(locale); - } - - if (skippedLocales.length) { - console.log("\x1b[33m", `Skipped the following locales because they use the same strings as ${DEFAULT_LOCALE} or its language locale: ${skippedLocales.join(", ")}`, "\x1b[0m"); - } - if (extraLocales.length) { - console.log("\x1b[33m", `Skipped the following locales because they are not in CENTRAL_LOCALES: ${extraLocales.join(", ")}`, "\x1b[0m"); - } - - // Convert ja-JP-mac lang tag to ja-JP-macos bcp47 to work around bug 1478930 - const bcp47String = localizedLocales.join(" ").replace(/(ja-JP-mac)/, "$1os"); - - // Provide some help to copy/paste locales if tests are failing - console.log(`\nIf aboutNewTabService tests are failing for unexpected locales, make sure its list is updated:\nconst ACTIVITY_STREAM_BCP47 = "${bcp47String}".split(" ");`); + mkdir("-p", prerenderedPath); + writeFiles(prerenderedPath, STATIC_FILES, options); } main(); diff --git a/browser/components/newtab/bin/vendor.js b/browser/components/newtab/bin/vendor.js index 4f6b543ce8..5bb994de20 100644 --- a/browser/components/newtab/bin/vendor.js +++ b/browser/components/newtab/bin/vendor.js @@ -13,8 +13,6 @@ const filesToVendor = { "react/umd/react.development.js": "react-dev.js", "react-dom/umd/react-dom.production.min.js": "react-dom.js", "react-dom/umd/react-dom.development.js": "react-dom-dev.js", - "react-intl/LICENSE.md": "REACT_INTL_LICENSE", - "react-intl/dist/react-intl.min.js": "react-intl.js", "react-redux/LICENSE.md": "REACT_REDUX_LICENSE", "react-redux/dist/react-redux.min.js": "react-redux.js", }; diff --git a/browser/components/newtab/jar.mn b/browser/components/newtab/jar.mn index e93f7d3604..edc64f186a 100644 --- a/browser/components/newtab/jar.mn +++ b/browser/components/newtab/jar.mn @@ -14,7 +14,6 @@ browser.jar: res/activity-stream/vendor/react-dom-dev.js (./vendor/react-dom-dev.js) #endif res/activity-stream/vendor/prop-types.js (./vendor/prop-types.js) - res/activity-stream/vendor/react-intl.js (./vendor/react-intl.js) res/activity-stream/vendor/redux.js (./vendor/redux.js) res/activity-stream/vendor/react-redux.js (./vendor/react-redux.js) res/activity-stream/data/content/assets/ (./data/content/assets/*) @@ -27,7 +26,8 @@ browser.jar: #else res/activity-stream/css/activity-stream.css (./css/activity-stream-linux.css) #endif + res/activity-stream/prerendered/activity-stream.html (./prerendered/activity-stream.html) #ifndef RELEASE_OR_BETA - res/activity-stream/prerendered/static/activity-stream-debug.html (./prerendered/static/activity-stream-debug.html) + res/activity-stream/prerendered/activity-stream-debug.html (./prerendered/activity-stream-debug.html) #endif - res/activity-stream/prerendered/ (./prerendered/locales/*) + res/activity-stream/prerendered/activity-stream-noscripts.html (./prerendered/activity-stream-noscripts.html) diff --git a/browser/components/newtab/lib/ActivityStreamMessageChannel.jsm b/browser/components/newtab/lib/ActivityStreamMessageChannel.jsm index b21d5e1c3e..53aca154d9 100644 --- a/browser/components/newtab/lib/ActivityStreamMessageChannel.jsm +++ b/browser/components/newtab/lib/ActivityStreamMessageChannel.jsm @@ -6,8 +6,8 @@ const { AboutNewTab } = ChromeUtils.import( "resource:///modules/AboutNewTab.jsm" -); // Remove when updating eslint-plugin-mozilla 0.14.0+ -/* globals RemotePages */ const { RemotePages } = ChromeUtils.import( +); +const { RemotePages } = ChromeUtils.import( "resource://gre/modules/remotepagemanager/RemotePageManagerParent.jsm" ); diff --git a/browser/components/newtab/nsIAboutNewTabService.idl b/browser/components/newtab/nsIAboutNewTabService.idl index a157bfdf82..e8b0593711 100644 --- a/browser/components/newtab/nsIAboutNewTabService.idl +++ b/browser/components/newtab/nsIAboutNewTabService.idl @@ -49,11 +49,6 @@ interface nsIAboutNewTabService : nsISupports */ readonly attribute bool activityStreamDebug; - /** - * Returns the locale of the activity stream interface - */ - readonly attribute ACString activityStreamLocale; - /** * Resets to the default resource and also resets the * overridden attribute to false. diff --git a/browser/components/newtab/package-lock.json b/browser/components/newtab/package-lock.json index fac2f6a6f1..45816d73f9 100644 --- a/browser/components/newtab/package-lock.json +++ b/browser/components/newtab/package-lock.json @@ -5361,32 +5361,6 @@ "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", "dev": true }, - "intl-format-cache": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/intl-format-cache/-/intl-format-cache-2.2.2.tgz", - "integrity": "sha512-B5XmTTcF7Yz5VACwVLmAUkqK27RnOCvgi0kwA6Al/vzVPHy+h+G8J7SCyzZDMjb/UB8fqBEyrzecyn1ksRIi9A==" - }, - "intl-messageformat": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-2.2.0.tgz", - "integrity": "sha1-NFvNRt5jC3aDMwwuUhd/9eq0hPw=", - "requires": { - "intl-messageformat-parser": "1.4.0" - } - }, - "intl-messageformat-parser": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-1.4.0.tgz", - "integrity": "sha1-tD1FqXRoytvkQzHXS7Ho3qRPwHU=" - }, - "intl-relativeformat": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/intl-relativeformat/-/intl-relativeformat-2.1.1.tgz", - "integrity": "sha512-bAhhgiCiN+MVjQeK2v9YqCRJQ8FfV0tk9WVh1RYkRJerpYhmpBe2I4XxpneY232Cp9ujHVl3Q465UJkHQEQdmQ==", - "requires": { - "intl-messageformat": "^2.0.0" - } - }, "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -8718,18 +8692,6 @@ } } }, - "react-intl": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-2.9.0.tgz", - "integrity": "sha512-27jnDlb/d2A7mSJwrbOBnUgD+rPep+abmoJE511Tf8BnoONIAUehy/U1zZCHGO17mnOwMWxqN4qC0nW11cD6rA==", - "requires": { - "hoist-non-react-statics": "^3.3.0", - "intl-format-cache": "^2.0.5", - "intl-messageformat": "^2.1.0", - "intl-relativeformat": "^2.1.0", - "invariant": "^2.1.1" - } - }, "react-is": { "version": "16.8.4", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.4.tgz", diff --git a/browser/components/newtab/package.json b/browser/components/newtab/package.json index 04acdd6a64..71eb915bc2 100644 --- a/browser/components/newtab/package.json +++ b/browser/components/newtab/package.json @@ -11,7 +11,6 @@ "fluent-react": "0.8.4", "react": "16.8.6", "react-dom": "16.8.6", - "react-intl": "2.9.0", "react-redux": "7.0.3", "redux": "4.0.1", "reselect": "4.0.0" diff --git a/browser/components/newtab/prerendered/static/activity-stream-debug.html b/browser/components/newtab/prerendered/activity-stream-debug.html similarity index 88% rename from browser/components/newtab/prerendered/static/activity-stream-debug.html rename to browser/components/newtab/prerendered/activity-stream-debug.html index f313e1d78f..961f06cadf 100644 --- a/browser/components/newtab/prerendered/static/activity-stream-debug.html +++ b/browser/components/newtab/prerendered/activity-stream-debug.html @@ -1,5 +1,5 @@ <!doctype html> -<html lang="en-US" dir="ltr"> +<html> <head> <meta charset="utf-8"> <meta http-equiv="Content-Security-Policy" content="default-src 'none'; object-src 'none'; script-src resource: chrome:; connect-src https:; img-src https: data: blob:; style-src 'unsafe-inline';"> @@ -19,10 +19,8 @@ <script src="resource://activity-stream/vendor/react-dev.js"></script> <script src="resource://activity-stream/vendor/react-dom-dev.js"></script> <script src="resource://activity-stream/vendor/prop-types.js"></script> - <script src="resource://activity-stream/vendor/react-intl.js"></script> <script src="resource://activity-stream/vendor/redux.js"></script> <script src="resource://activity-stream/vendor/react-redux.js"></script> - <script src="resource://activity-stream/prerendered/en-US/activity-stream-strings.js"></script> <script src="resource://activity-stream/data/content/activity-stream.bundle.js"></script> </body> </html> diff --git a/browser/components/newtab/prerendered/locales/en-US/activity-stream-noscripts.html b/browser/components/newtab/prerendered/activity-stream-noscripts.html similarity index 96% rename from browser/components/newtab/prerendered/locales/en-US/activity-stream-noscripts.html rename to browser/components/newtab/prerendered/activity-stream-noscripts.html index aebe8bd066..bbe8f75807 100644 --- a/browser/components/newtab/prerendered/locales/en-US/activity-stream-noscripts.html +++ b/browser/components/newtab/prerendered/activity-stream-noscripts.html @@ -1,5 +1,5 @@ <!doctype html> -<html lang="en-US" dir="ltr"> +<html> <head> <meta charset="utf-8"> <meta http-equiv="Content-Security-Policy" content="default-src 'none'; object-src 'none'; script-src resource: chrome:; connect-src https:; img-src https: data: blob:; style-src 'unsafe-inline';"> diff --git a/browser/components/newtab/prerendered/locales/en-US/activity-stream.html b/browser/components/newtab/prerendered/activity-stream.html similarity index 88% rename from browser/components/newtab/prerendered/locales/en-US/activity-stream.html rename to browser/components/newtab/prerendered/activity-stream.html index a6a5b2bc1f..2c248121ed 100644 --- a/browser/components/newtab/prerendered/locales/en-US/activity-stream.html +++ b/browser/components/newtab/prerendered/activity-stream.html @@ -1,5 +1,5 @@ <!doctype html> -<html lang="en-US" dir="ltr"> +<html> <head> <meta charset="utf-8"> <meta http-equiv="Content-Security-Policy" content="default-src 'none'; object-src 'none'; script-src resource: chrome:; connect-src https:; img-src https: data: blob:; style-src 'unsafe-inline';"> @@ -19,10 +19,8 @@ <script src="resource://activity-stream/vendor/react.js"></script> <script src="resource://activity-stream/vendor/react-dom.js"></script> <script src="resource://activity-stream/vendor/prop-types.js"></script> - <script src="resource://activity-stream/vendor/react-intl.js"></script> <script src="resource://activity-stream/vendor/redux.js"></script> <script src="resource://activity-stream/vendor/react-redux.js"></script> - <script src="resource://activity-stream/prerendered/en-US/activity-stream-strings.js"></script> <script src="resource://activity-stream/data/content/activity-stream.bundle.js"></script> </body> </html> diff --git a/browser/components/newtab/prerendered/locales/en-US/activity-stream-strings.js b/browser/components/newtab/prerendered/locales/en-US/activity-stream-strings.js deleted file mode 100644 index 67578c835f..0000000000 --- a/browser/components/newtab/prerendered/locales/en-US/activity-stream-strings.js +++ /dev/null @@ -1,2 +0,0 @@ -// Note - this is a generated en-US file. -window.gActivityStreamStrings = {}; diff --git a/browser/components/newtab/vendor/REACT_INTL_LICENSE b/browser/components/newtab/vendor/REACT_INTL_LICENSE deleted file mode 100644 index 0da75135fb..0000000000 --- a/browser/components/newtab/vendor/REACT_INTL_LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright 2014 Yahoo Inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of the Yahoo Inc. nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL YAHOO! INC. BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/browser/components/newtab/vendor/react-intl.js b/browser/components/newtab/vendor/react-intl.js deleted file mode 100644 index 6b57fbcf30..0000000000 --- a/browser/components/newtab/vendor/react-intl.js +++ /dev/null @@ -1,2 +0,0 @@ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("prop-types"),require("react")):"function"==typeof define&&define.amd?define(["exports","prop-types","react"],t):t(e.ReactIntl={},e.PropTypes,e.React)}(this,function(e,t,r){"use strict";function n(e){var t,r,n,o,a=Array.prototype.slice.call(arguments,1);for(t=0,r=a.length;t<r;t+=1)if(n=a[t])for(o in n)L.call(n,o)&&(e[o]=n[o]);return e}function o(e,t,r){this.locales=e,this.formats=t,this.pluralFn=r}function a(e){this.id=e}function i(e,t,r,n,o){this.id=e,this.useOrdinal=t,this.offset=r,this.options=n,this.pluralFn=o}function s(e,t,r,n){this.id=e,this.offset=t,this.numberFormat=r,this.string=n}function l(e,t){this.id=e,this.options=t}function u(e,t,r){var n="string"==typeof e?u.__parse(e):e;if(!n||"messageFormatPattern"!==n.type)throw new TypeError("A message must be provided as a String or AST.");r=this._mergeFormats(u.formats,r),S(this,"_locale",{value:this._resolveLocale(t)});var o=this._findPluralRuleFunction(this._locale),a=this._compilePattern(n,t,r,o),i=this;this.format=function(t){try{return i._format(a,t)}catch(t){throw t.variableId?new Error("The intl string context variable '"+t.variableId+"' was not provided to the string '"+e+"'"):t}}}function c(e){return 400*e/146097}function f(e,t){t=t||{},z(e)&&(e=e.concat()),B(this,"_locale",{value:this._resolveLocale(e)}),B(this,"_options",{value:{style:this._resolveStyle(t.style),units:this._isValidUnits(t.units)&&t.units}}),B(this,"_locales",{value:e}),B(this,"_fields",{value:this._findFields(this._locale)}),B(this,"_messages",{value:J(null)});var r=this;this.format=function(e,t){return r._format(e,t)}}function p(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];(Array.isArray(e)?e:[e]).forEach(function(e){e&&e.locale&&(u.__addLocaleData(e),f.__addLocaleData(e))})}function h(e){for(var t=(e||"").split("-");t.length>0;){if(m(t.join("-")))return!0;t.pop()}return!1}function m(e){var t=e&&e.toLowerCase();return!(!u.__localeData__[t]||!f.__localeData__[t])}function d(e,t,r){if("string"!=typeof t){if(Ie){var n=De(t);n&&n!==Ie&&d(e,n,r)}var o=Ae(t);Me&&(o=o.concat(Me(t)));for(var a=0;a<o.length;++a){var i=o[a];if(!(Ce[i]||ke[i]||r&&r[i])){var s=Re(t,i);try{Ee(e,i,s)}catch(e){}}}return e}return e}function y(e){return(""+e).replace(qe,function(e){return Ze[e]})}function v(e,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return t.reduce(function(t,n){return e.hasOwnProperty(n)?t[n]=e[n]:r.hasOwnProperty(n)&&(t[n]=r[n]),t},{})}function g(){var e=(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{}).intl;Se(e,"[React Intl] Could not find required `intl` object. <IntlProvider> needs to exist in the component ancestry.")}function _(e,t){if(e===t)return!0;if("object"!==(void 0===e?"undefined":ee(e))||null===e||"object"!==(void 0===t?"undefined":ee(t))||null===t)return!1;var r=Object.keys(e),n=Object.keys(t);if(r.length!==n.length)return!1;for(var o=Object.prototype.hasOwnProperty.bind(t),a=0;a<r.length;a++)if(!o(r[a])||e[r[a]]!==t[r[a]])return!1;return!0}function b(e,t,r){var n=e.props,o=e.state,a=e.context,i=void 0===a?{}:a,s=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},l=i.intl,u=void 0===l?{}:l,c=s.intl,f=void 0===c?{}:c;return!_(t,n)||!_(r,o)||!(f===u||_(v(f,Ue),v(u,Ue)))}function w(e,t){return"[React Intl] "+e+(t?"\n"+t:"")}function F(e){}function O(e){return e.displayName||e.name||"Component"}function x(e){return u.prototype._resolveLocale(e)}function T(e){return u.prototype._findPluralRuleFunction(e)}function P(e){var t=Be(null);return function(){var r=Array.prototype.slice.call(arguments),n=j(r),o=n&&t[n];return o||(o=new(He.apply(e,[null].concat(r))),n&&(t[n]=o)),o}}function j(e){if("undefined"!=typeof JSON){var t,r,n,o=[];for(t=0,r=e.length;t<r;t+=1)(n=e[t])&&"object"==typeof n?o.push(N(n)):o.push(n);return JSON.stringify(o)}}function N(e){var t,r,n,o,a=[],i=[];for(t in e)e.hasOwnProperty(t)&&i.push(t);var s=i.sort();for(r=0,n=s.length;r<n;r+=1)(o={})[t=s[r]]=e[t],a[r]=o;return a}function C(e){var t=f.thresholds;t.second=e.second,t.minute=e.minute,t.hour=e.hour,t.day=e.day,t.month=e.month,t["second-short"]=e["second-short"],t["minute-short"]=e["minute-short"],t["hour-short"]=e["hour-short"],t["day-short"]=e["day-short"],t["month-short"]=e["month-short"]}function k(e,t,r,n){var o=e&&e[t]&&e[t][r];if(o)return o;n(w("No "+t+" format named: "+r))}function E(e,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},o=e.locale,a=e.formats,i=e.messages,s=e.defaultLocale,l=e.defaultFormats,u=r.id,c=r.defaultMessage;Se(u,"[React Intl] An `id` must be provided to format a message.");var f=i&&i[u];if(!(Object.keys(n).length>0))return f||c||u;var p=void 0,h=e.onError||F;if(f)try{p=t.getMessageFormat(f,o,a).format(n)}catch(e){h(w('Error formatting message: "'+u+'" for locale: "'+o+'"'+(c?", using default message as fallback.":""),e))}else(!c||o&&o.toLowerCase()!==s.toLowerCase())&&h(w('Missing message: "'+u+'" for locale: "'+o+'"'+(c?", using default message as fallback.":"")));if(!p&&c)try{p=t.getMessageFormat(c,s,l).format(n)}catch(e){h(w('Error formatting the default message for: "'+u+'"',e))}return p||h(w('Cannot format message: "'+u+'", using message '+(f||c?"source":"id")+" as fallback.")),p||f||c||u}function A(e){var t=Math.abs(e);return t<it?"second":t<st?"minute":t<lt?"hour":"day"}function M(e){switch(e){case"second":return at;case"minute":return it;case"hour":return st;case"day":return lt;default:return ut}}function R(e,t){if(e===t)return!0;var r=new Date(e).getTime(),n=new Date(t).getTime();return isFinite(r)&&isFinite(n)&&r===n}if(void 0===r)throw new ReferenceError("React must be loaded before ReactIntl.");t=t&&t.hasOwnProperty("default")?t.default:t;var D="default"in r?r.default:r,I={locale:"en",pluralRuleFunction:function(e,t){var r=String(e).split("."),n=!r[1],o=Number(r[0])==e,a=o&&r[0].slice(-1),i=o&&r[0].slice(-2);return t?1==a&&11!=i?"one":2==a&&12!=i?"two":3==a&&13!=i?"few":"other":1==e&&n?"one":"other"},fields:{year:{displayName:"year",relative:{0:"this year",1:"next year","-1":"last year"},relativeTime:{future:{one:"in {0} year",other:"in {0} years"},past:{one:"{0} year ago",other:"{0} years ago"}}},"year-short":{displayName:"yr.",relative:{0:"this yr.",1:"next yr.","-1":"last yr."},relativeTime:{future:{one:"in {0} yr.",other:"in {0} yr."},past:{one:"{0} yr. ago",other:"{0} yr. ago"}}},month:{displayName:"month",relative:{0:"this month",1:"next month","-1":"last month"},relativeTime:{future:{one:"in {0} month",other:"in {0} months"},past:{one:"{0} month ago",other:"{0} months ago"}}},"month-short":{displayName:"mo.",relative:{0:"this mo.",1:"next mo.","-1":"last mo."},relativeTime:{future:{one:"in {0} mo.",other:"in {0} mo."},past:{one:"{0} mo. ago",other:"{0} mo. ago"}}},day:{displayName:"day",relative:{0:"today",1:"tomorrow","-1":"yesterday"},relativeTime:{future:{one:"in {0} day",other:"in {0} days"},past:{one:"{0} day ago",other:"{0} days ago"}}},"day-short":{displayName:"day",relative:{0:"today",1:"tomorrow","-1":"yesterday"},relativeTime:{future:{one:"in {0} day",other:"in {0} days"},past:{one:"{0} day ago",other:"{0} days ago"}}},hour:{displayName:"hour",relative:{0:"this hour"},relativeTime:{future:{one:"in {0} hour",other:"in {0} hours"},past:{one:"{0} hour ago",other:"{0} hours ago"}}},"hour-short":{displayName:"hr.",relative:{0:"this hour"},relativeTime:{future:{one:"in {0} hr.",other:"in {0} hr."},past:{one:"{0} hr. ago",other:"{0} hr. ago"}}},minute:{displayName:"minute",relative:{0:"this minute"},relativeTime:{future:{one:"in {0} minute",other:"in {0} minutes"},past:{one:"{0} minute ago",other:"{0} minutes ago"}}},"minute-short":{displayName:"min.",relative:{0:"this minute"},relativeTime:{future:{one:"in {0} min.",other:"in {0} min."},past:{one:"{0} min. ago",other:"{0} min. ago"}}},second:{displayName:"second",relative:{0:"now"},relativeTime:{future:{one:"in {0} second",other:"in {0} seconds"},past:{one:"{0} second ago",other:"{0} seconds ago"}}},"second-short":{displayName:"sec.",relative:{0:"now"},relativeTime:{future:{one:"in {0} sec.",other:"in {0} sec."},past:{one:"{0} sec. ago",other:"{0} sec. ago"}}}}},L=Object.prototype.hasOwnProperty,S=function(){try{return!!Object.defineProperty({},"a",{})}catch(e){return!1}}()?Object.defineProperty:function(e,t,r){"get"in r&&e.__defineGetter__?e.__defineGetter__(t,r.get):(!L.call(e,t)||"value"in r)&&(e[t]=r.value)},U=Object.create||function(e,t){function r(){}var n,o;r.prototype=e,n=new r;for(o in t)L.call(t,o)&&S(n,o,t[o]);return n};o.prototype.compile=function(e){return this.pluralStack=[],this.currentPlural=null,this.pluralNumberFormat=null,this.compileMessage(e)},o.prototype.compileMessage=function(e){if(!e||"messageFormatPattern"!==e.type)throw new Error('Message AST is not of type: "messageFormatPattern"');var t,r,n,o=e.elements,a=[];for(t=0,r=o.length;t<r;t+=1)switch((n=o[t]).type){case"messageTextElement":a.push(this.compileMessageText(n));break;case"argumentElement":a.push(this.compileArgument(n));break;default:throw new Error("Message element does not have a valid type")}return a},o.prototype.compileMessageText=function(e){return this.currentPlural&&/(^|[^\\])#/g.test(e.value)?(this.pluralNumberFormat||(this.pluralNumberFormat=new Intl.NumberFormat(this.locales)),new s(this.currentPlural.id,this.currentPlural.format.offset,this.pluralNumberFormat,e.value)):e.value.replace(/\\#/g,"#")},o.prototype.compileArgument=function(e){var t=e.format;if(!t)return new a(e.id);var r,n=this.formats,o=this.locales,s=this.pluralFn;switch(t.type){case"numberFormat":return r=n.number[t.style],{id:e.id,format:new Intl.NumberFormat(o,r).format};case"dateFormat":return r=n.date[t.style],{id:e.id,format:new Intl.DateTimeFormat(o,r).format};case"timeFormat":return r=n.time[t.style],{id:e.id,format:new Intl.DateTimeFormat(o,r).format};case"pluralFormat":return r=this.compileOptions(e),new i(e.id,t.ordinal,t.offset,r,s);case"selectFormat":return r=this.compileOptions(e),new l(e.id,r);default:throw new Error("Message element does not have a valid format type")}},o.prototype.compileOptions=function(e){var t=e.format,r=t.options,n={};this.pluralStack.push(this.currentPlural),this.currentPlural="pluralFormat"===t.type?e:null;var o,a,i;for(o=0,a=r.length;o<a;o+=1)n[(i=r[o]).selector]=this.compileMessage(i.value);return this.currentPlural=this.pluralStack.pop(),n},a.prototype.format=function(e){return e||"number"==typeof e?"string"==typeof e?e:String(e):""},i.prototype.getOption=function(e){var t=this.options;return t["="+e]||t[this.pluralFn(e-this.offset,this.useOrdinal)]||t.other},s.prototype.format=function(e){var t=this.numberFormat.format(e-this.offset);return this.string.replace(/(^|[^\\])#/g,"$1"+t).replace(/\\#/g,"#")},l.prototype.getOption=function(e){var t=this.options;return t[e]||t.other};var Z=function(){function e(e,t,r,n,o,a){this.message=e,this.expected=t,this.found=r,this.offset=n,this.line=o,this.column=a,this.name="SyntaxError"}return function(e,t){function r(){this.constructor=e}r.prototype=t.prototype,e.prototype=new r}(e,Error),{SyntaxError:e,parse:function(t){function r(e){return Je!==e&&(Je>e&&(Je=0,$e={line:1,column:1,seenCR:!1}),function(e,r,n){var o,a;for(o=Je;o<n;o++)"\n"===(a=t.charAt(o))?(e.seenCR||e.line++,e.column=1,e.seenCR=!1):"\r"===a||"\u2028"===a||"\u2029"===a?(e.line++,e.column=1,e.seenCR=!0):(e.column++,e.seenCR=!1)}($e,0,e),Je=e),$e}function n(e){Be<ze||(Be>ze&&(ze=Be,Ke=[]),Ke.push(e))}function o(){return a()}function a(){var e,t;for(e=[],t=i();t!==C;)e.push(t),t=i();return e!==C&&(e=A(e)),e}function i(){var e;return(e=l())===C&&(e=c()),e}function s(){var e,r,n,o,a,i;if(e=Be,r=[],n=Be,(o=w())!==C&&(a=P())!==C&&(i=w())!==C?n=o=[o,a,i]:(Be=n,n=M),n!==C)for(;n!==C;)r.push(n),n=Be,(o=w())!==C&&(a=P())!==C&&(i=w())!==C?n=o=[o,a,i]:(Be=n,n=M);else r=M;return r!==C&&(r=R(r)),(e=r)===C&&(e=Be,(r=b())!==C&&(r=t.substring(e,Be)),e=r),e}function l(){var e;return(e=s())!==C&&(e=D(e)),e}function u(){var e,r,o;if((e=x())===C){if(e=Be,r=[],I.test(t.charAt(Be))?(o=t.charAt(Be),Be++):(o=C,0===Qe&&n(L)),o!==C)for(;o!==C;)r.push(o),I.test(t.charAt(Be))?(o=t.charAt(Be),Be++):(o=C,0===Qe&&n(L));else r=M;r!==C&&(r=t.substring(e,Be)),e=r}return e}function c(){var e,r,o,a,i,s,l;return e=Be,123===t.charCodeAt(Be)?(r=S,Be++):(r=C,0===Qe&&n(U)),r!==C&&w()!==C&&(o=u())!==C&&w()!==C?(a=Be,44===t.charCodeAt(Be)?(i=q,Be++):(i=C,0===Qe&&n(G)),i!==C&&(s=w())!==C&&(l=f())!==C?a=i=[i,s,l]:(Be=a,a=M),a===C&&(a=Z),a!==C&&(i=w())!==C?(125===t.charCodeAt(Be)?(s=H,Be++):(s=C,0===Qe&&n(W)),s!==C?e=r=V(o,a):(Be=e,e=M)):(Be=e,e=M)):(Be=e,e=M),e}function f(){var e;return(e=p())===C&&(e=h())===C&&(e=m())===C&&(e=d()),e}function p(){var e,r,o,a,i,s;return e=Be,t.substr(Be,6)===B?(r=B,Be+=6):(r=C,0===Qe&&n(J)),r===C&&(t.substr(Be,4)===$?(r=$,Be+=4):(r=C,0===Qe&&n(z)),r===C&&(t.substr(Be,4)===K?(r=K,Be+=4):(r=C,0===Qe&&n(Q)))),r!==C&&w()!==C?(o=Be,44===t.charCodeAt(Be)?(a=q,Be++):(a=C,0===Qe&&n(G)),a!==C&&(i=w())!==C&&(s=P())!==C?o=a=[a,i,s]:(Be=o,o=M),o===C&&(o=Z),o!==C?e=r=X(r,o):(Be=e,e=M)):(Be=e,e=M),e}function h(){var e,r,o,a;return e=Be,t.substr(Be,6)===Y?(r=Y,Be+=6):(r=C,0===Qe&&n(ee)),r!==C&&w()!==C?(44===t.charCodeAt(Be)?(o=q,Be++):(o=C,0===Qe&&n(G)),o!==C&&w()!==C&&(a=_())!==C?e=r=te(a):(Be=e,e=M)):(Be=e,e=M),e}function m(){var e,r,o,a;return e=Be,t.substr(Be,13)===re?(r=re,Be+=13):(r=C,0===Qe&&n(ne)),r!==C&&w()!==C?(44===t.charCodeAt(Be)?(o=q,Be++):(o=C,0===Qe&&n(G)),o!==C&&w()!==C&&(a=_())!==C?e=r=oe(a):(Be=e,e=M)):(Be=e,e=M),e}function d(){var e,r,o,a,i;if(e=Be,t.substr(Be,6)===ae?(r=ae,Be+=6):(r=C,0===Qe&&n(ie)),r!==C)if(w()!==C)if(44===t.charCodeAt(Be)?(o=q,Be++):(o=C,0===Qe&&n(G)),o!==C)if(w()!==C){if(a=[],(i=v())!==C)for(;i!==C;)a.push(i),i=v();else a=M;a!==C?e=r=se(a):(Be=e,e=M)}else Be=e,e=M;else Be=e,e=M;else Be=e,e=M;else Be=e,e=M;return e}function y(){var e,r,o,a;return e=Be,r=Be,61===t.charCodeAt(Be)?(o=le,Be++):(o=C,0===Qe&&n(ue)),o!==C&&(a=x())!==C?r=o=[o,a]:(Be=r,r=M),r!==C&&(r=t.substring(e,Be)),(e=r)===C&&(e=P()),e}function v(){var e,r,o,i,s;return e=Be,w()!==C&&(r=y())!==C&&w()!==C?(123===t.charCodeAt(Be)?(o=S,Be++):(o=C,0===Qe&&n(U)),o!==C&&w()!==C&&(i=a())!==C&&w()!==C?(125===t.charCodeAt(Be)?(s=H,Be++):(s=C,0===Qe&&n(W)),s!==C?e=ce(r,i):(Be=e,e=M)):(Be=e,e=M)):(Be=e,e=M),e}function g(){var e,r,o;return e=Be,t.substr(Be,7)===fe?(r=fe,Be+=7):(r=C,0===Qe&&n(pe)),r!==C&&w()!==C&&(o=x())!==C?e=r=he(o):(Be=e,e=M),e}function _(){var e,t,r,n;if(e=Be,(t=g())===C&&(t=Z),t!==C)if(w()!==C){if(r=[],(n=v())!==C)for(;n!==C;)r.push(n),n=v();else r=M;r!==C?e=t=me(t,r):(Be=e,e=M)}else Be=e,e=M;else Be=e,e=M;return e}function b(){var e,r;if(Qe++,e=[],ye.test(t.charAt(Be))?(r=t.charAt(Be),Be++):(r=C,0===Qe&&n(ve)),r!==C)for(;r!==C;)e.push(r),ye.test(t.charAt(Be))?(r=t.charAt(Be),Be++):(r=C,0===Qe&&n(ve));else e=M;return Qe--,e===C&&(r=C,0===Qe&&n(de)),e}function w(){var e,r,o;for(Qe++,e=Be,r=[],o=b();o!==C;)r.push(o),o=b();return r!==C&&(r=t.substring(e,Be)),e=r,Qe--,e===C&&(r=C,0===Qe&&n(ge)),e}function F(){var e;return _e.test(t.charAt(Be))?(e=t.charAt(Be),Be++):(e=C,0===Qe&&n(be)),e}function O(){var e;return we.test(t.charAt(Be))?(e=t.charAt(Be),Be++):(e=C,0===Qe&&n(Fe)),e}function x(){var e,r,o,a,i;if(48===t.charCodeAt(Be)?(e=Oe,Be++):(e=C,0===Qe&&n(xe)),e===C){if(e=Be,r=Be,Te.test(t.charAt(Be))?(o=t.charAt(Be),Be++):(o=C,0===Qe&&n(Pe)),o!==C){for(a=[],i=F();i!==C;)a.push(i),i=F();a!==C?r=o=[o,a]:(Be=r,r=M)}else Be=r,r=M;r!==C&&(r=t.substring(e,Be)),e=r}return e!==C&&(e=je(e)),e}function T(){var e,r,o,a,i,s,l,u;return Ne.test(t.charAt(Be))?(e=t.charAt(Be),Be++):(e=C,0===Qe&&n(Ce)),e===C&&(e=Be,t.substr(Be,2)===ke?(r=ke,Be+=2):(r=C,0===Qe&&n(Ee)),r!==C&&(r=Ae()),(e=r)===C&&(e=Be,t.substr(Be,2)===Me?(r=Me,Be+=2):(r=C,0===Qe&&n(Re)),r!==C&&(r=De()),(e=r)===C&&(e=Be,t.substr(Be,2)===Ie?(r=Ie,Be+=2):(r=C,0===Qe&&n(Le)),r!==C&&(r=Se()),(e=r)===C&&(e=Be,t.substr(Be,2)===Ue?(r=Ue,Be+=2):(r=C,0===Qe&&n(Ze)),r!==C&&(r=qe()),(e=r)===C&&(e=Be,t.substr(Be,2)===Ge?(r=Ge,Be+=2):(r=C,0===Qe&&n(He)),r!==C?(o=Be,a=Be,(i=O())!==C&&(s=O())!==C&&(l=O())!==C&&(u=O())!==C?a=i=[i,s,l,u]:(Be=a,a=M),a!==C&&(a=t.substring(o,Be)),(o=a)!==C?e=r=We(o):(Be=e,e=M)):(Be=e,e=M)))))),e}function P(){var e,t;if(e=[],(t=T())!==C)for(;t!==C;)e.push(t),t=T();else e=M;return e!==C&&(e=Ve(e)),e}var j,N=arguments.length>1?arguments[1]:{},C={},k={start:o},E=o,A=function(e){return{type:"messageFormatPattern",elements:e}},M=C,R=function(e){var t,r,n,o,a,i="";for(t=0,n=e.length;t<n;t+=1)for(r=0,a=(o=e[t]).length;r<a;r+=1)i+=o[r];return i},D=function(e){return{type:"messageTextElement",value:e}},I=/^[^ \t\n\r,.+={}#]/,L={type:"class",value:"[^ \\t\\n\\r,.+={}#]",description:"[^ \\t\\n\\r,.+={}#]"},S="{",U={type:"literal",value:"{",description:'"{"'},Z=null,q=",",G={type:"literal",value:",",description:'","'},H="}",W={type:"literal",value:"}",description:'"}"'},V=function(e,t){return{type:"argumentElement",id:e,format:t&&t[2]}},B="number",J={type:"literal",value:"number",description:'"number"'},$="date",z={type:"literal",value:"date",description:'"date"'},K="time",Q={type:"literal",value:"time",description:'"time"'},X=function(e,t){return{type:e+"Format",style:t&&t[2]}},Y="plural",ee={type:"literal",value:"plural",description:'"plural"'},te=function(e){return{type:e.type,ordinal:!1,offset:e.offset||0,options:e.options}},re="selectordinal",ne={type:"literal",value:"selectordinal",description:'"selectordinal"'},oe=function(e){return{type:e.type,ordinal:!0,offset:e.offset||0,options:e.options}},ae="select",ie={type:"literal",value:"select",description:'"select"'},se=function(e){return{type:"selectFormat",options:e}},le="=",ue={type:"literal",value:"=",description:'"="'},ce=function(e,t){return{type:"optionalFormatPattern",selector:e,value:t}},fe="offset:",pe={type:"literal",value:"offset:",description:'"offset:"'},he=function(e){return e},me=function(e,t){return{type:"pluralFormat",offset:e,options:t}},de={type:"other",description:"whitespace"},ye=/^[ \t\n\r]/,ve={type:"class",value:"[ \\t\\n\\r]",description:"[ \\t\\n\\r]"},ge={type:"other",description:"optionalWhitespace"},_e=/^[0-9]/,be={type:"class",value:"[0-9]",description:"[0-9]"},we=/^[0-9a-f]/i,Fe={type:"class",value:"[0-9a-f]i",description:"[0-9a-f]i"},Oe="0",xe={type:"literal",value:"0",description:'"0"'},Te=/^[1-9]/,Pe={type:"class",value:"[1-9]",description:"[1-9]"},je=function(e){return parseInt(e,10)},Ne=/^[^{}\\\0-\x1F \t\n\r]/,Ce={type:"class",value:"[^{}\\\\\\0-\\x1F \\t\\n\\r]",description:"[^{}\\\\\\0-\\x1F \\t\\n\\r]"},ke="\\\\",Ee={type:"literal",value:"\\\\",description:'"\\\\\\\\"'},Ae=function(){return"\\"},Me="\\#",Re={type:"literal",value:"\\#",description:'"\\\\#"'},De=function(){return"\\#"},Ie="\\{",Le={type:"literal",value:"\\{",description:'"\\\\{"'},Se=function(){return"{"},Ue="\\}",Ze={type:"literal",value:"\\}",description:'"\\\\}"'},qe=function(){return"}"},Ge="\\u",He={type:"literal",value:"\\u",description:'"\\\\u"'},We=function(e){return String.fromCharCode(parseInt(e,16))},Ve=function(e){return e.join("")},Be=0,Je=0,$e={line:1,column:1,seenCR:!1},ze=0,Ke=[],Qe=0;if("startRule"in N){if(!(N.startRule in k))throw new Error("Can't start parsing from rule \""+N.startRule+'".');E=k[N.startRule]}if((j=E())!==C&&Be===t.length)return j;throw j!==C&&Be<t.length&&n({type:"end",description:"end of input"}),function(n,o,a){var i=r(a),s=a<t.length?t.charAt(a):null;return null!==o&&function(e){var t=1;for(e.sort(function(e,t){return e.description<t.description?-1:e.description>t.description?1:0});t<e.length;)e[t-1]===e[t]?e.splice(t,1):t++}(o),new e(null!==n?n:function(e,t){var r,n,o,a=new Array(e.length);for(o=0;o<e.length;o++)a[o]=e[o].description;return r=e.length>1?a.slice(0,-1).join(", ")+" or "+a[e.length-1]:a[0],n=t?'"'+function(e){function r(e){return e.charCodeAt(0).toString(16).toUpperCase()}return t.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\x08/g,"\\b").replace(/\t/g,"\\t").replace(/\n/g,"\\n").replace(/\f/g,"\\f").replace(/\r/g,"\\r").replace(/[\x00-\x07\x0B\x0E\x0F]/g,function(e){return"\\x0"+r(e)}).replace(/[\x10-\x1F\x80-\xFF]/g,function(e){return"\\x"+r(e)}).replace(/[\u0180-\u0FFF]/g,function(e){return"\\u0"+r(e)}).replace(/[\u1080-\uFFFF]/g,function(e){return"\\u"+r(e)})}()+'"':"end of input","Expected "+r+" but "+n+" found."}(o,s),o,s,a,i.line,i.column)}(null,Ke,ze)}}}();S(u,"formats",{enumerable:!0,value:{number:{currency:{style:"currency"},percent:{style:"percent"}},date:{short:{month:"numeric",day:"numeric",year:"2-digit"},medium:{month:"short",day:"numeric",year:"numeric"},long:{month:"long",day:"numeric",year:"numeric"},full:{weekday:"long",month:"long",day:"numeric",year:"numeric"}},time:{short:{hour:"numeric",minute:"numeric"},medium:{hour:"numeric",minute:"numeric",second:"numeric"},long:{hour:"numeric",minute:"numeric",second:"numeric",timeZoneName:"short"},full:{hour:"numeric",minute:"numeric",second:"numeric",timeZoneName:"short"}}}}),S(u,"__localeData__",{value:U(null)}),S(u,"__addLocaleData",{value:function(e){if(!e||!e.locale)throw new Error("Locale data provided to IntlMessageFormat is missing a `locale` property");u.__localeData__[e.locale.toLowerCase()]=e}}),S(u,"__parse",{value:Z.parse}),S(u,"defaultLocale",{enumerable:!0,writable:!0,value:void 0}),u.prototype.resolvedOptions=function(){return{locale:this._locale}},u.prototype._compilePattern=function(e,t,r,n){return new o(t,r,n).compile(e)},u.prototype._findPluralRuleFunction=function(e){for(var t=u.__localeData__,r=t[e.toLowerCase()];r;){if(r.pluralRuleFunction)return r.pluralRuleFunction;r=r.parentLocale&&t[r.parentLocale.toLowerCase()]}throw new Error("Locale data added to IntlMessageFormat is missing a `pluralRuleFunction` for :"+e)},u.prototype._format=function(e,t){var r,n,o,a,i,s,l="";for(r=0,n=e.length;r<n;r+=1)if("string"!=typeof(o=e[r])){if(a=o.id,!t||!L.call(t,a))throw s=new Error("A value must be provided for: "+a),s.variableId=a,s;i=t[a],o.options?l+=this._format(o.getOption(i),t):l+=o.format(i)}else l+=o;return l},u.prototype._mergeFormats=function(e,t){var r,o,a={};for(r in e)L.call(e,r)&&(a[r]=o=U(e[r]),t&&L.call(t,r)&&n(o,t[r]));return a},u.prototype._resolveLocale=function(e){"string"==typeof e&&(e=[e]),e=(e||[]).concat(u.defaultLocale);var t,r,n,o,a=u.__localeData__;for(t=0,r=e.length;t<r;t+=1)for(n=e[t].toLowerCase().split("-");n.length;){if(o=a[n.join("-")])return o.locale;n.pop()}var i=e.pop();throw new Error("No locale data has been added to IntlMessageFormat for: "+e.join(", ")+", or the default locale: "+i)};var q={locale:"en",pluralRuleFunction:function(e,t){var r=String(e).split("."),n=!r[1],o=Number(r[0])==e,a=o&&r[0].slice(-1),i=o&&r[0].slice(-2);return t?1==a&&11!=i?"one":2==a&&12!=i?"two":3==a&&13!=i?"few":"other":1==e&&n?"one":"other"}};u.__addLocaleData(q),u.defaultLocale="en";var G=Math.round,H=function(e,t){var r=G((t=+t)-(e=+e)),n=G(r/1e3),o=G(n/60),a=G(o/60),i=G(a/24),s=G(i/7),l=c(i),u=G(12*l),f=G(l);return{millisecond:r,second:n,"second-short":n,minute:o,"minute-short":o,hour:a,"hour-short":a,day:i,"day-short":i,week:s,"week-short":s,month:u,"month-short":u,year:f,"year-short":f}},W=Object.prototype.hasOwnProperty,V=Object.prototype.toString,B=function(){try{return!!Object.defineProperty({},"a",{})}catch(e){return!1}}()?Object.defineProperty:function(e,t,r){"get"in r&&e.__defineGetter__?e.__defineGetter__(t,r.get):(!W.call(e,t)||"value"in r)&&(e[t]=r.value)},J=Object.create||function(e,t){function r(){}var n,o;r.prototype=e,n=new r;for(o in t)W.call(t,o)&&B(n,o,t[o]);return n},$=Array.prototype.indexOf||function(e,t){var r=this;if(!r.length)return-1;for(var n=t||0,o=r.length;n<o;n++)if(r[n]===e)return n;return-1},z=Array.isArray||function(e){return"[object Array]"===V.call(e)},K=Date.now||function(){return(new Date).getTime()},Q=["second","second-short","minute","minute-short","hour","hour-short","day","day-short","month","month-short","year","year-short"],X=["best fit","numeric"];B(f,"__localeData__",{value:J(null)}),B(f,"__addLocaleData",{value:function(e){if(!e||!e.locale)throw new Error("Locale data provided to IntlRelativeFormat is missing a `locale` property value");f.__localeData__[e.locale.toLowerCase()]=e,u.__addLocaleData(e)}}),B(f,"defaultLocale",{enumerable:!0,writable:!0,value:void 0}),B(f,"thresholds",{enumerable:!0,value:{second:45,"second-short":45,minute:45,"minute-short":45,hour:22,"hour-short":22,day:26,"day-short":26,month:11,"month-short":11}}),f.prototype.resolvedOptions=function(){return{locale:this._locale,style:this._options.style,units:this._options.units}},f.prototype._compileMessage=function(e){var t,r=this._locales,n=this._fields[e].relativeTime,o="",a="";for(t in n.future)n.future.hasOwnProperty(t)&&(o+=" "+t+" {"+n.future[t].replace("{0}","#")+"}");for(t in n.past)n.past.hasOwnProperty(t)&&(a+=" "+t+" {"+n.past[t].replace("{0}","#")+"}");return new u("{when, select, future {{0, plural, "+o+"}}past {{0, plural, "+a+"}}}",r)},f.prototype._getMessage=function(e){var t=this._messages;return t[e]||(t[e]=this._compileMessage(e)),t[e]},f.prototype._getRelativeUnits=function(e,t){var r=this._fields[t];if(r.relative)return r.relative[e]},f.prototype._findFields=function(e){for(var t=f.__localeData__,r=t[e.toLowerCase()];r;){if(r.fields)return r.fields;r=r.parentLocale&&t[r.parentLocale.toLowerCase()]}throw new Error("Locale data added to IntlRelativeFormat is missing `fields` for :"+e)},f.prototype._format=function(e,t){var r=t&&void 0!==t.now?t.now:K();if(void 0===e&&(e=r),!isFinite(r))throw new RangeError("The `now` option provided to IntlRelativeFormat#format() is not in valid range.");if(!isFinite(e))throw new RangeError("The date value provided to IntlRelativeFormat#format() is not in valid range.");var n=H(r,e),o=this._options.units||this._selectUnits(n),a=n[o];if("numeric"!==this._options.style){var i=this._getRelativeUnits(a,o);if(i)return i}return this._getMessage(o).format({0:Math.abs(a),when:a<0?"past":"future"})},f.prototype._isValidUnits=function(e){if(!e||$.call(Q,e)>=0)return!0;if("string"==typeof e){var t=/s$/.test(e)&&e.substr(0,e.length-1);if(t&&$.call(Q,t)>=0)throw new Error('"'+e+'" is not a valid IntlRelativeFormat `units` value, did you mean: '+t)}throw new Error('"'+e+'" is not a valid IntlRelativeFormat `units` value, it must be one of: "'+Q.join('", "')+'"')},f.prototype._resolveLocale=function(e){"string"==typeof e&&(e=[e]),e=(e||[]).concat(f.defaultLocale);var t,r,n,o,a=f.__localeData__;for(t=0,r=e.length;t<r;t+=1)for(n=e[t].toLowerCase().split("-");n.length;){if(o=a[n.join("-")])return o.locale;n.pop()}var i=e.pop();throw new Error("No locale data has been added to IntlRelativeFormat for: "+e.join(", ")+", or the default locale: "+i)},f.prototype._resolveStyle=function(e){if(!e)return X[0];if($.call(X,e)>=0)return e;throw new Error('"'+e+'" is not a valid IntlRelativeFormat `style` value, it must be one of: "'+X.join('", "')+'"')},f.prototype._selectUnits=function(e){var t,r,n,o=Q.filter(function(e){return e.indexOf("-short")<1});for(t=0,r=o.length;t<r&&(n=o[t],!(Math.abs(e[n])<f.thresholds[n]));t+=1);return n};var Y={locale:"en",pluralRuleFunction:function(e,t){var r=String(e).split("."),n=!r[1],o=Number(r[0])==e,a=o&&r[0].slice(-1),i=o&&r[0].slice(-2);return t?1==a&&11!=i?"one":2==a&&12!=i?"two":3==a&&13!=i?"few":"other":1==e&&n?"one":"other"},fields:{year:{displayName:"year",relative:{0:"this year",1:"next year","-1":"last year"},relativeTime:{future:{one:"in {0} year",other:"in {0} years"},past:{one:"{0} year ago",other:"{0} years ago"}}},"year-short":{displayName:"yr.",relative:{0:"this yr.",1:"next yr.","-1":"last yr."},relativeTime:{future:{one:"in {0} yr.",other:"in {0} yr."},past:{one:"{0} yr. ago",other:"{0} yr. ago"}}},month:{displayName:"month",relative:{0:"this month",1:"next month","-1":"last month"},relativeTime:{future:{one:"in {0} month",other:"in {0} months"},past:{one:"{0} month ago",other:"{0} months ago"}}},"month-short":{displayName:"mo.",relative:{0:"this mo.",1:"next mo.","-1":"last mo."},relativeTime:{future:{one:"in {0} mo.",other:"in {0} mo."},past:{one:"{0} mo. ago",other:"{0} mo. ago"}}},day:{displayName:"day",relative:{0:"today",1:"tomorrow","-1":"yesterday"},relativeTime:{future:{one:"in {0} day",other:"in {0} days"},past:{one:"{0} day ago",other:"{0} days ago"}}},"day-short":{displayName:"day",relative:{0:"today",1:"tomorrow","-1":"yesterday"},relativeTime:{future:{one:"in {0} day",other:"in {0} days"},past:{one:"{0} day ago",other:"{0} days ago"}}},hour:{displayName:"hour",relative:{0:"this hour"},relativeTime:{future:{one:"in {0} hour",other:"in {0} hours"},past:{one:"{0} hour ago",other:"{0} hours ago"}}},"hour-short":{displayName:"hr.",relative:{0:"this hour"},relativeTime:{future:{one:"in {0} hr.",other:"in {0} hr."},past:{one:"{0} hr. ago",other:"{0} hr. ago"}}},minute:{displayName:"minute",relative:{0:"this minute"},relativeTime:{future:{one:"in {0} minute",other:"in {0} minutes"},past:{one:"{0} minute ago",other:"{0} minutes ago"}}},"minute-short":{displayName:"min.",relative:{0:"this minute"},relativeTime:{future:{one:"in {0} min.",other:"in {0} min."},past:{one:"{0} min. ago",other:"{0} min. ago"}}},second:{displayName:"second",relative:{0:"now"},relativeTime:{future:{one:"in {0} second",other:"in {0} seconds"},past:{one:"{0} second ago",other:"{0} seconds ago"}}},"second-short":{displayName:"sec.",relative:{0:"now"},relativeTime:{future:{one:"in {0} sec.",other:"in {0} sec."},past:{one:"{0} sec. ago",other:"{0} sec. ago"}}}}};f.__addLocaleData(Y),f.defaultLocale="en";var ee="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},te=(function(){function e(e){this.value=e}function t(t){function r(o,a){try{var i=t[o](a),s=i.value;s instanceof e?Promise.resolve(s.value).then(function(e){r("next",e)},function(e){r("throw",e)}):n(i.done?"return":"normal",i.value)}catch(e){n("throw",e)}}function n(e,t){switch(e){case"return":o.resolve({value:t,done:!0});break;case"throw":o.reject(t);break;default:o.resolve({value:t,done:!1})}(o=o.next)?r(o.key,o.arg):a=null}var o,a;this._invoke=function(e,t){return new Promise(function(n,i){var s={key:e,arg:t,resolve:n,reject:i,next:null};a?a=a.next=s:(o=a=s,r(e,t))})},"function"!=typeof t.return&&(this.return=void 0)}"function"==typeof Symbol&&Symbol.asyncIterator&&(t.prototype[Symbol.asyncIterator]=function(){return this}),t.prototype.next=function(e){return this._invoke("next",e)},t.prototype.throw=function(e){return this._invoke("throw",e)},t.prototype.return=function(e){return this._invoke("return",e)}}(),function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}),re=function(){function e(e,t){for(var r=0;r<t.length;r++){var n=t[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}return function(t,r,n){return r&&e(t.prototype,r),n&&e(t,n),t}}(),ne=function(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e},oe=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e},ae=function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)},ie=function(e,t){var r={};for(var n in e)t.indexOf(n)>=0||Object.prototype.hasOwnProperty.call(e,n)&&(r[n]=e[n]);return r},se=function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t},le=function(e){if(Array.isArray(e)){for(var t=0,r=Array(e.length);t<e.length;t++)r[t]=e[t];return r}return Array.from(e)},ue=t.bool,ce=t.number,fe=t.string,pe=t.func,he=t.object,me=t.oneOf,de=t.shape,ye=t.any,ve=t.oneOfType,ge=me(["best fit","lookup"]),_e=me(["narrow","short","long"]),be=me(["numeric","2-digit"]),we=pe.isRequired,Fe={locale:fe,timeZone:fe,formats:he,messages:he,textComponent:ye,defaultLocale:fe,defaultFormats:he,onError:pe},Oe={formatDate:we,formatTime:we,formatRelative:we,formatNumber:we,formatPlural:we,formatMessage:we,formatHTMLMessage:we},xe=de(oe({},Fe,Oe,{formatters:he,now:we})),Te=(fe.isRequired,ve([fe,he]),{localeMatcher:ge,formatMatcher:me(["basic","best fit"]),timeZone:fe,hour12:ue,weekday:_e,era:_e,year:be,month:me(["numeric","2-digit","narrow","short","long"]),day:be,hour:be,minute:be,second:be,timeZoneName:me(["short","long"])}),Pe={localeMatcher:ge,style:me(["decimal","currency","percent"]),currency:fe,currencyDisplay:me(["symbol","code","name"]),useGrouping:ue,minimumIntegerDigits:ce,minimumFractionDigits:ce,maximumFractionDigits:ce,minimumSignificantDigits:ce,maximumSignificantDigits:ce},je={style:me(["best fit","numeric"]),units:me(["second","minute","hour","day","month","year","second-short","minute-short","hour-short","day-short","month-short","year-short"])},Ne={style:me(["cardinal","ordinal"])},Ce={childContextTypes:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},ke={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},Ee=Object.defineProperty,Ae=Object.getOwnPropertyNames,Me=Object.getOwnPropertySymbols,Re=Object.getOwnPropertyDescriptor,De=Object.getPrototypeOf,Ie=De&&De(Object),Le=d,Se=function(e,t,r,n,o,a,i,s){if(!e){var l;if(void 0===t)l=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var u=[r,n,o,a,i,s],c=0;(l=new Error(t.replace(/%s/g,function(){return u[c++]}))).name="Invariant Violation"}throw l.framesToPop=1,l}},Ue=Object.keys(Fe),Ze={"&":"&",">":">","<":"<",'"':""","'":"'"},qe=/[&><"']/g,Ge=function e(t){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};te(this,e);var n="ordinal"===r.style,o=T(x(t));this.format=function(e){return o(e,n)}},He=Function.prototype.bind||function(e){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var t=Array.prototype.slice.call(arguments,1),r=this,n=function(){},o=function(){return r.apply(this instanceof n?this:e,t.concat(Array.prototype.slice.call(arguments)))};return this.prototype&&(n.prototype=this.prototype),o.prototype=new n,o},We=Object.prototype.hasOwnProperty,Ve=function(){try{return!!Object.defineProperty({},"a",{})}catch(e){return!1}}()?Object.defineProperty:function(e,t,r){"get"in r&&e.__defineGetter__?e.__defineGetter__(t,r.get):(!We.call(e,t)||"value"in r)&&(e[t]=r.value)},Be=Object.create||function(e,t){function r(){}var n,o;r.prototype=e,n=new r;for(o in t)We.call(t,o)&&Ve(n,o,t[o]);return n},Je=Object.keys(Te),$e=Object.keys(Pe),ze=Object.keys(je),Ke=Object.keys(Ne),Qe={second:60,minute:60,hour:24,day:30,month:12},Xe=Object.freeze({formatDate:function(e,t,r){var n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},o=e.locale,a=e.formats,i=e.timeZone,s=n.format,l=e.onError||F,u=new Date(r),c=oe({},i&&{timeZone:i},s&&k(a,"date",s,l)),f=v(n,Je,c);try{return t.getDateTimeFormat(o,f).format(u)}catch(e){l(w("Error formatting date.",e))}return String(u)},formatTime:function(e,t,r){var n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},o=e.locale,a=e.formats,i=e.timeZone,s=n.format,l=e.onError||F,u=new Date(r),c=oe({},i&&{timeZone:i},s&&k(a,"time",s,l)),f=v(n,Je,c);f.hour||f.minute||f.second||(f=oe({},f,{hour:"numeric",minute:"numeric"}));try{return t.getDateTimeFormat(o,f).format(u)}catch(e){l(w("Error formatting time.",e))}return String(u)},formatRelative:function(e,t,r){var n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},o=e.locale,a=e.formats,i=n.format,s=e.onError||F,l=new Date(r),u=new Date(n.now),c=i&&k(a,"relative",i,s),p=v(n,ze,c),h=oe({},f.thresholds);C(Qe);try{return t.getRelativeFormat(o,p).format(l,{now:isFinite(u)?u:t.now()})}catch(e){s(w("Error formatting relative time.",e))}finally{C(h)}return String(l)},formatNumber:function(e,t,r){var n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},o=e.locale,a=e.formats,i=n.format,s=e.onError||F,l=i&&k(a,"number",i,s),u=v(n,$e,l);try{return t.getNumberFormat(o,u).format(r)}catch(e){s(w("Error formatting number.",e))}return String(r)},formatPlural:function(e,t,r){var n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},o=e.locale,a=v(n,Ke),i=e.onError||F;try{return t.getPluralFormat(o,a).format(r)}catch(e){i(w("Error formatting plural.",e))}return"other"},formatMessage:E,formatHTMLMessage:function(e,t,r){var n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};return E(e,t,r,Object.keys(n).reduce(function(e,t){var r=n[t];return e[t]="string"==typeof r?y(r):r,e},{}))}}),Ye=Object.keys(Fe),et=Object.keys(Oe),tt={formats:{},messages:{},timeZone:null,textComponent:"span",defaultLocale:"en",defaultFormats:{},onError:F},rt=function(e){function t(e){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};te(this,t);var n=se(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,r));Se("undefined"!=typeof Intl,"[React Intl] The `Intl` APIs must be available in the runtime, and do not appear to be built-in. An `Intl` polyfill should be loaded.\nSee: http://formatjs.io/guides/runtime-environments/");var o=r.intl,a=void 0;a=isFinite(e.initialNow)?Number(e.initialNow):o?o.now():Date.now();var i=(o||{}).formatters,s=void 0===i?{getDateTimeFormat:P(Intl.DateTimeFormat),getNumberFormat:P(Intl.NumberFormat),getMessageFormat:P(u),getRelativeFormat:P(f),getPluralFormat:P(Ge)}:i;return n.state=oe({},s,{now:function(){return n._didDisplay?Date.now():a}}),n}return ae(t,e),re(t,[{key:"getConfig",value:function(){var e=this.context.intl,t=v(this.props,Ye,e);for(var r in tt)void 0===t[r]&&(t[r]=tt[r]);if(!h(t.locale)){var n=t,o=n.locale,a=n.defaultLocale,i=n.defaultFormats;(0,n.onError)(w('Missing locale data for locale: "'+o+'". Using default locale: "'+a+'" as fallback.')),t=oe({},t,{locale:a,formats:i,messages:tt.messages})}return t}},{key:"getBoundFormatFns",value:function(e,t){return et.reduce(function(r,n){return r[n]=Xe[n].bind(null,e,t),r},{})}},{key:"getChildContext",value:function(){var e=this.getConfig(),t=this.getBoundFormatFns(e,this.state),r=this.state,n=r.now,o=ie(r,["now"]);return{intl:oe({},e,t,{formatters:o,now:n})}}},{key:"shouldComponentUpdate",value:function(){for(var e=arguments.length,t=Array(e),r=0;r<e;r++)t[r]=arguments[r];return b.apply(void 0,[this].concat(t))}},{key:"componentDidMount",value:function(){this._didDisplay=!0}},{key:"render",value:function(){return r.Children.only(this.props.children)}}]),t}(r.Component);rt.displayName="IntlProvider",rt.contextTypes={intl:xe},rt.childContextTypes={intl:xe.isRequired};var nt=function(e){function t(e,r){te(this,t);var n=se(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,r));return g(r),n}return ae(t,e),re(t,[{key:"shouldComponentUpdate",value:function(){for(var e=arguments.length,t=Array(e),r=0;r<e;r++)t[r]=arguments[r];return b.apply(void 0,[this].concat(t))}},{key:"render",value:function(){var e=this.context.intl,t=e.formatDate,r=e.textComponent,n=this.props,o=n.value,a=n.children,i=t(o,this.props);return"function"==typeof a?a(i):D.createElement(r,null,i)}}]),t}(r.Component);nt.displayName="FormattedDate",nt.contextTypes={intl:xe};var ot=function(e){function t(e,r){te(this,t);var n=se(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,r));return g(r),n}return ae(t,e),re(t,[{key:"shouldComponentUpdate",value:function(){for(var e=arguments.length,t=Array(e),r=0;r<e;r++)t[r]=arguments[r];return b.apply(void 0,[this].concat(t))}},{key:"render",value:function(){var e=this.context.intl,t=e.formatTime,r=e.textComponent,n=this.props,o=n.value,a=n.children,i=t(o,this.props);return"function"==typeof a?a(i):D.createElement(r,null,i)}}]),t}(r.Component);ot.displayName="FormattedTime",ot.contextTypes={intl:xe};var at=1e3,it=6e4,st=36e5,lt=864e5,ut=2147483647,ct=function(e){function t(e,r){te(this,t);var n=se(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,r));g(r);var o=isFinite(e.initialNow)?Number(e.initialNow):r.intl.now();return n.state={now:o},n}return ae(t,e),re(t,[{key:"scheduleNextUpdate",value:function(e,t){var r=this;clearTimeout(this._timer);var n=e.value,o=e.units,a=e.updateInterval,i=new Date(n).getTime();if(a&&isFinite(i)){var s=i-t.now,l=M(o||A(s)),u=Math.abs(s%l),c=s<0?Math.max(a,l-u):Math.max(a,u);this._timer=setTimeout(function(){r.setState({now:r.context.intl.now()})},c)}}},{key:"componentDidMount",value:function(){this.scheduleNextUpdate(this.props,this.state)}},{key:"componentWillReceiveProps",value:function(e){R(e.value,this.props.value)||this.setState({now:this.context.intl.now()})}},{key:"shouldComponentUpdate",value:function(){for(var e=arguments.length,t=Array(e),r=0;r<e;r++)t[r]=arguments[r];return b.apply(void 0,[this].concat(t))}},{key:"componentWillUpdate",value:function(e,t){this.scheduleNextUpdate(e,t)}},{key:"componentWillUnmount",value:function(){clearTimeout(this._timer)}},{key:"render",value:function(){var e=this.context.intl,t=e.formatRelative,r=e.textComponent,n=this.props,o=n.value,a=n.children,i=t(o,oe({},this.props,this.state));return"function"==typeof a?a(i):D.createElement(r,null,i)}}]),t}(r.Component);ct.displayName="FormattedRelative",ct.contextTypes={intl:xe},ct.defaultProps={updateInterval:1e4};var ft=function(e){function t(e,r){te(this,t);var n=se(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,r));return g(r),n}return ae(t,e),re(t,[{key:"shouldComponentUpdate",value:function(){for(var e=arguments.length,t=Array(e),r=0;r<e;r++)t[r]=arguments[r];return b.apply(void 0,[this].concat(t))}},{key:"render",value:function(){var e=this.context.intl,t=e.formatNumber,r=e.textComponent,n=this.props,o=n.value,a=n.children,i=t(o,this.props);return"function"==typeof a?a(i):D.createElement(r,null,i)}}]),t}(r.Component);ft.displayName="FormattedNumber",ft.contextTypes={intl:xe};var pt=function(e){function t(e,r){te(this,t);var n=se(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,r));return g(r),n}return ae(t,e),re(t,[{key:"shouldComponentUpdate",value:function(){for(var e=arguments.length,t=Array(e),r=0;r<e;r++)t[r]=arguments[r];return b.apply(void 0,[this].concat(t))}},{key:"render",value:function(){var e=this.context.intl,t=e.formatPlural,r=e.textComponent,n=this.props,o=n.value,a=n.other,i=n.children,s=t(o,this.props),l=this.props[s]||a;return"function"==typeof i?i(l):D.createElement(r,null,l)}}]),t}(r.Component);pt.displayName="FormattedPlural",pt.contextTypes={intl:xe},pt.defaultProps={style:"cardinal"};var ht=function(e,t){return E({},{getMessageFormat:P(u)},e,t)},mt=function(e){function t(e,r){te(this,t);var n=se(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,r));return e.defaultMessage||g(r),n}return ae(t,e),re(t,[{key:"shouldComponentUpdate",value:function(e){var t=this.props.values;if(!_(e.values,t))return!0;for(var r=oe({},e,{values:t}),n=arguments.length,o=Array(n>1?n-1:0),a=1;a<n;a++)o[a-1]=arguments[a];return b.apply(void 0,[this,r].concat(o))}},{key:"render",value:function(){var e=this.context.intl||{},t=e.formatMessage,n=void 0===t?ht:t,o=e.textComponent,a=void 0===o?"span":o,i=this.props,s=i.id,l=i.description,u=i.defaultMessage,c=i.values,f=i.tagName,p=void 0===f?a:f,h=i.children,m=void 0,d=void 0,y=void 0;if(c&&Object.keys(c).length>0){var v=Math.floor(1099511627776*Math.random()).toString(16),g=function(){var e=0;return function(){return"ELEMENT-"+v+"-"+(e+=1)}}();m="@__"+v+"__@",d={},y={},Object.keys(c).forEach(function(e){var t=c[e];if(r.isValidElement(t)){var n=g();d[e]=m+n+m,y[n]=t}else d[e]=t})}var _=n({id:s,description:l,defaultMessage:u},d||c),b=void 0;return b=y&&Object.keys(y).length>0?_.split(m).filter(function(e){return!!e}).map(function(e){return y[e]||e}):[_],"function"==typeof h?h.apply(void 0,le(b)):r.createElement.apply(void 0,[p,null].concat(le(b)))}}]),t}(r.Component);mt.displayName="FormattedMessage",mt.contextTypes={intl:xe},mt.defaultProps={values:{}};var dt=function(e){function t(e,r){te(this,t);var n=se(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,r));return g(r),n}return ae(t,e),re(t,[{key:"shouldComponentUpdate",value:function(e){var t=this.props.values;if(!_(e.values,t))return!0;for(var r=oe({},e,{values:t}),n=arguments.length,o=Array(n>1?n-1:0),a=1;a<n;a++)o[a-1]=arguments[a];return b.apply(void 0,[this,r].concat(o))}},{key:"render",value:function(){var e=this.context.intl,t=e.formatHTMLMessage,r=e.textComponent,n=this.props,o=n.id,a=n.description,i=n.defaultMessage,s=n.values,l=n.tagName,u=void 0===l?r:l,c=n.children,f=t({id:o,description:a,defaultMessage:i},s);if("function"==typeof c)return c(f);var p={__html:f};return D.createElement(u,{dangerouslySetInnerHTML:p})}}]),t}(r.Component);dt.displayName="FormattedHTMLMessage",dt.contextTypes={intl:xe},dt.defaultProps={values:{}},p(I),e.addLocaleData=p,e.intlShape=xe,e.injectIntl=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.intlPropName,o=void 0===n?"intl":n,a=t.withRef,i=void 0!==a&&a,s=function(t){function r(e,t){te(this,r);var n=se(this,(r.__proto__||Object.getPrototypeOf(r)).call(this,e,t));return g(t),n}return ae(r,t),re(r,[{key:"getWrappedInstance",value:function(){return Se(i,"[React Intl] To access the wrapped instance, the `{withRef: true}` option must be set when calling: `injectIntl()`"),this._wrappedInstance}},{key:"render",value:function(){var t=this;return D.createElement(e,oe({},this.props,ne({},o,this.context.intl),{ref:i?function(e){return t._wrappedInstance=e}:null}))}}]),r}(r.Component);return s.displayName="InjectIntl("+O(e)+")",s.contextTypes={intl:xe},s.WrappedComponent=e,Le(s,e)},e.defineMessages=function(e){return e},e.IntlProvider=rt,e.FormattedDate=nt,e.FormattedTime=ot,e.FormattedRelative=ct,e.FormattedNumber=ft,e.FormattedPlural=pt,e.FormattedMessage=mt,e.FormattedHTMLMessage=dt,Object.defineProperty(e,"__esModule",{value:!0})}); -//# sourceMappingURL=react-intl.min.js.map diff --git a/browser/components/newtab/webpack.system-addon.config.js b/browser/components/newtab/webpack.system-addon.config.js index 4b7cb08c70..eea3415f03 100644 --- a/browser/components/newtab/webpack.system-addon.config.js +++ b/browser/components/newtab/webpack.system-addon.config.js @@ -49,7 +49,6 @@ module.exports = (env = {}) => ({ "prop-types": "PropTypes", "react": "React", "react-dom": "ReactDOM", - "react-intl": "ReactIntl", "redux": "Redux", "react-redux": "ReactRedux", }, diff --git a/browser/components/protocolhandler/test/mochitest.ini b/browser/components/protocolhandler/test/mochitest.ini index 27e6cae556..4ada8373cc 100644 --- a/browser/components/protocolhandler/test/mochitest.ini +++ b/browser/components/protocolhandler/test/mochitest.ini @@ -1,2 +1 @@ [test_registerHandler.html] -[test_registerHandler_disabled.html] diff --git a/browser/components/protocolhandler/test/test_registerHandler_disabled.html b/browser/components/protocolhandler/test/test_registerHandler_disabled.html deleted file mode 100644 index 58e3b8aab0..0000000000 --- a/browser/components/protocolhandler/test/test_registerHandler_disabled.html +++ /dev/null @@ -1,38 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=402788 ---> -<head> - <title>Test for Bug 1398169 - - - - -Mozilla Bug 1398169 -

- -
-
-
- - diff --git a/browser/components/sessionstore/SessionFile.jsm b/browser/components/sessionstore/SessionFile.jsm index f4a9c22e51..2d1a77bb63 100644 --- a/browser/components/sessionstore/SessionFile.jsm +++ b/browser/components/sessionstore/SessionFile.jsm @@ -43,7 +43,6 @@ XPCOMUtils.defineLazyServiceGetter( XPCOMUtils.defineLazyModuleGetters(this, { RunState: "resource:///modules/sessionstore/RunState.jsm", - SessionStartup: "resource:///modules/sessionstore/SessionStartup.jsm", SessionStore: "resource:///modules/sessionstore/SessionStore.jsm", SessionWorker: "resource:///modules/sessionstore/SessionWorker.jsm", }); @@ -442,8 +441,7 @@ var SessionFileInternal = { RunState.setClosed(); } - let performShutdownCleanup = - isFinalWrite && !SessionStartup.isAutomaticRestoreEnabled(); + let performShutdownCleanup = isFinalWrite && !SessionStore.willAutoRestore; this._attempts++; let options = { isFinalWrite, performShutdownCleanup }; diff --git a/browser/components/sessionstore/SessionStartup.jsm b/browser/components/sessionstore/SessionStartup.jsm index 479ff8a83e..206702c4ff 100644 --- a/browser/components/sessionstore/SessionStartup.jsm +++ b/browser/components/sessionstore/SessionStartup.jsm @@ -66,15 +66,15 @@ const TYPE_DEFER_SESSION = 3; // 'browser.startup.page' preference value to resume the previous session. const BROWSER_STARTUP_RESUME_SESSION = 3; -function warning(aMsg, aException) { +function warning(msg, exception) { let consoleMsg = Cc["@mozilla.org/scripterror;1"].createInstance( Ci.nsIScriptError ); consoleMsg.init( - aMsg, - aException.fileName, + msg, + exception.fileName, null, - aException.lineNumber, + exception.lineNumber, 0, Ci.nsIScriptError.warningFlag, "component javascript" @@ -101,9 +101,9 @@ var SessionStartup = { RESUME_SESSION: TYPE_RESUME_SESSION, DEFER_SESSION: TYPE_DEFER_SESSION, - // the state to restore at startup + // The state to restore at startup. _initialState: null, - _sessionType: TYPE_NO_SESSION, + _sessionType: null, _initialized: false, // Stores whether the previous session crashed. @@ -116,7 +116,7 @@ var SessionStartup = { /** * Initialize the component */ - init: function sss_init() { + init() { Services.obs.notifyObservers(null, "sessionstore-init-started"); StartupPerformance.init(); @@ -148,25 +148,20 @@ var SessionStartup = { ); } - this._resumeSessionEnabled = - Services.prefs.getBoolPref("browser.sessionstore.resume_session_once") || - Services.prefs.getIntPref("browser.startup.page") == - BROWSER_STARTUP_RESUME_SESSION; - SessionFile.read().then(this._onSessionFileRead.bind(this), console.error); }, - // Wrap a string as a nsISupports - _createSupportsString: function ssfi_createSupportsString(aData) { + // Wrap a string as a nsISupports. + _createSupportsString(data) { let string = Cc["@mozilla.org/supports-string;1"].createInstance( Ci.nsISupportsString ); - string.data = aData; + string.data = data; return string; }, /** - * Complete initialization once the Session File has been read + * Complete initialization once the Session File has been read. * * @param source The Session State string read from disk. * @param parsed The object obtained by parsing |source| as JSON. @@ -220,15 +215,11 @@ var SessionStartup = { ); }, 60000); - // If this is a normal restore then throw away any previous session - if (!this._resumeSessionEnabled && this._initialState) { + // If this is a normal restore then throw away any previous session. + if (!this.isAutomaticRestoreEnabled() && this._initialState) { delete this._initialState.lastSessionState; } - let resumeFromCrash = Services.prefs.getBoolPref( - "browser.sessionstore.resume_from_crash" - ); - CrashMonitor.previousCheckpoints.then(checkpoints => { if (checkpoints) { // If the previous session finished writing the final state, we'll @@ -242,7 +233,7 @@ var SessionStartup = { // a version including the Crash Monitor, or if the checkpoints file // was removed, or on first startup with this profile, or after Firefox Reset. - // There was no checkpoints file and no sessionstore.js or its backups + // There was no checkpoints file and no sessionstore.js or its backups, // so we will assume that this was a fresh profile. this._previousSessionCrashed = false; } else { @@ -270,19 +261,11 @@ var SessionStartup = { .getHistogramById("SHUTDOWN_OK") .add(!this._previousSessionCrashed); - // set the startup type - if (this._previousSessionCrashed && resumeFromCrash) { - this._sessionType = this.RECOVER_SESSION; - } else if (!this._previousSessionCrashed && this._resumeSessionEnabled) { - this._sessionType = this.RESUME_SESSION; - } else if (this._initialState) { - this._sessionType = this.DEFER_SESSION; - } else { - this._initialState = null; - } // reset the state Services.obs.addObserver(this, "sessionstore-windows-restored", true); - if (this._sessionType != this.NO_SESSION) { + if (this.sessionType == this.NO_SESSION) { + this._initialState = null; // Reset the state. + } else { Services.obs.addObserver(this, "browser:purge-session-history", true); } @@ -296,17 +279,17 @@ var SessionStartup = { /** * Handle notifications */ - observe: function sss_observe(aSubject, aTopic, aData) { - switch (aTopic) { + observe(subject, topic, data) { + switch (topic) { case "sessionstore-windows-restored": Services.obs.removeObserver(this, "sessionstore-windows-restored"); - // free _initialState after nsSessionStore is done with it + // Free _initialState after nsSessionStore is done with it. this._initialState = null; this._didRestore = true; break; case "browser:purge-session-history": Services.obs.removeObserver(this, "browser:purge-session-history"); - // reset all state on sanitization + // Reset all state on sanitization. this._sessionType = this.NO_SESSION; break; } @@ -325,15 +308,6 @@ var SessionStartup = { return this._initialState; }, - /** - * Determines whether there is a pending session restore. Should only be - * called after initialization has completed. - * @returns bool - */ - doRestore: function sss_doRestore() { - return this._willRestore(); - }, - /** * Determines whether automatic session restoration is enabled for this * launch of the browser. This does not include crash restoration. In @@ -342,28 +316,39 @@ var SessionStartup = { * @returns bool */ isAutomaticRestoreEnabled() { - if (PrivateBrowsingUtils.permanentPrivateBrowsing) { - return false; + if (this._resumeSessionEnabled === null) { + this._resumeSessionEnabled = + !PrivateBrowsingUtils.permanentPrivateBrowsing && + (Services.prefs.getBoolPref( + "browser.sessionstore.resume_session_once" + ) || + Services.prefs.getIntPref("browser.startup.page") == + BROWSER_STARTUP_RESUME_SESSION); } - return ( - Services.prefs.getBoolPref("browser.sessionstore.resume_session_once") || - Services.prefs.getIntPref("browser.startup.page") == - BROWSER_STARTUP_RESUME_SESSION - ); + return this._resumeSessionEnabled; }, /** * Determines whether there is a pending session restore. * @returns bool */ - _willRestore() { + willRestore() { return ( - this._sessionType == this.RECOVER_SESSION || - this._sessionType == this.RESUME_SESSION + this.sessionType == this.RECOVER_SESSION || + this.sessionType == this.RESUME_SESSION ); }, + /** + * Determines whether there is a pending session restore and if that will refer + * back to a crash. + * @returns bool + */ + willRestoreAsCrashed() { + return this.sessionType == this.RECOVER_SESSION; + }, + /** * Returns a boolean or a promise that resolves to a boolean, indicating * whether we will restore a session that ends up replacing the homepage. @@ -378,7 +363,7 @@ var SessionStartup = { // it when recovering from a crash, which we'll only know after reading the // session file, but waiting for that would delay loading the homepage in // the non-crash case. - if (!this._initialState && !this._resumeSessionEnabled) { + if (!this._initialState && !this.isAutomaticRestoreEnabled()) { return false; } // If we've already restored the session, we won't override again. @@ -391,7 +376,7 @@ var SessionStartup = { // If there are valid windows with not only pinned tabs, signal that we // will override the default homepage by restoring a session. resolve( - this._willRestore() && + this.willRestore() && this._initialState && this._initialState.windows && this._initialState.windows.some(w => w.tabs.some(t => !t.pinned)) @@ -404,6 +389,22 @@ var SessionStartup = { * Get the type of pending session store, if any. */ get sessionType() { + if (this._sessionType === null) { + let resumeFromCrash = Services.prefs.getBoolPref( + "browser.sessionstore.resume_from_crash" + ); + // Set the startup type. + if (this.isAutomaticRestoreEnabled()) { + this._sessionType = this.RESUME_SESSION; + } else if (this._previousSessionCrashed && resumeFromCrash) { + this._sessionType = this.RECOVER_SESSION; + } else if (this._initialState) { + this._sessionType = this.DEFER_SESSION; + } else { + this._sessionType = this.NO_SESSION; + } + } + return this._sessionType; }, @@ -414,6 +415,11 @@ var SessionStartup = { return this._previousSessionCrashed; }, + resetForTest() { + this._resumeSessionEnabled = null; + this._sessionType = null; + }, + QueryInterface: ChromeUtils.generateQI([ Ci.nsIObserver, Ci.nsISupportsWeakReference, diff --git a/browser/components/sessionstore/SessionStore.jsm b/browser/components/sessionstore/SessionStore.jsm index 30dbd8f12f..2ba91e981e 100644 --- a/browser/components/sessionstore/SessionStore.jsm +++ b/browser/components/sessionstore/SessionStore.jsm @@ -172,6 +172,9 @@ const RESTORE_TAB_CONTENT_REASON = { NAVIGATE_AND_RESTORE: 1, }; +// 'browser.startup.page' preference value to resume the previous session. +const BROWSER_STARTUP_RESUME_SESSION = 3; + ChromeUtils.import("resource://gre/modules/PrivateBrowsingUtils.jsm", this); ChromeUtils.import("resource://gre/modules/Services.jsm", this); ChromeUtils.import("resource://gre/modules/TelemetryTimestamps.jsm", this); @@ -250,6 +253,10 @@ var SessionStore = { return SessionStoreInternal.lastClosedObjectType; }, + get willAutoRestore() { + return SessionStoreInternal.willAutoRestore; + }, + init: function ss_init() { SessionStoreInternal.init(); }, @@ -655,6 +662,19 @@ var SessionStoreInternal = { return "tab"; }, + /** + * Returns a boolean that determines whether the session will be automatically + * restored upon the _next_ startup or a restart. + */ + get willAutoRestore() { + return ( + !PrivateBrowsingUtils.permanentPrivateBrowsing && + (Services.prefs.getBoolPref("browser.sessionstore.resume_session_once") || + Services.prefs.getIntPref("browser.startup.page") == + BROWSER_STARTUP_RESUME_SESSION) + ); + }, + /** * Initialize the sessionstore service. */ @@ -684,7 +704,7 @@ var SessionStoreInternal = { let state; let ss = SessionStartup; - if (ss.doRestore() || ss.sessionType == ss.DEFER_SESSION) { + if (ss.willRestore() || ss.sessionType == ss.DEFER_SESSION) { state = ss.state; } @@ -716,7 +736,7 @@ var SessionStoreInternal = { // restore it LastSession.setState(state.lastSessionState); - if (ss.previousSessionCrashed) { + if (ss.willRestoreAsCrashed()) { this._recentCrashes = ((state.session && state.session.recentCrashes) || 0) + 1; @@ -1372,7 +1392,10 @@ var SessionStoreInternal = { if (closedWindowState) { let newWindowState; - if (AppConstants.platform == "macosx" || !this._doResumeSession()) { + if ( + AppConstants.platform == "macosx" || + !SessionStartup.willRestore() + ) { // We want to split the window up into pinned tabs and unpinned tabs. // Pinned tabs should be restored. If there are any remaining tabs, // they should be added back to _closedWindows. @@ -5164,17 +5187,6 @@ var SessionStoreInternal = { return window; }, - /** - * Whether or not to resume session, if not recovering from a crash. - * @returns bool - */ - _doResumeSession: function ssi_doResumeSession() { - return ( - this._prefBranch.getIntPref("startup.page") == 3 || - this._prefBranch.getBoolPref("sessionstore.resume_session_once") - ); - }, - /** * whether the user wants to load any other page at startup * (except the homepage) - needed for determining whether to overwrite the current tabs diff --git a/browser/components/sessionstore/test/browser_354894_perwindowpb.js b/browser/components/sessionstore/test/browser_354894_perwindowpb.js index 97391eea18..b13c08ced3 100644 --- a/browser/components/sessionstore/test/browser_354894_perwindowpb.js +++ b/browser/components/sessionstore/test/browser_354894_perwindowpb.js @@ -24,6 +24,7 @@ * notifications. The latter won't. */ +ChromeUtils.import("resource:///modules/sessionstore/SessionStartup.jsm", this); // The rejection "BrowserWindowTracker.getTopWindow(...) is null" is left // unhandled in some cases. This bug should be fixed, but for the moment this // file is whitelisted. @@ -115,6 +116,9 @@ let setupTest = async function(options, testFunction) { ["browser.startup.page", 3], ["browser.tabs.warnOnClose", false] ); + // SessionStartup caches pref values, but as this test tries to simulate a + // startup scenario, we'll reset them here. + SessionStartup.resetForTest(); // Observe these, and also use to count the number of hits let observing = { @@ -156,6 +160,8 @@ let setupTest = async function(options, testFunction) { } await popPrefs(); + // Act like nothing ever happened. + SessionStartup.resetForTest(); }; /** diff --git a/browser/components/sessionstore/test/browser_background_tab_crash.js b/browser/components/sessionstore/test/browser_background_tab_crash.js index 2cff8dfbc1..c42718054f 100644 --- a/browser/components/sessionstore/test/browser_background_tab_crash.js +++ b/browser/components/sessionstore/test/browser_background_tab_crash.js @@ -244,7 +244,7 @@ add_task(async function test_preload_crash() { NewTabPagePreloading.removePreloadedBrowser(window); // Create a fresh preloaded browser - NewTabPagePreloading.maybeCreatePreloadedBrowser(window); + await BrowserTestUtils.maybeCreatePreloadedBrowser(gBrowser); await BrowserTestUtils.crashBrowser(gBrowser.preloadedBrowser, false); diff --git a/browser/config/version.txt b/browser/config/version.txt index cf1f85ecfa..7489cae399 100644 --- a/browser/config/version.txt +++ b/browser/config/version.txt @@ -1 +1 @@ -68.13.9 +68.14.0 diff --git a/browser/config/version_display.txt b/browser/config/version_display.txt index e3c65f1127..10247aeb1e 100644 --- a/browser/config/version_display.txt +++ b/browser/config/version_display.txt @@ -1 +1 @@ -68.13.9b +68.14.0b diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index a26a220795..06f2aabebc 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -167,9 +167,6 @@ ; JavaScript components @RESPATH@/browser/components/BrowserComponents.manifest -@RESPATH@/components/EnterprisePolicies.js -@RESPATH@/components/EnterprisePoliciesContent.js -@RESPATH@/components/EnterprisePolicies.manifest @RESPATH@/components/extensions.manifest #ifdef MOZ_UPDATER @RESPATH@/components/nsUpdateService.manifest @@ -263,17 +260,14 @@ ; [Webide Files] @RESPATH@/browser/chrome/webide@JAREXT@ @RESPATH@/browser/chrome/webide.manifest -@RESPATH@/browser/@PREF_DIR@/webide.js ; [DevTools Startup Files] @RESPATH@/browser/chrome/devtools-startup@JAREXT@ @RESPATH@/browser/chrome/devtools-startup.manifest -@RESPATH@/browser/@PREF_DIR@/devtools-startup.js ; DevTools @RESPATH@/browser/chrome/devtools@JAREXT@ @RESPATH@/browser/chrome/devtools.manifest -@RESPATH@/browser/@PREF_DIR@/devtools-client.js @RESPATH@/browser/@PREF_DIR@/debugger.js ; shell icons diff --git a/caps/BasePrincipal.cpp b/caps/BasePrincipal.cpp index b0704a9544..53d6d9dfcb 100644 --- a/caps/BasePrincipal.cpp +++ b/caps/BasePrincipal.cpp @@ -915,6 +915,21 @@ BasePrincipal::GetLocalStorageQuotaKey(nsACString& aKey) { return NS_OK; } +NS_IMETHODIMP +BasePrincipal::GetIsScriptAllowedByPolicy(bool* aIsScriptAllowedByPolicy) { + *aIsScriptAllowedByPolicy = false; + nsCOMPtr prinURI; + nsresult rv = GetURI(getter_AddRefs(prinURI)); + if (NS_FAILED(rv) || !prinURI) { + return NS_OK; + } + nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); + if (!ssm) { + return NS_ERROR_UNEXPECTED; + } + return ssm->PolicyAllowsScript(prinURI, aIsScriptAllowedByPolicy); +} + bool SiteIdentifier::Equals(const SiteIdentifier& aOther) const { MOZ_ASSERT(IsInitialized()); MOZ_ASSERT(aOther.IsInitialized()); diff --git a/caps/BasePrincipal.h b/caps/BasePrincipal.h index fde5359273..cb638642f1 100644 --- a/caps/BasePrincipal.h +++ b/caps/BasePrincipal.h @@ -146,7 +146,9 @@ class BasePrincipal : public nsJSPrincipals { NS_IMETHOD AllowsRelaxStrictFileOriginPolicy(nsIURI* aURI, bool* aRes) override; NS_IMETHOD CreateReferrerInfo(mozilla::dom::ReferrerPolicy aReferrerPolicy, - nsIReferrerInfo** _retval) override; + nsIReferrerInfo** _retval) override; + NS_IMETHOD GetIsScriptAllowedByPolicy( + bool* aIsScriptAllowedByPolicy) override; nsresult ToJSON(nsACString& aJSON); static already_AddRefed FromJSON(const nsACString& aJSON); // Method populates a passed Json::Value with serializable fields diff --git a/caps/ContentPrincipal.cpp b/caps/ContentPrincipal.cpp index 63f2c94518..c23d9467a6 100644 --- a/caps/ContentPrincipal.cpp +++ b/caps/ContentPrincipal.cpp @@ -353,14 +353,17 @@ ContentPrincipal::SetDomain(nsIURI* aDomain) { // Set the changed-document-domain flag on compartments containing realms // using this principal. - auto cb = [](JSContext*, void*, JS::Handle aRealm) { + auto cb = [](JSContext*, void*, JS::Realm* aRealm, + const JS::AutoRequireNoGC& nogc) { JS::Compartment* comp = JS::GetCompartmentForRealm(aRealm); xpc::SetCompartmentChangedDocumentDomain(comp); }; JSPrincipals* principals = nsJSPrincipals::get(static_cast(this)); - AutoSafeJSContext cx; - JS::IterateRealmsWithPrincipals(cx, principals, nullptr, cb); + + dom::AutoJSAPI jsapi; + jsapi.Init(); + JS::IterateRealmsWithPrincipals(jsapi.cx(), principals, nullptr, cb); return NS_OK; } diff --git a/caps/nsIPrincipal.idl b/caps/nsIPrincipal.idl index 626c9d830c..5d82ddf27f 100644 --- a/caps/nsIPrincipal.idl +++ b/caps/nsIPrincipal.idl @@ -430,6 +430,12 @@ interface nsIPrincipal : nsISerializable * Returns true if the URI is an Onion URI */ [infallible] readonly attribute boolean isOnion; + + /* + * Returns true if the Domain Policy allows js execution + * for the Principals URI + */ + readonly attribute boolean isScriptAllowedByPolicy; }; /** diff --git a/config/check_spidermonkey_style.py b/config/check_spidermonkey_style.py index 3e51350c35..a609677dcd 100644 --- a/config/check_spidermonkey_style.py +++ b/config/check_spidermonkey_style.py @@ -60,6 +60,7 @@ included_inclnames_to_ignore = set([ 'double-conversion/double-conversion.h', # strange MFBT case 'javascript-trace.h', # generated in $OBJDIR if HAVE_DTRACE is defined 'frontend/ReservedWordsGenerated.h', # generated in $OBJDIR + 'frontend/smoosh_generated.h', # generated in $OBJDIR 'gc/StatsPhasesGenerated.h', # generated in $OBJDIR 'gc/StatsPhasesGenerated.inc', # generated in $OBJDIR 'jit/CacheIROpsGenerated.h', # generated in $OBJDIR @@ -96,6 +97,7 @@ included_inclnames_to_ignore = set([ 'unicode/ucurr.h', # ICU 'unicode/udat.h', # ICU 'unicode/udata.h', # ICU + 'unicode/udateintervalformat.h', # ICU 'unicode/udatpg.h', # ICU 'unicode/udisplaycontext.h', # ICU 'unicode/uenum.h', # ICU @@ -301,7 +303,7 @@ def check_style(enable_fixup): js_public_root = os.path.join('js', 'public') for dirpath, dirnames, filenames in os.walk(js_public_root): for filename in filenames: - if filename.endswith('.h'): + if filename.endswith(('.h', '.msg')): filepath = os.path.join(dirpath, filename).replace('\\', '/') inclname = 'js/' + filepath[len('js/public/'):] js_names[filepath] = inclname @@ -362,12 +364,21 @@ def is_module_header(enclosing_inclname, header_inclname): module = module_name(enclosing_inclname) - # Normal case, e.g. module == "foo/Bar", header_inclname == "foo/Bar.h". + # Normal case, for example: + # module == "vm/Runtime", header_inclname == "vm/Runtime.h". if module == module_name(header_inclname): return True - # A public header, e.g. module == "foo/Bar", header_inclname == "js/Bar.h". - m = re.match(r'js\/(.*)\.h', header_inclname) + # A public header, for example: + # + # module == "vm/CharacterEncoding", + # header_inclname == "js/CharacterEncoding.h" + # + # or (for implementation files for js/public/*/*.h headers) + # + # module == "vm/SourceHook", + # header_inclname == "js/experimental/SourceHook.h" + m = re.match(r'js\/.*?([^\/]+)\.h', header_inclname) if m is not None and module.endswith('/' + m.group(1)): return True @@ -400,7 +411,7 @@ class Include(object): 4. foo/Bar.h 5. jsfooinlines.h 6. foo/Bar-inl.h - 7. non-.h, e.g. *.tbl, *.msg + 7. non-.h, e.g. *.tbl, *.msg (these can be scattered throughout files) ''' if self.is_system: diff --git a/config/external/icu/i18n/sources.mozbuild b/config/external/icu/i18n/sources.mozbuild index be7f29f629..3cef96e2e4 100644 --- a/config/external/icu/i18n/sources.mozbuild +++ b/config/external/icu/i18n/sources.mozbuild @@ -157,6 +157,7 @@ SOURCES += [ '/intl/icu/source/i18n/ucol_sit.cpp', '/intl/icu/source/i18n/ucoleitr.cpp', '/intl/icu/source/i18n/udat.cpp', + '/intl/icu/source/i18n/udateintervalformat.cpp', '/intl/icu/source/i18n/udatpg.cpp', '/intl/icu/source/i18n/ufieldpositer.cpp', '/intl/icu/source/i18n/uitercollationiterator.cpp', diff --git a/config/makefiles/xpidl/Makefile.in b/config/makefiles/xpidl/Makefile.in index 33e450c8a7..17a79fde76 100644 --- a/config/makefiles/xpidl/Makefile.in +++ b/config/makefiles/xpidl/Makefile.in @@ -29,8 +29,11 @@ idl_deps_dir := .deps dist_idl_dir := $(DIST)/idl dist_include_dir := $(DIST)/include dist_xpcrs_dir := $(DIST)/xpcrs +stub_file := xptdata.stub process_py := $(topsrcdir)/python/mozbuild/mozbuild/action/xpidl-process.py -generated_file := $(topobjdir)/xpcom/reflect/xptinfo/xptdata.cpp +target_file := $(topobjdir)/xpcom/reflect/xptinfo/xptdata.cpp +xptdata_h := $(dist_include_dir)/xptdata.h +generated_files := $(target_file) $(xptdata_h) code_gen_py := $(topsrcdir)/xpcom/reflect/xptinfo/xptcodegen.py code_gen_deps := $(topsrcdir)/xpcom/ds/tools/perfecthash.py @@ -60,17 +63,21 @@ xpt_files := $(addsuffix .xpt,$(xpidl_modules)) depends_files := $(foreach root,$(xpidl_modules),$(idl_deps_dir)/$(root).pp) -GARBAGE += $(xpt_files) $(depends_files) $(generated_file) +GARBAGE += $(stub_file) $(xpt_files) $(depends_files) $(target_file) ifdef COMPILE_ENVIRONMENT -xpidl:: $(generated_file) +xpidl:: $(generated_files) endif +# See bug 1420119 for why we need the semicolon. +$(target_file) $(xptdata_h) : $(stub_file) ; + $(xpt_files): $(process_py) $(call mkdir_deps,$(idl_deps_dir) $(dist_include_dir) $(dist_xpcrs_dir)) -$(generated_file): $(xpt_files) $(code_gen_py) $(code_gen_deps) +$(stub_file) : $(xpt_files) $(code_gen_py) $(code_gen_deps) $(REPORT_BUILD) - $(PYTHON_PATH) $(PLY_INCLUDE) $(code_gen_py) $(generated_file) $(xpt_files) + $(PYTHON_PATH) $(PLY_INCLUDE) $(code_gen_py) $(generated_files) $(xpt_files) + @touch $@ -include $(depends_files) diff --git a/config/recurse.mk b/config/recurse.mk index 3ad1d68759..d45af11a48 100644 --- a/config/recurse.mk +++ b/config/recurse.mk @@ -182,12 +182,17 @@ xpcom/xpidl/export: xpcom/idl-parser/xpidl/export dom/bindings/export: layout/style/export ifdef ENABLE_CLANG_PLUGIN -$(filter-out config/host build/unix/stdc++compat/% build/clang-plugin/%,$(compile_targets)): build/clang-plugin/host build/clang-plugin/tests/target-objects +# Only target rules use the clang plugin. +$(filter %/target %/target-objects,$(filter-out config/export config/host build/unix/stdc++compat/% build/clang-plugin/%,$(compile_targets))): build/clang-plugin/host build/clang-plugin/tests/target-objects build/clang-plugin/tests/target-objects: build/clang-plugin/host # clang-plugin tests require js-confdefs.h on js standalone builds and mozilla-config.h on # other builds, because they are -include'd. ifdef JS_STANDALONE +# The js/src/export target only exists when CURRENT_TIER is export. If we're in a later tier, +# we can assume js/src/export has happened anyways. +ifeq ($(CURRENT_TIER),export) build/clang-plugin/tests/target-objects: js/src/export +endif else build/clang-plugin/tests/target-objects: mozilla-config.h endif diff --git a/config/system-headers.mozbuild b/config/system-headers.mozbuild index ce57d0abbe..9a00f9cde5 100644 --- a/config/system-headers.mozbuild +++ b/config/system-headers.mozbuild @@ -1327,6 +1327,7 @@ if CONFIG['MOZ_SYSTEM_ICU']: 'unicode/ucurr.h', 'unicode/udat.h', 'unicode/udata.h', + 'unicode/udateintervalformat.h', 'unicode/udatpg.h', 'unicode/udisplaycontext.h', 'unicode/uldnames.h', diff --git a/docshell/base/BrowsingContext.cpp b/docshell/base/BrowsingContext.cpp index 14afa14f82..608d7e52b4 100644 --- a/docshell/base/BrowsingContext.cpp +++ b/docshell/base/BrowsingContext.cpp @@ -599,7 +599,7 @@ class RemoteLocationProxy auto location = static_cast(GetNative(aProxy)); CycleCollectionNoteChild(aCb, location->GetBrowsingContext(), - "js::GetObjectPrivate(obj)->GetBrowsingContext()"); + "JS::GetPrivate(obj)->GetBrowsingContext()"); } }; diff --git a/docshell/base/nsDefaultURIFixup.cpp b/docshell/base/nsDefaultURIFixup.cpp index 17b7eac5e2..346080adcf 100644 --- a/docshell/base/nsDefaultURIFixup.cpp +++ b/docshell/base/nsDefaultURIFixup.cpp @@ -711,13 +711,13 @@ bool nsDefaultURIFixup::PossiblyHostPortUrl(const nsACString& aUrl) { ++iter; // Count the number of digits after the colon and before the - // next forward slash (or end of string) + // next forward slash, question mark, hash sign, or end of string. uint32_t digitCount = 0; while (iter != iterEnd && digitCount <= 5) { if (IsAsciiDigit(*iter)) { digitCount++; - } else if (*iter == '/') { + } else if (*iter == '/' || *iter == '?' || *iter == '#') { break; } else { // Whatever it is, it ain't a port! diff --git a/docshell/base/timeline/AutoGlobalTimelineMarker.cpp b/docshell/base/timeline/AutoGlobalTimelineMarker.cpp index aff730f782..cfe3ce8f1c 100644 --- a/docshell/base/timeline/AutoGlobalTimelineMarker.cpp +++ b/docshell/base/timeline/AutoGlobalTimelineMarker.cpp @@ -11,9 +11,8 @@ namespace mozilla { AutoGlobalTimelineMarker::AutoGlobalTimelineMarker( const char* aName, MarkerStackRequest aStackRequest /* = STACK */ - MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) + ) : mName(aName), mStackRequest(aStackRequest) { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; MOZ_ASSERT(NS_IsMainThread()); RefPtr timelines = TimelineConsumers::Get(); diff --git a/docshell/base/timeline/AutoGlobalTimelineMarker.h b/docshell/base/timeline/AutoGlobalTimelineMarker.h index 21465e7e5b..4ec7c4e391 100644 --- a/docshell/base/timeline/AutoGlobalTimelineMarker.h +++ b/docshell/base/timeline/AutoGlobalTimelineMarker.h @@ -5,7 +5,6 @@ #ifndef mozilla_AutoGlobalTimelineMarker_h_ #define mozilla_AutoGlobalTimelineMarker_h_ -#include "mozilla/GuardObjects.h" #include "TimelineMarkerEnums.h" namespace mozilla { @@ -26,7 +25,6 @@ namespace mozilla { // ... // } class MOZ_RAII AutoGlobalTimelineMarker { - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER; // The name of the marker we are adding. const char* mName; @@ -36,8 +34,7 @@ class MOZ_RAII AutoGlobalTimelineMarker { public: explicit AutoGlobalTimelineMarker( const char* aName, - MarkerStackRequest aStackRequest = - MarkerStackRequest::STACK MOZ_GUARD_OBJECT_NOTIFIER_PARAM); + MarkerStackRequest aStackRequest = MarkerStackRequest::STACK); ~AutoGlobalTimelineMarker(); AutoGlobalTimelineMarker(const AutoGlobalTimelineMarker& aOther) = delete; diff --git a/docshell/base/timeline/ObservedDocShell.cpp b/docshell/base/timeline/ObservedDocShell.cpp index 8be6caaf46..884fdef59a 100644 --- a/docshell/base/timeline/ObservedDocShell.cpp +++ b/docshell/base/timeline/ObservedDocShell.cpp @@ -159,7 +159,7 @@ void ObservedDocShell::PopMarkers( } } - mTimelineMarkers.SwapElements(keptStartMarkers); + mTimelineMarkers = std::move(keptStartMarkers); } } // namespace mozilla diff --git a/docshell/build/components.conf b/docshell/build/components.conf index 63aeb0eb82..36889cf230 100644 --- a/docshell/build/components.conf +++ b/docshell/build/components.conf @@ -58,8 +58,10 @@ Classes = [ }, { 'name': 'URIFixup', + 'js_name': 'uriFixup', 'cid': '{214c48a0-b57f-11d4-959c-0020183bf181}', 'contract_ids': ['@mozilla.org/docshell/urifixup;1'], + 'interfaces': ['nsIURIFixup'], 'type': 'nsDefaultURIFixup', 'headers': ['/docshell/base/nsDefaultURIFixup.h'], }, diff --git a/docshell/test/browser/browser_timelineMarkers-frame-04.js b/docshell/test/browser/browser_timelineMarkers-frame-04.js index 29972be84b..a05804c5b3 100644 --- a/docshell/test/browser/browser_timelineMarkers-frame-04.js +++ b/docshell/test/browser/browser_timelineMarkers-frame-04.js @@ -41,7 +41,11 @@ var TESTS = [ }, ]; -if (Services.prefs.getBoolPref("javascript.options.asyncstack")) { +if ( + !Services.prefs.getBoolPref( + "javascript.options.asyncstack_capture_debuggee_only" + ) +) { TESTS.push( { desc: "Async stack trace on Javascript marker", diff --git a/docshell/test/browser/browser_timelineMarkers-frame-05.js b/docshell/test/browser/browser_timelineMarkers-frame-05.js index 266b2b5dd3..b5c245e451 100644 --- a/docshell/test/browser/browser_timelineMarkers-frame-05.js +++ b/docshell/test/browser/browser_timelineMarkers-frame-05.js @@ -97,7 +97,11 @@ var TESTS = [ }, ]; -if (Services.prefs.getBoolPref("javascript.options.asyncstack")) { +if ( + !Services.prefs.getBoolPref( + "javascript.options.asyncstack_capture_debuggee_only" + ) +) { TESTS.push({ desc: "Async stack trace on Promise", searchFor: "ConsoleTime", diff --git a/docshell/test/unit/test_nsDefaultURIFixup_search.js b/docshell/test/unit/test_nsDefaultURIFixup_search.js index 20187ec4c2..0be452ddd7 100644 --- a/docshell/test/unit/test_nsDefaultURIFixup_search.js +++ b/docshell/test/unit/test_nsDefaultURIFixup_search.js @@ -71,6 +71,18 @@ var data = [ wrong: "://user:pass@example.com:8080/this/is/a/test.html", fixed: "http://user:pass@example.com:8080/this/is/a/test.html", }, + { + wrong: "localhost:8080/?param=1", + fixed: "http://localhost:8080/?param=1", + }, + { + wrong: "localhost:8080?param=1", + fixed: "http://localhost:8080/?param=1", + }, + { + wrong: "localhost:8080#somewhere", + fixed: "http://localhost:8080/#somewhere", + }, { wrong: "whatever://this/is/a@b/test.html", fixed: kSearchEngineURL.replace( diff --git a/editor/libeditor/EditorBase.cpp b/editor/libeditor/EditorBase.cpp index c39e9b5348..7711c00534 100644 --- a/editor/libeditor/EditorBase.cpp +++ b/editor/libeditor/EditorBase.cpp @@ -29,7 +29,6 @@ #include "mozilla/BasePrincipal.h" // for BasePrincipal #include "mozilla/CheckedInt.h" // for CheckedInt #include "mozilla/ComposerCommandsUpdater.h" // for ComposerCommandsUpdater -#include "mozilla/ComputedStyle.h" // for ComputedStyle #include "mozilla/CSSEditUtils.h" // for CSSEditUtils #include "mozilla/EditAction.h" // for EditSubAction #include "mozilla/EditorDOMPoint.h" // for EditorDOMPoint @@ -78,7 +77,6 @@ #include "nsCaseTreatment.h" #include "nsCharTraits.h" // for NS_IS_HIGH_SURROGATE, etc. #include "nsComponentManagerUtils.h" // for do_CreateInstance -#include "nsComputedDOMStyle.h" // for nsComputedDOMStyle #include "nsContentUtils.h" // for nsContentUtils #include "nsDOMString.h" // for DOMStringIsNull #include "nsDebug.h" // for NS_WARNING, etc. @@ -126,6 +124,8 @@ namespace mozilla { using namespace dom; using namespace widget; +using ChildBlockBoundary = HTMLEditUtils::ChildBlockBoundary; + /***************************************************************************** * mozilla::EditorBase *****************************************************************************/ @@ -2585,7 +2585,8 @@ nsINode* EditorBase::GetFirstEditableNode(nsINode* aRoot) { MOZ_ASSERT(aRoot); EditorType editorType = GetEditorType(); - nsIContent* content = GetLeftmostChild(aRoot); + nsIContent* content = + HTMLEditUtils::GetFirstLeafChild(*aRoot, ChildBlockBoundary::TreatAsLeaf); if (content && !EditorUtils::IsEditableContent(*content, editorType)) { content = GetNextEditableNode(*content); } @@ -2815,7 +2816,7 @@ nsresult EditorBase::DeleteTextWithTransaction(Text& aTextNode, return rv; } -nsIContent* EditorBase::GetPreviousNodeInternal(nsINode& aNode, +nsIContent* EditorBase::GetPreviousNodeInternal(const nsINode& aNode, bool aFindEditableNode, bool aFindAnyDataNode, bool aNoBlockCrossing) const { @@ -2856,24 +2857,25 @@ nsIContent* EditorBase::GetPreviousNodeInternal(const EditorRawDOMPoint& aPoint, // unless there isn't one, in which case we are at the end of the node // and want the deep-right child. - nsIContent* rightMostContent = - GetRightmostChild(aPoint.GetContainer(), aNoBlockCrossing); - if (!rightMostContent) { + nsIContent* lastLeafContent = HTMLEditUtils::GetLastLeafChild( + *aPoint.GetContainer(), aNoBlockCrossing ? ChildBlockBoundary::TreatAsLeaf + : ChildBlockBoundary::Ignore); + if (!lastLeafContent) { return nullptr; } if ((!aFindEditableNode || - EditorUtils::IsEditableContent(*rightMostContent, GetEditorType())) && - (aFindAnyDataNode || EditorUtils::IsElementOrText(*rightMostContent))) { - return rightMostContent; + EditorUtils::IsEditableContent(*lastLeafContent, GetEditorType())) && + (aFindAnyDataNode || EditorUtils::IsElementOrText(*lastLeafContent))) { + return lastLeafContent; } // restart the search from the non-editable node we just found - return GetPreviousNodeInternal(*rightMostContent, aFindEditableNode, + return GetPreviousNodeInternal(*lastLeafContent, aFindEditableNode, aFindAnyDataNode, aNoBlockCrossing); } -nsIContent* EditorBase::GetNextNodeInternal(nsINode& aNode, +nsIContent* EditorBase::GetNextNodeInternal(const nsINode& aNode, bool aFindEditableNode, bool aFindAnyDataNode, bool aNoBlockCrossing) const { @@ -2909,24 +2911,25 @@ nsIContent* EditorBase::GetNextNodeInternal(const EditorRawDOMPoint& aPoint, return point.GetChild(); } - nsIContent* leftMostContent = - GetLeftmostChild(point.GetChild(), aNoBlockCrossing); - if (!leftMostContent) { + nsIContent* firstLeafContent = HTMLEditUtils::GetFirstLeafChild( + *point.GetChild(), aNoBlockCrossing ? ChildBlockBoundary::TreatAsLeaf + : ChildBlockBoundary::Ignore); + if (!firstLeafContent) { return point.GetChild(); } - if (!IsDescendantOfEditorRoot(leftMostContent)) { + if (!IsDescendantOfEditorRoot(firstLeafContent)) { return nullptr; } if ((!aFindEditableNode || - EditorUtils::IsEditableContent(*leftMostContent, GetEditorType())) && - (aFindAnyDataNode || EditorUtils::IsElementOrText(*leftMostContent))) { - return leftMostContent; + EditorUtils::IsEditableContent(*firstLeafContent, GetEditorType())) && + (aFindAnyDataNode || EditorUtils::IsElementOrText(*firstLeafContent))) { + return firstLeafContent; } // restart the search from the non-editable node we just found - return GetNextNodeInternal(*leftMostContent, aFindEditableNode, + return GetNextNodeInternal(*firstLeafContent, aFindEditableNode, aFindAnyDataNode, aNoBlockCrossing); } @@ -2942,14 +2945,15 @@ nsIContent* EditorBase::GetNextNodeInternal(const EditorRawDOMPoint& aPoint, aFindAnyDataNode, aNoBlockCrossing); } -nsIContent* EditorBase::FindNextLeafNode(nsINode* aCurrentNode, bool aGoForward, +nsIContent* EditorBase::FindNextLeafNode(const nsINode* aCurrentNode, + bool aGoForward, bool bNoBlockCrossing) const { // called only by GetPriorNode so we don't need to check params. MOZ_ASSERT( IsDescendantOfEditorRoot(aCurrentNode) && !IsEditorRoot(aCurrentNode), "Bogus arguments"); - nsINode* cur = aCurrentNode; + const nsINode* cur = aCurrentNode; for (;;) { // if aCurrentNode has a sibling in the right direction, return // that sibling's closest child (or itself if it has no children) @@ -2960,14 +2964,14 @@ nsIContent* EditorBase::FindNextLeafNode(nsINode* aCurrentNode, bool aGoForward, // don't look inside prevsib, since it is a block return sibling; } - nsIContent* leaf = aGoForward - ? GetLeftmostChild(sibling, bNoBlockCrossing) - : GetRightmostChild(sibling, bNoBlockCrossing); - if (!leaf) { - return sibling; - } - - return leaf; + ChildBlockBoundary childBlockBoundary = + bNoBlockCrossing ? ChildBlockBoundary::TreatAsLeaf + : ChildBlockBoundary::Ignore; + nsIContent* leafContent = + aGoForward + ? HTMLEditUtils::GetFirstLeafChild(*sibling, childBlockBoundary) + : HTMLEditUtils::GetLastLeafChild(*sibling, childBlockBoundary); + return leafContent ? leafContent : sibling; } nsINode* parent = cur->GetParentNode(); @@ -2992,7 +2996,7 @@ nsIContent* EditorBase::FindNextLeafNode(nsINode* aCurrentNode, bool aGoForward, return nullptr; } -nsIContent* EditorBase::FindNode(nsINode* aCurrentNode, bool aGoForward, +nsIContent* EditorBase::FindNode(const nsINode* aCurrentNode, bool aGoForward, bool aEditableNode, bool aFindAnyDataNode, bool bNoBlockCrossing) const { if (IsEditorRoot(aCurrentNode)) { @@ -3020,55 +3024,7 @@ nsIContent* EditorBase::FindNode(nsINode* aCurrentNode, bool aGoForward, bNoBlockCrossing); } -nsIContent* EditorBase::GetRightmostChild(nsINode* aCurrentNode, - bool bNoBlockCrossing) const { - if (NS_WARN_IF(!aCurrentNode)) { - return nullptr; - } - nsIContent* content = aCurrentNode->GetLastChild(); - if (!content) { - return nullptr; - } - for (;;) { - if (bNoBlockCrossing && HTMLEditUtils::IsBlockElement(*content)) { - return content; - } - nsIContent* nextContent = content->GetLastChild(); - if (!nextContent) { - return content; - } - content = nextContent; - } - - MOZ_ASSERT_UNREACHABLE("What part of for(;;) do you not understand?"); - return nullptr; -} - -nsIContent* EditorBase::GetLeftmostChild(nsINode* aCurrentNode, - bool bNoBlockCrossing) const { - if (NS_WARN_IF(!aCurrentNode)) { - return nullptr; - } - nsIContent* content = aCurrentNode->GetFirstChild(); - if (!content) { - return nullptr; - } - for (;;) { - if (bNoBlockCrossing && HTMLEditUtils::IsBlockElement(*content)) { - return content; - } - nsIContent* next = content->GetFirstChild(); - if (!next) { - return content; - } - content = next; - } - - MOZ_ASSERT_UNREACHABLE("What part of for(;;) do you not understand?"); - return nullptr; -} - -bool EditorBase::IsRoot(nsINode* inNode) const { +bool EditorBase::IsRoot(const nsINode* inNode) const { if (NS_WARN_IF(!inNode)) { return false; } @@ -3076,7 +3032,7 @@ bool EditorBase::IsRoot(nsINode* inNode) const { return inNode == rootNode; } -bool EditorBase::IsEditorRoot(nsINode* aNode) const { +bool EditorBase::IsEditorRoot(const nsINode* aNode) const { if (NS_WARN_IF(!aNode)) { return false; } @@ -3084,7 +3040,7 @@ bool EditorBase::IsEditorRoot(nsINode* aNode) const { return aNode == rootNode; } -bool EditorBase::IsDescendantOfRoot(nsINode* inNode) const { +bool EditorBase::IsDescendantOfRoot(const nsINode* inNode) const { if (NS_WARN_IF(!inNode)) { return false; } @@ -3096,7 +3052,7 @@ bool EditorBase::IsDescendantOfRoot(nsINode* inNode) const { return inNode->IsInclusiveDescendantOf(root); } -bool EditorBase::IsDescendantOfEditorRoot(nsINode* aNode) const { +bool EditorBase::IsDescendantOfEditorRoot(const nsINode* aNode) const { if (NS_WARN_IF(!aNode)) { return false; } @@ -3225,36 +3181,6 @@ nsresult EditorBase::GetEndChildNode(const Selection& aSelection, return NS_OK; } -/** - * IsPreformatted() checks the style info for the node for the preformatted - * text style. - */ -// static -bool EditorBase::IsPreformatted(nsINode* aNode) { - if (NS_WARN_IF(!aNode)) { - return false; - } - // Look at the node (and its parent if it's not an element), and grab its - // ComputedStyle. - Element* element = aNode->GetAsElementOrParentElement(); - if (!element) { - return false; - } - - RefPtr elementStyle = - nsComputedDOMStyle::GetComputedStyleNoFlush(element, nullptr); - if (!elementStyle) { - // Consider nodes without a ComputedStyle to be NOT preformatted: - // For instance, this is true of JS tags inside the body (which show - // up as #text nodes but have no ComputedStyle). - return false; - } - - const nsStyleText* styleText = elementStyle->StyleText(); - - return styleText->WhiteSpaceIsSignificant(); -} - nsresult EditorBase::EnsureNoPaddingBRElementForEmptyEditor() { MOZ_ASSERT(IsEditActionDataAvailable()); @@ -4260,23 +4186,10 @@ nsresult EditorBase::DeleteSelectionWithTransaction( return rv; } -nsresult EditorBase::CreateRange(nsINode* aStartContainer, int32_t aStartOffset, - nsINode* aEndContainer, int32_t aEndOffset, - nsRange** aRange) { - RefPtr range = nsRange::Create( - aStartContainer, aStartOffset, aEndContainer, aEndOffset, IgnoreErrors()); - if (!range) { - NS_WARNING("nsRange::Create() failed"); - return NS_ERROR_FAILURE; - } - range.forget(aRange); - return NS_OK; -} - nsresult EditorBase::AppendNodeToSelectionAsRange(nsINode* aNode) { MOZ_ASSERT(IsEditActionDataAvailable()); - if (NS_WARN_IF(!aNode) && NS_WARN_IF(!aNode->IsContent())) { + if (NS_WARN_IF(!aNode) || NS_WARN_IF(!aNode->IsContent())) { return NS_ERROR_INVALID_ARG; } @@ -4285,15 +4198,11 @@ nsresult EditorBase::AppendNodeToSelectionAsRange(nsINode* aNode) { return NS_ERROR_FAILURE; } - RefPtr range; - nsresult rv = CreateRange(atContent.GetContainer(), atContent.Offset(), - atContent.GetContainer(), atContent.Offset() + 1, - getter_AddRefs(range)); - if (NS_FAILED(rv)) { - NS_WARNING("EditorBase::CreateRange() failed"); - return rv; - } + RefPtr range = nsRange::Create( + atContent.ToRawRangeBoundary(), + atContent.NextPoint().ToRawRangeBoundary(), IgnoreErrors()); if (NS_WARN_IF(!range)) { + NS_WARNING("nsRange::Create() failed"); return NS_ERROR_FAILURE; } diff --git a/editor/libeditor/EditorBase.h b/editor/libeditor/EditorBase.h index 44af69ac65..8c8caab29e 100644 --- a/editor/libeditor/EditorBase.h +++ b/editor/libeditor/EditorBase.h @@ -1601,22 +1601,22 @@ class EditorBase : public nsIEditor, const EditorRawDOMPoint& aPoint) const { return GetPreviousNodeInternal(aPoint, true, true, true); } - nsIContent* GetPreviousNode(nsINode& aNode) const { + nsIContent* GetPreviousNode(const nsINode& aNode) const { return GetPreviousNodeInternal(aNode, false, true, false); } - nsIContent* GetPreviousElementOrText(nsINode& aNode) const { + nsIContent* GetPreviousElementOrText(const nsINode& aNode) const { return GetPreviousNodeInternal(aNode, false, false, false); } - nsIContent* GetPreviousEditableNode(nsINode& aNode) const { + nsIContent* GetPreviousEditableNode(const nsINode& aNode) const { return GetPreviousNodeInternal(aNode, true, true, false); } - nsIContent* GetPreviousNodeInBlock(nsINode& aNode) const { + nsIContent* GetPreviousNodeInBlock(const nsINode& aNode) const { return GetPreviousNodeInternal(aNode, false, true, true); } - nsIContent* GetPreviousElementOrTextInBlock(nsINode& aNode) const { + nsIContent* GetPreviousElementOrTextInBlock(const nsINode& aNode) const { return GetPreviousNodeInternal(aNode, false, false, true); } - nsIContent* GetPreviousEditableNodeInBlock(nsINode& aNode) const { + nsIContent* GetPreviousEditableNodeInBlock(const nsINode& aNode) const { return GetPreviousNodeInternal(aNode, true, true, true); } @@ -1675,50 +1675,36 @@ class EditorBase : public nsIEditor, const EditorDOMPointBase& aPoint) const { return GetNextNodeInternal(aPoint, true, true, true); } - nsIContent* GetNextNode(nsINode& aNode) const { + nsIContent* GetNextNode(const nsINode& aNode) const { return GetNextNodeInternal(aNode, false, true, false); } - nsIContent* GetNextElementOrText(nsINode& aNode) const { + nsIContent* GetNextElementOrText(const nsINode& aNode) const { return GetNextNodeInternal(aNode, false, false, false); } - nsIContent* GetNextEditableNode(nsINode& aNode) const { + nsIContent* GetNextEditableNode(const nsINode& aNode) const { return GetNextNodeInternal(aNode, true, true, false); } - nsIContent* GetNextNodeInBlock(nsINode& aNode) const { + nsIContent* GetNextNodeInBlock(const nsINode& aNode) const { return GetNextNodeInternal(aNode, false, true, true); } - nsIContent* GetNextElementOrTextInBlock(nsINode& aNode) const { + nsIContent* GetNextElementOrTextInBlock(const nsINode& aNode) const { return GetNextNodeInternal(aNode, false, false, true); } - nsIContent* GetNextEditableNodeInBlock(nsINode& aNode) const { + nsIContent* GetNextEditableNodeInBlock(const nsINode& aNode) const { return GetNextNodeInternal(aNode, true, true, true); } - /** - * Get the rightmost child of aCurrentNode; - * return nullptr if aCurrentNode has no children. - */ - nsIContent* GetRightmostChild(nsINode* aCurrentNode, - bool bNoBlockCrossing = false) const; - - /** - * Get the leftmost child of aCurrentNode; - * return nullptr if aCurrentNode has no children. - */ - nsIContent* GetLeftmostChild(nsINode* aCurrentNode, - bool bNoBlockCrossing = false) const; - /** * Returns true if aNode is our root node. */ - bool IsRoot(nsINode* inNode) const; - bool IsEditorRoot(nsINode* aNode) const; + bool IsRoot(const nsINode* inNode) const; + bool IsEditorRoot(const nsINode* aNode) const; /** * Returns true if aNode is a descendant of our root node. */ - bool IsDescendantOfRoot(nsINode* inNode) const; - bool IsDescendantOfEditorRoot(nsINode* aNode) const; + bool IsDescendantOfRoot(const nsINode* inNode) const; + bool IsDescendantOfEditorRoot(const nsINode* aNode) const; /** * Counts number of editable child nodes. @@ -1757,16 +1743,6 @@ class EditorBase : public nsIEditor, */ nsresult CollapseSelectionToEnd(); - /** - * Helpers to add a node to the selection. - * Used by table cell selection methods. - */ - nsresult CreateRange(nsINode* aStartContainer, int32_t aStartOffset, - nsINode* aEndContainer, int32_t aEndOffset, - nsRange** aRange); - - static bool IsPreformatted(nsINode* aNode); - /** * AllowsTransactionsToChangeSelection() returns true if editor allows any * transactions to change Selection. Otherwise, transactions shouldn't @@ -2050,9 +2026,9 @@ class EditorBase : public nsIEditor, /** * Helper for GetPreviousNodeInternal() and GetNextNodeInternal(). */ - nsIContent* FindNextLeafNode(nsINode* aCurrentNode, bool aGoForward, + nsIContent* FindNextLeafNode(const nsINode* aCurrentNode, bool aGoForward, bool bNoBlockCrossing) const; - nsIContent* FindNode(nsINode* aCurrentNode, bool aGoForward, + nsIContent* FindNode(const nsINode* aCurrentNode, bool aGoForward, bool aEditableNode, bool aFindAnyDataNode, bool bNoBlockCrossing) const; @@ -2069,7 +2045,8 @@ class EditorBase : public nsIEditor, * aFindEditableNode is true. If there is no * previous node, returns nullptr. */ - nsIContent* GetPreviousNodeInternal(nsINode& aNode, bool aFindEditableNode, + nsIContent* GetPreviousNodeInternal(const nsINode& aNode, + bool aFindEditableNode, bool aFindAnyDataNode, bool aNoBlockCrossing) const; @@ -2094,9 +2071,9 @@ class EditorBase : public nsIEditor, * aFindEditableNode is true. If there is no * next node, returns nullptr. */ - nsIContent* GetNextNodeInternal(nsINode& aNode, bool aFindEditableNode, + nsIContent* GetNextNodeInternal(const nsINode& aNode, bool aFindEditableNode, bool aFindAnyDataNode, - bool bNoBlockCrossing) const; + bool aNoBlockCrossing) const; /** * And another version that takes a point in DOM tree rather than a node. diff --git a/editor/libeditor/EditorUtils.cpp b/editor/libeditor/EditorUtils.cpp index 278e4da50f..509658c7dd 100644 --- a/editor/libeditor/EditorUtils.cpp +++ b/editor/libeditor/EditorUtils.cpp @@ -2,22 +2,25 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "mozilla/EditorUtils.h" +#include "EditorUtils.h" +#include "mozilla/ComputedStyle.h" #include "mozilla/ContentIterator.h" #include "mozilla/EditorDOMPoint.h" #include "mozilla/OwningNonNull.h" #include "mozilla/TextEditor.h" +#include "mozilla/dom/Document.h" #include "mozilla/dom/HTMLBRElement.h" #include "mozilla/dom/Selection.h" #include "mozilla/dom/Text.h" #include "nsContentUtils.h" #include "nsComponentManagerUtils.h" +#include "nsComputedDOMStyle.h" #include "nsError.h" #include "nsIContent.h" -#include "mozilla/dom/Document.h" #include "nsIInterfaceRequestorUtils.h" #include "nsINode.h" +#include "nsStyleStruct.h" class nsISupports; class nsRange; @@ -229,4 +232,25 @@ void EditorUtils::MaskString(nsString& aString, Text* aText, } } +// static +bool EditorUtils::IsContentPreformatted(nsIContent& aContent) { + // Look at the node (and its parent if it's not an element), and grab its + // ComputedStyle. + Element* element = aContent.GetAsElementOrParentElement(); + if (!element) { + return false; + } + + RefPtr elementStyle = + nsComputedDOMStyle::GetComputedStyleNoFlush(element, nullptr); + if (!elementStyle) { + // Consider nodes without a ComputedStyle to be NOT preformatted: + // For instance, this is true of JS tags inside the body (which show + // up as #text nodes but have no ComputedStyle). + return false; + } + + return elementStyle->StyleText()->WhiteSpaceIsSignificant(); +} + } // namespace mozilla diff --git a/editor/libeditor/EditorUtils.h b/editor/libeditor/EditorUtils.h index c4eadc4710..a23d34f416 100644 --- a/editor/libeditor/EditorUtils.h +++ b/editor/libeditor/EditorUtils.h @@ -854,6 +854,12 @@ class EditorUtils final { !EditorUtils::IsPaddingBRElementForEmptyEditor(aContent); } + /** + * IsContentPreformatted() checks the style info for the node for the + * preformatted text style. This does NOT flush layout. + */ + static bool IsContentPreformatted(nsIContent& aContent); + /** * Helper method for `AppendString()` and `AppendSubString()`. This should * be called only when `aText` is in a password field. This method masks diff --git a/editor/libeditor/HTMLAbsPositionEditor.cpp b/editor/libeditor/HTMLAbsPositionEditor.cpp index e407d9abdc..99df8d7a41 100644 --- a/editor/libeditor/HTMLAbsPositionEditor.cpp +++ b/editor/libeditor/HTMLAbsPositionEditor.cpp @@ -80,7 +80,7 @@ HTMLEditor::GetAbsolutelyPositionedSelectionContainer() const { AutoTArray, 24> arrayOfParentElements; for (Element* element : - InclusiveAncestorsOfType(*selectionContainerElement)) { + selectionContainerElement->InclusiveAncestorsOfType()) { arrayOfParentElements.AppendElement(element); } diff --git a/editor/libeditor/HTMLEditSubActionHandler.cpp b/editor/libeditor/HTMLEditSubActionHandler.cpp index 81b3b3dea6..37f18f2353 100644 --- a/editor/libeditor/HTMLEditSubActionHandler.cpp +++ b/editor/libeditor/HTMLEditSubActionHandler.cpp @@ -67,6 +67,7 @@ namespace mozilla { using namespace dom; using StyleDifference = HTMLEditUtils::StyleDifference; +using ChildBlockBoundary = HTMLEditUtils::ChildBlockBoundary; enum { kLonely = 0, kPrevSib = 1, kNextSib = 2, kBothSibs = 3 }; @@ -97,14 +98,11 @@ static bool IsStyleCachePreservingSubAction(EditSubAction aEditSubAction) { } class MOZ_RAII AutoSetTemporaryAncestorLimiter final { - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER; public: - explicit AutoSetTemporaryAncestorLimiter( - HTMLEditor& aHTMLEditor, Selection& aSelection, - nsINode& aStartPointNode MOZ_GUARD_OBJECT_NOTIFIER_PARAM) { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - + explicit AutoSetTemporaryAncestorLimiter(HTMLEditor& aHTMLEditor, + Selection& aSelection, + nsINode& aStartPointNode) { MOZ_ASSERT(aSelection.GetType() == SelectionType::eNormal); if (aSelection.GetAncestorLimiter()) { @@ -1009,7 +1007,7 @@ AlignStateAtSelection::AlignStateAtSelection(HTMLEditor& aHTMLEditor, } for (nsIContent* containerContent : - InclusiveAncestorsOfType(*editTargetContent)) { + editTargetContent->InclusiveAncestorsOfType()) { // If the node is a parent `` element of edit target, let's break // here to materialize the 'inline-block' behaviour of html tables // regarding to text alignment. @@ -1453,7 +1451,8 @@ EditActionResult HTMLEditor::HandleInsertText( } EditorDOMPoint pointToInsert(firstRange->StartRef()); - if (NS_WARN_IF(!pointToInsert.IsSet())) { + if (NS_WARN_IF(!pointToInsert.IsSet()) || + NS_WARN_IF(!pointToInsert.IsInContentNode())) { return EditActionHandled(NS_ERROR_FAILURE); } MOZ_ASSERT(pointToInsert.IsSetAndValid()); @@ -1521,7 +1520,8 @@ EditActionResult HTMLEditor::HandleInsertText( // is our text going to be PREformatted? // We remember this so that we know how to handle tabs. - bool isPRE = EditorBase::IsPreformatted(pointToInsert.GetContainer()); + bool isPRE = + EditorUtils::IsContentPreformatted(*pointToInsert.ContainerAsContent()); // turn off the edit listener: we know how to // build the "doc changed range" ourselves, and it's @@ -7825,8 +7825,9 @@ nsresult HTMLEditor::MaybeExtendSelectionToHardLineEdgesForBlockEditAction() { // of going "down" into a block and "up" out of a block. if (wsScannerAtEnd.StartsFromOtherBlockElement()) { // endpoint is just after the close of a block. - nsINode* child = GetRightmostChild( - wsScannerAtEnd.StartReasonOtherBlockElementPtr(), true); + nsIContent* child = HTMLEditUtils::GetLastLeafChild( + *wsScannerAtEnd.StartReasonOtherBlockElementPtr(), + ChildBlockBoundary::TreatAsLeaf); if (child) { newEndPoint.SetAfter(child); } @@ -7853,8 +7854,9 @@ nsresult HTMLEditor::MaybeExtendSelectionToHardLineEdgesForBlockEditAction() { // of going "down" into a block and "up" out of a block. if (wsScannerAtStart.EndsByOtherBlockElement()) { // startpoint is just before the start of a block. - nsINode* child = GetLeftmostChild( - wsScannerAtStart.EndReasonOtherBlockElementPtr(), true); + nsINode* child = HTMLEditUtils::GetFirstLeafChild( + *wsScannerAtStart.EndReasonOtherBlockElementPtr(), + ChildBlockBoundary::TreatAsLeaf); if (child) { newStartPoint.Set(child); } @@ -8072,8 +8074,8 @@ EditorDOMPoint HTMLEditor::GetCurrentHardLineEndPoint( } // Check for newlines in pre-formatted text nodes. - if (EditorBase::IsPreformatted(nextEditableContent) && - nextEditableContent->IsText()) { + if (nextEditableContent->IsText() && + EditorUtils::IsContentPreformatted(*nextEditableContent)) { nsAutoString textContent; nextEditableContent->GetAsText()->GetData(textContent); int32_t newlinePos = textContent.FindChar(nsCRT::LF); @@ -8793,8 +8795,7 @@ nsIContent* HTMLEditor::GetMostAncestorInlineElement(nsINode& aNode) const { // Looks for the highest inline parent in the editing host. nsIContent* topMostInlineContent = aNode.AsContent(); - for (nsIContent* content : - InclusiveAncestorsOfType(*aNode.GetParent())) { + for (nsIContent* content : aNode.AncestorsOfType()) { if (content == host || !HTMLEditUtils::IsInlineElement(*content)) { break; } @@ -9237,7 +9238,8 @@ nsresult HTMLEditor::SplitParagraph( // selection to beginning of right hand para; // look inside any containers that are up front. - nsCOMPtr child = GetLeftmostChild(&aParentDivOrP, true); + nsCOMPtr child = HTMLEditUtils::GetFirstLeafChild( + aParentDivOrP, ChildBlockBoundary::TreatAsLeaf); if (child && (child->IsText() || HTMLEditUtils::IsContainerNode(*child))) { nsresult rv = CollapseSelectionToStartOf(*child); if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) { diff --git a/editor/libeditor/HTMLEditUtils.h b/editor/libeditor/HTMLEditUtils.h index b6ae52cafa..fd19902adf 100644 --- a/editor/libeditor/HTMLEditUtils.h +++ b/editor/libeditor/HTMLEditUtils.h @@ -193,6 +193,280 @@ class HTMLEditUtils final { static bool IsNonListSingleLineContainer(nsINode& aNode); static bool IsSingleLineContainer(nsINode& aNode); + /** + * GetLastLeafChild() returns rightmost leaf content in aNode. It depends on + * aChildBlockBoundary whether this scans into a block child or treat + * block as a leaf. + */ + enum class ChildBlockBoundary { + // Even if there is a child block, keep scanning a leaf content in it. + Ignore, + // If there is a child block, return it. + TreatAsLeaf, + }; + static nsIContent* GetLastLeafChild(nsINode& aNode, + ChildBlockBoundary aChildBlockBoundary) { + for (nsIContent* content = aNode.GetLastChild(); content; + content = content->GetLastChild()) { + if (aChildBlockBoundary == ChildBlockBoundary::TreatAsLeaf && + HTMLEditUtils::IsBlockElement(*content)) { + return content; + } + if (!content->HasChildren()) { + return content; + } + } + return nullptr; + } + + /** + * GetFirstLeafChild() returns leftmost leaf content in aNode. It depends on + * aChildBlockBoundary whether this scans into a block child or treat + * block as a leaf. + */ + static nsIContent* GetFirstLeafChild(nsINode& aNode, + ChildBlockBoundary aChildBlockBoundary) { + for (nsIContent* content = aNode.GetFirstChild(); content; + content = content->GetFirstChild()) { + if (aChildBlockBoundary == ChildBlockBoundary::TreatAsLeaf && + HTMLEditUtils::IsBlockElement(*content)) { + return content; + } + if (!content->HasChildren()) { + return content; + } + } + return nullptr; + } + + /** + * GetNextLeafContentOrNextBlockElement() returns next leaf content or + * next block element of aStartContent inside aAncestorLimiter. + * Note that the result may be a contet outside aCurrentBlock if + * aStartContent equals aCurrentBlock. + * + * @param aStartContent The start content to scan next content. + * @param aCurrentBlock Must be ancestor of aStartContent. Dispite + * the name, inline content is allowed if + * aStartContent is in an inline editing host. + * @param aAncestorLimiter Optional, setting this guarantees the + * result is in aAncestorLimiter unless + * aStartContent is not a descendant of this. + */ + static nsIContent* GetNextLeafContentOrNextBlockElement( + nsIContent& aStartContent, nsIContent& aCurrentBlock, + Element* aAncestorLimiter = nullptr) { + if (&aStartContent == aAncestorLimiter) { + return nullptr; + } + + nsIContent* nextContent = aStartContent.GetNextSibling(); + if (!nextContent) { + if (!aStartContent.GetParentElement()) { + NS_WARNING("Reached orphan node while climbing up the DOM tree"); + return nullptr; + } + for (Element* parentElement : aStartContent.AncestorsOfType()) { + if (parentElement == &aCurrentBlock) { + return nullptr; + } + if (parentElement == aAncestorLimiter) { + NS_WARNING("Reached editing host while climbing up the DOM tree"); + return nullptr; + } + nextContent = parentElement->GetNextSibling(); + if (nextContent) { + break; + } + if (!parentElement->GetParentElement()) { + NS_WARNING("Reached orphan node while climbing up the DOM tree"); + return nullptr; + } + } + MOZ_ASSERT(nextContent); + } + + // We have a next content. If it's a block, return it. + if (HTMLEditUtils::IsBlockElement(*nextContent)) { + return nextContent; + } + if (HTMLEditUtils::IsContainerNode(*nextContent)) { + // Else if it's a container, get deep leftmost child + if (nsIContent* child = HTMLEditUtils::GetFirstLeafChild( + *nextContent, ChildBlockBoundary::Ignore)) { + return child; + } + } + // Else return the next content itself. + return nextContent; + } + + /** + * Similar to the above method, but take a DOM point to specify scan start + * point. + */ + template + static nsIContent* GetNextLeafContentOrNextBlockElement( + const EditorDOMPointBase& aStartPoint, nsIContent& aCurrentBlock, + Element* aAncestorLimiter = nullptr) { + MOZ_ASSERT(aStartPoint.IsSet()); + + if (!aStartPoint.IsInContentNode()) { + return nullptr; + } + if (aStartPoint.IsInTextNode()) { + return HTMLEditUtils::GetNextLeafContentOrNextBlockElement( + *aStartPoint.ContainerAsText(), aCurrentBlock, aAncestorLimiter); + } + if (!HTMLEditUtils::IsContainerNode(*aStartPoint.ContainerAsContent())) { + return HTMLEditUtils::GetNextLeafContentOrNextBlockElement( + *aStartPoint.ContainerAsContent(), aCurrentBlock, aAncestorLimiter); + } + + nsCOMPtr nextContent = aStartPoint.GetChild(); + if (!nextContent) { + if (aStartPoint.GetContainer() == &aCurrentBlock) { + // We are at end of the block. + return nullptr; + } + + // We are at end of non-block container + return HTMLEditUtils::GetNextLeafContentOrNextBlockElement( + *aStartPoint.ContainerAsContent(), aCurrentBlock, aAncestorLimiter); + } + + // We have a next node. If it's a block, return it. + if (HTMLEditUtils::IsBlockElement(*nextContent)) { + return nextContent; + } + if (HTMLEditUtils::IsContainerNode(*nextContent)) { + // else if it's a container, get deep leftmost child + if (nsIContent* child = HTMLEditUtils::GetFirstLeafChild( + *nextContent, ChildBlockBoundary::Ignore)) { + return child; + } + } + // Else return the node itself + return nextContent; + } + + /** + * GetPreviousLeafContentOrPreviousBlockElement() returns previous leaf + * content or previous block element of aStartContent inside + * aAncestorLimiter. + * Note that the result may be a contet outside aCurrentBlock if + * aStartContent equals aCurrentBlock. + * + * @param aStartContent The start content to scan previous content. + * @param aCurrentBlock Must be ancestor of aStartContent. Dispite + * the name, inline content is allowed if + * aStartContent is in an inline editing host. + * @param aAncestorLimiter Optional, setting this guarantees the + * result is in aAncestorLimiter unless + * aStartContent is not a descendant of this. + */ + static nsIContent* GetPreviousLeafContentOrPreviousBlockElement( + nsIContent& aStartContent, nsIContent& aCurrentBlock, + Element* aAncestorLimiter = nullptr) { + if (&aStartContent == aAncestorLimiter) { + return nullptr; + } + + nsIContent* previousContent = aStartContent.GetPreviousSibling(); + if (!previousContent) { + if (!aStartContent.GetParentElement()) { + NS_WARNING("Reached orphan node while climbing up the DOM tree"); + return nullptr; + } + for (Element* parentElement : aStartContent.AncestorsOfType()) { + if (parentElement == &aCurrentBlock) { + return nullptr; + } + if (parentElement == aAncestorLimiter) { + NS_WARNING("Reached editing host while climbing up the DOM tree"); + return nullptr; + } + previousContent = parentElement->GetPreviousSibling(); + if (previousContent) { + break; + } + if (!parentElement->GetParentElement()) { + NS_WARNING("Reached orphan node while climbing up the DOM tree"); + return nullptr; + } + } + MOZ_ASSERT(previousContent); + } + + // We have a next content. If it's a block, return it. + if (HTMLEditUtils::IsBlockElement(*previousContent)) { + return previousContent; + } + if (HTMLEditUtils::IsContainerNode(*previousContent)) { + // Else if it's a container, get deep rightmost child + if (nsIContent* child = HTMLEditUtils::GetLastLeafChild( + *previousContent, ChildBlockBoundary::Ignore)) { + return child; + } + } + // Else return the next content itself. + return previousContent; + } + + /** + * Similar to the above method, but take a DOM point to specify scan start + * point. + */ + template + static nsIContent* GetPreviousLeafContentOrPreviousBlockElement( + const EditorDOMPointBase& aStartPoint, nsIContent& aCurrentBlock, + Element* aAncestorLimiter = nullptr) { + MOZ_ASSERT(aStartPoint.IsSet()); + + if (!aStartPoint.IsInContentNode()) { + return nullptr; + } + if (aStartPoint.IsInTextNode()) { + return HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement( + *aStartPoint.ContainerAsText(), aCurrentBlock, aAncestorLimiter); + } + if (!HTMLEditUtils::IsContainerNode(*aStartPoint.ContainerAsContent())) { + return HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement( + *aStartPoint.ContainerAsContent(), aCurrentBlock, aAncestorLimiter); + } + + if (aStartPoint.IsStartOfContainer()) { + if (aStartPoint.GetContainer() == &aCurrentBlock) { + // We are at start of the block. + return nullptr; + } + + // We are at start of non-block container + return HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement( + *aStartPoint.ContainerAsContent(), aCurrentBlock, aAncestorLimiter); + } + + nsCOMPtr previousContent = + aStartPoint.GetPreviousSiblingOfChild(); + if (NS_WARN_IF(!previousContent)) { + return nullptr; + } + + // We have a prior node. If it's a block, return it. + if (HTMLEditUtils::IsBlockElement(*previousContent)) { + return previousContent; + } + if (HTMLEditUtils::IsContainerNode(*previousContent)) { + // Else if it's a container, get deep rightmost child + if (nsIContent* child = HTMLEditUtils::GetLastLeafChild( + *previousContent, ChildBlockBoundary::Ignore)) { + return child; + } + } + // Else return the node itself + return previousContent; + } + /** * GetAncestorBlockElement() returns parent or nearest ancestor of aContent * which is a block element. If aAncestorLimiter is not nullptr, @@ -209,12 +483,7 @@ class HTMLEditUtils final { return nullptr; } - if (!aContent.GetParent()) { - return nullptr; - } - - for (Element* element : dom::InclusiveAncestorsOfType( - const_cast(*aContent.GetParent()))) { + for (Element* element : aContent.AncestorsOfType()) { if (HTMLEditUtils::IsBlockElement(*element)) { return element; } @@ -257,8 +526,7 @@ class HTMLEditUtils final { if (!aContent.GetParent()) { return nullptr; } - for (Element* element : dom::InclusiveAncestorsOfType( - const_cast(aContent))) { + for (Element* element : aContent.InclusiveAncestorsOfType()) { if (HTMLEditUtils::IsTable(element)) { return element; } diff --git a/editor/libeditor/HTMLEditor.cpp b/editor/libeditor/HTMLEditor.cpp index 932e70311f..e96a02097b 100644 --- a/editor/libeditor/HTMLEditor.cpp +++ b/editor/libeditor/HTMLEditor.cpp @@ -63,6 +63,8 @@ namespace mozilla { using namespace dom; using namespace widget; +using ChildBlockBoundary = HTMLEditUtils::ChildBlockBoundary; + const char16_t kNBSP = 160; // Some utilities to handle overloading of "A" tag for link and named anchor. @@ -855,7 +857,7 @@ void HTMLEditor::IsPrevCharInNodeWhitespace(nsIContent* aContent, } } -bool HTMLEditor::IsVisibleBRElement(nsINode* aNode) { +bool HTMLEditor::IsVisibleBRElement(const nsINode* aNode) { MOZ_ASSERT(aNode); if (!aNode->IsHTMLElement(nsGkAtoms::br)) { return false; @@ -2519,7 +2521,7 @@ Element* HTMLEditor::GetInclusiveAncestorByTagNameInternal( bool lookForLink = IsLinkTag(aTagName); bool lookForNamedAnchor = IsNamedAnchorTag(aTagName); - for (Element* element : InclusiveAncestorsOfType(*currentElement)) { + for (Element* element : currentElement->InclusiveAncestorsOfType()) { // Stop searching if parent is a body element. Note: Originally used // IsRoot() to/ stop at table cells, but that's too messy when you are // trying to find the parent table. @@ -2739,9 +2741,21 @@ already_AddRefed HTMLEditor::GetSelectedElement(const nsAtom* aTagName, // -

[def}

// Note that we don't need special handling for because double // clicking it selects the element and we use the first path to handle it. - if (lastElementInRange->GetNextSibling() && - lastElementInRange->GetNextSibling()->IsHTMLElement(nsGkAtoms::br)) { - return nullptr; + // Additionally, we have this case too: + // -

[def}

+ // In these cases, the
element is not listed up by PostContentIterator. + // So, we should return nullptr if next sibling is a `
` element or + // next sibling starts with `
` element. + if (nsIContent* nextSibling = lastElementInRange->GetNextSibling()) { + if (nextSibling->IsHTMLElement(nsGkAtoms::br)) { + return nullptr; + } + nsIContent* firstEditableLeaf = HTMLEditUtils::GetFirstLeafChild( + *nextSibling, ChildBlockBoundary::Ignore); + if (firstEditableLeaf && + firstEditableLeaf->IsHTMLElement(nsGkAtoms::br)) { + return nullptr; + } } if (!aTagName) { @@ -3057,8 +3071,7 @@ nsresult HTMLEditor::RemoveEmptyInclusiveAncestorInlineElements( } OwningNonNull content = aContent; - for (nsIContent* parentContent : - InclusiveAncestorsOfType(*aContent.GetParent())) { + for (nsIContent* parentContent : aContent.AncestorsOfType()) { if (HTMLEditUtils::IsBlockElement(*parentContent) || parentContent->Length() != 1 || !HTMLEditUtils::IsSimplyEditableNode(*parentContent) || @@ -4836,7 +4849,7 @@ nsIContent* HTMLEditor::GetNextHTMLSibling(nsINode* aNode, } nsIContent* HTMLEditor::GetPreviousHTMLElementOrTextInternal( - nsINode& aNode, bool aNoBlockCrossing) const { + const nsINode& aNode, bool aNoBlockCrossing) const { if (NS_WARN_IF(!GetActiveEditingHost())) { return nullptr; } @@ -4874,7 +4887,7 @@ nsIContent* HTMLEditor::GetPreviousEditableHTMLNodeInternal( } nsIContent* HTMLEditor::GetNextHTMLElementOrTextInternal( - nsINode& aNode, bool aNoBlockCrossing) const { + const nsINode& aNode, bool aNoBlockCrossing) const { if (NS_WARN_IF(!GetActiveEditingHost())) { return nullptr; } @@ -4948,7 +4961,8 @@ nsIContent* HTMLEditor::GetLastEditableChild(nsINode& aNode) const { } nsIContent* HTMLEditor::GetFirstEditableLeaf(nsINode& aNode) const { - nsIContent* child = GetLeftmostChild(&aNode); + nsIContent* child = + HTMLEditUtils::GetFirstLeafChild(aNode, ChildBlockBoundary::Ignore); while (child && (!EditorUtils::IsEditableContent(*child, EditorType::HTML) || child->HasChildren())) { child = GetNextEditableHTMLNode(*child); @@ -4963,7 +4977,8 @@ nsIContent* HTMLEditor::GetFirstEditableLeaf(nsINode& aNode) const { } nsIContent* HTMLEditor::GetLastEditableLeaf(nsINode& aNode) const { - nsCOMPtr child = GetRightmostChild(&aNode, false); + nsIContent* child = + HTMLEditUtils::GetLastLeafChild(aNode, ChildBlockBoundary::Ignore); while (child && (!EditorUtils::IsEditableContent(*child, EditorType::HTML) || child->HasChildren())) { child = GetPreviousEditableHTMLNode(*child); diff --git a/editor/libeditor/HTMLEditor.h b/editor/libeditor/HTMLEditor.h index a2655ef5b7..1774ab6e8a 100644 --- a/editor/libeditor/HTMLEditor.h +++ b/editor/libeditor/HTMLEditor.h @@ -919,7 +919,7 @@ class HTMLEditor final : public TextEditor, /** * Small utility routine to test if a break node is visible to user. */ - bool IsVisibleBRElement(nsINode* aNode); + bool IsVisibleBRElement(const nsINode* aNode); /** * Helper routines for font size changing. @@ -999,10 +999,10 @@ class HTMLEditor final : public TextEditor, * EditorBase::GetPreviousElementOrText*() but this won't return nodes * outside active editing host. */ - nsIContent* GetPreviousHTMLElementOrText(nsINode& aNode) const { + nsIContent* GetPreviousHTMLElementOrText(const nsINode& aNode) const { return GetPreviousHTMLElementOrTextInternal(aNode, false); } - nsIContent* GetPreviousHTMLElementOrTextInBlock(nsINode& aNode) const { + nsIContent* GetPreviousHTMLElementOrTextInBlock(const nsINode& aNode) const { return GetPreviousHTMLElementOrTextInternal(aNode, true); } template @@ -1020,7 +1020,7 @@ class HTMLEditor final : public TextEditor, * GetPreviousHTMLElementOrTextInternal() methods are common implementation * of above methods. Please don't use this method directly. */ - nsIContent* GetPreviousHTMLElementOrTextInternal(nsINode& aNode, + nsIContent* GetPreviousHTMLElementOrTextInternal(const nsINode& aNode, bool aNoBlockCrossing) const; template nsIContent* GetPreviousHTMLElementOrTextInternal( @@ -1068,10 +1068,10 @@ class HTMLEditor final : public TextEditor, * On the other hand, methods which take |nsINode&| start to search from * next node of aNode. */ - nsIContent* GetNextHTMLElementOrText(nsINode& aNode) const { + nsIContent* GetNextHTMLElementOrText(const nsINode& aNode) const { return GetNextHTMLElementOrTextInternal(aNode, false); } - nsIContent* GetNextHTMLElementOrTextInBlock(nsINode& aNode) const { + nsIContent* GetNextHTMLElementOrTextInBlock(const nsINode& aNode) const { return GetNextHTMLElementOrTextInternal(aNode, true); } template @@ -1089,7 +1089,7 @@ class HTMLEditor final : public TextEditor, * GetNextHTMLNodeInternal() methods are common implementation * of above methods. Please don't use this method directly. */ - nsIContent* GetNextHTMLElementOrTextInternal(nsINode& aNode, + nsIContent* GetNextHTMLElementOrTextInternal(const nsINode& aNode, bool aNoBlockCrossing) const; template nsIContent* GetNextHTMLElementOrTextInternal( diff --git a/editor/libeditor/HTMLStyleEditor.cpp b/editor/libeditor/HTMLStyleEditor.cpp index 6900ea9fdc..02087e22ec 100644 --- a/editor/libeditor/HTMLStyleEditor.cpp +++ b/editor/libeditor/HTMLStyleEditor.cpp @@ -45,6 +45,8 @@ namespace mozilla { using namespace dom; +using ChildBlockBoundary = HTMLEditUtils::ChildBlockBoundary; + nsresult HTMLEditor::SetInlinePropertyAsAction(nsAtom& aProperty, nsAtom* aAttribute, const nsAString& aValue, @@ -757,7 +759,7 @@ SplitNodeResult HTMLEditor::SplitAncestorStyledInlineElementsAt( AutoTArray, 24> arrayOfParents; for (nsIContent* content : - InclusiveAncestorsOfType(*aPointToSplit.GetContainer())) { + aPointToSplit.GetContainer()->InclusiveAncestorsOfType()) { if (HTMLEditUtils::IsBlockElement(*content) || !content->GetParent() || !EditorUtils::IsEditableContent(*content->GetParent(), EditorType::HTML)) { @@ -882,10 +884,10 @@ EditResult HTMLEditor::ClearStyleAt(const EditorDOMPoint& aPoint, // the next node. The first example should become // `

abc

`. // ^^^^^^^^^^^^^^ - nsIContent* leftmostChildOfNextNode = - GetLeftmostChild(splitResult.GetNextNode()); - EditorDOMPoint atStartOfNextNode(leftmostChildOfNextNode - ? leftmostChildOfNextNode + nsIContent* firstLeafChildOfNextNode = HTMLEditUtils::GetFirstLeafChild( + *splitResult.GetNextNode(), ChildBlockBoundary::Ignore); + EditorDOMPoint atStartOfNextNode(firstLeafChildOfNextNode + ? firstLeafChildOfNextNode : splitResult.GetNextNode(), 0); RefPtr brElement; @@ -940,11 +942,13 @@ EditResult HTMLEditor::ClearStyleAt(const EditorDOMPoint& aPoint, // Now, we want to put `
` element into the empty split node if // it was in next node of the first split. // E.g., `

a
bc

` - nsIContent* leftmostChild = - GetLeftmostChild(splitResultAtStartOfNextNode.GetPreviousNode()); + nsIContent* firstLeafChildOfPreviousNode = HTMLEditUtils::GetFirstLeafChild( + *splitResultAtStartOfNextNode.GetPreviousNode(), + ChildBlockBoundary::Ignore); EditorDOMPoint pointToPutCaret( - leftmostChild ? leftmostChild - : splitResultAtStartOfNextNode.GetPreviousNode(), + firstLeafChildOfPreviousNode + ? firstLeafChildOfPreviousNode + : splitResultAtStartOfNextNode.GetPreviousNode(), 0); // If the right node starts with a `
`, suck it out of right node and into // the left node left node. This is so we you don't revert back to the @@ -1184,7 +1188,7 @@ nsresult HTMLEditor::PromoteRangeIfStartsOrEndsInNamedAnchor(nsRange& aRange) { } EditorRawDOMPoint newRangeStart(aRange.StartRef()); for (Element* element : - InclusiveAncestorsOfType(*aRange.GetStartContainer())) { + aRange.GetStartContainer()->InclusiveAncestorsOfType()) { if (element->IsHTMLElement(nsGkAtoms::body)) { break; } @@ -1204,7 +1208,7 @@ nsresult HTMLEditor::PromoteRangeIfStartsOrEndsInNamedAnchor(nsRange& aRange) { EditorRawDOMPoint newRangeEnd(aRange.EndRef()); for (Element* element : - InclusiveAncestorsOfType(*aRange.GetEndContainer())) { + aRange.GetEndContainer()->InclusiveAncestorsOfType()) { if (element->IsHTMLElement(nsGkAtoms::body)) { break; } @@ -1239,7 +1243,7 @@ nsresult HTMLEditor::PromoteInlineRange(nsRange& aRange) { } EditorRawDOMPoint newRangeStart(aRange.StartRef()); for (nsIContent* content : - InclusiveAncestorsOfType(*aRange.GetStartContainer())) { + aRange.GetStartContainer()->InclusiveAncestorsOfType()) { MOZ_ASSERT(newRangeStart.GetContainer() == content); if (content->IsHTMLElement(nsGkAtoms::body) || !EditorUtils::IsEditableContent(*content, EditorType::HTML) || @@ -1257,7 +1261,7 @@ nsresult HTMLEditor::PromoteInlineRange(nsRange& aRange) { EditorRawDOMPoint newRangeEnd(aRange.EndRef()); for (nsIContent* content : - InclusiveAncestorsOfType(*aRange.GetEndContainer())) { + aRange.GetEndContainer()->InclusiveAncestorsOfType()) { MOZ_ASSERT(newRangeEnd.GetContainer() == content); if (content->IsHTMLElement(nsGkAtoms::body) || !EditorUtils::IsEditableContent(*content, EditorType::HTML) || diff --git a/editor/libeditor/TextEditor.cpp b/editor/libeditor/TextEditor.cpp index b6e4945069..59dd254a04 100644 --- a/editor/libeditor/TextEditor.cpp +++ b/editor/libeditor/TextEditor.cpp @@ -7,6 +7,7 @@ #include #include "EditAggregateTransaction.h" +#include "HTMLEditUtils.h" #include "InternetCiter.h" #include "PlaceholderTransaction.h" #include "gfxFontUtils.h" @@ -68,6 +69,8 @@ namespace mozilla { using namespace dom; +using ChildBlockBoundary = HTMLEditUtils::ChildBlockBoundary; + TextEditor::TextEditor() : mMaxTextLength(-1), mUnmaskedStart(UINT32_MAX), @@ -1004,11 +1007,12 @@ nsresult TextEditor::UndoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) { // at redo, or doing it everywhere else that might care. Since undo // and redo are relatively rare, it makes sense to take the (small) // performance hit here. - nsIContent* leftMostChild = GetLeftmostChild(mRootElement); - if (leftMostChild && - EditorUtils::IsPaddingBRElementForEmptyEditor(*leftMostChild)) { + nsIContent* firstLeafChild = HTMLEditUtils::GetFirstLeafChild( + *mRootElement, ChildBlockBoundary::Ignore); + if (firstLeafChild && + EditorUtils::IsPaddingBRElementForEmptyEditor(*firstLeafChild)) { mPaddingBRElementForEmptyEditor = - static_cast(leftMostChild); + static_cast(firstLeafChild); } else { mPaddingBRElementForEmptyEditor = nullptr; } diff --git a/editor/libeditor/TypeInState.cpp b/editor/libeditor/TypeInState.cpp index c51eff0673..8e4a170849 100644 --- a/editor/libeditor/TypeInState.cpp +++ b/editor/libeditor/TypeInState.cpp @@ -206,15 +206,9 @@ void TypeInState::ClearProp(nsAtom* aProp, nsAtom* aAttr) { * Caller assumes ownership of PropItem and must delete it. */ UniquePtr TypeInState::TakeClearProperty() { - size_t count = mClearedArray.Length(); - if (!count) { - return nullptr; - } - - --count; // indices are zero based - PropItem* propItem = mClearedArray[count]; - mClearedArray.RemoveElementAt(count); - return UniquePtr(propItem); + return mClearedArray.Length() + ? UniquePtr{mClearedArray.PopLastElement()} + : nullptr; } /** @@ -222,14 +216,8 @@ UniquePtr TypeInState::TakeClearProperty() { * Caller assumes ownership of PropItem and must delete it. */ UniquePtr TypeInState::TakeSetProperty() { - size_t count = mSetArray.Length(); - if (!count) { - return nullptr; - } - count--; // indices are zero based - PropItem* propItem = mSetArray[count]; - mSetArray.RemoveElementAt(count); - return UniquePtr(propItem); + return mSetArray.Length() ? UniquePtr{mSetArray.PopLastElement()} + : nullptr; } /** diff --git a/editor/libeditor/WSRunObject.cpp b/editor/libeditor/WSRunObject.cpp index 16011dbd23..389fd42371 100644 --- a/editor/libeditor/WSRunObject.cpp +++ b/editor/libeditor/WSRunObject.cpp @@ -32,6 +32,8 @@ namespace mozilla { using namespace dom; +using ChildBlockBoundary = HTMLEditUtils::ChildBlockBoundary; + const char16_t kNBSP = 160; template WSRunScanner::WSRunScanner(const HTMLEditor* aHTMLEditor, @@ -674,7 +676,7 @@ nsIContent* WSRunScanner::GetEditableBlockParentOrTopmotEditableInlineContent( // it's not collapsed only when inserting composition string so that // it's possible but shouldn't occur actually. nsIContent* editableBlockParentOrTopmotEditableInlineContent = nullptr; - for (nsIContent* content : InclusiveAncestorsOfType(*aContent)) { + for (nsIContent* content : aContent->InclusiveAncestorsOfType()) { if (!EditorUtils::IsEditableContent(*content, EditorType::HTML)) { break; } @@ -743,16 +745,19 @@ nsresult WSRunScanner::GetWSNodes() { while (!mStartNode) { // we haven't found the start of ws yet. Keep looking - nsCOMPtr priorNode = GetPreviousWSNode( - start, editableBlockParentOrTopmotEditableInlineContent); - if (priorNode) { - if (HTMLEditUtils::IsBlockElement(*priorNode)) { + nsIContent* previousLeafContentOrBlock = + HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement( + start, *editableBlockParentOrTopmotEditableInlineContent, + mEditingHost); + if (previousLeafContentOrBlock) { + if (HTMLEditUtils::IsBlockElement(*previousLeafContentOrBlock)) { mStartNode = start.GetContainer(); mStartOffset = start.Offset(); mStartReason = WSType::OtherBlockBoundary; - mStartReasonContent = priorNode; - } else if (priorNode->IsText() && priorNode->IsEditable()) { - RefPtr textNode = priorNode->AsText(); + mStartReasonContent = previousLeafContentOrBlock; + } else if (previousLeafContentOrBlock->IsText() && + previousLeafContentOrBlock->IsEditable()) { + RefPtr textNode = previousLeafContentOrBlock->AsText(); mNodeArray.InsertElementAt(0, textNode); const nsTextFragment* textFrag = &textNode->TextFragment(); uint32_t len = textNode->TextLength(); @@ -760,7 +765,7 @@ nsresult WSRunScanner::GetWSNodes() { if (len < 1) { // Zero length text node. Set start point to it // so we can get past it! - start.Set(priorNode, 0); + start.Set(previousLeafContentOrBlock, 0); } else { for (int32_t pos = len - 1; pos >= 0; pos--) { // sanity bounds check the char position. bug 136165 @@ -794,12 +799,12 @@ nsresult WSRunScanner::GetWSNodes() { // not a break but still serves as a terminator to ws runs. mStartNode = start.GetContainer(); mStartOffset = start.Offset(); - if (priorNode->IsHTMLElement(nsGkAtoms::br)) { + if (previousLeafContentOrBlock->IsHTMLElement(nsGkAtoms::br)) { mStartReason = WSType::BRElement; } else { mStartReason = WSType::SpecialContent; } - mStartReasonContent = priorNode; + mStartReasonContent = previousLeafContentOrBlock; } } else { // no prior node means we exhausted @@ -849,17 +854,20 @@ nsresult WSRunScanner::GetWSNodes() { while (!mEndNode) { // we haven't found the end of ws yet. Keep looking - nsCOMPtr nextNode = - GetNextWSNode(end, editableBlockParentOrTopmotEditableInlineContent); - if (nextNode) { - if (HTMLEditUtils::IsBlockElement(*nextNode)) { + nsIContent* nextLeafContentOrBlock = + HTMLEditUtils::GetNextLeafContentOrNextBlockElement( + end, *editableBlockParentOrTopmotEditableInlineContent, + mEditingHost); + if (nextLeafContentOrBlock) { + if (HTMLEditUtils::IsBlockElement(*nextLeafContentOrBlock)) { // we encountered a new block. therefore no more ws. mEndNode = end.GetContainer(); mEndOffset = end.Offset(); mEndReason = WSType::OtherBlockBoundary; - mEndReasonContent = nextNode; - } else if (nextNode->IsText() && nextNode->IsEditable()) { - RefPtr textNode = nextNode->AsText(); + mEndReasonContent = nextLeafContentOrBlock; + } else if (nextLeafContentOrBlock->IsText() && + nextLeafContentOrBlock->IsEditable()) { + RefPtr textNode = nextLeafContentOrBlock->AsText(); mNodeArray.AppendElement(textNode); const nsTextFragment* textFrag = &textNode->TextFragment(); uint32_t len = textNode->TextLength(); @@ -902,12 +910,12 @@ nsresult WSRunScanner::GetWSNodes() { // serves as a terminator to ws runs. mEndNode = end.GetContainer(); mEndOffset = end.Offset(); - if (nextNode->IsHTMLElement(nsGkAtoms::br)) { + if (nextLeafContentOrBlock->IsHTMLElement(nsGkAtoms::br)) { mEndReason = WSType::BRElement; } else { mEndReason = WSType::SpecialContent; } - mEndReasonContent = nextNode; + mEndReasonContent = nextLeafContentOrBlock; } } else { // no next node means we exhausted @@ -931,7 +939,9 @@ void WSRunScanner::GetRuns() { // the scan range isn't in preformatted element, we need to check only the // style at mScanStartPoint since the range would be replaced and the start // style will be applied to all new string. - mPRE = EditorBase::IsPreformatted(mScanStartPoint.GetContainer()); + mPRE = + mScanStartPoint.IsInContentNode() && + EditorUtils::IsContentPreformatted(*mScanStartPoint.ContainerAsContent()); // if it's preformatedd, or if we are surrounded by text or special, it's all // one big normal ws run if (mPRE || @@ -1085,212 +1095,6 @@ void WSRunScanner::InitializeWithSingleFragment( mEndRun = mStartRun; } -nsIContent* WSRunScanner::GetPreviousWSNodeInner(nsINode* aStartNode, - nsINode* aBlockParent) const { - // Can't really recycle various getnext/prior routines because we have - // special needs here. Need to step into inline containers but not block - // containers. - MOZ_ASSERT(aStartNode && aBlockParent); - - if (aStartNode == mEditingHost) { - NS_WARNING( - "WSRunScanner::GetPreviousWSNodeInner() was called with editing host"); - return nullptr; - } - - nsCOMPtr previousContent = aStartNode->GetPreviousSibling(); - OwningNonNull curNode = *aStartNode; - while (!previousContent) { - // We have exhausted nodes in parent of aStartNode. - nsCOMPtr curParent = curNode->GetParentNode(); - if (!curParent) { - NS_WARNING("Reached orphan node while climbing up the DOM tree"); - return nullptr; - } - if (curParent == aBlockParent) { - // We have exhausted nodes in the block parent. The convention here is - // to return null. - return nullptr; - } - if (curParent == mEditingHost) { - NS_WARNING("Reached editing host while climbing up the DOM tree"); - return nullptr; - } - // We have a parent: look for previous sibling - previousContent = curParent->GetPreviousSibling(); - curNode = curParent; - } - - if (!previousContent) { - return nullptr; - } - - // We have a prior node. If it's a block, return it. - if (HTMLEditUtils::IsBlockElement(*previousContent)) { - return previousContent; - } - if (HTMLEditUtils::IsContainerNode(*previousContent)) { - // Else if it's a container, get deep rightmost child - nsCOMPtr child = - mHTMLEditor->GetRightmostChild(previousContent); - if (child) { - return child; - } - } - // Else return the node itself - return previousContent; -} - -nsIContent* WSRunScanner::GetPreviousWSNode(const EditorDOMPoint& aPoint, - nsINode* aBlockParent) const { - // Can't really recycle various getnext/prior routines because we - // have special needs here. Need to step into inline containers but - // not block containers. - MOZ_ASSERT(aPoint.IsSet() && aBlockParent); - - if (aPoint.IsInTextNode()) { - return GetPreviousWSNodeInner(aPoint.GetContainer(), aBlockParent); - } - if (!aPoint.IsInContentNode() || - !HTMLEditUtils::IsContainerNode(*aPoint.ContainerAsContent())) { - return GetPreviousWSNodeInner(aPoint.GetContainer(), aBlockParent); - } - - if (!aPoint.Offset()) { - if (aPoint.GetContainer() == aBlockParent) { - // We are at start of the block. - return nullptr; - } - - // We are at start of non-block container - return GetPreviousWSNodeInner(aPoint.GetContainer(), aBlockParent); - } - - if (NS_WARN_IF(!aPoint.IsInContentNode())) { - return nullptr; - } - - nsCOMPtr previousContent = aPoint.GetPreviousSiblingOfChild(); - if (NS_WARN_IF(!previousContent)) { - return nullptr; - } - - // We have a prior node. If it's a block, return it. - if (HTMLEditUtils::IsBlockElement(*previousContent)) { - return previousContent; - } - if (HTMLEditUtils::IsContainerNode(*previousContent)) { - // Else if it's a container, get deep rightmost child - nsCOMPtr child = - mHTMLEditor->GetRightmostChild(previousContent); - if (child) { - return child; - } - } - // Else return the node itself - return previousContent; -} - -nsIContent* WSRunScanner::GetNextWSNodeInner(nsINode* aStartNode, - nsINode* aBlockParent) const { - // Can't really recycle various getnext/prior routines because we have - // special needs here. Need to step into inline containers but not block - // containers. - MOZ_ASSERT(aStartNode && aBlockParent); - - if (aStartNode == mEditingHost) { - NS_WARNING( - "WSRunScanner::GetNextWSNodeInner() was called with editing host"); - return nullptr; - } - - nsCOMPtr nextContent = aStartNode->GetNextSibling(); - nsCOMPtr curNode = aStartNode; - while (!nextContent) { - // We have exhausted nodes in parent of aStartNode. - nsCOMPtr curParent = curNode->GetParentNode(); - if (!curParent) { - NS_WARNING("Reached orphan node while climbing up the DOM tree"); - return nullptr; - } - if (curParent == aBlockParent) { - // We have exhausted nodes in the block parent. The convention here is - // to return null. - return nullptr; - } - if (curParent == mEditingHost) { - NS_WARNING("Reached editing host while climbing up the DOM tree"); - return nullptr; - } - // We have a parent: look for next sibling - nextContent = curParent->GetNextSibling(); - curNode = curParent; - } - - if (!nextContent) { - return nullptr; - } - - // We have a next node. If it's a block, return it. - if (HTMLEditUtils::IsBlockElement(*nextContent)) { - return nextContent; - } - if (HTMLEditUtils::IsContainerNode(*nextContent)) { - // Else if it's a container, get deep leftmost child - nsCOMPtr child = mHTMLEditor->GetLeftmostChild(nextContent); - if (child) { - return child; - } - } - // Else return the node itself - return nextContent; -} - -nsIContent* WSRunScanner::GetNextWSNode(const EditorDOMPoint& aPoint, - nsINode* aBlockParent) const { - // Can't really recycle various getnext/prior routines because we have - // special needs here. Need to step into inline containers but not block - // containers. - MOZ_ASSERT(aPoint.IsSet() && aBlockParent); - - if (aPoint.IsInTextNode()) { - return GetNextWSNodeInner(aPoint.GetContainer(), aBlockParent); - } - if (!aPoint.IsInContentNode() || - !HTMLEditUtils::IsContainerNode(*aPoint.ContainerAsContent())) { - return GetNextWSNodeInner(aPoint.GetContainer(), aBlockParent); - } - - if (NS_WARN_IF(!aPoint.IsInContentNode())) { - return nullptr; - } - - nsCOMPtr nextContent = aPoint.GetChild(); - if (!nextContent) { - if (aPoint.GetContainer() == aBlockParent) { - // We are at end of the block. - return nullptr; - } - - // We are at end of non-block container - return GetNextWSNodeInner(aPoint.GetContainer(), aBlockParent); - } - - // We have a next node. If it's a block, return it. - if (HTMLEditUtils::IsBlockElement(*nextContent)) { - return nextContent; - } - if (HTMLEditUtils::IsContainerNode(*nextContent)) { - // else if it's a container, get deep leftmost child - nsCOMPtr child = mHTMLEditor->GetLeftmostChild(nextContent); - if (child) { - return child; - } - } - // Else return the node itself - return nextContent; -} - nsresult WSRunObject::PrepareToDeleteRangePriv(WSRunObject* aEndObject) { // this routine adjust whitespace before *this* and after aEndObject // in preperation for the two areas to become adjacent after the diff --git a/editor/libeditor/WSRunObject.h b/editor/libeditor/WSRunObject.h index 847d654fa7..e4821bf5a7 100644 --- a/editor/libeditor/WSRunObject.h +++ b/editor/libeditor/WSRunObject.h @@ -574,15 +574,6 @@ class MOZ_STACK_CLASS WSRunScanner { nsIContent* GetEditableBlockParentOrTopmotEditableInlineContent( nsIContent* aContent) const; - nsIContent* GetPreviousWSNodeInner(nsINode* aStartNode, - nsINode* aBlockParent) const; - nsIContent* GetPreviousWSNode(const EditorDOMPoint& aPoint, - nsINode* aBlockParent) const; - nsIContent* GetNextWSNodeInner(nsINode* aStartNode, - nsINode* aBlockParent) const; - nsIContent* GetNextWSNode(const EditorDOMPoint& aPoint, - nsINode* aBlockParent) const; - /** * GetPreviousCharPoint() and GetPreviousCharPointFromPointInText() return * previous character's point of of aPoint. If there is no character before diff --git a/editor/libeditor/tests/test_cut_copy_password.html b/editor/libeditor/tests/test_cut_copy_password.html index a1998d459e..e15682cf97 100644 --- a/editor/libeditor/tests/test_cut_copy_password.html +++ b/editor/libeditor/tests/test_cut_copy_password.html @@ -13,29 +13,23 @@ SimpleTest.waitForFocus(async () => { let input = document.getElementsByTagName("input")[0]; let editor = SpecialPowers.wrap(input).editor; const kMask = editor.passwordMask; - function copyToClipboard(aExpectedValue) { - return new Promise(async resolve => { - try { - await SimpleTest.promiseClipboardChange( - aExpectedValue, () => { SpecialPowers.doCommand(window, "cmd_copy"); }, - undefined, undefined, aExpectedValue === null); - } catch (e) { - console.error(e); - } - resolve(); - }); + async function copyToClipboard(aExpectedValue) { + try { + await SimpleTest.promiseClipboardChange( + aExpectedValue, () => { SpecialPowers.doCommand(window, "cmd_copy"); }, + undefined, undefined, aExpectedValue === null); + } catch (e) { + console.error(e); + } } - function cutToClipboard(aExpectedValue) { - return new Promise(async resolve => { - try { - await SimpleTest.promiseClipboardChange( - aExpectedValue, () => { SpecialPowers.doCommand(window, "cmd_cut"); }, - undefined, undefined, aExpectedValue === null); - } catch (e) { - console.error(e); - } - resolve(); - }); + async function cutToClipboard(aExpectedValue) { + try { + await SimpleTest.promiseClipboardChange( + aExpectedValue, () => { SpecialPowers.doCommand(window, "cmd_cut"); }, + undefined, undefined, aExpectedValue === null); + } catch (e) { + console.error(e); + } } input.value = "abcdef"; input.focus(); diff --git a/editor/libeditor/tests/test_nsIHTMLEditor_getSelectedElement.html b/editor/libeditor/tests/test_nsIHTMLEditor_getSelectedElement.html index 99dd9ca9f2..255f5b7a7c 100644 --- a/editor/libeditor/tests/test_nsIHTMLEditor_getSelectedElement.html +++ b/editor/libeditor/tests/test_nsIHTMLEditor_getSelectedElement.html @@ -779,6 +779,27 @@ SimpleTest.waitForFocus(async function() { null, "#9-1 nsIHTMLEditor::getSelectedElement(\"anchor\") should return null when Selection starts from a text node in element and ends before the following
element"); + editor.innerHTML = "

b1
b2

"; + editor.focus(); + + //

[b1}
b2

+ // This is usual case of double-clicking in the first element. + range = document.createRange(); + range.setStart(editor.firstChild.firstChild.firstChild, 0); + range.setEnd(editor.firstChild.firstChild.nextSibling, 0); + selection.removeAllRanges(); + selection.addRange(range); + + is(SpecialPowers.unwrap(getHTMLEditor().getSelectedElement("")), + null, + "#10-1 nsIHTMLEditor::getSelectedElement(\"\") should return null when Selection starts from a text node in element and ends before the following
element in another element"); + is(SpecialPowers.unwrap(getHTMLEditor().getSelectedElement("href")), + null, + "#10-1 nsIHTMLEditor::getSelectedElement(\"href\") should return null when Selection starts from a text node in element and ends before the following
element in another element"); + is(SpecialPowers.unwrap(getHTMLEditor().getSelectedElement("anchor")), + null, + "#10-1 nsIHTMLEditor::getSelectedElement(\"anchor\") should return null when Selection starts from a text node in element and ends before the following
element in another element"); + SimpleTest.finish(); }); diff --git a/extensions/permissions/components.conf b/extensions/permissions/components.conf index 07c5ff7ffa..d7d65bb1ba 100644 --- a/extensions/permissions/components.conf +++ b/extensions/permissions/components.conf @@ -12,8 +12,10 @@ Classes = [ 'categories': {'content-policy': '@mozilla.org/permissions/contentblocker;1'}, }, { + 'js_name': 'perms', 'cid': '{4f6b5e00-0c36-11d5-a535-0010a401eb10}', 'contract_ids': ['@mozilla.org/permissionmanager;1'], + 'interfaces': ['nsIPermissionManager'], 'singleton': True, 'type': 'nsIPermissionManager', 'constructor': 'nsPermissionManager::GetXPCOMSingleton', diff --git a/extensions/permissions/nsPermissionManager.cpp b/extensions/permissions/nsPermissionManager.cpp index 39868e0eac..b37fb29497 100644 --- a/extensions/permissions/nsPermissionManager.cpp +++ b/extensions/permissions/nsPermissionManager.cpp @@ -8,7 +8,6 @@ #include "mozilla/BasePrincipal.h" #include "mozilla/ContentPrincipal.h" #include "mozilla/DebugOnly.h" -#include "mozilla/Pair.h" #include "mozilla/Services.h" #include "mozilla/SystemGroup.h" #include "nsPermissionManager.h" @@ -2130,7 +2129,7 @@ nsPermissionManager::RemoveAllSince(int64_t aSince) { template nsresult nsPermissionManager::RemovePermissionEntries(T aCondition) { - Vector, nsCString>, 10> array; + Vector, nsCString>, 10> array; for (auto iter = mPermissionTable.Iter(); !iter.Done(); iter.Next()) { PermissionHashKey* entry = iter.Get(); for (const auto& permEntry : entry->GetPermissions()) { @@ -2153,7 +2152,7 @@ nsresult nsPermissionManager::RemovePermissionEntries(T aCondition) { for (auto& i : array) { // AddInternal handles removal, so let it do the work... - AddInternal(i.first(), i.second(), nsIPermissionManager::UNKNOWN_ACTION, 0, + AddInternal(i.first, i.second, nsIPermissionManager::UNKNOWN_ACTION, 0, nsIPermissionManager::EXPIRE_NEVER, 0, 0, nsPermissionManager::eNotify, nsPermissionManager::eWriteToDB); } @@ -2679,7 +2678,7 @@ nsresult nsPermissionManager::RemovePermissionsWithAttributes( nsresult nsPermissionManager::RemovePermissionsWithAttributes( mozilla::OriginAttributesPattern& aPattern) { - Vector, nsCString>, 10> permissions; + Vector, nsCString>, 10> permissions; for (auto iter = mPermissionTable.Iter(); !iter.Done(); iter.Next()) { PermissionHashKey* entry = iter.Get(); @@ -2702,7 +2701,7 @@ nsresult nsPermissionManager::RemovePermissionsWithAttributes( } for (auto& i : permissions) { - AddInternal(i.first(), i.second(), nsIPermissionManager::UNKNOWN_ACTION, 0, + AddInternal(i.first, i.second, nsIPermissionManager::UNKNOWN_ACTION, 0, nsIPermissionManager::EXPIRE_NEVER, 0, 0, nsPermissionManager::eNotify, nsPermissionManager::eWriteToDB); } diff --git a/gfx/2d/BasePoint.h b/gfx/2d/BasePoint.h index e4ac67b61a..7e2a5ebc7c 100644 --- a/gfx/2d/BasePoint.h +++ b/gfx/2d/BasePoint.h @@ -7,6 +7,7 @@ #include #include +#include #include "mozilla/Attributes.h" #include "mozilla/FloatingPoint.h" #include "mozilla/TypeTraits.h" @@ -93,8 +94,8 @@ struct BasePoint { // "Finite" means not inf and not NaN bool IsFinite() const { - typedef typename mozilla::Conditional::value, - float, double>::Type FloatType; + using FloatType = + std::conditional_t::value, float, double>; return (mozilla::IsFinite(FloatType(x)) && mozilla::IsFinite(FloatType(y))); return true; } diff --git a/gfx/2d/BaseRect.h b/gfx/2d/BaseRect.h index debbd83f1e..7de91e1ef6 100644 --- a/gfx/2d/BaseRect.h +++ b/gfx/2d/BaseRect.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "mozilla/Assertions.h" #include "mozilla/FloatingPoint.h" @@ -62,8 +63,8 @@ struct BaseRect { // "Finite" means not inf and not NaN bool IsFinite() const { - typedef typename mozilla::Conditional::value, - float, double>::Type FloatType; + using FloatType = + std::conditional_t::value, float, double>; return (mozilla::IsFinite(FloatType(x)) && mozilla::IsFinite(FloatType(y)) && mozilla::IsFinite(FloatType(width)) && diff --git a/gfx/2d/GenericRefCounted.h b/gfx/2d/GenericRefCounted.h index 7a552a28c7..dbc0a04483 100644 --- a/gfx/2d/GenericRefCounted.h +++ b/gfx/2d/GenericRefCounted.h @@ -9,6 +9,8 @@ #ifndef MOZILLA_GENERICREFCOUNTED_H_ #define MOZILLA_GENERICREFCOUNTED_H_ +#include + #include "mozilla/RefPtr.h" #include "mozilla/RefCounted.h" @@ -99,8 +101,9 @@ class GenericRefCounted : public GenericRefCountedBase { } private: - typename Conditional, - MozRefCountType>::Type refCnt; + std::conditional_t, + MozRefCountType> + refCnt; }; } // namespace detail diff --git a/gfx/ipc/GPUProcessManager.h b/gfx/ipc/GPUProcessManager.h index 5322ea4831..643d715d7e 100644 --- a/gfx/ipc/GPUProcessManager.h +++ b/gfx/ipc/GPUProcessManager.h @@ -181,6 +181,9 @@ class GPUProcessManager final : public GPUProcessHost::Listener { // Returns whether or not a GPU process was ever launched. bool AttemptedGPUProcess() const { return mNumProcessAttempts > 0; } + // Returns the process host + GPUProcessHost* Process() { return mProcess; } + private: // Called from our xpcom-shutdown observer. void OnXPCOMShutdown(); diff --git a/gfx/layers/AnimationInfo.cpp b/gfx/layers/AnimationInfo.cpp index 3694a378b8..bb661f9c94 100644 --- a/gfx/layers/AnimationInfo.cpp +++ b/gfx/layers/AnimationInfo.cpp @@ -131,7 +131,7 @@ void AnimationInfo::TransferMutatedFlagToLayer(Layer* aLayer) { bool AnimationInfo::ApplyPendingUpdatesForThisTransaction() { if (mPendingAnimations) { - mPendingAnimations->SwapElements(mAnimations); + mAnimations = std::move(*mPendingAnimations); mPendingAnimations = nullptr; return true; } diff --git a/gfx/layers/ImageContainer.cpp b/gfx/layers/ImageContainer.cpp index 86596c63a1..460fc8322d 100644 --- a/gfx/layers/ImageContainer.cpp +++ b/gfx/layers/ImageContainer.cpp @@ -85,10 +85,7 @@ UniquePtr BufferRecycleBin::GetBuffer(uint32_t aSize) { return UniquePtr(new (fallible) uint8_t[aSize]); } - uint32_t last = mRecycledBuffers.Length() - 1; - UniquePtr result = std::move(mRecycledBuffers[last]); - mRecycledBuffers.RemoveElementAt(last); - return result; + return mRecycledBuffers.PopLastElement(); } void BufferRecycleBin::ClearRecycledBuffers() { @@ -289,7 +286,7 @@ void ImageContainer::SetCurrentImageInternal( } } - mCurrentImages.SwapElements(newImages); + mCurrentImages = std::move(newImages); } void ImageContainer::ClearImagesFromImageBridge() { diff --git a/gfx/layers/LayerSorter.cpp b/gfx/layers/LayerSorter.cpp index d829ee3d3d..6a831be9bf 100644 --- a/gfx/layers/LayerSorter.cpp +++ b/gfx/layers/LayerSorter.cpp @@ -299,12 +299,9 @@ void SortLayersBy3DZOrder(nsTArray& aLayers) { // and remove edges from it. do { if (!noIncoming.IsEmpty()) { - uint32_t last = noIncoming.Length() - 1; - - Layer* layer = noIncoming.ElementAt(last); + Layer* layer = noIncoming.PopLastElement(); MOZ_ASSERT(layer); // don't let null layer pointers sneak into sortedList - noIncoming.RemoveElementAt(last); sortedList.AppendElement(layer); nsTArray::Edge> outgoing; diff --git a/gfx/layers/Layers.cpp b/gfx/layers/Layers.cpp index b873818e80..fd11bdbc9c 100644 --- a/gfx/layers/Layers.cpp +++ b/gfx/layers/Layers.cpp @@ -2321,12 +2321,12 @@ void RecordCompositionPayloadsPresented( TimeStamp presented = TimeStamp::Now(); for (const CompositionPayload& payload : aPayloads) { #if MOZ_GECKO_PROFILER - if (profiler_is_active()) { + if (profiler_can_accept_markers()) { nsPrintfCString marker( "Payload Presented, type: %d latency: %dms\n", int32_t(payload.mType), int32_t((presented - payload.mTimeStamp).ToMilliseconds())); - profiler_add_marker(marker.get(), JS::ProfilingCategoryPair::GRAPHICS); + PROFILER_ADD_MARKER(marker.get(), GRAPHICS); } #endif diff --git a/gfx/layers/ProfilerScreenshots.cpp b/gfx/layers/ProfilerScreenshots.cpp index 2c906dcb61..2ad0973408 100644 --- a/gfx/layers/ProfilerScreenshots.cpp +++ b/gfx/layers/ProfilerScreenshots.cpp @@ -66,7 +66,7 @@ void ProfilerScreenshots::SubmitScreenshot( originalSize, scaledSize, timeStamp]() { // Create a new surface that wraps backingSurface's data but has the // correct size. - { + if (profiler_can_accept_markers()) { DataSourceSurface::ScopedMap scopedMap(backingSurface, DataSourceSurface::READ); RefPtr surf = @@ -81,6 +81,7 @@ void ProfilerScreenshots::SubmitScreenshot( gfxUtils::eDataURIEncode, nullptr, &dataURL); if (NS_SUCCEEDED(rv)) { // Add a marker with the data URL. + AUTO_PROFILER_STATS(add_marker_with_ScreenshotPayload); profiler_add_marker_for_thread( sourceThread, JS::ProfilingCategoryPair::GRAPHICS, "CompositorScreenshot", diff --git a/gfx/layers/TreeTraversal.h b/gfx/layers/TreeTraversal.h index 6b358e8829..8f6c2e3547 100644 --- a/gfx/layers/TreeTraversal.h +++ b/gfx/layers/TreeTraversal.h @@ -6,6 +6,7 @@ #define mozilla_layers_TreeTraversal_h #include +#include namespace mozilla { namespace layers { @@ -84,11 +85,11 @@ class ReverseIterator { template static auto ForEachNode(Node aRoot, const PreAction& aPreAction, - const PostAction& aPostAction) -> - typename EnableIf< + const PostAction& aPostAction) + -> std::enable_if_t< IsSame::value && IsSame::value, - bool>::Type { + bool> { if (!aRoot) { return false; } @@ -125,10 +126,10 @@ static auto ForEachNode(Node aRoot, const PreAction& aPreAction, template static auto ForEachNode(Node aRoot, const PreAction& aPreAction, - const PostAction& aPostAction) -> - typename EnableIf::value && - IsSame::value, - void>::Type { + const PostAction& aPostAction) + -> std::enable_if_t::value && + IsSame::value, + void> { if (!aRoot) { return; } @@ -147,9 +148,8 @@ static auto ForEachNode(Node aRoot, const PreAction& aPreAction, * ForEachNode pre-order traversal, using TraversalFlag. */ template -auto ForEachNode(Node aRoot, const PreAction& aPreAction) -> - typename EnableIf::value, - bool>::Type { +auto ForEachNode(Node aRoot, const PreAction& aPreAction) -> std::enable_if_t< + IsSame::value, bool> { return ForEachNode( aRoot, aPreAction, [](Node aNode) { return TraversalFlag::Continue; }); } @@ -158,9 +158,9 @@ auto ForEachNode(Node aRoot, const PreAction& aPreAction) -> * ForEachNode pre-order, not using TraversalFlag. */ template -auto ForEachNode(Node aRoot, const PreAction& aPreAction) -> - typename EnableIf::value, - void>::Type { +auto ForEachNode(Node aRoot, const PreAction& aPreAction) + -> std::enable_if_t::value, + void> { ForEachNode(aRoot, aPreAction, [](Node aNode) {}); } @@ -168,10 +168,9 @@ auto ForEachNode(Node aRoot, const PreAction& aPreAction) -> * ForEachNode post-order traversal, using TraversalFlag. */ template -auto ForEachNodePostOrder(Node aRoot, const PostAction& aPostAction) -> - typename EnableIf< - IsSame::value, - bool>::Type { +auto ForEachNodePostOrder(Node aRoot, const PostAction& aPostAction) + -> std::enable_if_t< + IsSame::value, bool> { return ForEachNode( aRoot, [](Node aNode) { return TraversalFlag::Continue; }, aPostAction); } @@ -180,9 +179,9 @@ auto ForEachNodePostOrder(Node aRoot, const PostAction& aPostAction) -> * ForEachNode post-order, not using TraversalFlag. */ template -auto ForEachNodePostOrder(Node aRoot, const PostAction& aPostAction) -> - typename EnableIf::value, - void>::Type { +auto ForEachNodePostOrder(Node aRoot, const PostAction& aPostAction) + -> std::enable_if_t::value, + void> { ForEachNode( aRoot, [](Node aNode) {}, aPostAction); } diff --git a/gfx/layers/apz/test/gtest/APZTestCommon.h b/gfx/layers/apz/test/gtest/APZTestCommon.h index 9c26e414be..4cde2d644d 100644 --- a/gfx/layers/apz/test/gtest/APZTestCommon.h +++ b/gfx/layers/apz/test/gtest/APZTestCommon.h @@ -191,8 +191,8 @@ class MockContentControllerDelayed : public MockContentController { // in the queue after this function is called. Only when the return // value is 0 is the queue guaranteed to be empty. int RunThroughDelayedTasks() { - nsTArray, TimeStamp>> runQueue; - runQueue.SwapElements(mTaskQueue); + nsTArray, TimeStamp>> runQueue = + std::move(mTaskQueue); int numTasks = runQueue.Length(); for (int i = 0; i < numTasks; i++) { mTime = runQueue[i].second; diff --git a/gfx/layers/client/MultiTiledContentClient.cpp b/gfx/layers/client/MultiTiledContentClient.cpp index 28bd927edb..c87484d971 100644 --- a/gfx/layers/client/MultiTiledContentClient.cpp +++ b/gfx/layers/client/MultiTiledContentClient.cpp @@ -186,8 +186,7 @@ void ClientMultiTiledLayerBuffer::Update(const nsIntRegion& newValidRegion, const size_t oldTileCount = mRetainedTiles.Length(); const size_t newTileCount = newTiles.mSize.width * newTiles.mSize.height; - nsTArray oldRetainedTiles; - mRetainedTiles.SwapElements(oldRetainedTiles); + nsTArray oldRetainedTiles = std::move(mRetainedTiles); mRetainedTiles.SetLength(newTileCount); for (size_t oldIndex = 0; oldIndex < oldTileCount; oldIndex++) { diff --git a/gfx/layers/client/TextureClient.h b/gfx/layers/client/TextureClient.h index 238bcb72af..337ca03857 100644 --- a/gfx/layers/client/TextureClient.h +++ b/gfx/layers/client/TextureClient.h @@ -771,14 +771,10 @@ class TextureClientReleaseTask : public Runnable { // Automatically lock and unlock a texture. Since texture locking is fallible, // Succeeded() must be checked on the guard object before proceeding. class MOZ_RAII TextureClientAutoLock { - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER; public: - TextureClientAutoLock(TextureClient* aTexture, - OpenMode aMode MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + TextureClientAutoLock(TextureClient* aTexture, OpenMode aMode) : mTexture(aTexture), mSucceeded(false) { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - mSucceeded = mTexture->Lock(aMode); #ifdef DEBUG mChecked = false; diff --git a/gfx/layers/composite/CompositorScreenshotGrabber.cpp b/gfx/layers/composite/CompositorScreenshotGrabber.cpp index aa54021aa6..ee861a17f3 100644 --- a/gfx/layers/composite/CompositorScreenshotGrabber.cpp +++ b/gfx/layers/composite/CompositorScreenshotGrabber.cpp @@ -85,8 +85,8 @@ void CompositorScreenshotGrabber::MaybeProcessQueue() { void CompositorScreenshotGrabber::NotifyEmptyFrame() { #ifdef MOZ_GECKO_PROFILER - profiler_add_marker("NoCompositorScreenshot because nothing changed", - JS::ProfilingCategoryPair::GRAPHICS); + PROFILER_ADD_MARKER("NoCompositorScreenshot because nothing changed", + GRAPHICS); #endif } diff --git a/gfx/layers/composite/ContainerLayerComposite.cpp b/gfx/layers/composite/ContainerLayerComposite.cpp index f0d6e775ff..5ac3f2323b 100644 --- a/gfx/layers/composite/ContainerLayerComposite.cpp +++ b/gfx/layers/composite/ContainerLayerComposite.cpp @@ -100,9 +100,9 @@ static void PrintUniformityInfo(Layer* aLayer) { } Point translation = transform.As2D().GetTranslation(); - profiler_add_marker("LayerTranslation", JS::ProfilingCategoryPair::GRAPHICS, - MakeUnique( - aLayer, translation, TimeStamp::Now())); + PROFILER_ADD_MARKER_WITH_PAYLOAD("LayerTranslation", GRAPHICS, + LayerTranslationMarkerPayload, + (aLayer, translation, TimeStamp::Now())); #endif } diff --git a/gfx/layers/ipc/CompositorBridgeChild.cpp b/gfx/layers/ipc/CompositorBridgeChild.cpp index 4640b70a04..d5b7a96110 100644 --- a/gfx/layers/ipc/CompositorBridgeChild.cpp +++ b/gfx/layers/ipc/CompositorBridgeChild.cpp @@ -51,7 +51,6 @@ #include "VsyncSource.h" using mozilla::Unused; -using mozilla::dom::BrowserChildBase; using mozilla::gfx::GPUProcessManager; using mozilla::layers::LayerTransactionChild; @@ -645,17 +644,15 @@ mozilla::ipc::IPCResult CompositorBridgeChild::RecvRemotePaintIsReady() { // do_QueryReference so I'm using static_cast<> MOZ_LAYERS_LOG( ("[RemoteGfx] CompositorBridgeChild received RemotePaintIsReady")); - RefPtr iBrowserChildBase(do_QueryReferent(mWeakBrowserChild)); - if (!iBrowserChildBase) { + RefPtr iBrowserChild(do_QueryReferent(mWeakBrowserChild)); + if (!iBrowserChild) { MOZ_LAYERS_LOG( ("[RemoteGfx] Note: BrowserChild was released before " "RemotePaintIsReady. " "MozAfterRemotePaint will not be sent to listener.")); return IPC_OK(); } - BrowserChildBase* browserChildBase = - static_cast(iBrowserChildBase.get()); - BrowserChild* browserChild = static_cast(browserChildBase); + BrowserChild* browserChild = static_cast(iBrowserChild.get()); MOZ_ASSERT(browserChild); Unused << browserChild->SendRemotePaintIsReady(); mWeakBrowserChild = nullptr; @@ -668,7 +665,7 @@ void CompositorBridgeChild::RequestNotifyAfterRemotePaint( "NULL BrowserChild not allowed in " "CompositorBridgeChild::RequestNotifyAfterRemotePaint"); mWeakBrowserChild = - do_GetWeakReference(static_cast(aBrowserChild)); + do_GetWeakReference(static_cast(aBrowserChild)); if (!mCanSend) { return; } @@ -677,13 +674,11 @@ void CompositorBridgeChild::RequestNotifyAfterRemotePaint( void CompositorBridgeChild::CancelNotifyAfterRemotePaint( BrowserChild* aBrowserChild) { - RefPtr iBrowserChildBase(do_QueryReferent(mWeakBrowserChild)); - if (!iBrowserChildBase) { + RefPtr iBrowserChild(do_QueryReferent(mWeakBrowserChild)); + if (!iBrowserChild) { return; } - BrowserChildBase* browserChildBase = - static_cast(iBrowserChildBase.get()); - BrowserChild* browserChild = static_cast(browserChildBase); + BrowserChild* browserChild = static_cast(iBrowserChild.get()); if (browserChild == aBrowserChild) { mWeakBrowserChild = nullptr; } diff --git a/gfx/layers/ipc/CompositorBridgeParent.cpp b/gfx/layers/ipc/CompositorBridgeParent.cpp index f8f1ec41a7..e9ee5c1ce1 100644 --- a/gfx/layers/ipc/CompositorBridgeParent.cpp +++ b/gfx/layers/ipc/CompositorBridgeParent.cpp @@ -2131,8 +2131,8 @@ already_AddRefed CompositorBridgeParent::GetAPZCTreeManager( static void InsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp) { MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); if (profiler_thread_is_being_profiled()) { - profiler_add_marker("VsyncTimestamp", JS::ProfilingCategoryPair::GRAPHICS, - MakeUnique(aVsyncTimestamp)); + PROFILER_ADD_MARKER_WITH_PAYLOAD("VsyncTimestamp", GRAPHICS, + VsyncMarkerPayload, (aVsyncTimestamp)); } } #endif @@ -2685,19 +2685,40 @@ int32_t RecordContentFrameTime(const VsyncId& aTxnId, int32_t fracLatencyNorm = lround(latencyNorm * 100.0); #ifdef MOZ_GECKO_PROFILER - if (profiler_is_active()) { + if (profiler_can_accept_markers()) { class ContentFramePayload : public ProfilerMarkerPayload { public: ContentFramePayload(const mozilla::TimeStamp& aStartTime, const mozilla::TimeStamp& aEndTime) : ProfilerMarkerPayload(aStartTime, aEndTime) {} - virtual void StreamPayload(SpliceableJSONWriter& aWriter, - const TimeStamp& aProcessStartTime, - UniqueStacks& aUniqueStacks) override { + mozilla::BlocksRingBuffer::Length TagAndSerializationBytes() + const override { + return CommonPropsTagAndSerializationBytes(); + } + void SerializeTagAndPayload( + mozilla::BlocksRingBuffer::EntryWriter& aEntryWriter) const override { + static const DeserializerTag tag = TagForDeserializer(Deserialize); + SerializeTagAndCommonProps(tag, aEntryWriter); + } + void StreamPayload(SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const override { StreamCommonProps("CONTENT_FRAME_TIME", aWriter, aProcessStartTime, aUniqueStacks); } + + private: + explicit ContentFramePayload(CommonProps&& aCommonProps) + : ProfilerMarkerPayload(std::move(aCommonProps)) {} + static mozilla::UniquePtr Deserialize( + mozilla::BlocksRingBuffer::EntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + return UniquePtr( + new ContentFramePayload(std::move(props))); + } }; + AUTO_PROFILER_STATS(add_marker_with_ContentFramePayload); profiler_add_marker_for_thread( profiler_current_thread_id(), JS::ProfilingCategoryPair::GRAPHICS, "CONTENT_FRAME_TIME", diff --git a/gfx/layers/ipc/ContentCompositorBridgeParent.cpp b/gfx/layers/ipc/ContentCompositorBridgeParent.cpp index e63dc7b5db..c09c85f653 100644 --- a/gfx/layers/ipc/ContentCompositorBridgeParent.cpp +++ b/gfx/layers/ipc/ContentCompositorBridgeParent.cpp @@ -378,19 +378,40 @@ void ContentCompositorBridgeParent::ShadowLayersUpdated( auto endTime = TimeStamp::Now(); #ifdef MOZ_GECKO_PROFILER - if (profiler_is_active()) { + if (profiler_can_accept_markers()) { class ContentBuildPayload : public ProfilerMarkerPayload { public: ContentBuildPayload(const mozilla::TimeStamp& aStartTime, const mozilla::TimeStamp& aEndTime) : ProfilerMarkerPayload(aStartTime, aEndTime) {} - virtual void StreamPayload(SpliceableJSONWriter& aWriter, - const TimeStamp& aProcessStartTime, - UniqueStacks& aUniqueStacks) override { + mozilla::BlocksRingBuffer::Length TagAndSerializationBytes() + const override { + return CommonPropsTagAndSerializationBytes(); + } + void SerializeTagAndPayload( + mozilla::BlocksRingBuffer::EntryWriter& aEntryWriter) const override { + static const DeserializerTag tag = TagForDeserializer(Deserialize); + SerializeTagAndCommonProps(tag, aEntryWriter); + } + void StreamPayload(SpliceableJSONWriter& aWriter, + const TimeStamp& aProcessStartTime, + UniqueStacks& aUniqueStacks) const override { StreamCommonProps("CONTENT_FULL_PAINT_TIME", aWriter, aProcessStartTime, aUniqueStacks); } + + private: + explicit ContentBuildPayload(CommonProps&& aCommonProps) + : ProfilerMarkerPayload(std::move(aCommonProps)) {} + static mozilla::UniquePtr Deserialize( + mozilla::BlocksRingBuffer::EntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + return UniquePtr( + new ContentBuildPayload(std::move(props))); + } }; + AUTO_PROFILER_STATS(add_marker_with_ContentBuildPayload); profiler_add_marker_for_thread( profiler_current_thread_id(), JS::ProfilingCategoryPair::GRAPHICS, "CONTENT_FULL_PAINT_TIME", diff --git a/gfx/layers/mlgpu/MLGPUScreenshotGrabber.cpp b/gfx/layers/mlgpu/MLGPUScreenshotGrabber.cpp index 18fe1074bb..1841cdd3bd 100644 --- a/gfx/layers/mlgpu/MLGPUScreenshotGrabber.cpp +++ b/gfx/layers/mlgpu/MLGPUScreenshotGrabber.cpp @@ -99,8 +99,8 @@ void MLGPUScreenshotGrabber::MaybeProcessQueue() { void MLGPUScreenshotGrabber::NotifyEmptyFrame() { #ifdef MOZ_GECKO_PROFILER - profiler_add_marker("NoCompositorScreenshot because nothing changed", - JS::ProfilingCategoryPair::GRAPHICS); + PROFILER_ADD_MARKER("NoCompositorScreenshot because nothing changed", + GRAPHICS); #endif } diff --git a/gfx/layers/opengl/OGLShaderConfig.h b/gfx/layers/opengl/OGLShaderConfig.h index 412d2c7b2a..7051d5421a 100644 --- a/gfx/layers/opengl/OGLShaderConfig.h +++ b/gfx/layers/opengl/OGLShaderConfig.h @@ -8,7 +8,6 @@ #include "gfxTypes.h" #include "ImageTypes.h" #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc -#include "mozilla/Pair.h" // for Pair #include "mozilla/RefPtr.h" // for RefPtr #include "mozilla/gfx/Matrix.h" // for Matrix4x4 #include "mozilla/gfx/Rect.h" // for Rect diff --git a/gfx/layers/opengl/OGLShaderProgram.cpp b/gfx/layers/opengl/OGLShaderProgram.cpp index 04d6505c84..ed85e906ec 100644 --- a/gfx/layers/opengl/OGLShaderProgram.cpp +++ b/gfx/layers/opengl/OGLShaderProgram.cpp @@ -183,7 +183,7 @@ ProgramProfileOGL ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig) { vs << "attribute vec2 aCoord;" << endl; } - result.mAttributes.AppendElement(Pair{"aCoord", 0}); + result.mAttributes.AppendElement(std::pair{"aCoord", 0}); if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) { vs << "uniform mat4 uTextureTransform;" << endl; @@ -192,7 +192,8 @@ ProgramProfileOGL ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig) { if (aConfig.mFeatures & ENABLE_DYNAMIC_GEOMETRY) { vs << "attribute vec2 aTexCoord;" << endl; - result.mAttributes.AppendElement(Pair{"aTexCoord", 1}); + result.mAttributes.AppendElement( + std::pair{"aTexCoord", 1}); } } @@ -930,9 +931,8 @@ bool ShaderProgramOGL::CreateProgram(const char* aVertexShaderString, mGL->fAttachShader(result, vertexShader); mGL->fAttachShader(result, fragmentShader); - for (Pair& attribute : mProfile.mAttributes) { - mGL->fBindAttribLocation(result, attribute.second(), - attribute.first().get()); + for (std::pair& attribute : mProfile.mAttributes) { + mGL->fBindAttribLocation(result, attribute.second, attribute.first.get()); } mGL->fLinkProgram(result); diff --git a/gfx/layers/opengl/OGLShaderProgram.h b/gfx/layers/opengl/OGLShaderProgram.h index ec2c6fe1c6..23de656180 100644 --- a/gfx/layers/opengl/OGLShaderProgram.h +++ b/gfx/layers/opengl/OGLShaderProgram.h @@ -9,6 +9,7 @@ #include "OGLShaderConfig.h" #include +#include namespace mozilla { namespace layers { @@ -46,7 +47,7 @@ struct ProgramProfileOGL { std::string mFragmentShaderString; // the vertex attributes - nsTArray> mAttributes; + nsTArray> mAttributes; KnownUniform mUniforms[KnownUniform::KnownUniformCount]; nsTArray mDefines; diff --git a/gfx/layers/wr/IpcResourceUpdateQueue.cpp b/gfx/layers/wr/IpcResourceUpdateQueue.cpp index 4186c8f271..a09804fc39 100644 --- a/gfx/layers/wr/IpcResourceUpdateQueue.cpp +++ b/gfx/layers/wr/IpcResourceUpdateQueue.cpp @@ -70,11 +70,9 @@ layers::OffsetRange ShmSegmentsWriter::Write(Range aBytes) { if (!AllocChunk()) { // Allocation failed, so roll back to the state at the start of this // Write() call and abort. - for (size_t i = mSmallAllocs.Length(); currAllocLen < i; i--) { - MOZ_ASSERT(i > 0); - RefCountedShmem& shm = mSmallAllocs.ElementAt(i - 1); + while (mSmallAllocs.Length() > currAllocLen) { + RefCountedShmem shm = mSmallAllocs.PopLastElement(); RefCountedShm::Dealloc(mShmAllocator, shm); - mSmallAllocs.RemoveElementAt(i - 1); } MOZ_ASSERT(mSmallAllocs.Length() == currAllocLen); return layers::OffsetRange(0, start, 0); @@ -143,8 +141,8 @@ void ShmSegmentsWriter::Flush(nsTArray& aSmallAllocs, nsTArray& aLargeAllocs) { MOZ_ASSERT(aSmallAllocs.IsEmpty()); MOZ_ASSERT(aLargeAllocs.IsEmpty()); - mSmallAllocs.SwapElements(aSmallAllocs); - mLargeAllocs.SwapElements(aLargeAllocs); + aSmallAllocs = std::move(mSmallAllocs); + aLargeAllocs = std::move(mLargeAllocs); mCursor = 0; } @@ -414,8 +412,7 @@ void IpcResourceUpdateQueue::Flush( nsTArray& aUpdates, nsTArray& aSmallAllocs, nsTArray& aLargeAllocs) { - aUpdates.Clear(); - mUpdates.SwapElements(aUpdates); + aUpdates = std::move(mUpdates); mWriter.Flush(aSmallAllocs, aLargeAllocs); } diff --git a/gfx/layers/wr/WebRenderBridgeParent.cpp b/gfx/layers/wr/WebRenderBridgeParent.cpp index 18f72e826d..808919da6f 100644 --- a/gfx/layers/wr/WebRenderBridgeParent.cpp +++ b/gfx/layers/wr/WebRenderBridgeParent.cpp @@ -67,7 +67,7 @@ void gecko_profiler_add_text_marker(const char* name, const char* text_bytes, size_t text_len, uint64_t microseconds) { #ifdef MOZ_GECKO_PROFILER if (profiler_thread_is_being_profiled()) { - auto now = mozilla::TimeStamp::Now(); + auto now = mozilla::TimeStamp::NowUnfuzzed(); auto start = now - mozilla::TimeDuration::FromMicroseconds(microseconds); profiler_add_text_marker(name, nsDependentCSubstring(text_bytes, text_len), JS::ProfilingCategoryPair::GRAPHICS, start, now); @@ -203,20 +203,43 @@ class SceneBuiltNotification : public wr::NotificationHandler { "SceneBuiltNotificationRunnable", [parent, epoch, startTime]() { auto endTime = TimeStamp::Now(); #ifdef MOZ_GECKO_PROFILER - if (profiler_is_active()) { + if (profiler_can_accept_markers()) { class ContentFullPaintPayload : public ProfilerMarkerPayload { public: ContentFullPaintPayload(const mozilla::TimeStamp& aStartTime, const mozilla::TimeStamp& aEndTime) : ProfilerMarkerPayload(aStartTime, aEndTime) {} + mozilla::BlocksRingBuffer::Length TagAndSerializationBytes() + const override { + return CommonPropsTagAndSerializationBytes(); + } + void SerializeTagAndPayload( + mozilla::BlocksRingBuffer::EntryWriter& aEntryWriter) + const override { + static const DeserializerTag tag = + TagForDeserializer(Deserialize); + SerializeTagAndCommonProps(tag, aEntryWriter); + } void StreamPayload(SpliceableJSONWriter& aWriter, const TimeStamp& aProcessStartTime, - UniqueStacks& aUniqueStacks) override { + UniqueStacks& aUniqueStacks) const override { StreamCommonProps("CONTENT_FULL_PAINT_TIME", aWriter, aProcessStartTime, aUniqueStacks); } + + private: + explicit ContentFullPaintPayload(CommonProps&& aCommonProps) + : ProfilerMarkerPayload(std::move(aCommonProps)) {} + static mozilla::UniquePtr Deserialize( + mozilla::BlocksRingBuffer::EntryReader& aEntryReader) { + ProfilerMarkerPayload::CommonProps props = + DeserializeCommonProps(aEntryReader); + return UniquePtr( + new ContentFullPaintPayload(std::move(props))); + } }; + AUTO_PROFILER_STATS(add_marker_with_ContentFullPaintPayload); profiler_add_marker_for_thread( profiler_current_thread_id(), JS::ProfilingCategoryPair::GRAPHICS, "CONTENT_FULL_PAINT_TIME", diff --git a/gfx/layers/wr/WebRenderCommandBuilder.cpp b/gfx/layers/wr/WebRenderCommandBuilder.cpp index d3514b4d34..7b39a67e40 100644 --- a/gfx/layers/wr/WebRenderCommandBuilder.cpp +++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp @@ -304,7 +304,7 @@ struct DIGroup { // current item being processed. IntRect mClippedImageBounds; // mLayerBounds with the clipping of any // containers applied - Maybe> mKey; + Maybe> mKey; std::vector> mFonts; DIGroup() @@ -337,7 +337,7 @@ struct DIGroup { void ClearImageKey(RenderRootStateManager* aManager, bool aForce = false) { if (mKey) { MOZ_RELEASE_ASSERT(aForce || mInvalidRect.IsEmpty()); - aManager->AddBlobImageKeyForDiscard(mKey.value().second()); + aManager->AddBlobImageKeyForDiscard(mKey.value().second); mKey = Nothing(); } mFonts.clear(); @@ -610,7 +610,7 @@ struct DIGroup { // so request it be updated unconditionally (wr should be able to easily // detect if this is a no-op on its side, if that matters) aResources.SetBlobImageVisibleArea( - mKey.value().second(), + mKey.value().second, ViewAs(mVisibleRect, PixelCastJustification::LayerIsImage)); mLastVisibleRect = mVisibleRect; @@ -698,7 +698,7 @@ struct DIGroup { PixelCastJustification::LayerIsImage))) { return; } - mKey = Some(MakePair(aBuilder.GetRenderRoot(), key)); + mKey = Some(std::make_pair(aBuilder.GetRenderRoot(), key)); } else { wr::ImageDescriptor descriptor(dtSize, 0, dt->GetFormat(), opacity); @@ -712,7 +712,7 @@ struct DIGroup { GP("Update Blob %d %d %d %d\n", mInvalidRect.x, mInvalidRect.y, mInvalidRect.width, mInvalidRect.height); if (!aResources.UpdateBlobImage( - mKey.value().second(), descriptor, bytes, + mKey.value().second, descriptor, bytes, ViewAs(mVisibleRect, PixelCastJustification::LayerIsImage), dirtyRect)) { @@ -721,7 +721,7 @@ struct DIGroup { } mFonts = std::move(fonts); aResources.SetBlobImageVisibleArea( - mKey.value().second(), + mKey.value().second, ViewAs(mVisibleRect, PixelCastJustification::LayerIsImage)); mLastVisibleRect = mVisibleRect; PushImage(aBuilder, itemBounds); @@ -749,7 +749,7 @@ struct DIGroup { aBuilder.SetHitTestInfo(mScrollId, hitInfo, SideBits::eNone); aBuilder.PushImage(dest, dest, !backfaceHidden, wr::ToImageRendering(sampleFilter), - wr::AsImageKey(mKey.value().second())); + wr::AsImageKey(mKey.value().second)); aBuilder.ClearHitTestInfo(); } diff --git a/gfx/src/nsRegion.h b/gfx/src/nsRegion.h index 61cfc07ac1..9bfdcd8c04 100644 --- a/gfx/src/nsRegion.h +++ b/gfx/src/nsRegion.h @@ -497,13 +497,12 @@ class nsRegion { } nsRegion(const nsRegion& aRegion) { Copy(aRegion); } - nsRegion(nsRegion&& aRegion) { - mBands.SwapElements(aRegion.mBands); - mBounds = aRegion.mBounds; + nsRegion(nsRegion&& aRegion) + : mBands(std::move(aRegion.mBands)), mBounds(aRegion.mBounds) { aRegion.SetEmpty(); } nsRegion& operator=(nsRegion&& aRegion) { - mBands.SwapElements(aRegion.mBands); + mBands = std::move(aRegion.mBands); mBounds = aRegion.mBounds; aRegion.SetEmpty(); return *this; diff --git a/gfx/thebes/gfxFontEntry.cpp b/gfx/thebes/gfxFontEntry.cpp index 1378181493..59493c9348 100644 --- a/gfx/thebes/gfxFontEntry.cpp +++ b/gfx/thebes/gfxFontEntry.cpp @@ -1471,10 +1471,9 @@ void gfxFontFamily::FindFontForChar(GlobalFontMatch* aMatchData) { #ifdef MOZ_GECKO_PROFILER nsCString charAndName; - if (profiler_is_active()) { + if (profiler_can_accept_markers()) { charAndName = nsPrintfCString("\\u%x %s", aMatchData->mCh, mName.get()); } - AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("gfxFontFamily::FindFontForChar", LAYOUT, charAndName); #endif diff --git a/gfx/thebes/gfxSkipChars.h b/gfx/thebes/gfxSkipChars.h index fc90022885..16ccecbdce 100644 --- a/gfx/thebes/gfxSkipChars.h +++ b/gfx/thebes/gfxSkipChars.h @@ -82,7 +82,7 @@ class gfxSkipChars { void KeepChar() { KeepChars(1); } void TakeFrom(gfxSkipChars* aSkipChars) { - mRanges.SwapElements(aSkipChars->mRanges); + mRanges = std::move(aSkipChars->mRanges); mCharCount = aSkipChars->mCharCount; aSkipChars->mCharCount = 0; } diff --git a/gfx/thebes/gfxUserFontSet.cpp b/gfx/thebes/gfxUserFontSet.cpp index 63d222302c..ad0be42ef4 100644 --- a/gfx/thebes/gfxUserFontSet.cpp +++ b/gfx/thebes/gfxUserFontSet.cpp @@ -322,7 +322,7 @@ void gfxUserFontEntry::StoreUserFontData(gfxFontEntry* aFontEntry, userFontData->mFormat = src.mFormatFlags; userFontData->mRealName = aOriginalName; if (aMetadata) { - userFontData->mMetadata.SwapElements(*aMetadata); + userFontData->mMetadata = std::move(*aMetadata); userFontData->mMetaOrigLen = aMetaOrigLen; userFontData->mCompression = aCompression; } diff --git a/gfx/webrender_bindings/WebRenderAPI.h b/gfx/webrender_bindings/WebRenderAPI.h index bc9befaaa2..bdcdbe6581 100644 --- a/gfx/webrender_bindings/WebRenderAPI.h +++ b/gfx/webrender_bindings/WebRenderAPI.h @@ -665,28 +665,22 @@ class DisplayListBuilder final { class MOZ_RAII SpaceAndClipChainHelper final { public: SpaceAndClipChainHelper(DisplayListBuilder& aBuilder, - wr::WrSpaceAndClipChain aSpaceAndClipChain - MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + wr::WrSpaceAndClipChain aSpaceAndClipChain) : mBuilder(aBuilder), mOldSpaceAndClipChain(aBuilder.mCurrentSpaceAndClipChain) { aBuilder.mCurrentSpaceAndClipChain = aSpaceAndClipChain; - MOZ_GUARD_OBJECT_NOTIFIER_INIT; } SpaceAndClipChainHelper(DisplayListBuilder& aBuilder, - wr::WrSpatialId aSpatialId - MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + wr::WrSpatialId aSpatialId) : mBuilder(aBuilder), mOldSpaceAndClipChain(aBuilder.mCurrentSpaceAndClipChain) { aBuilder.mCurrentSpaceAndClipChain.space = aSpatialId; - MOZ_GUARD_OBJECT_NOTIFIER_INIT; } SpaceAndClipChainHelper(DisplayListBuilder& aBuilder, - wr::WrClipChainId aClipChainId - MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + wr::WrClipChainId aClipChainId) : mBuilder(aBuilder), mOldSpaceAndClipChain(aBuilder.mCurrentSpaceAndClipChain) { aBuilder.mCurrentSpaceAndClipChain.clip_chain = aClipChainId.id; - MOZ_GUARD_OBJECT_NOTIFIER_INIT; } ~SpaceAndClipChainHelper() { @@ -698,7 +692,6 @@ class MOZ_RAII SpaceAndClipChainHelper final { DisplayListBuilder& mBuilder; wr::WrSpaceAndClipChain mOldSpaceAndClipChain; - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; Maybe SurfaceFormatToImageFormat(gfx::SurfaceFormat aFormat); diff --git a/image/ClippedImage.cpp b/image/ClippedImage.cpp index 15eea9f3ef..9a11bbac4c 100644 --- a/image/ClippedImage.cpp +++ b/image/ClippedImage.cpp @@ -15,7 +15,6 @@ #include "mozilla/gfx/2D.h" #include "mozilla/Move.h" #include "mozilla/RefPtr.h" -#include "mozilla/Pair.h" #include "mozilla/Tuple.h" #include "ImageRegion.h" @@ -238,13 +237,14 @@ ClippedImage::GetFrameAtSize(const IntSize& aSize, uint32_t aWhichFrame, return GetFrame(aWhichFrame, aFlags); } -Pair> ClippedImage::GetFrameInternal( +std::pair> ClippedImage::GetFrameInternal( const nsIntSize& aSize, const Maybe& aSVGContext, uint32_t aWhichFrame, uint32_t aFlags, float aOpacity) { if (!ShouldClip()) { RefPtr surface = InnerImage()->GetFrame(aWhichFrame, aFlags); - return MakePair(surface ? ImgDrawResult::SUCCESS : ImgDrawResult::NOT_READY, - std::move(surface)); + return std::make_pair( + surface ? ImgDrawResult::SUCCESS : ImgDrawResult::NOT_READY, + std::move(surface)); } float frameToDraw = InnerImage()->GetFrameIndex(aWhichFrame); @@ -257,7 +257,8 @@ Pair> ClippedImage::GetFrameInternal( IntSize(aSize.width, aSize.height), SurfaceFormat::B8G8R8A8); if (!target || !target->IsValid()) { NS_ERROR("Could not create a DrawTarget"); - return MakePair(ImgDrawResult::TEMPORARY_ERROR, RefPtr()); + return std::make_pair(ImgDrawResult::TEMPORARY_ERROR, + RefPtr()); } RefPtr ctx = gfxContext::CreateOrNull(target); @@ -284,7 +285,7 @@ Pair> ClippedImage::GetFrameInternal( MOZ_ASSERT(mCachedSurface, "Should have a cached surface now"); RefPtr surface = mCachedSurface->Surface(); - return MakePair(mCachedSurface->GetDrawResult(), std::move(surface)); + return std::make_pair(mCachedSurface->GetDrawResult(), std::move(surface)); } NS_IMETHODIMP_(bool) diff --git a/image/ClippedImage.h b/image/ClippedImage.h index f415d23ee8..52417b04c9 100644 --- a/image/ClippedImage.h +++ b/image/ClippedImage.h @@ -11,6 +11,8 @@ #include "mozilla/RefPtr.h" #include "mozilla/UniquePtr.h" +#include + namespace mozilla { namespace image { @@ -74,7 +76,7 @@ class ClippedImage : public ImageWrapper { virtual ~ClippedImage(); private: - Pair> GetFrameInternal( + std::pair> GetFrameInternal( const nsIntSize& aSize, const Maybe& aSVGContext, uint32_t aWhichFrame, uint32_t aFlags, float aOpacity); bool ShouldClip(); diff --git a/image/SurfaceCache.cpp b/image/SurfaceCache.cpp index e6c300615a..b29903cac6 100644 --- a/image/SurfaceCache.cpp +++ b/image/SurfaceCache.cpp @@ -15,7 +15,6 @@ #include "mozilla/DebugOnly.h" #include "mozilla/Likely.h" #include "mozilla/Move.h" -#include "mozilla/Pair.h" #include "mozilla/RefPtr.h" #include "mozilla/StaticMutex.h" #include "mozilla/StaticPrefs_image.h" diff --git a/image/test/crashtests/crashtests.list b/image/test/crashtests/crashtests.list index 2a0b55aa9e..cb693fca63 100644 --- a/image/test/crashtests/crashtests.list +++ b/image/test/crashtests/crashtests.list @@ -9,7 +9,7 @@ load 570451.png # Bug 1390704 - Skip on debug because it triggers a quadratic behavior that makes it take # so much time that it can trip on the reftest timeout of 5 minutes. skip-if(Android||isDebugBuild) load 694165-1.xhtml -load 681190.html +pref(canvas.mozgetasfile.enabled,true) load 681190.html load 732319-1.html load 844403-1.html load 856616.gif diff --git a/intl/build/components.conf b/intl/build/components.conf index f343378662..6afdea6608 100644 --- a/intl/build/components.conf +++ b/intl/build/components.conf @@ -4,8 +4,10 @@ Classes = [ { + 'js_name': 'strings', 'cid': '{d85a17c1-aa7c-11d2-9b8c-00805f8a16d9}', 'contract_ids': ['@mozilla.org/intl/stringbundle;1'], + 'interfaces': ['nsIStringBundleService'], 'type': 'nsStringBundleService', 'headers': ['/intl/strres/nsStringBundleService.h'], 'init_method': 'Init', diff --git a/intl/icu/README.md b/intl/icu/README.md new file mode 100644 index 0000000000..7caf30ceea --- /dev/null +++ b/intl/icu/README.md @@ -0,0 +1,238 @@ +# Introduction + +Internationalization (i18n, "i" then 18 letters then "n") is the process of handling data with respect to a particular locale: + +* The number 5 representing five US dollars might be formatted as + * "$5.00" in American English, + * "US$5.00" in Canadian English, or + * "5,00 $US" in French. +* A list of people's names in a phone book would sort + * in English alphabetically; but + * in German, where "ä"/"ö"/"ü" are often interchangeable with "ae"/"oe"/"ue", alphabetically but with vowels with umlauts treated as their two-vowel counterparts. +* The currency whose code is "CHF" might be formatted as + * "Swiss Franc" in English, but + * "franc suisse" in French. +* The Unix time 1590803313070 might format as the time string + * "9:48:33 PM Eastern Daylight Time" in American English, but + * "21:48:33 Nordamerikanische Ostküsten-Sommerzeit" in German. + +i18n encompasses far more than this, but you get the basic idea. + +# Internationalization in SpiderMonkey and Gecko + +SpiderMonkey implements extensive i18n capabilities through the [ECMAScript Internationalization API](https://tc39.es/ecma402/) and the global `Intl` object. Gecko requires i18n capabilities to implement text shaping, sort operations in some contexts, and various other features. + +SpiderMonkey and Gecko use [ICU](http://site.icu-project.org/), Internationalization Components for Unicode, to implement many low-level i18n operations. (Line breaking, implemented instead in `intl/lwbrk`, is a notable exception.) Gecko and SpiderMonkey also use ICU's implementations of certain i18n-_adjacent_ operations (for example, Unicode normalization). + +ICU date/time formatting functionality requires extensive knowledge of time zone names and when zone transitions occur. The IANA `tzdata` database supplies this information. + +A final note of caution: ICU carefully depends upon an exact Unicode version. Other parts of SpiderMonkey and Gecko have separate dependencies on an exact Unicode version. Updates to ICU and related components _must_ be synchronized with those updates so that the entirety of SpiderMonkey, and the entirety of Gecko including SpiderMonkey within it, advance to new Unicode versions in lockstep.[^lockstep] + +[^lockstep]: The steps involved in updating Gecko-in-general's Unicode version, and updating SpiderMonkey's code dependent on Unicode version, are [documented on WikiMO](https://wiki.mozilla.org/I18n:Updating_Unicode_version). + +# Building SpiderMonkey or Gecko with ICU + +SpiderMonkey and Gecko can be built using either a periodically-updated copy of ICU in `intl/icu/source` (using time zone data in `intl/tzdata/source`), or using a system-provided ICU library (dependent on its own `tzdata` information). Pass `--with-system-icu` when configuring to use system ICU. (Using system ICU will disable some `Intl` functionality, such as historically accurate time zone calculations, that can't be readily supported without a precisely-controlled ICU.) ICU version requirements advance fairly quickly as Gecko depends on features and bug fixes in newer ICU releases. You'll get a build error if you try to use an unsupported ICU. + +SpiderMonkey's `Intl` API may be built or disabled by configuring `--with-intl-api` (the default) or `--without-intl-api`. SpiderMonkey built without the `Intl` API doesn't require ICU. However, if you build without the `Intl` API, some non-`Intl` JavaScript functionality will not exist (`String.prototype.normalize`) or won't fully work (for example, `String.prototype.toLocale{Lower,Upper}Case` will not respect a provided locale, and the various `toLocaleString` functions have best-effort behavior). + +# Using ICU functionality in SpiderMonkey and Gecko + +ICU headers are considered system headers by the Gecko build system, so they must be listed in `config/system-headers.mozbuild`. Code that wishes to use ICU functionality may use `#include "unicode/unorm.h"` or similar to do so. + +Gecko and SpiderMonkey code may use ICU's stable C API (ICU4C). These functions are stable and shouldn't change as ICU updates occur. (ICU4C's `enum` initializers are not always stable: while initializer values are stable, new initializers are sometimes added, perhaps behind `#ifdef U_HIDE_DRAFT_API`. This may be necessary for exhaustive `switch`es to add `#ifdef`s around some `case`s.) + +Gecko and SpiderMonkey are strongly discouraged from using ICU's C++ API (unfortunately including all smart pointer classes), because the C++ API doesn't provide ICU4C's compatibility guarantees. Rarely, we tolerate C++ API use when no stable option exists. But the API has to "look" reasonably stable, and we usually want to start a discussion with upstream about adding a stable API to eventually use. Use symbols from `namespace icu` to access ICU C++ functionality. _Talk to the current imported-ICU owner (presently Jeff Walden) before you start doing any of this!_ + +# SpiderMonkey and Gecko's imported ICU + +## Build system + +The system for building ICU lives in `config/external/icu` and `intl/icu/icu_sources_data.py`. We generate a Mozilla-compatible build system rather than using ICU's build system. The build system is shared by SpiderMonkey and Gecko both. + +ICU includes functionality we never use, so we don't naively compile all of it. We extract the list of files to compile from `intl/icu/source/{common,i18n}/Makefile.in` and then apply a manually-maintained blacklist (stored in `intl/icu_sources_data.py`) when we update ICU. + +## Locale and time zone data + +ICU contains a considerable amount of raw locale data: formatting characteristics for each locale, strings for things like currencies and languages for each locale, localized time zone specifiers, and so on. This data lives in human-readable files in `intl/icu/source/data`. Time zone data in `intl/tzdata/source` is stored in partially-compiled formats (some of them only partly human-readable). + +However, a normal Gecko build never uses these files! Instead, both ICU and `tzdata` data are precompiled into a large, endian-specific `icudtNNE.dat` (`NN` = ICU version, `E` = endianness) file.[^why-icudt-not-rebuilt-every-time] That file is added to `config/external/icu/data/` and is checked into the Mozilla tree, to be directly incorporated into Gecko/SpiderMonkey builds. For size reasons, only the little-endian version is checked into the tree. + +ICU's locale data covers _all_ ICU internationalization features, including ones we never need. We trim locale data to size with a `intl/icu/data_filter.json` [data filter](https://github.com/unicode-org/icu/blob/master/docs/userguide/icu_data/buildtool.md) when compiling `icudtNNE.dat`. Removing _too much_ data won't _necessarily_ break the build, so it's important that we have automated tests for the locale data we actually use in order to detect mistakes. + +[^why-icudt-not-rebuilt-every-time]: `icudtNNE.dat` isn't compiled during a SpiderMonkey/Gecko build because it would require ICU command-line tools. And it's a pain to either compile and run them during the build, or to require them as build dependencies. + +## Local patching of ICU + +We generally don't patch our copy of ICU except for compelling need. When we do patch, we usually only apply reasonably small patches that have been reviewed and landed upstream (so that our patch will be obsolete when we next update ICU). + +Local patches are stored in the `intl/icu-patches` directory. They're applied when ICU is updated, so merely updating ICU files in place won't persist changes across an ICU update. + +## Updating imported code + +The process of updating imported i18n-relevant code is _semi_-automated. We use a series of shell and Python scripts to do the job. + +### Updating ICU + +New ICU versions are announced on the [icu-announce](https://lists.sourceforge.net/lists/listinfo/icu-announce) mailing list. Both release candidates and actual releases are announced here. It's a good idea to attempt to update ICU when a release candidate is announced, just in case some serious problem is present (especially one that would be painful to fix through local patching). + +`intl/update-icu.sh` updates our ICU to a given ICU release:[^icu-git-argument] + +```bash +$ cd "$topsrcdir/intl" +$ # Ensure certain Python modules in the tree are accessible when updating. +$ export PYTHONPATH="$topsrcdir/build/pymake/:$topsrcdir/python/mozbuild/" +$ # +$ ./update-icu.sh https://github.com/unicode-org/icu.git release-67-1 +``` + +[^icu-git-argument]: The ICU Git URL argument lets you update from a local ICU clone. This can speed up work when you're updating to a new ICU release and need to adjust or add new local patches. + +But usually you'll want to update to the latest commit from the corresponding ICU maintenance branch so that you pick up fixes landed post-release: + +```bash +$ cd "$topsrcdir/intl" +$ # Ensure certain Python modules in the tree are accessible when updating. +$ export PYTHONPATH="$topsrcdir/build/pymake/:$topsrcdir/python/mozbuild/" +$ # +$ ./update-icu.sh https://github.com/unicode-org/icu.git maint/maint-67 +``` + +Updating ICU will also update the language tag registry (which records language tag semantics needed to correctly implement `Intl` functionality). Therefore it's likely necessary to update SpiderMonkey's language tag handling after running this[^update-icu-warning-langtags]. See below where the `langtags` mode of `make_intl_data.py` is discussed. + +[^update-icu-warning-langtags]: `update-icu.sh` will print a notice as a reminder of this: + + ```bash + INFO: Please run 'js/src/builtin/intl/make_intl_data.py langtags' to update additional language tag files for SpiderMonkey. + ``` + +`update-icu.sh` is intended for _replayability_, not for hands-off runnability. It downloads ICU source, prunes various irrelevant files, replaces `intl/icu/source` with the new files -- and then blindly applies local patches in fixed order. + +Often a local patch won't apply, or new patches must be applied to successfully build. In this case you'll have to manually edit `update-icu.sh` to abort after only _some_ patches have been applied, make whatever changes are necessary by hand, generate a new/updated patch file by hand, then carefully reattempt updating. (The people who have updated ICU in the past, usually jwalden and anba, follow this awkward process and don't have good ideas on how to improve it.) + +Any time ICU is updated, you'll need to fully rebuild whichever of SpiderMonkey or Gecko you're building. For SpiderMonkey, delete your object directory and reconfigure from scratch. For Gecko, change the message in the top-level [`CLOBBER`](https://searchfox.org/mozilla-central/source/CLOBBER) file. + +### Updating tzdata + +ICU contains a copy of `tzdata`, but that copy is whatever `tzdata` release was current at the time the ICU release was finalized. Time zone data changes much more often than that: every time some national legislature or tinpot dictator decides to alter time zones.[^tzdata-release-frequency] The [`tz-announce`](https://mm.icann.org/pipermail/tz-announce/) mailing list announces changes as they occur. (Note that we can't _immediately_ update when a release occurs: ICU's [`icu-data`](https://github.com/unicode-org/icu-data) repository must be updated before we can update our `tzdata`.) + +[^tzdata-release-frequency]: To give a sense of how frequently `tzdata` is updated, and the irregularity of releases over time: + + * 2019 had three `tzdata` releases, 2019a through 2019c. + * 2018 had nine `tzdata` releases, 2018a through 2018i. + * 2017 had three `tzdata` releases, 2017a through 2017c. + +Therefore, either (usually) after you update ICU _or_ when a new `tzdata` release occurs, you'll need to update our imported `tzdata` files. (If you do need to update time zone data, note that you'll also need to additionally update SpiderMonkey's time zone handling, described further below.) This also suitably updates `config/external/icu/data/icudtNNE.data`. (If you've just run `update-icu.sh`, it will warn you that you need to do this.[^update-icu-warning-old-tzdata]) + +[^update-icu-warning-old-tzdata]: For example: + + ``` + WARN: Local tzdata (2020a) is newer than ICU tzdata (2019c), please run './update-tzdata.sh 2020a' + ``` + +First, make sure you have a usable `icupkg` on your system.[^icupkg-on-system] Then run the `update-tzdata.sh` script to update `intl/tzdata` and `icudtNNE.data`: + +```bash +$ cd "$topsrcdir/intl" +$ ./update-tzdata.sh 2020a # or whatever the latest release is +``` + +[^icupkg-on-system]: To install `icupkg` on your system: + + * On Fedora, use `sudo dnf install icu`. + * On Ubuntu, use `sudo apt-get install icu-devtools`. + * On Mac OS X, use `brew install icu4c`. + * On Windows, you'll need to [download a binary build of ICU for Windows](https://github.com/unicode-org/icu/releases/tag/release-67-1) and use the `bin/icupkg.exe` or `bin64/icupkg.exe` utility inside it. + + If you're on Windows, or for some reason you don't want to use the `icupkg` now in your `$PATH`, you can manually specify it on the command line using the `-e /path/to/icupkg` flag: + + ```bash + $ cd "$topsrcdir/intl" + $ ./update-tzdata.sh -e /path/to/icupkg 2020a # or whatever the latest release is + ``` + + _In principle_, the `icupkg` you use _should_ be the one from the ICU release/maintenance branch being built: if there's a mismatch, you might encounter an ICU "format version not supported" error. If you're on Windows, make sure to download a binary build for that release/branch. On other platforms, you might have to build your own ICU from source. The steps required to do this are left as an exercise for the reader. (In the somewhat longer term, the update commands might be changed to do this themselves.) + +If `tzdata` must be updated on trunk, you'll almost certainly have to backport the update to Beta and ESR. Don't attempt to backport the literal patch; just run the appropriate commands documented here to do so. + +### Updating SpiderMonkey `Intl` data + +SpiderMonkey itself can't blindly invoke ICU to perform every i18n operation, because sometimes ICU behavior deviates from what web specifications require. Therefore, when ICU is updated, we also must update SpiderMonkey itself as well (including various generated tests). Such updating is performed using the various modes of `js/src/builtin/make_intl_data.py`. + +#### Updating SpiderMonkey time zone handling + +The ECMAScript Internationalization API requires that time zone identifiers (`America/New_York`, `Antarctica/McMurdo`, etc.) be interpreted according to [`IANA`](https://www.iana.org/time-zones) semantics. Unfortunately, ICU doesn't precisely implement those semantics. (See comments in `js/src/builtin/intl/SharedIntlData.h` for details.) Therefore SpiderMonkey has to do certain pre- and post-processing based on what's in IANA but not in ICU, and what's in ICU that isn't in IANA. + +Use `make_intl_data.py`'s `tzdata` mode to update time zone information: + +```bash +$ cd "$topsrcdir/js/src/builtin/intl" +$ # make_intl_data.py requires yaml. +$ export PYTHONPATH="$topsrcdir/third_party/python/pyyaml/lib3/" +$ python3 ./make_intl_data.py tzdata +``` + +The `tzdata` mode accepts two optional arguments that generally will not be needed: + +* **`--tz`** will act using data from a local `tzdata/` directory containing raw `tzdata` source (note that this is _not_ the same as what is in `intl/tzdata/source`). It may be useful to help debug problems that arise during an update. +* **`--ignore-backzone`** will omit time zone information before 1970. SpiderMonkey and Gecko include this information by default. However, because (by deliberate policy) `tzdata` information before 1970 is not reliable to the same degree as data since 1970, and backzone data has a size cost, a SpiderMonkey embedding or custom Gecko build might decide to omit it. + +#### Updating SpiderMonkey language tag handling + +Language tags (`en`, `de-CH`, `ar-u-ca-islamicc`, and so on) are the primary means of specifying localization characteristics. The ECMAScript Internationalization API supports certain operations that depend upon the current state of the language tag registry (stored in the Unicode Common Locale Data Repository, CLDR, a repository of all locale-specific characteristics) that specifies subtag semantics: + +* `Intl.getCanonicalLocales` and `Intl.Locale` must replace alias subtags with their preferred forms. For example, `ar-u-ca-islamic-civil` uses the preferred Islamic calendar subtag, while `ar-u-ca-islamicc` uses an alias. +* `Intl.Locale.prototype.maximize` and `Intl.Locale.prototype.minimize` accept a language tag and add or remove "likely" subtags from it. For example, `de` most likely refers to German using Latin script in Germany, so it maximizes to `de-Latn-DE` -- and in reverse, `de-Latn-DE` minimizes to simply `de`. + +These decisions vary over time: as countries change[^soviet-union], as customs change, as language prevalence in regions varies, etc. + +[^soviet-union]: For just one relevant example, the breakup of the Soviet Union is the cause of numerous entries in the language tag registry. `ru-SU`, Russian as used in the Soviet Union, is now expressed as `ru-RU`, Russian as used in Russia; `ab-SU`, Abkhazian as used in the Soviet Union, is now expressed as `ab-GE`, Abkhazian as used in Georgia; and so on for all the other satellite states. + +Use `make_intl_data.py`'s `langtags` mode to update language tag information to the same CLDR version used by ICU: + +```bash +$ cd "$topsrcdir/js/src/builtin/intl" +$ # make_intl_data.py requires yaml. +$ export PYTHONPATH="$topsrcdir/third_party/python/pyyaml/lib3/" +$ python3 ./make_intl_data.py langtags +``` + +The CLDR version used will be printed in the header of CLDR-sensitive generated files. For example, `js/src/builtin/intl/LanguageTagGenerated.cpp` currently begins with: + +```c++ +// Generated by make_intl_data.py. DO NOT EDIT. +// Version: CLDR-37 +// URL: https://unicode.org/Public/cldr/37/core.zip +``` + +#### Updating SpiderMonkey currency support + +Currencies use different numbers of fractional digits in their preferred formatting. Most currencies use two decimal digits; a handful use no fractional digits or some other number. Currency fractional digit is maintained by ISO and must be updated as currencies change their preferred fractional digits or new currencies arise that don't use two decimal digits. + +Currency updates are fairly uncommon, so it'll be rare to need to update currency info. A [newsletter](https://www.currency-iso.org/en/home/amendments/newsletter.html) periodically sends updates about changes. + +Use `make_intl_data.py`'s `currency` mode to update currency fractional digit information: + +```bash +$ cd "$topsrcdir/js/src/builtin/intl" +$ # make_intl_data.py requires yaml. +$ export PYTHONPATH="$topsrcdir/third_party/python/pyyaml/lib3/" +$ python3 ./make_intl_data.py currency +``` + +#### Updating SpiderMonkey measurement formatting support + +The `Intl` API supports formatting numbers as measurement units (for example, "17 meters" or "42 meters per second"). It specifies a list of units that must be supported, that we centrally record in `js/src/builtin/intl/SanctionedSimpleUnitIdentifiers.yaml`, that we verify are supported by ICU and generate supporting files from. + +If `Intl`'s list of supported units is ever updated, two separate changes will be required. + +First, `intl/icu/data_filter.json` must be updated to incorporate localized strings for the new unit. These strings are stored in `icudtNNE.dat`, so you'll have to re-update ICU (and likely reimport `tzdata` as well, if it's been updated since the last ICU update) to rewrite that file. + +Second, use `make_intl_data.py`'s `units` mode to update unit handling and associated tests in SpiderMonkey: + +```bash +$ cd "$topsrcdir/js/src/builtin/intl" +$ # make_intl_data.py requires yaml. +$ export PYTHONPATH="$topsrcdir/third_party/python/pyyaml/lib3/" +$ python3 ./make_intl_data.py units +``` diff --git a/intl/icu_sources_data.py b/intl/icu_sources_data.py index 17bf8a19df..ee63cd7f67 100644 --- a/intl/icu_sources_data.py +++ b/intl/icu_sources_data.py @@ -83,7 +83,6 @@ UNUSED_SOURCES = sets.Set([ 'intl/icu/source/i18n/translit.cpp', 'intl/icu/source/i18n/transreg.cpp', 'intl/icu/source/i18n/tridpars.cpp', - 'intl/icu/source/i18n/udateintervalformat.cpp', 'intl/icu/source/i18n/unesctrn.cpp', 'intl/icu/source/i18n/uni2name.cpp', 'intl/icu/source/i18n/uregexc.cpp', diff --git a/intl/l10n/Fluent.jsm b/intl/l10n/Fluent.jsm index e984b5bfcf..efbf28cd23 100644 --- a/intl/l10n/Fluent.jsm +++ b/intl/l10n/Fluent.jsm @@ -14,7 +14,7 @@ */ -/* fluent@0.12.0 */ +/* fluent-bundle@0.14.1 */ /* global Intl */ @@ -27,15 +27,14 @@ */ class FluentType { /** - * Create an `FluentType` instance. + * Create a `FluentType` instance. * - * @param {Any} value - JavaScript value to wrap. - * @param {Object} opts - Configuration. + * @param {Any} value - JavaScript value to wrap. * @returns {FluentType} */ - constructor(value, opts) { + constructor(value) { + /** The wrapped native value. */ this.value = value; - this.opts = opts; } /** @@ -51,55 +50,102 @@ class FluentType { * Format this instance of `FluentType` to a string. * * Formatted values are suitable for use outside of the `FluentBundle`. - * This method can use `Intl` formatters memoized by the `FluentBundle` - * instance passed as an argument. + * This method can use `Intl` formatters available through the `scope` + * argument. * - * @param {FluentBundle} [bundle] + * @abstract + * @param {Scope} scope * @returns {string} */ - toString() { + toString(scope) { // eslint-disable-line no-unused-vars throw new Error("Subclasses of FluentType must implement toString."); } } +/** + * A `FluentType` representing no correct value. + */ class FluentNone extends FluentType { + /** + * Create an instance of `FluentNone` with an optional fallback value. + * @param {string} value - The fallback value of this `FluentNone`. + * @returns {FluentType} + */ + constructor(value = "???") { + super(value); + } + + /** + * Format this `FluentNone` to the fallback string. + * @returns {string} + */ toString() { - return this.value || "???"; + return `{${this.value}}`; } } +/** + * A `FluentType` representing a number. + */ class FluentNumber extends FluentType { + /** + * Create an instance of `FluentNumber` with options to the + * `Intl.NumberFormat` constructor. + * @param {number} value + * @param {Intl.NumberFormatOptions} opts + * @returns {FluentType} + */ constructor(value, opts) { - super(parseFloat(value), opts); + super(value); + /** Options passed to Intl.NumberFormat. */ + this.opts = opts; } - toString(bundle) { + /** + * Format this `FluentNumber` to a string. + * @param {Scope} scope + * @returns {string} + */ + toString(scope) { try { - const nf = bundle._memoizeIntlObject( - Intl.NumberFormat, this.opts - ); + const nf = scope.memoizeIntlObject(Intl.NumberFormat, this.opts); return nf.format(this.value); - } catch (e) { - // XXX Report the error. - return this.value; + } catch (err) { + scope.reportError(err); + return this.value.toString(10); } } } +/** + * A `FluentType` representing a date and time. + */ class FluentDateTime extends FluentType { + /** + * Create an instance of `FluentDateTime` with options to the + * `Intl.DateTimeFormat` constructor. + * @param {number} value - timestamp in milliseconds + * @param {Intl.DateTimeFormatOptions} opts + * @returns {FluentType} + */ constructor(value, opts) { - super(new Date(value), opts); + super(value); + /** Options passed to Intl.DateTimeFormat. */ + this.opts = opts; } - toString(bundle) { + /** + * Format this `FluentDateTime` to a string. + * @param {Scope} scope + * @returns {string} + */ + toString(scope) { try { - const dtf = bundle._memoizeIntlObject( - Intl.DateTimeFormat, this.opts - ); + const dtf = scope.memoizeIntlObject(Intl.DateTimeFormat, this.opts); return dtf.format(this.value); - } catch (e) { - // XXX Report the error. - return this.value; + } catch (err) { + scope.reportError(err); + return (new Date(this.value)).toISOString(); } } } @@ -117,13 +163,6 @@ class FluentDateTime extends FluentType { * `FluentType`. Functions must return `FluentType` objects as well. */ -const builtins = { - "NUMBER": ([arg], opts) => - new FluentNumber(arg.valueOf(), merge(arg.opts, opts)), - "DATETIME": ([arg], opts) => - new FluentDateTime(arg.valueOf(), merge(arg.opts, opts)), -}; - function merge(argopts, opts) { return Object.assign({}, argopts, values(opts)); } @@ -136,10 +175,44 @@ function values(opts) { return unwrapped; } +function NUMBER([arg], opts) { + if (arg instanceof FluentNone) { + return new FluentNone(`NUMBER(${arg.valueOf()})`); + } + + let value = Number(arg.valueOf()); + if (Number.isNaN(value)) { + throw new TypeError("Invalid argument to NUMBER"); + } + + return new FluentNumber(value, merge(arg.opts, opts)); +} + +function DATETIME([arg], opts) { + if (arg instanceof FluentNone) { + return new FluentNone(`DATETIME(${arg.valueOf()})`); + } + + let value = Number(arg.valueOf()); + if (Number.isNaN(value)) { + throw new TypeError("Invalid argument to DATETIME"); + } + + return new FluentDateTime(value, merge(arg.opts, opts)); +} + +const builtins = /*#__PURE__*/Object.freeze({ + __proto__: null, + NUMBER: NUMBER, + DATETIME: DATETIME +}); + /* global Intl */ -// Prevent expansion of too long placeables. -const MAX_PLACEABLE_LENGTH = 2500; +// The maximum number of placeables which can be expanded in a single call to +// `formatPattern`. The limit protects against the Billion Laughs and Quadratic +// Blowup attacks. See https://msdn.microsoft.com/en-us/magazine/ee335713.aspx. +const MAX_PLACEABLES = 100; // Unicode bidi isolation characters. const FSI = "\u2068"; @@ -147,7 +220,7 @@ const PDI = "\u2069"; // Helper: match a variant key to the given selector. -function match(bundle, selector, key) { +function match(scope, selector, key) { if (key === selector) { // Both are strings. return true; @@ -161,8 +234,8 @@ function match(bundle, selector, key) { } if (selector instanceof FluentNumber && typeof key === "string") { - let category = bundle - ._memoizeIntlObject(Intl.PluralRules, selector.opts) + let category = scope + .memoizeIntlObject(Intl.PluralRules, selector.opts) .select(selector.value); if (key === category) { return true; @@ -175,49 +248,31 @@ function match(bundle, selector, key) { // Helper: resolve the default variant from a list of variants. function getDefault(scope, variants, star) { if (variants[star]) { - return Type(scope, variants[star]); + return resolvePattern(scope, variants[star].value); } - scope.errors.push(new RangeError("No default")); + scope.reportError(new RangeError("No default")); return new FluentNone(); } // Helper: resolve arguments to a call expression. function getArguments(scope, args) { const positional = []; - const named = {}; + const named = Object.create(null); for (const arg of args) { if (arg.type === "narg") { - named[arg.name] = Type(scope, arg.value); + named[arg.name] = resolveExpression(scope, arg.value); } else { - positional.push(Type(scope, arg)); + positional.push(resolveExpression(scope, arg)); } } - return [positional, named]; + return {positional, named}; } // Resolve an expression to a Fluent type. -function Type(scope, expr) { - // A fast-path for strings which are the most common case. Since they - // natively have the `toString` method they can be used as if they were - // a FluentType instance without incurring the cost of creating one. - if (typeof expr === "string") { - return scope.bundle._transform(expr); - } - - // A fast-path for `FluentNone` which doesn't require any additional logic. - if (expr instanceof FluentNone) { - return expr; - } - - // The Runtime AST (Entries) encodes patterns (complex strings with - // placeables) as Arrays. - if (Array.isArray(expr)) { - return Pattern(scope, expr); - } - +function resolveExpression(scope, expr) { switch (expr.type) { case "str": return expr.value; @@ -235,15 +290,6 @@ function Type(scope, expr) { return FunctionReference(scope, expr); case "select": return SelectExpression(scope, expr); - case undefined: { - // If it's a node with a value, resolve the value. - if (expr.value !== null && expr.value !== undefined) { - return Type(scope, expr.value); - } - - scope.errors.push(new RangeError("No value")); - return new FluentNone(); - } default: return new FluentNone(); } @@ -251,15 +297,26 @@ function Type(scope, expr) { // Resolve a reference to a variable. function VariableReference(scope, {name}) { - if (!scope.args || !scope.args.hasOwnProperty(name)) { - if (scope.insideTermReference === false) { - scope.errors.push(new ReferenceError(`Unknown variable: ${name}`)); + let arg; + if (scope.params) { + // We're inside a TermReference. It's OK to reference undefined parameters. + if (Object.prototype.hasOwnProperty.call(scope.params, name)) { + arg = scope.params[name]; + } else { + return new FluentNone(`$${name}`); } + } else if ( + scope.args + && Object.prototype.hasOwnProperty.call(scope.args, name) + ) { + // We're in the top-level Pattern or inside a MessageReference. Missing + // variables references produce ReferenceErrors. + arg = scope.args[name]; + } else { + scope.reportError(new ReferenceError(`Unknown variable: $${name}`)); return new FluentNone(`$${name}`); } - const arg = scope.args[name]; - // Return early if the argument already is an instance of FluentType. if (arg instanceof FluentType) { return arg; @@ -273,11 +330,11 @@ function VariableReference(scope, {name}) { return new FluentNumber(arg); case "object": if (arg instanceof Date) { - return new FluentDateTime(arg); + return new FluentDateTime(arg.getTime()); } default: - scope.errors.push( - new TypeError(`Unsupported variable type: ${name}, ${typeof arg}`) + scope.reportError( + new TypeError(`Variable type not supported: $${name}, ${typeof arg}`) ); return new FluentNone(`$${name}`); } @@ -287,21 +344,25 @@ function VariableReference(scope, {name}) { function MessageReference(scope, {name, attr}) { const message = scope.bundle._messages.get(name); if (!message) { - const err = new ReferenceError(`Unknown message: ${name}`); - scope.errors.push(err); + scope.reportError(new ReferenceError(`Unknown message: ${name}`)); return new FluentNone(name); } if (attr) { - const attribute = message.attrs && message.attrs[attr]; + const attribute = message.attributes[attr]; if (attribute) { - return Type(scope, attribute); + return resolvePattern(scope, attribute); } - scope.errors.push(new ReferenceError(`Unknown attribute: ${attr}`)); - return Type(scope, message); + scope.reportError(new ReferenceError(`Unknown attribute: ${attr}`)); + return new FluentNone(`${name}.${attr}`); } - return Type(scope, message); + if (message.value) { + return resolvePattern(scope, message.value); + } + + scope.reportError(new ReferenceError(`No value: ${name}`)); + return new FluentNone(name); } // Resolve a call to a Term with key-value arguments. @@ -309,25 +370,27 @@ function TermReference(scope, {name, attr, args}) { const id = `-${name}`; const term = scope.bundle._terms.get(id); if (!term) { - const err = new ReferenceError(`Unknown term: ${id}`); - scope.errors.push(err); + scope.reportError(new ReferenceError(`Unknown term: ${id}`)); return new FluentNone(id); } - // Every TermReference has its own args. - const [, keyargs] = getArguments(scope, args); - const local = {...scope, args: keyargs, insideTermReference: true}; - if (attr) { - const attribute = term.attrs && term.attrs[attr]; + const attribute = term.attributes[attr]; if (attribute) { - return Type(local, attribute); + // Every TermReference has its own variables. + scope.params = getArguments(scope, args).named; + const resolved = resolvePattern(scope, attribute); + scope.params = null; + return resolved; } - scope.errors.push(new ReferenceError(`Unknown attribute: ${attr}`)); - return Type(local, term); + scope.reportError(new ReferenceError(`Unknown attribute: ${attr}`)); + return new FluentNone(`${id}.${attr}`); } - return Type(local, term); + scope.params = getArguments(scope, args).named; + const resolved = resolvePattern(scope, term.value); + scope.params = null; + return resolved; } // Resolve a call to a Function with positional and key-value arguments. @@ -336,47 +399,46 @@ function FunctionReference(scope, {name, args}) { // the `FluentBundle` constructor. const func = scope.bundle._functions[name] || builtins[name]; if (!func) { - scope.errors.push(new ReferenceError(`Unknown function: ${name}()`)); + scope.reportError(new ReferenceError(`Unknown function: ${name}()`)); return new FluentNone(`${name}()`); } if (typeof func !== "function") { - scope.errors.push(new TypeError(`Function ${name}() is not callable`)); + scope.reportError(new TypeError(`Function ${name}() is not callable`)); return new FluentNone(`${name}()`); } try { - return func(...getArguments(scope, args)); - } catch (e) { - // XXX Report errors. - return new FluentNone(); + let resolved = getArguments(scope, args); + return func(resolved.positional, resolved.named); + } catch (err) { + scope.reportError(err); + return new FluentNone(`${name}()`); } } // Resolve a select expression to the member object. function SelectExpression(scope, {selector, variants, star}) { - let sel = Type(scope, selector); + let sel = resolveExpression(scope, selector); if (sel instanceof FluentNone) { - const variant = getDefault(scope, variants, star); - return Type(scope, variant); + return getDefault(scope, variants, star); } // Match the selector against keys of each variant, in order. for (const variant of variants) { - const key = Type(scope, variant.key); - if (match(scope.bundle, sel, key)) { - return Type(scope, variant); + const key = resolveExpression(scope, variant.key); + if (match(scope, sel, key)) { + return resolvePattern(scope, variant.value); } } - const variant = getDefault(scope, variants, star); - return Type(scope, variant); + return getDefault(scope, variants, star); } // Resolve a pattern (a complex string with placeables). -function Pattern(scope, ptn) { +function resolveComplexPattern(scope, ptn) { if (scope.dirty.has(ptn)) { - scope.errors.push(new RangeError("Cyclic reference")); + scope.reportError(new RangeError("Cyclic reference")); return new FluentNone(); } @@ -394,23 +456,24 @@ function Pattern(scope, ptn) { continue; } - const part = Type(scope, elem).toString(scope.bundle); + scope.placeables++; + if (scope.placeables > MAX_PLACEABLES) { + scope.dirty.delete(ptn); + // This is a fatal error which causes the resolver to instantly bail out + // on this pattern. The length check protects against excessive memory + // usage, and throwing protects against eating up the CPU when long + // placeables are deeply nested. + throw new RangeError( + `Too many placeables expanded: ${scope.placeables}, ` + + `max allowed is ${MAX_PLACEABLES}` + ); + } if (useIsolating) { result.push(FSI); } - if (part.length > MAX_PLACEABLE_LENGTH) { - scope.errors.push( - new RangeError( - "Too many characters in placeable " + - `(${part.length}, max allowed is ${MAX_PLACEABLE_LENGTH})` - ) - ); - result.push(part.slice(MAX_PLACEABLE_LENGTH)); - } else { - result.push(part); - } + result.push(resolveExpression(scope, elem).toString(scope)); if (useIsolating) { result.push(PDI); @@ -421,28 +484,237 @@ function Pattern(scope, ptn) { return result.join(""); } +// Resolve a simple or a complex Pattern to a FluentString (which is really the +// string primitive). +function resolvePattern(scope, node) { + // Resolve a simple pattern. + if (typeof node === "string") { + return scope.bundle._transform(node); + } + + return resolveComplexPattern(scope, node); +} + +class Scope { + constructor(bundle, errors, args) { + /** The bundle for which the given resolution is happening. */ + this.bundle = bundle; + /** The list of errors collected while resolving. */ + this.errors = errors; + /** A dict of developer-provided variables. */ + this.args = args; + + /** The Set of patterns already encountered during this resolution. + * Used to detect and prevent cyclic resolutions. */ + this.dirty = new WeakSet(); + /** A dict of parameters passed to a TermReference. */ + this.params = null; + /** The running count of placeables resolved so far. Used to detect the + * Billion Laughs and Quadratic Blowup attacks. */ + this.placeables = 0; + } + + reportError(error) { + if (!this.errors) { + throw error; + } + this.errors.push(error); + } + + memoizeIntlObject(ctor, opts) { + let cache = this.bundle._intls.get(ctor); + if (!cache) { + cache = {}; + this.bundle._intls.set(ctor, cache); + } + let id = JSON.stringify(opts); + if (!cache[id]) { + cache[id] = new ctor(this.bundle.locales, opts); + } + return cache[id]; + } +} + /** - * Format a translation into a string. - * - * @param {FluentBundle} bundle - * A FluentBundle instance which will be used to resolve the - * contextual information of the message. - * @param {Object} args - * List of arguments provided by the developer which can be accessed - * from the message. - * @param {Object} message - * An object with the Message to be resolved. - * @param {Array} errors - * An error array that any encountered errors will be appended to. - * @returns {FluentType} + * Message bundles are single-language stores of translation resources. They are + * responsible for formatting message values and attributes to strings. */ -function resolve(bundle, args, message, errors = []) { - const scope = { - bundle, args, errors, dirty: new WeakSet(), - // TermReferences are resolved in a new scope. - insideTermReference: false, - }; - return Type(scope, message).toString(bundle); +class FluentBundle { + /** + * Create an instance of `FluentBundle`. + * + * The `locales` argument is used to instantiate `Intl` formatters used by + * translations. The `options` object can be used to configure the bundle. + * + * Examples: + * + * let bundle = new FluentBundle(["en-US", "en"]); + * + * let bundle = new FluentBundle(locales, {useIsolating: false}); + * + * let bundle = new FluentBundle(locales, { + * useIsolating: true, + * functions: { + * NODE_ENV: () => process.env.NODE_ENV + * } + * }); + * + * Available options: + * + * - `functions` - an object of additional functions available to + * translations as builtins. + * + * - `useIsolating` - boolean specifying whether to use Unicode isolation + * marks (FSI, PDI) for bidi interpolations. Default: `true`. + * + * - `transform` - a function used to transform string parts of patterns. + * + * @param {(string|Array.)} locales - The locales of the bundle + * @param {Object} [options] + * @returns {FluentBundle} + */ + constructor(locales, { + functions = {}, + useIsolating = true, + transform = v => v, + } = {}) { + this.locales = Array.isArray(locales) ? locales : [locales]; + + this._terms = new Map(); + this._messages = new Map(); + this._functions = functions; + this._useIsolating = useIsolating; + this._transform = transform; + this._intls = new WeakMap(); + } + + /** + * Check if a message is present in the bundle. + * + * @param {string} id - The identifier of the message to check. + * @returns {bool} + */ + hasMessage(id) { + return this._messages.has(id); + } + + /** + * Return a raw unformatted message object from the bundle. + * + * Raw messages are `{value, attributes}` shapes containing translation units + * called `Patterns`. `Patterns` are implementation-specific; they should be + * treated as black boxes and formatted with `FluentBundle.formatPattern`. + * + * interface RawMessage { + * value: Pattern | null; + * attributes: Record; + * } + * + * @param {string} id - The identifier of the message to check. + * @returns {{value: ?Pattern, attributes: Object.}} + */ + getMessage(id) { + return this._messages.get(id); + } + + /** + * Add a translation resource to the bundle. + * + * The translation resource must be an instance of `FluentResource`. + * + * let res = new FluentResource("foo = Foo"); + * bundle.addResource(res); + * bundle.getMessage("foo"); + * // → {value: .., attributes: {..}} + * + * Available options: + * + * - `allowOverrides` - boolean specifying whether it's allowed to override + * an existing message or term with a new value. Default: `false`. + * + * @param {FluentResource} res - FluentResource object. + * @param {Object} [options] + * @returns {Array.} + */ + addResource(res, { + allowOverrides = false, + } = {}) { + const errors = []; + + for (let i = 0; i < res.body.length; i++) { + let entry = res.body[i]; + if (entry.id.startsWith("-")) { + // Identifiers starting with a dash (-) define terms. Terms are private + // and cannot be retrieved from FluentBundle. + if (allowOverrides === false && this._terms.has(entry.id)) { + errors.push(`Attempt to override an existing term: "${entry.id}"`); + continue; + } + this._terms.set(entry.id, entry); + } else { + if (allowOverrides === false && this._messages.has(entry.id)) { + errors.push(`Attempt to override an existing message: "${entry.id}"`); + continue; + } + this._messages.set(entry.id, entry); + } + } + + return errors; + } + + /** + * Format a `Pattern` to a string. + * + * Format a raw `Pattern` into a string. `args` will be used to resolve + * references to variables passed as arguments to the translation. + * + * In case of errors `formatPattern` will try to salvage as much of the + * translation as possible and will still return a string. For performance + * reasons, the encountered errors are not returned but instead are appended + * to the `errors` array passed as the third argument. + * + * let errors = []; + * bundle.addResource( + * new FluentResource("hello = Hello, {$name}!")); + * + * let hello = bundle.getMessage("hello"); + * if (hello.value) { + * bundle.formatPattern(hello.value, {name: "Jane"}, errors); + * // Returns "Hello, Jane!" and `errors` is empty. + * + * bundle.formatPattern(hello.value, undefined, errors); + * // Returns "Hello, {$name}!" and `errors` is now: + * // [] + * } + * + * If `errors` is omitted, the first encountered error will be thrown. + * + * @param {Pattern} pattern + * @param {?Object} args + * @param {?Array.} errors + * @returns {string} + */ + formatPattern(pattern, args, errors) { + // Resolve a simple pattern without creating a scope. No error handling is + // required; by definition simple patterns don't have placeables. + if (typeof pattern === "string") { + return this._transform(pattern); + } + + // Resolve a complex pattern. + let scope = new Scope(this, errors, args); + try { + let value = resolveComplexPattern(scope, pattern); + return value.toString(scope); + } catch (err) { + if (scope.errors) { + scope.errors.push(err); + return new FluentNone().toString(scope); + } + throw err; + } + } } class FluentError extends Error {} @@ -495,21 +767,18 @@ const TOKEN_COLON = /\s*:\s*/y; const TOKEN_COMMA = /\s*,?\s*/y; const TOKEN_BLANK = /\s+/y; -// Maximum number of placeables in a single Pattern to protect against Quadratic -// Blowup attacks. See https://msdn.microsoft.com/en-us/magazine/ee335713.aspx. -const MAX_PLACEABLES = 100; - /** - * Fluent Resource is a structure storing a map of parsed localization entries. + * Fluent Resource is a structure storing parsed localization entries. */ -class FluentResource extends Map { - /** - * Create a new FluentResource from Fluent code. - */ - static fromString(source) { +class FluentResource { + constructor(source) { + this.body = this._parse(source); + } + + _parse(source) { RE_MESSAGE_START.lastIndex = 0; - let resource = new this(); + let resource = []; let cursor = 0; // Iterate over the beginnings of messages and terms to efficiently skip @@ -522,7 +791,7 @@ class FluentResource extends Map { cursor = RE_MESSAGE_START.lastIndex; try { - resource.set(next[1], parseMessage()); + resource.push(parseMessage(next[1])); } catch (err) { if (err instanceof FluentError) { // Don't report any Fluent syntax errors. Skip directly to the @@ -535,7 +804,8 @@ class FluentResource extends Map { return resource; - // The parser implementation is inlined below for performance reasons. + // The parser implementation is inlined below for performance reasons, + // as well as for convenience of accessing `source` and `cursor`. // The parser focuses on minimizing the number of false negatives at the // expense of increasing the risk of false positives. In other words, it @@ -597,22 +867,19 @@ class FluentResource extends Map { return match(re)[1]; } - function parseMessage() { + function parseMessage(id) { let value = parsePattern(); - let attrs = parseAttributes(); + let attributes = parseAttributes(); - if (attrs === null) { - if (value === null) { - throw new FluentError("Expected message value or attributes"); - } - return value; + if (value === null && Object.keys(attributes).length === 0) { + throw new FluentError("Expected message value or attributes"); } - return {value, attrs}; + return {id, value, attributes}; } function parseAttributes() { - let attrs = {}; + let attrs = Object.create(null); while (test(RE_ATTRIBUTE_START)) { let name = match1(RE_ATTRIBUTE_START); @@ -623,7 +890,7 @@ class FluentResource extends Map { attrs[name] = value; } - return Object.keys(attrs).length > 0 ? attrs : null; + return attrs; } function parsePattern() { @@ -664,8 +931,6 @@ class FluentResource extends Map { // Parse a complex pattern as an array of elements. function parsePatternElements(elements = [], commonIndent) { - let placeableCount = 0; - while (true) { if (test(RE_TEXT_RUN)) { elements.push(match1(RE_TEXT_RUN)); @@ -673,9 +938,6 @@ class FluentResource extends Map { } if (source[cursor] === "{") { - if (++placeableCount > MAX_PLACEABLES) { - throw new FluentError("Too many placeables"); - } elements.push(parsePlaceable()); continue; } @@ -705,9 +967,6 @@ class FluentResource extends Map { if (element.type === "indent") { // Dedent indented lines by the maximum common indent. element = element.value.slice(0, element.value.length - commonIndent); - } else if (element.type === "str") { - // Optimize StringLiterals into their value. - element = element.value; } if (element) { baked.push(element); @@ -837,7 +1096,7 @@ class FluentResource extends Map { consumeToken(TOKEN_BRACKET_OPEN, FluentError); let key = test(RE_NUMBER_LITERAL) ? parseNumberLiteral() - : match1(RE_IDENTIFIER); + : {type: "str", value: match1(RE_IDENTIFIER)}; consumeToken(TOKEN_BRACKET_CLOSE, FluentError); return key; } @@ -949,267 +1208,6 @@ class FluentResource extends Map { } } -/** - * Message bundles are single-language stores of translations. They are - * responsible for parsing translation resources in the Fluent syntax and can - * format translation units (entities) to strings. - * - * Always use `FluentBundle.format` to retrieve translation units from a - * bundle. Translations can contain references to other entities or variables, - * conditional logic in form of select expressions, traits which describe their - * grammatical features, and can use Fluent builtins which make use of the - * `Intl` formatters to format numbers, dates, lists and more into the - * bundle's language. See the documentation of the Fluent syntax for more - * information. - */ -class FluentBundle { - /** - * Create an instance of `FluentBundle`. - * - * The `locales` argument is used to instantiate `Intl` formatters used by - * translations. The `options` object can be used to configure the bundle. - * - * Examples: - * - * const bundle = new FluentBundle(locales); - * - * const bundle = new FluentBundle(locales, { useIsolating: false }); - * - * const bundle = new FluentBundle(locales, { - * useIsolating: true, - * functions: { - * NODE_ENV: () => process.env.NODE_ENV - * } - * }); - * - * Available options: - * - * - `functions` - an object of additional functions available to - * translations as builtins. - * - * - `useIsolating` - boolean specifying whether to use Unicode isolation - * marks (FSI, PDI) for bidi interpolations. - * Default: true - * - * - `transform` - a function used to transform string parts of patterns. - * - * @param {string|Array} locales - Locale or locales of the bundle - * @param {Object} [options] - * @returns {FluentBundle} - */ - constructor(locales, { - functions = {}, - useIsolating = true, - transform = v => v, - } = {}) { - this.locales = Array.isArray(locales) ? locales : [locales]; - - this._terms = new Map(); - this._messages = new Map(); - this._functions = functions; - this._useIsolating = useIsolating; - this._transform = transform; - this._intls = new WeakMap(); - } - - /* - * Return an iterator over public `[id, message]` pairs. - * - * @returns {Iterator} - */ - get messages() { - return this._messages[Symbol.iterator](); - } - - /* - * Check if a message is present in the bundle. - * - * @param {string} id - The identifier of the message to check. - * @returns {bool} - */ - hasMessage(id) { - return this._messages.has(id); - } - - /* - * Return the internal representation of a message. - * - * The internal representation should only be used as an argument to - * `FluentBundle.format`. - * - * @param {string} id - The identifier of the message to check. - * @returns {Any} - */ - getMessage(id) { - return this._messages.get(id); - } - - /** - * Add a translation resource to the bundle. - * - * The translation resource must use the Fluent syntax. It will be parsed by - * the bundle and each translation unit (message) will be available in the - * bundle by its identifier. - * - * bundle.addMessages('foo = Foo'); - * bundle.getMessage('foo'); - * - * // Returns a raw representation of the 'foo' message. - * - * bundle.addMessages('bar = Bar'); - * bundle.addMessages('bar = Newbar', { allowOverrides: true }); - * bundle.getMessage('bar'); - * - * // Returns a raw representation of the 'bar' message: Newbar. - * - * Parsed entities should be formatted with the `format` method in case they - * contain logic (references, select expressions etc.). - * - * Available options: - * - * - `allowOverrides` - boolean specifying whether it's allowed to override - * an existing message or term with a new value. - * Default: false - * - * @param {string} source - Text resource with translations. - * @param {Object} [options] - * @returns {Array} - */ - addMessages(source, options) { - const res = FluentResource.fromString(source); - return this.addResource(res, options); - } - - /** - * Add a translation resource to the bundle. - * - * The translation resource must be an instance of FluentResource, - * e.g. parsed by `FluentResource.fromString`. - * - * let res = FluentResource.fromString("foo = Foo"); - * bundle.addResource(res); - * bundle.getMessage('foo'); - * - * // Returns a raw representation of the 'foo' message. - * - * let res = FluentResource.fromString("bar = Bar"); - * bundle.addResource(res); - * res = FluentResource.fromString("bar = Newbar"); - * bundle.addResource(res, { allowOverrides: true }); - * bundle.getMessage('bar'); - * - * // Returns a raw representation of the 'bar' message: Newbar. - * - * Parsed entities should be formatted with the `format` method in case they - * contain logic (references, select expressions etc.). - * - * Available options: - * - * - `allowOverrides` - boolean specifying whether it's allowed to override - * an existing message or term with a new value. - * Default: false - * - * @param {FluentResource} res - FluentResource object. - * @param {Object} [options] - * @returns {Array} - */ - addResource(res, { - allowOverrides = false, - } = {}) { - const errors = []; - - for (const [id, value] of res) { - if (id.startsWith("-")) { - // Identifiers starting with a dash (-) define terms. Terms are private - // and cannot be retrieved from FluentBundle. - if (allowOverrides === false && this._terms.has(id)) { - errors.push(`Attempt to override an existing term: "${id}"`); - continue; - } - this._terms.set(id, value); - } else { - if (allowOverrides === false && this._messages.has(id)) { - errors.push(`Attempt to override an existing message: "${id}"`); - continue; - } - this._messages.set(id, value); - } - } - - return errors; - } - - /** - * Format a message to a string or null. - * - * Format a raw `message` from the bundle into a string (or a null if it has - * a null value). `args` will be used to resolve references to variables - * passed as arguments to the translation. - * - * In case of errors `format` will try to salvage as much of the translation - * as possible and will still return a string. For performance reasons, the - * encountered errors are not returned but instead are appended to the - * `errors` array passed as the third argument. - * - * const errors = []; - * bundle.addMessages('hello = Hello, { $name }!'); - * const hello = bundle.getMessage('hello'); - * bundle.format(hello, { name: 'Jane' }, errors); - * - * // Returns 'Hello, Jane!' and `errors` is empty. - * - * bundle.format(hello, undefined, errors); - * - * // Returns 'Hello, name!' and `errors` is now: - * - * [] - * - * @param {Object | string} message - * @param {Object | undefined} args - * @param {Array} errors - * @returns {?string} - */ - format(message, args, errors) { - // optimize entities which are simple strings with no attributes - if (typeof message === "string") { - return this._transform(message); - } - - // optimize entities with null values - if (message === null || message.value === null) { - return null; - } - - // optimize simple-string entities with attributes - if (typeof message.value === "string") { - return this._transform(message.value); - } - - return resolve(this, args, message, errors); - } - - _memoizeIntlObject(ctor, opts) { - const cache = this._intls.get(ctor) || {}; - const id = JSON.stringify(opts); - - if (!cache[id]) { - cache[id] = new ctor(this.locales, opts); - this._intls.set(ctor, cache); - } - - return cache[id]; - } -} - -/* - * @module fluent - * @overview - * - * `fluent` is a JavaScript implementation of Project Fluent, a localization - * framework designed to unleash the expressive power of the natural language. - * - */ - this.EXPORTED_SYMBOLS = [ ...Object.keys({ FluentBundle, diff --git a/intl/l10n/L10nRegistry.jsm b/intl/l10n/L10nRegistry.jsm index 9d469072a7..e77f4c86f8 100644 --- a/intl/l10n/L10nRegistry.jsm +++ b/intl/l10n/L10nRegistry.jsm @@ -654,7 +654,7 @@ class FileSource { if (data === false) { this.cache[fullPath] = false; } else { - this.cache[fullPath] = FluentResource.fromString(data); + this.cache[fullPath] = new FluentResource(data); } return this.cache[fullPath]; @@ -663,7 +663,7 @@ class FileSource { // async return this.cache[fullPath] = L10nRegistry.load(fullPath).then( data => { - return this.cache[fullPath] = FluentResource.fromString(data); + return this.cache[fullPath] = new FluentResource(data); }, err => { this.cache[fullPath] = false; diff --git a/intl/l10n/Localization.jsm b/intl/l10n/Localization.jsm index 2000f8a61f..bf4de032e3 100644 --- a/intl/l10n/Localization.jsm +++ b/intl/l10n/Localization.jsm @@ -329,9 +329,9 @@ class Localization { /** * Format translations into {value, attributes} objects. * - * The fallback logic is the same as in `formatValues` but the argument type - * is stricter (an array of arrays) and it returns {value, attributes} - * objects which are suitable for the translation of DOM elements. + * The fallback logic is the same as in `formatValues` but it returns {value, + * attributes} objects which are suitable for the translation of DOM + * elements. * * docL10n.formatMessages([ * {id: 'hello', args: { who: 'Mary' }}, @@ -372,8 +372,8 @@ class Localization { /** * Retrieve translations corresponding to the passed keys. * - * A generalized version of `Localization.formatValue`. Keys can - * either be simple string identifiers or `[id, args]` arrays. + * A generalized version of `Localization.formatValue`. Keys must + * be `{id, args}` objects. * * docL10n.formatValues([ * {id: 'hello', args: { who: 'Mary' }}, @@ -510,26 +510,26 @@ Localization.prototype.QueryInterface = ChromeUtils.generateQI([ ]); /** - * Format the value of a message into a string. + * Format the value of a message into a string or `null`. * * This function is passed as a method to `keysFromBundle` and resolve * a value of a single L10n Entity using provided `FluentBundle`. - * - * If the function fails to retrieve the entity, it will return an ID of it. - * If formatting fails, it will return a partially resolved entity. - * - * In both cases, an error is being added to the errors array. + + * If the message doesn't have a value, return `null`. * * @param {FluentBundle} bundle - * @param {Array} errors - * @param {string} id - * @param {Object} args - * @returns {string} + * @param {Array} errors + * @param {Object} message + * @param {Object} args + * @returns {string|null} * @private */ -function valueFromBundle(bundle, errors, id, args) { - const msg = bundle.getMessage(id); - return bundle.format(msg, args, errors); +function valueFromBundle(bundle, errors, message, args) { + if (message.value) { + return bundle.formatPattern(message.value, args, errors); + } + + return null; } /** @@ -541,34 +541,29 @@ function valueFromBundle(bundle, errors, id, args) { * The function will return an object with a value and attributes of the * entity. * - * If the function fails to retrieve the entity, the value is set to the ID of - * an entity, and attributes to `null`. If formatting fails, it will return - * a partially resolved value and attributes. - * - * In both cases, an error is being added to the errors array. - * * @param {FluentBundle} bundle * @param {Array} errors - * @param {String} id - * @param {Object} args + * @param {Object} message + * @param {Object} args * @returns {Object} * @private */ -function messageFromBundle(bundle, errors, id, args) { - const msg = bundle.getMessage(id); - +function messageFromBundle(bundle, errors, message, args) { const formatted = { - value: bundle.format(msg, args, errors), + value: null, attributes: null, }; - if (msg.attrs) { - formatted.attributes = []; - for (const [name, attr] of Object.entries(msg.attrs)) { - const value = bundle.format(attr, args, errors); - if (value !== null) { - formatted.attributes.push({name, value}); - } + if (message.value) { + formatted.value = bundle.formatPattern(message.value, args, errors); + } + + let attrNames = Object.keys(message.attributes); + if (attrNames.length > 0) { + formatted.attributes = new Array(attrNames.length); + for (let [i, name] of attrNames.entries()) { + let value = bundle.formatPattern(message.attributes[name], args, errors); + formatted.attributes[i] = {name, value}; } } @@ -616,9 +611,10 @@ function keysFromBundle(method, bundle, keys, translations) { return; } - if (bundle.hasMessage(id)) { + let message = bundle.getMessage(id); + if (message) { messageErrors.length = 0; - translations[i] = method(bundle, messageErrors, id, args); + translations[i] = method(bundle, messageErrors, message, args); if (messageErrors.length > 0) { const locale = bundle.locales[0]; const errors = messageErrors.join(", "); diff --git a/intl/l10n/test/mochitest/localization/test_formatMessages.html b/intl/l10n/test/mochitest/localization/test_formatMessages.html index c069c32b2b..b6a368ad79 100644 --- a/intl/l10n/test/mochitest/localization/test_formatMessages.html +++ b/intl/l10n/test/mochitest/localization/test_formatMessages.html @@ -7,20 +7,20 @@ diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index b1363f1a60..4960c86d65 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -1,3 +1,4 @@ +/* eslint-disable dot-notation */ /* 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/. */ diff --git a/layout/style/test/test_flexbox_order.html b/layout/style/test/test_flexbox_order.html index 368803657c..64b5431da8 100644 --- a/layout/style/test/test_flexbox_order.html +++ b/layout/style/test/test_flexbox_order.html @@ -175,7 +175,7 @@ function main() { // Initial sanity-check: should be in expected document order let initialSnapshot = snapshotWindow(window, false); - complainIfSnapshotsDiffer(initialSnapshot, gRefSnapshots["abc"], + complainIfSnapshotsDiffer(initialSnapshot, gRefSnapshots.abc, "initial flex container rendering, " + "no 'order' value yet"); diff --git a/layout/style/test/test_flexbox_order_abspos.html b/layout/style/test/test_flexbox_order_abspos.html index 67d8bbbbf2..bf4c99aa76 100644 --- a/layout/style/test/test_flexbox_order_abspos.html +++ b/layout/style/test/test_flexbox_order_abspos.html @@ -198,7 +198,7 @@ function main() { // Initial sanity-check: should be in expected document order let initialSnapshot = snapshotWindow(window, false); - complainIfSnapshotsDiffer(initialSnapshot, gRefSnapshots["abc"], + complainIfSnapshotsDiffer(initialSnapshot, gRefSnapshots.abc, "initial flex container rendering, " + "no 'order' value yet"); diff --git a/layout/style/test/test_flexbox_order_table.html b/layout/style/test/test_flexbox_order_table.html index 9cc1cb6cf8..2423d5d6d6 100644 --- a/layout/style/test/test_flexbox_order_table.html +++ b/layout/style/test/test_flexbox_order_table.html @@ -179,7 +179,7 @@ function main() { // Initial sanity-check: should be in expected document order let initialSnapshot = snapshotWindow(window, false); - complainIfSnapshotsDiffer(initialSnapshot, gRefSnapshots["abc"], + complainIfSnapshotsDiffer(initialSnapshot, gRefSnapshots.abc, "initial flex container rendering, " + "no 'order' value yet"); diff --git a/layout/style/test/test_garbage_at_end_of_declarations.html b/layout/style/test/test_garbage_at_end_of_declarations.html index 15c464a36d..0e59867693 100644 --- a/layout/style/test/test_garbage_at_end_of_declarations.html +++ b/layout/style/test/test_garbage_at_end_of_declarations.html @@ -18,8 +18,10 @@
 
-    
+    
   
-  
+  
     
diff --git a/toolkit/components/aboutperformance/jar.mn b/toolkit/components/aboutperformance/jar.mn index 96e046d8ee..2f3577616f 100644 --- a/toolkit/components/aboutperformance/jar.mn +++ b/toolkit/components/aboutperformance/jar.mn @@ -5,3 +5,4 @@ toolkit.jar: content/global/aboutPerformance.xhtml (content/aboutPerformance.xhtml) content/global/aboutPerformance.js (content/aboutPerformance.js) + content/global/aboutPerformance.css (content/aboutPerformance.css) diff --git a/toolkit/components/antitracking/AntiTrackingCommon.cpp b/toolkit/components/antitracking/AntiTrackingCommon.cpp index 759a6e9a60..8b02617421 100644 --- a/toolkit/components/antitracking/AntiTrackingCommon.cpp +++ b/toolkit/components/antitracking/AntiTrackingCommon.cpp @@ -11,7 +11,6 @@ #include "mozilla/IntegerPrintfMacros.h" #include "mozilla/Logging.h" #include "mozilla/MruCache.h" -#include "mozilla/Pair.h" #include "mozilla/ScopeExit.h" #include "mozilla/StaticPrefs_extensions.h" #include "mozilla/StaticPrefs_privacy.h" @@ -467,8 +466,8 @@ void ReportUnblockingToConsole( nsresult rv = NS_DispatchToCurrentThreadQueue( NS_NewRunnableFunction( "ReportUnblockingToConsoleDelayed", - [doc, principal, trackingOrigin, sourceLine, lineNumber, - columnNumber, aReason]() { + [doc, principal, trackingOrigin, sourceLine, lineNumber, columnNumber, + aReason]() { nsAutoString origin; nsresult rv = nsContentUtils::GetUTFOrigin(principal, origin); if (NS_WARN_IF(NS_FAILED(rv))) { @@ -493,8 +492,9 @@ void ReportUnblockingToConsole( } nsContentUtils::ReportToConsole( - nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Content Blocking"), - doc, nsContentUtils::eNECKO_PROPERTIES, messageWithSameOrigin, + nsIScriptError::warningFlag, + NS_LITERAL_CSTRING("Content Blocking"), doc, + nsContentUtils::eNECKO_PROPERTIES, messageWithSameOrigin, params, nullptr, sourceLine, lineNumber, columnNumber); }), kMaxConsoleOutputDelayMs, EventQueuePriority::Idle); @@ -529,19 +529,19 @@ already_AddRefed GetTopWindow(nsPIDOMWindowInner* aWindow) { class TemporaryAccessGrantCacheKey : public PLDHashEntryHdr { public: - typedef Pair, nsCString> KeyType; + typedef std::pair, nsCString> KeyType; typedef const KeyType* KeyTypePointer; explicit TemporaryAccessGrantCacheKey(KeyTypePointer aKey) - : mPrincipal(aKey->first()), mType(aKey->second()) {} + : mPrincipal(aKey->first), mType(aKey->second) {} TemporaryAccessGrantCacheKey(TemporaryAccessGrantCacheKey&& aOther) = default; ~TemporaryAccessGrantCacheKey() = default; - KeyType GetKey() const { return MakePair(mPrincipal, mType); } + KeyType GetKey() const { return std::make_pair(mPrincipal, mType); } bool KeyEquals(KeyTypePointer aKey) const { - return !!mPrincipal == !!aKey->first() && mType == aKey->second() && - (mPrincipal ? (mPrincipal->Equals(aKey->first())) : true); + return !!mPrincipal == !!aKey->first && mType == aKey->second && + (mPrincipal ? (mPrincipal->Equals(aKey->first)) : true); } static KeyTypePointer KeyToPointer(KeyType& aKey) { return &aKey; } @@ -550,9 +550,9 @@ class TemporaryAccessGrantCacheKey : public PLDHashEntryHdr { return 0; } - BasePrincipal* bp = BasePrincipal::Cast(aKey->first()); + BasePrincipal* bp = BasePrincipal::Cast(aKey->first); return HashGeneric(bp->GetOriginNoSuffixHash(), bp->GetOriginSuffixHash(), - HashString(aKey->second())); + HashString(aKey->second)); } enum { ALLOW_MEMMOVE = true }; @@ -575,8 +575,8 @@ class TemporaryAccessGrantObserver final : public nsIObserver { sObservers = MakeUnique(); } Unused << sObservers - ->LookupForAdd(MakePair(nsCOMPtr(aPrincipal), - nsCString(aType))) + ->LookupForAdd(std::make_pair( + nsCOMPtr(aPrincipal), nsCString(aType))) .OrInsert([&]() -> nsITimer* { // Only create a new observer if we don't have a matching // entry in our hashtable. @@ -641,7 +641,7 @@ TemporaryAccessGrantObserver::Observe(nsISupports* aSubject, const char* aTopic, Unused << mPM->RemoveFromPrincipal(mPrincipal, mType); MOZ_ASSERT(sObservers); - sObservers->Remove(MakePair(mPrincipal, mType)); + sObservers->Remove(std::make_pair(mPrincipal, mType)); } else if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) { nsCOMPtr observerService = mozilla::services::GetObserverService(); @@ -1763,23 +1763,23 @@ nsresult AntiTrackingCommon::IsOnContentBlockingAllowList( // Check both the normal mode and private browsing mode user override // permissions. - Pair types[] = { + std::pair types[] = { {NS_LITERAL_CSTRING("trackingprotection"), false}, {NS_LITERAL_CSTRING("trackingprotection-pb"), true}}; for (size_t i = 0; i < ArrayLength(types); ++i) { - if (aIsPrivateBrowsing != types[i].second()) { + if (aIsPrivateBrowsing != types[i].second) { continue; } uint32_t permissions = nsIPermissionManager::UNKNOWN_ACTION; nsresult rv = permManager->TestPermissionFromPrincipal( - aTopWinPrincipal, types[i].first(), &permissions); + aTopWinPrincipal, types[i].first, &permissions); NS_ENSURE_SUCCESS(rv, rv); if (permissions == nsIPermissionManager::ALLOW_ACTION) { aIsAllowListed = true; - LOG(("Found user override type %s", types[i].first().get())); + LOG(("Found user override type %s", types[i].first.get())); // Stop checking the next permisson type if we decided to override. break; } diff --git a/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp b/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp index 07ad2f465a..f17f032f81 100644 --- a/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp +++ b/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp @@ -492,9 +492,10 @@ void BackgroundHangThread::ReportHang(TimeDuration aHangTime) { // If the profiler is enabled, add a marker. #ifdef MOZ_GECKO_PROFILER - if (profiler_is_active()) { + if (profiler_can_accept_markers()) { TimeStamp endTime = TimeStamp::Now(); TimeStamp startTime = endTime - aHangTime; + AUTO_PROFILER_STATS(add_marker_with_HangMarkerPayload); profiler_add_marker_for_thread( mStackHelper.GetThreadId(), JS::ProfilingCategoryPair::OTHER, "BHR-detected hang", MakeUnique(startTime, endTime)); diff --git a/toolkit/components/backgroundhangmonitor/tests/child_cause_hang.js b/toolkit/components/backgroundhangmonitor/tests/child_cause_hang.js index 7c468b93e9..f48b3b94dc 100644 --- a/toolkit/components/backgroundhangmonitor/tests/child_cause_hang.js +++ b/toolkit/components/backgroundhangmonitor/tests/child_cause_hang.js @@ -11,7 +11,7 @@ function ensureProfilerInitialized() { return false; } let features = ["stackwalk"]; - Services.profiler.StartProfiler(1000, 10, features, features.length); + Services.profiler.StartProfiler(1000, 10, features); Services.profiler.StopProfiler(); return true; } diff --git a/toolkit/components/backgroundhangmonitor/tests/test_BHRObserver.js b/toolkit/components/backgroundhangmonitor/tests/test_BHRObserver.js index b5ef90c3bf..b00f68578d 100644 --- a/toolkit/components/backgroundhangmonitor/tests/test_BHRObserver.js +++ b/toolkit/components/backgroundhangmonitor/tests/test_BHRObserver.js @@ -14,7 +14,7 @@ function ensureProfilerInitialized() { return false; } let features = ["stackwalk"]; - Services.profiler.StartProfiler(1000, 10, features, features.length); + Services.profiler.StartProfiler(1000, 10, features); Services.profiler.StopProfiler(); return true; } diff --git a/toolkit/components/build/components.conf b/toolkit/components/build/components.conf index 2429c9d21e..58ad0dfaba 100644 --- a/toolkit/components/build/components.conf +++ b/toolkit/components/build/components.conf @@ -78,8 +78,10 @@ Classes = [ }, { 'name': 'AppStartup', + 'js_name': 'startup', 'cid': '{7dd4d320-c84b-4624-8d45-7bb9b2356977}', 'contract_ids': ['@mozilla.org/toolkit/app-startup;1'], + 'interfaces': ['nsIAppStartup'], 'type': 'nsAppStartup', 'headers': ['/toolkit/components/startup/nsAppStartup.h'], 'init_method': 'Init', diff --git a/toolkit/components/captivedetect/CaptiveDetect.jsm b/toolkit/components/captivedetect/CaptiveDetect.jsm index e9aef2ccaf..6e222129c5 100644 --- a/toolkit/components/captivedetect/CaptiveDetect.jsm +++ b/toolkit/components/captivedetect/CaptiveDetect.jsm @@ -511,13 +511,14 @@ CaptivePortalDetector.prototype = { }, }; -/* globals debug: true */ var debug; if (DEBUG) { + // eslint-disable-next-line no-global-assign debug = function(s) { dump("-*- CaptivePortalDetector component: " + s + "\n"); }; } else { + // eslint-disable-next-line no-global-assign debug = function(s) {}; } diff --git a/toolkit/components/cleardata/components.conf b/toolkit/components/cleardata/components.conf index 15cfdafd66..2bd2fdb3d0 100644 --- a/toolkit/components/cleardata/components.conf +++ b/toolkit/components/cleardata/components.conf @@ -4,8 +4,10 @@ Classes = [ { + 'js_name': 'clearData', 'cid': '{0c06583d-7dd8-4293-b1a5-912205f779aa}', 'contract_ids': ['@mozilla.org/clear-data-service;1'], + 'interfaces': ['nsIClearDataService'], 'jsm': 'resource://gre/modules/ClearDataService.jsm', 'constructor': 'ClearDataService', }, diff --git a/toolkit/components/commandlines/nsCommandLine.cpp b/toolkit/components/commandlines/nsCommandLine.cpp index 78f52496de..8d453a3e7d 100644 --- a/toolkit/components/commandlines/nsCommandLine.cpp +++ b/toolkit/components/commandlines/nsCommandLine.cpp @@ -99,9 +99,7 @@ nsCommandLine::RemoveArguments(int32_t aStart, int32_t aEnd) { NS_ENSURE_ARG_MIN(aStart, 0); NS_ENSURE_ARG_MAX(uint32_t(aEnd) + 1, mArgs.Length()); - for (int32_t i = aEnd; i >= aStart; --i) { - mArgs.RemoveElementAt(i); - } + mArgs.RemoveElementsAt(mArgs.begin() + aStart, mArgs.begin() + aEnd + 1); return NS_OK; } diff --git a/toolkit/components/crashes/CrashManager.jsm b/toolkit/components/crashes/CrashManager.jsm index ebb526a015..a9c79d629c 100644 --- a/toolkit/components/crashes/CrashManager.jsm +++ b/toolkit/components/crashes/CrashManager.jsm @@ -21,7 +21,7 @@ const { TelemetryController } = ChromeUtils.import( ChromeUtils.import("resource://gre/modules/Timer.jsm", this); ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", this); -var EXPORTED_SYMBOLS = ["CrashManager"]; +var EXPORTED_SYMBOLS = ["CrashManager", "getCrashManager"]; /** * How long to wait after application startup before crash event files are @@ -1552,3 +1552,7 @@ XPCOMUtils.defineLazyGetter(this.CrashManager, "Singleton", function() { return gCrashManager; }); + +function getCrashManager() { + return CrashManager.Singleton; +} diff --git a/toolkit/components/crashes/components.conf b/toolkit/components/crashes/components.conf index ad28e46ab4..38414e0c2e 100644 --- a/toolkit/components/crashes/components.conf +++ b/toolkit/components/crashes/components.conf @@ -10,4 +10,11 @@ Classes = [ 'constructor': 'CrashService', 'categories': {'profile-after-change': 'CrashService'}, }, + { + 'js_name': 'crashmanager', + 'cid': '{c887b6a9-a5eb-4566-a440-bebaea3e54fd}', + 'contract_ids': ['@mozilla.org/crashmanager;1'], + 'jsm': 'resource://gre/modules/CrashManager.jsm', + 'constructor': 'getCrashManager', + }, ] diff --git a/toolkit/components/ctypes/tests/unit/test_jsctypes.js b/toolkit/components/ctypes/tests/unit/test_jsctypes.js index 5b84dd4043..06645dc780 100644 --- a/toolkit/components/ctypes/tests/unit/test_jsctypes.js +++ b/toolkit/components/ctypes/tests/unit/test_jsctypes.js @@ -3952,6 +3952,7 @@ function run_variadic_tests(library) { ctypes.int32_t.ptr, ctypes.uint8_t, ctypes.uint8_t, + ctypes.int32_t.ptr, "..." ); // Note that vector_add_va zeroes out result first. diff --git a/toolkit/components/enterprisepolicies/EnterprisePolicies.jsm b/toolkit/components/enterprisepolicies/EnterprisePolicies.jsm new file mode 100644 index 0000000000..0d615ed560 --- /dev/null +++ b/toolkit/components/enterprisepolicies/EnterprisePolicies.jsm @@ -0,0 +1,22 @@ +/* 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/. */ + +var EXPORTED_SYMBOLS = ["EnterprisePolicies"]; + +function EnterprisePolicies() { + // eslint-disable-next-line mozilla/use-services + const appinfo = Cc["@mozilla.org/xre/app-info;1"].getService( + Ci.nsIXULRuntime + ); + if (appinfo.processType == appinfo.PROCESS_TYPE_DEFAULT) { + const { EnterprisePoliciesManager } = ChromeUtils.import( + "resource://gre/modules/EnterprisePoliciesParent.jsm" + ); + return new EnterprisePoliciesManager(); + } + const { EnterprisePoliciesManagerContent } = ChromeUtils.import( + "resource://gre/modules/EnterprisePoliciesContent.jsm" + ); + return new EnterprisePoliciesManagerContent(); +} diff --git a/toolkit/components/enterprisepolicies/EnterprisePolicies.manifest b/toolkit/components/enterprisepolicies/EnterprisePolicies.manifest deleted file mode 100644 index b3a542b7fd..0000000000 --- a/toolkit/components/enterprisepolicies/EnterprisePolicies.manifest +++ /dev/null @@ -1,5 +0,0 @@ -component {ea4e1414-779b-458b-9d1f-d18e8efbc145} EnterprisePolicies.js process=main -contract @mozilla.org/enterprisepolicies;1 {ea4e1414-779b-458b-9d1f-d18e8efbc145} process=main - -component {dc6358f8-d167-4566-bf5b-4350b5e6a7a2} EnterprisePoliciesContent.js process=content -contract @mozilla.org/enterprisepolicies;1 {dc6358f8-d167-4566-bf5b-4350b5e6a7a2} process=content diff --git a/toolkit/components/enterprisepolicies/EnterprisePoliciesContent.js b/toolkit/components/enterprisepolicies/EnterprisePoliciesContent.jsm similarity index 55% rename from toolkit/components/enterprisepolicies/EnterprisePoliciesContent.js rename to toolkit/components/enterprisepolicies/EnterprisePoliciesContent.jsm index c2efb692cc..367e7a5f85 100644 --- a/toolkit/components/enterprisepolicies/EnterprisePoliciesContent.js +++ b/toolkit/components/enterprisepolicies/EnterprisePoliciesContent.jsm @@ -2,35 +2,26 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -const { XPCOMUtils } = ChromeUtils.import( - "resource://gre/modules/XPCOMUtils.jsm" -); +var EXPORTED_SYMBOLS = ["EnterprisePoliciesManagerContent"]; + const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); -function EnterprisePoliciesManagerContent() {} - -EnterprisePoliciesManagerContent.prototype = { - classID: Components.ID("{dc6358f8-d167-4566-bf5b-4350b5e6a7a2}"), - QueryInterface: ChromeUtils.generateQI([Ci.nsIEnterprisePolicies]), - _xpcom_factory: XPCOMUtils.generateSingletonFactory( - EnterprisePoliciesManagerContent - ), - +class EnterprisePoliciesManagerContent { get status() { return ( Services.cpmm.sharedData.get("EnterprisePolicies:Status") || Ci.nsIEnterprisePolicies.INACTIVE ); - }, + } isAllowed(feature) { let disallowedFeatures = Services.cpmm.sharedData.get( "EnterprisePolicies:DisallowedFeatures" ); return !(disallowedFeatures && disallowedFeatures.has(feature)); - }, -}; + } +} -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ - EnterprisePoliciesManagerContent, -]); +EnterprisePoliciesManagerContent.prototype.QueryInterface = ChromeUtils.generateQI( + [Ci.nsIEnterprisePolicies] +); diff --git a/toolkit/components/enterprisepolicies/EnterprisePolicies.js b/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.jsm similarity index 98% rename from toolkit/components/enterprisepolicies/EnterprisePolicies.js rename to toolkit/components/enterprisepolicies/EnterprisePoliciesParent.jsm index dd26f75ed2..0ff41fce38 100644 --- a/toolkit/components/enterprisepolicies/EnterprisePolicies.js +++ b/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.jsm @@ -2,6 +2,8 @@ * 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/. */ +var EXPORTED_SYMBOLS = ["EnterprisePoliciesManager"]; + const { XPCOMUtils } = ChromeUtils.import( "resource://gre/modules/XPCOMUtils.jsm" ); @@ -77,15 +79,11 @@ function EnterprisePoliciesManager() { } EnterprisePoliciesManager.prototype = { - classID: Components.ID("{ea4e1414-779b-458b-9d1f-d18e8efbc145}"), QueryInterface: ChromeUtils.generateQI([ Ci.nsIObserver, Ci.nsISupportsWeakReference, Ci.nsIEnterprisePolicies, ]), - _xpcom_factory: XPCOMUtils.generateSingletonFactory( - EnterprisePoliciesManager - ), _initialize() { let provider = this._chooseProvider(); @@ -622,6 +620,3 @@ class macOSPoliciesProvider { return this._failed; } } - -var components = [EnterprisePoliciesManager]; -this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components); diff --git a/toolkit/components/enterprisepolicies/components.conf b/toolkit/components/enterprisepolicies/components.conf new file mode 100644 index 0000000000..c561ed2df7 --- /dev/null +++ b/toolkit/components/enterprisepolicies/components.conf @@ -0,0 +1,14 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +Classes = [ + { + 'js_name': 'policies', + 'cid': '{49e8d8ef-a713-492a-a3d2-5c9dad4ce2e5}', + 'contract_ids': ['@mozilla.org/enterprisepolicies;1'], + 'interfaces': ['nsIEnterprisePolicies'], + 'jsm': 'resource://gre/modules/EnterprisePolicies.jsm', + 'constructor': 'EnterprisePolicies', + }, +] diff --git a/toolkit/components/enterprisepolicies/moz.build b/toolkit/components/enterprisepolicies/moz.build index cc24eab9c7..b5342108d3 100644 --- a/toolkit/components/enterprisepolicies/moz.build +++ b/toolkit/components/enterprisepolicies/moz.build @@ -16,10 +16,14 @@ TEST_DIRS += [ ] if CONFIG['MOZ_WIDGET_TOOLKIT'] != "android": - EXTRA_COMPONENTS += [ - 'EnterprisePolicies.js', - 'EnterprisePolicies.manifest', - 'EnterprisePoliciesContent.js', + EXTRA_JS_MODULES += [ + 'EnterprisePolicies.jsm', + 'EnterprisePoliciesContent.jsm', + 'EnterprisePoliciesParent.jsm', + ] + + XPCOM_MANIFESTS += [ + 'components.conf', ] if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': diff --git a/toolkit/components/extensions/Extension.jsm b/toolkit/components/extensions/Extension.jsm index b2c5d4a256..0550caca7c 100644 --- a/toolkit/components/extensions/Extension.jsm +++ b/toolkit/components/extensions/Extension.jsm @@ -12,7 +12,6 @@ var EXPORTED_SYMBOLS = [ ]; /* exported Extension, ExtensionData */ -/* globals Extension ExtensionData */ /* * This file is the main entry point for extensions. When an extension @@ -308,7 +307,7 @@ var UninstallObserver = { // If LSNG is not enabled, we need to clear localStorage explicitly using // the old API. - if (!Services.lsm.nextGenLocalStorageEnabled) { + if (!Services.domStorageManager.nextGenLocalStorageEnabled) { // Clear localStorage created by the extension let storage = Services.domStorageManager.getStorage( null, diff --git a/toolkit/components/extensions/ExtensionContent.jsm b/toolkit/components/extensions/ExtensionContent.jsm index 4220bf33bd..591cae7e01 100644 --- a/toolkit/components/extensions/ExtensionContent.jsm +++ b/toolkit/components/extensions/ExtensionContent.jsm @@ -5,8 +5,6 @@ var EXPORTED_SYMBOLS = ["ExtensionContent"]; -/* globals ExtensionContent */ - const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); const { XPCOMUtils } = ChromeUtils.import( "resource://gre/modules/XPCOMUtils.jsm" diff --git a/toolkit/components/extensions/ExtensionPageChild.jsm b/toolkit/components/extensions/ExtensionPageChild.jsm index e6e3f81415..4f7d98269b 100644 --- a/toolkit/components/extensions/ExtensionPageChild.jsm +++ b/toolkit/components/extensions/ExtensionPageChild.jsm @@ -292,6 +292,10 @@ class ExtensionPageContextChild extends ExtensionBaseContextChild { constructor(extension, params) { super(extension, Object.assign(params, { envType: "addon_child" })); + if (this.viewType == "background") { + initializeBackgroundPage(this); + } + this.extension.views.add(this); } @@ -319,10 +323,6 @@ defineLazyGetter( this.callOnClose(childManager); - if (this.viewType == "background") { - initializeBackgroundPage(this); - } - return childManager; } ); diff --git a/toolkit/components/extensions/MatchPattern.h b/toolkit/components/extensions/MatchPattern.h index fcf3590238..b8a544fbb0 100644 --- a/toolkit/components/extensions/MatchPattern.h +++ b/toolkit/components/extensions/MatchPattern.h @@ -5,6 +5,8 @@ #ifndef mozilla_extensions_MatchPattern_h #define mozilla_extensions_MatchPattern_h +#include + #include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/MatchPatternBinding.h" #include "mozilla/extensions/MatchGlob.h" @@ -94,11 +96,11 @@ class AtomSet final : public RefCounted { } } - auto begin() const -> decltype(DeclVal().begin()) { + auto begin() const -> decltype(std::declval().begin()) { return mElems.begin(); } - auto end() const -> decltype(DeclVal().end()) { + auto end() const -> decltype(std::declval().end()) { return mElems.end(); } diff --git a/toolkit/components/extensions/parent/ext-geckoProfiler.js b/toolkit/components/extensions/parent/ext-geckoProfiler.js index d316be3aa8..416e495564 100644 --- a/toolkit/components/extensions/parent/ext-geckoProfiler.js +++ b/toolkit/components/extensions/parent/ext-geckoProfiler.js @@ -91,9 +91,7 @@ this.geckoProfiler = class extends ExtensionAPI { bufferSize, interval, features, - features.length, threads, - threads.length, windowLength ); } else { @@ -101,9 +99,7 @@ this.geckoProfiler = class extends ExtensionAPI { bufferSize, interval, features, - features.length, [], - 0, windowLength ); } @@ -125,6 +121,29 @@ this.geckoProfiler = class extends ExtensionAPI { Services.profiler.ResumeSampling(); }, + async dumpProfileToFile(fileName) { + if (!Services.profiler.IsActive()) { + throw new ExtensionError( + "The profiler is stopped. " + + "You need to start the profiler before you can capture a profile." + ); + } + + if (fileName.includes("\\") || fileName.includes("/")) { + throw new ExtensionError("Path cannot contain a subdirectory."); + } + + let fragments = [OS.Constants.Path.profileDir, "profiler", fileName]; + let filePath = OS.Path.join(...fragments); + + try { + await Services.profiler.dumpProfileToFileAsync(filePath); + } catch (e) { + Cu.reportError(e); + throw new ExtensionError(`Dumping profile to ${filePath} failed.`); + } + }, + async getProfile() { if (!Services.profiler.IsActive()) { throw new ExtensionError( diff --git a/toolkit/components/extensions/parent/ext-toolkit.js b/toolkit/components/extensions/parent/ext-toolkit.js index f3ad585afa..43b3bd0968 100644 --- a/toolkit/components/extensions/parent/ext-toolkit.js +++ b/toolkit/components/extensions/parent/ext-toolkit.js @@ -6,7 +6,7 @@ getContainerForCookieStoreId, isValidCookieStoreId, isContainerCookieStoreId, EventManager, URL */ -/* global getCookieStoreIdForTab:false, getCookieStoreIdForOriginAttributes:false, +/* global getCookieStoreIdForTab:false, getCookieStoreIdForContainer:false, getContainerForCookieStoreId: false, isValidCookieStoreId:false, isContainerCookieStoreId:false, diff --git a/toolkit/components/extensions/schemas/geckoProfiler.json b/toolkit/components/extensions/schemas/geckoProfiler.json index 6df6f3e023..b88384baec 100644 --- a/toolkit/components/extensions/schemas/geckoProfiler.json +++ b/toolkit/components/extensions/schemas/geckoProfiler.json @@ -27,7 +27,6 @@ "js", "leaf", "mainthreadio", - "memory", "privacy", "responsiveness", "screenshots", @@ -35,9 +34,10 @@ "stackwalk", "tasktracer", "threads", - "trackopts", "jstracer", - "jsallocations" + "jsallocations", + "nostacksampling", + "nativeallocations" ] }, { @@ -113,6 +113,19 @@ "async": true, "parameters": [] }, + { + "name": "dumpProfileToFile", + "type": "function", + "description": "Gathers the profile data from the current profiling session, and writes it to disk. The returned promise resolves to a path that locates the created file.", + "async": true, + "parameters": [ + { + "type": "string", + "name": "fileName", + "description": "The name of the file inside the profile/profiler directory" + } + ] + }, { "name": "getProfile", "type": "function", diff --git a/toolkit/components/extensions/test/browser/browser_ext_downloads_referrer.js b/toolkit/components/extensions/test/browser/browser_ext_downloads_referrer.js index b2931e0b6f..a6e8b54041 100644 --- a/toolkit/components/extensions/test/browser/browser_ext_downloads_referrer.js +++ b/toolkit/components/extensions/test/browser/browser_ext_downloads_referrer.js @@ -1,6 +1,3 @@ -/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set sts=2 sw=2 et tw=80: */ - "use strict"; const { BrowserTestUtils } = ChromeUtils.import( diff --git a/toolkit/components/extensions/test/browser/browser_ext_themes_additional_backgrounds_alignment.js b/toolkit/components/extensions/test/browser/browser_ext_themes_additional_backgrounds_alignment.js index 537f1f9aa0..f265a724e5 100644 --- a/toolkit/components/extensions/test/browser/browser_ext_themes_additional_backgrounds_alignment.js +++ b/toolkit/components/extensions/test/browser/browser_ext_themes_additional_backgrounds_alignment.js @@ -1,7 +1,5 @@ "use strict"; -/* globals InspectorUtils */ - // Case 1 - When there is a theme_frame image and additional_backgrounds_alignment is not specified. // So background-position should default to "right top" add_task(async function test_default_additional_backgrounds_alignment() { diff --git a/toolkit/components/extensions/test/mochitest/head_unlimitedStorage.js b/toolkit/components/extensions/test/mochitest/head_unlimitedStorage.js index 4f19730450..e2a899e05c 100644 --- a/toolkit/components/extensions/test/mochitest/head_unlimitedStorage.js +++ b/toolkit/components/extensions/test/mochitest/head_unlimitedStorage.js @@ -1,5 +1,3 @@ -/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set sts=2 sw=2 et tw=80: */ "use strict"; /* exported checkSitePermissions */ diff --git a/toolkit/components/extensions/test/mochitest/test_ext_background_page.html b/toolkit/components/extensions/test/mochitest/test_ext_background_page.html index 3107e09ec9..9cafd8a61a 100644 --- a/toolkit/components/extensions/test/mochitest/test_ext_background_page.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_background_page.html @@ -1,83 +1,84 @@ - - WebExtension test - - - - - - - + + WebExtension test + + + + + + + - - await extension.unload(); -}); - - - + diff --git a/toolkit/components/extensions/test/mochitest/test_ext_webnavigation.html b/toolkit/components/extensions/test/mochitest/test_ext_webnavigation.html index 27fce6ba7d..f471ef6a2f 100644 --- a/toolkit/components/extensions/test/mochitest/test_ext_webnavigation.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_webnavigation.html @@ -74,6 +74,7 @@ function backgroundScript() { const BASE = "http://mochi.test:8888/tests/toolkit/components/extensions/test/mochitest"; const URL = BASE + "/file_WebNavigation_page1.html"; +const FORM_URL = URL + "?"; const FRAME = BASE + "/file_WebNavigation_page2.html"; const FRAME2 = BASE + "/file_WebNavigation_page3.html"; const FRAME_PUSHSTATE = BASE + "/file_WebNavigation_page3_pushState.html"; @@ -187,11 +188,11 @@ add_task(async function webnav_transitions_props() { // transitionType: form_submit received = []; - await loadAndWait(win, "onCompleted", URL, () => { + await loadAndWait(win, "onCompleted", FORM_URL, () => { win.document.querySelector("form").submit(); }); - found = received.find((data) => (data.event == "onCommitted" && data.url == URL)); + found = received.find((data) => (data.event == "onCommitted" && data.url == FORM_URL)); ok(found, "Got the onCommitted event"); @@ -220,9 +221,9 @@ add_task(async function webnav_transitions_props() { // transitionQualifier: forward_back received = []; - await loadAndWait(win, "onCompleted", URL, () => { win.history.back(); }); + await loadAndWait(win, "onCompleted", FORM_URL, () => { win.history.back(); }); - found = received.find((data) => (data.event == "onCommitted" && data.url == URL)); + found = received.find((data) => (data.event == "onCommitted" && data.url == FORM_URL)); ok(found, "Got the onCommitted event"); diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_adoption_with_private_field_xrays.js b/toolkit/components/extensions/test/xpcshell/test_ext_adoption_with_private_field_xrays.js new file mode 100644 index 0000000000..bc4e0409cb --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/test_ext_adoption_with_private_field_xrays.js @@ -0,0 +1,160 @@ +"use strict"; + +// ExtensionContent.jsm needs to know when it's running from xpcshell, +// to use the right timeout for content scripts executed at document_idle. +ExtensionTestUtils.mockAppInfo(); +const server = createHttpServer(); +server.registerDirectory("/data/", do_get_file("data")); + +const BASE_URL = `http://localhost:${server.identity.primaryPort}/data`; + +add_task(async function test_contentscript_private_field_xrays() { + async function contentScript() { + let node = window.document.createElement("div"); + + class Base { + constructor(o) { + return o; + } + } + + class A extends Base { + #x = 5; + static gx(o) { + return o.#x; + } + static sx(o, v) { + o.#x = v; + } + } + + browser.test.log(A.toString()); + + // Stamp node with A's private field. + new A(node); + + browser.test.log("stamped"); + + browser.test.assertEq( + A.gx(node), + 5, + "We should be able to see our expando private field" + ); + browser.test.log("Read"); + browser.test.assertThrows( + () => A.gx(node.wrappedJSObject), + /Trying to read undeclared field/, + "Underlying object should not have our private field" + ); + + browser.test.log("threw"); + window.frames[0].document.adoptNode(node); + browser.test.log("adopted"); + browser.test.assertEq( + A.gx(node), + 5, + "Adoption should not change expando private field" + ); + browser.test.log("read"); + browser.test.assertThrows( + () => A.gx(node.wrappedJSObject), + /Trying to read undeclared field/, + "Adoption should really not change expandos private fields" + ); + browser.test.log("threw2"); + + // Repeat but now with an object that has a reference from the + // window it's being cloned into. + node = window.document.createElement("div"); + // Stamp node with A's private field. + new A(node); + A.sx(node, 6); + + browser.test.assertEq( + A.gx(node), + 6, + "We should be able to see our expando (2)" + ); + browser.test.assertThrows( + () => A.gx(node.wrappedJSObject), + /Trying to read undeclared field/, + "Underlying object should not have exxpando. (2)" + ); + + window.frames[0].wrappedJSObject.incoming = node.wrappedJSObject; + window.frames[0].document.adoptNode(node); + + browser.test.assertEq( + A.gx(node), + 6, + "We should be able to see our expando (3)" + ); + browser.test.assertThrows( + () => A.gx(node.wrappedJSObject), + /Trying to read undeclared field/, + "Underlying object should not have exxpando. (3)" + ); + + // Repeat once more, now with an expando that refers to the object itself + node = window.document.createElement("div"); + new A(node); + A.sx(node, node); + + browser.test.assertEq( + A.gx(node), + node, + "We should be able to see our self-referential expando (4)" + ); + browser.test.assertThrows( + () => A.gx(node.wrappedJSObject), + /Trying to read undeclared field/, + "Underlying object should not have exxpando. (4)" + ); + + window.frames[0].document.adoptNode(node); + + browser.test.assertEq( + A.gx(node), + node, + "Adoption should not change our self-referential expando (4)" + ); + browser.test.assertThrows( + () => A.gx(node.wrappedJSObject), + /Trying to read undeclared field/, + "Adoption should not change underlying object. (4)" + ); + + // And test what happens if we now set document.domain and cause + // wrapper remapping. + let doc = window.frames[0].document; + // eslint-disable-next-line no-self-assign + doc.domain = doc.domain; + + browser.test.notifyPass("privateFieldXRayAdoption"); + } + + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + content_scripts: [ + { + matches: ["http://*/*/file_toplevel.html"], + js: ["content_script.js"], + }, + ], + }, + + files: { + "content_script.js": contentScript, + }, + }); + + await extension.startup(); + let contentPage = await ExtensionTestUtils.loadContentPage( + `${BASE_URL}/file_toplevel.html` + ); + + await extension.awaitFinish("privateFieldXRayAdoption"); + + await contentPage.close(); + await extension.unload(); +}); diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_geckoProfiler_control.js b/toolkit/components/extensions/test/xpcshell/test_ext_geckoProfiler_control.js index 37c37ee2da..a92855d0de 100644 --- a/toolkit/components/extensions/test/xpcshell/test_ext_geckoProfiler_control.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_geckoProfiler_control.js @@ -1,5 +1,8 @@ "use strict"; +const {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm"); + + let getExtension = () => { return ExtensionTestUtils.loadExtension({ background: async function() { @@ -11,7 +14,7 @@ let getExtension = () => { } }; - browser.test.onMessage.addListener(async message => { + browser.test.onMessage.addListener(async (message, data) => { let result; switch (message) { case "start": @@ -58,6 +61,16 @@ let getExtension = () => { ); browser.test.sendMessage("tested profile"); break; + case "test dump to file": + try { + await browser.geckoProfiler.dumpProfileToFile(data.fileName); + browser.test.sendMessage("tested dump to file", {}); + } catch (e) { + browser.test.sendMessage("tested dump to file", { + error: e.message, + }); + } + break; case "test profile as array buffer": let arrayBuffer = await browser.geckoProfiler.getProfileAsArrayBuffer(); browser.test.assertTrue( @@ -107,6 +120,16 @@ let getExtension = () => { }); }; +let verifyProfileData = bytes => { + let textDecoder = new TextDecoder(); + let profile = JSON.parse(textDecoder.decode(bytes)); + ok("libs" in profile, "The profile contains libs."); + ok("meta" in profile, "The profile contains meta."); + ok("threads" in profile, "The profile contains threads."); + ok(profile.threads.some(t => t.name == "GeckoMain"), + "The profile contains a GeckoMain thread."); +}; + add_task(async function testProfilerControl() { const acceptedExtensionIdsPref = "extensions.geckoProfiler.acceptedExtensionIds"; @@ -126,6 +149,41 @@ add_task(async function testProfilerControl() { extension.sendMessage("test profile"); await extension.awaitMessage("tested profile"); + const profilerPath = OS.Path.join(OS.Constants.Path.profileDir, "profiler"); + let data, fileName, targetPath; + + // test with file name only + fileName = "bar.profile"; + targetPath = OS.Path.join(profilerPath, fileName); + extension.sendMessage("test dump to file", {fileName}); + data = await extension.awaitMessage("tested dump to file"); + equal(data.error, undefined, "No error thrown"); + ok(await OS.File.exists(targetPath), "Saved gecko profile exists."); + verifyProfileData(await OS.File.read(targetPath)); + + // test overwriting the formerly created file + extension.sendMessage("test dump to file", {fileName}); + data = await extension.awaitMessage("tested dump to file"); + equal(data.error, undefined, "No error thrown"); + ok(await OS.File.exists(targetPath), "Saved gecko profile exists."); + verifyProfileData(await OS.File.read(targetPath)); + + // test with a POSIX path, which is not allowed + fileName = "foo/bar.profile"; + targetPath = OS.Path.join(profilerPath, ...fileName.split("/")); + extension.sendMessage("test dump to file", {fileName}); + data = await extension.awaitMessage("tested dump to file"); + equal(data.error, "Path cannot contain a subdirectory."); + ok(!await OS.File.exists(targetPath), "Gecko profile hasn't been saved."); + + // test with a non POSIX path which is not allowed + fileName = "foo\\bar.profile"; + targetPath = OS.Path.join(profilerPath, ...fileName.split("\\")); + extension.sendMessage("test dump to file", {fileName}); + data = await extension.awaitMessage("tested dump to file"); + equal(data.error, "Path cannot contain a subdirectory."); + ok(!await OS.File.exists(targetPath), "Gecko profile hasn't been saved."); + extension.sendMessage("test profile as array buffer"); await extension.awaitMessage("tested profile as array buffer"); diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_geckoProfiler_schema.js b/toolkit/components/extensions/test/xpcshell/test_ext_geckoProfiler_schema.js index c9734c41dc..d0bbf7e60f 100644 --- a/toolkit/components/extensions/test/xpcshell/test_ext_geckoProfiler_schema.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_geckoProfiler_schema.js @@ -31,7 +31,7 @@ add_task(async function() { Services.prefs.clearUserPref(acceptedExtensionIdsPref); - const allFeaturesAcceptedByProfiler = Services.profiler.GetAllFeatures([]); + const allFeaturesAcceptedByProfiler = Services.profiler.GetAllFeatures(); ok( allFeaturesAcceptedByProfiler.length >= 2, "Either we've massively reduced the profiler's feature set, or something is wrong." @@ -47,7 +47,9 @@ add_task(async function() { } for (const feature of acceptedFeatures) { ok( - allFeaturesAcceptedByProfiler.includes(feature), + // Bug 1594566 - ignore Responsiveness until the extension is updated + allFeaturesAcceptedByProfiler.includes(feature) || + feature == "responsiveness", `The schema of the geckoProfiler.start() method mentions a "${feature}" feature which is not supported by the profiler.` ); } diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_schemas_interactive.js b/toolkit/components/extensions/test/xpcshell/test_ext_schemas_interactive.js index 961cb3a23d..66de5c8aba 100644 --- a/toolkit/components/extensions/test/xpcshell/test_ext_schemas_interactive.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_schemas_interactive.js @@ -57,7 +57,6 @@ let experimentFiles = { }; }, - /* globals ExtensionAPI */ "child.js": () => { this.userinputtest = class extends ExtensionAPI { getAPI(context) { diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_storage_sync.js b/toolkit/components/extensions/test/xpcshell/test_ext_storage_sync.js index 8be8c82812..35cf46dbdf 100644 --- a/toolkit/components/extensions/test/xpcshell/test_ext_storage_sync.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_storage_sync.js @@ -22,9 +22,6 @@ const { BulkKeyBundle } = ChromeUtils.import( ); const { Utils } = ChromeUtils.import("resource://services-sync/util.js"); -/* globals BulkKeyBundle, CommonUtils, EncryptionRemoteTransformer */ -/* globals Utils */ - function handleCannedResponse(cannedResponse, request, response) { response.setStatusLine( null, diff --git a/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini b/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini index 37314198d4..dc711e1d9e 100644 --- a/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini +++ b/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini @@ -23,7 +23,7 @@ skip-if = os == "android" skip-if = appname == "thunderbird" || os == "android" [test_ext_captivePortal.js] # As with test_captive_portal_service.js, we use the same limits here. -skip-if = os == "android" # CP service is disabled on Android +skip-if = os == "android" || (os == "mac" && debug) # CP service is disabled on Android, macosx1014/debug due to 1564534 run-sequentially = node server exceptions dont replay well [test_ext_cookieBehaviors.js] [test_ext_cookies_samesite.js] diff --git a/toolkit/components/extensions/test/xpcshell/xpcshell-content.ini b/toolkit/components/extensions/test/xpcshell/xpcshell-content.ini index bfee483bda..e03f6c7791 100644 --- a/toolkit/components/extensions/test/xpcshell/xpcshell-content.ini +++ b/toolkit/components/extensions/test/xpcshell/xpcshell-content.ini @@ -11,4 +11,6 @@ skip-if = (os == "android" && debug) || (os == "win" && debug) # Windows: Bug 14 [test_ext_contentScripts_register.js] [test_ext_contexts_gc.js] [test_ext_adoption_with_xrays.js] +[test_ext_adoption_with_private_field_xrays.js] +skip-if = !nightly_build [test_ext_shadowdom.js] diff --git a/toolkit/components/finalizationwitness/FinalizationWitnessService.cpp b/toolkit/components/finalizationwitness/FinalizationWitnessService.cpp index 86c0b9ae7b..0db4301bd1 100644 --- a/toolkit/components/finalizationwitness/FinalizationWitnessService.cpp +++ b/toolkit/components/finalizationwitness/FinalizationWitnessService.cpp @@ -7,6 +7,7 @@ #include "nsString.h" #include "jsapi.h" #include "js/CallNonGenericMethod.h" +#include "js/Object.h" // JS::GetClass, JS::GetReservedSlot #include "js/PropertySpec.h" #include "mozJSComponentLoader.h" #include "nsZipArchive.h" @@ -73,7 +74,7 @@ enum { WITNESS_SLOT_EVENT, WITNESS_INSTANCES_SLOTS }; */ already_AddRefed ExtractFinalizationEvent( JSObject* objSelf) { - JS::Value slotEvent = JS_GetReservedSlot(objSelf, WITNESS_SLOT_EVENT); + JS::Value slotEvent = JS::GetReservedSlot(objSelf, WITNESS_SLOT_EVENT); if (slotEvent.isUndefined()) { // Forget() has been called return nullptr; @@ -124,7 +125,7 @@ static const JSClass sWitnessClass = { &sWitnessClassOps}; bool IsWitness(JS::Handle v) { - return v.isObject() && JS_GetClass(&v.toObject()) == &sWitnessClass; + return v.isObject() && JS::GetClass(&v.toObject()) == &sWitnessClass; } /** diff --git a/toolkit/components/mozintl/components.conf b/toolkit/components/mozintl/components.conf index ce80b692cc..9339f8ee4a 100644 --- a/toolkit/components/mozintl/components.conf +++ b/toolkit/components/mozintl/components.conf @@ -11,8 +11,10 @@ Classes = [ }, { + 'js_name': 'intl', 'cid': '{35ec195a-e8d0-4300-83af-c8a2cc84b4a3}', 'contract_ids': ['@mozilla.org/mozintl;1'], + 'interfaces': ['mozIMozIntl'], 'jsm': 'resource://gre/modules/mozIntl.jsm', 'constructor': 'MozIntl', }, diff --git a/toolkit/components/osfile/NativeOSFileInternals.cpp b/toolkit/components/osfile/NativeOSFileInternals.cpp index 13436a77d5..deb0db9a27 100644 --- a/toolkit/components/osfile/NativeOSFileInternals.cpp +++ b/toolkit/components/osfile/NativeOSFileInternals.cpp @@ -35,6 +35,7 @@ #include "jsfriendapi.h" #include "js/ArrayBuffer.h" // JS::GetArrayBufferByteLength,IsArrayBufferObject,NewArrayBufferWithContents,StealArrayBufferContents #include "js/Conversions.h" +#include "js/experimental/TypedData.h" // JS_NewUint8ArrayWithBuffer #include "js/MemoryFunctions.h" #include "js/UniquePtr.h" #include "js/Utility.h" diff --git a/toolkit/components/osfile/modules/osfile_win_back.jsm b/toolkit/components/osfile/modules/osfile_win_back.jsm index ba44e8a50c..1b441c1ac3 100644 --- a/toolkit/components/osfile/modules/osfile_win_back.jsm +++ b/toolkit/components/osfile/modules/osfile_win_back.jsm @@ -20,7 +20,6 @@ */ /* eslint-env mozilla/chrome-worker, node */ -/* global OS */ // eslint-disable-next-line no-lone-blocks { diff --git a/toolkit/components/passwordmgr/LoginImport.jsm b/toolkit/components/passwordmgr/LoginImport.jsm index c3d839aa07..b1752b9dff 100644 --- a/toolkit/components/passwordmgr/LoginImport.jsm +++ b/toolkit/components/passwordmgr/LoginImport.jsm @@ -1,5 +1,3 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ diff --git a/toolkit/components/passwordmgr/components.conf b/toolkit/components/passwordmgr/components.conf index 8a442eb161..20d1d26d4e 100644 --- a/toolkit/components/passwordmgr/components.conf +++ b/toolkit/components/passwordmgr/components.conf @@ -4,8 +4,10 @@ Classes = [ { + 'js_name': 'logins', 'cid': '{cb9e0de8-3598-4ed7-857b-827f011ad5d8}', 'contract_ids': ['@mozilla.org/login-manager;1'], + 'interfaces': ['nsILoginManager'], 'jsm': 'resource://gre/modules/LoginManager.jsm', 'constructor': 'LoginManager', }, diff --git a/toolkit/components/passwordmgr/storage-mozStorage.js b/toolkit/components/passwordmgr/storage-mozStorage.js index 7d8bf0d3d1..93b3441bb2 100644 --- a/toolkit/components/passwordmgr/storage-mozStorage.js +++ b/toolkit/components/passwordmgr/storage-mozStorage.js @@ -634,8 +634,6 @@ LoginManagerStorage_mozStorage.prototype = { let params = { guid: aLogin.guid, timeDeleted: Date.now() }; let stmt = this._dbCreateStatement(query, params); stmt.execute(); - } catch (ex) { - throw ex; } finally { if (stmt) { stmt.reset(); diff --git a/toolkit/components/passwordmgr/test/browser/head.js b/toolkit/components/passwordmgr/test/browser/head.js index 73c8c5c4e1..17c6f19bc7 100644 --- a/toolkit/components/passwordmgr/test/browser/head.js +++ b/toolkit/components/passwordmgr/test/browser/head.js @@ -262,24 +262,22 @@ function openPasswordManager(openingFunc, waitForFilter) { // Autocomplete popup related functions // -function openACPopup(popup, browser, inputSelector) { - return new Promise(async resolve => { - let promiseShown = BrowserTestUtils.waitForEvent(popup, "popupshown"); +async function openACPopup(popup, browser, inputSelector) { + let promiseShown = BrowserTestUtils.waitForEvent(popup, "popupshown"); - await SimpleTest.promiseFocus(browser); - info("content window focused"); + await SimpleTest.promiseFocus(browser); + info("content window focused"); - // Focus the username field to open the popup. - await ContentTask.spawn(browser, [inputSelector], function openAutocomplete( - sel - ) { - content.document.querySelector(sel).focus(); - }); - - let shown = await promiseShown; - ok(shown, "autocomplete popup shown"); - resolve(shown); + // Focus the username field to open the popup. + await ContentTask.spawn(browser, [inputSelector], function openAutocomplete( + sel + ) { + content.document.querySelector(sel).focus(); }); + + let shown = await promiseShown; + ok(shown, "autocomplete popup shown"); + return shown; } // Contextmenu functions // diff --git a/toolkit/components/places/UnifiedComplete.jsm b/toolkit/components/places/UnifiedComplete.jsm index b4336422bf..66d2c29aff 100644 --- a/toolkit/components/places/UnifiedComplete.jsm +++ b/toolkit/components/places/UnifiedComplete.jsm @@ -2726,20 +2726,14 @@ UnifiedComplete.prototype = { this._promiseDatabase = (async () => { let conn = await PlacesUtils.promiseLargeCacheDBConnection(); - try { - Sqlite.shutdown.addBlocker( - "Places UnifiedComplete.js closing", - () => { - // Break a possible cycle through the - // previous result, the controller and - // ourselves. - this._currentSearch = null; - } - ); - } catch (ex) { - // It's too late to block shutdown. - throw ex; - } + // We don't catch exceptions here as it is too late to block shutdown. + Sqlite.shutdown.addBlocker("Places UnifiedComplete.js closing", () => { + // Break a possible cycle through the + // previous result, the controller and + // ourselves. + this._currentSearch = null; + }); + await UrlbarProviderOpenTabs.promiseDb(); return conn; })().catch(ex => { diff --git a/toolkit/components/prompts/src/components.conf b/toolkit/components/prompts/src/components.conf index a26b600b29..1c663311de 100644 --- a/toolkit/components/prompts/src/components.conf +++ b/toolkit/components/prompts/src/components.conf @@ -16,8 +16,10 @@ Classes = [ 'constructor': 'AuthPromptAdapterFactory', }, { + 'js_name': 'prompt', 'cid': '{7ad1b327-6dfa-46ec-9234-f2a620ea7e00}', 'contract_ids': ['@mozilla.org/embedcomp/prompt-service;1'], + 'interfaces': ['nsIPromptService'], 'jsm': 'resource://gre/modules/Prompter.jsm', 'constructor': 'EmbedPrompter', }, diff --git a/toolkit/components/search/components.conf b/toolkit/components/search/components.conf index 01090a30c7..da1c172063 100644 --- a/toolkit/components/search/components.conf +++ b/toolkit/components/search/components.conf @@ -6,8 +6,10 @@ HAVE_SIDEBAR = buildconfig.substs['MOZ_BUILD_APP'] in ('browser', 'mobile/androi Classes = [ { + 'js_name': 'search', 'cid': '{7319788a-fe93-4db3-9f39-818cf08f4256}', 'contract_ids': ['@mozilla.org/browser/search-service;1'], + 'interfaces': ['nsISearchService'], 'jsm': 'resource://gre/modules/SearchService.jsm', 'constructor': 'SearchService', 'processes': ProcessSelector.MAIN_PROCESS_ONLY, diff --git a/toolkit/components/search/tests/xpcshell/head_search.js b/toolkit/components/search/tests/xpcshell/head_search.js index f43df0af69..8d1814f3a3 100644 --- a/toolkit/components/search/tests/xpcshell/head_search.js +++ b/toolkit/components/search/tests/xpcshell/head_search.js @@ -383,8 +383,6 @@ async function withGeoServer( try { await testFn(gRequests); - } catch (ex) { - throw ex; } finally { srv.stop(() => {}); defaultBranch.setCharPref(PREF_SEARCH_URL, originalURL); diff --git a/toolkit/components/startup/nsAppStartup.cpp b/toolkit/components/startup/nsAppStartup.cpp index 50056de158..374abc98bd 100644 --- a/toolkit/components/startup/nsAppStartup.cpp +++ b/toolkit/components/startup/nsAppStartup.cpp @@ -48,7 +48,7 @@ # include #endif -//#include "mozilla/IOInterposer.h" +#include "mozilla/IOInterposer.h" #include "mozilla/Telemetry.h" #include "mozilla/StartupTimeline.h" @@ -591,11 +591,11 @@ nsAppStartup::Observe(nsISupports* aSubject, const char* aTopic, ExitLastWindowClosingSurvivalArea(); } else if (!strcmp(aTopic, "sessionstore-windows-restored")) { StartupTimeline::Record(StartupTimeline::SESSION_RESTORED); - //IOInterposer::EnteringNextStage(); + IOInterposer::EnteringNextStage(); } else if (!strcmp(aTopic, "sessionstore-init-started")) { StartupTimeline::Record(StartupTimeline::SESSION_RESTORE_INIT); } else if (!strcmp(aTopic, "xpcom-shutdown")) { - //IOInterposer::EnteringNextStage(); + IOInterposer::EnteringNextStage(); } else if (!strcmp(aTopic, "quit-application")) { StartupTimeline::Record(StartupTimeline::QUIT_APPLICATION); } else if (!strcmp(aTopic, "profile-before-change")) { diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 901f8f7b60..37e636202f 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -625,15 +625,6 @@ "bug_numbers": [1545093], "description": "Time from the beginning of the first slice to the end of the last slice (ms)" }, - "GC_BUDGET_MS": { - "record_in_processes": ["main", "content"], - "alert_emails": ["dev-telemetry-gc-alerts@mozilla.org"], - "expires_in_version": "never", - "kind": "linear", - "high": 100, - "n_buckets": 10, - "description": "Requested GC slice budget (ms)" - }, "GC_ANIMATION_MS": { "record_in_processes": ["main", "content"], "alert_emails": ["dev-telemetry-gc-alerts@mozilla.org", "jcoppeard@mozilla.com", "sdetar@mozilla.com"], @@ -683,24 +674,6 @@ "n_buckets": 50, "description": "Time spent running JS GC compact phase (ms)" }, - "GC_MARK_ROOTS_MS": { - "record_in_processes": ["main", "content"], - "alert_emails": ["dev-telemetry-gc-alerts@mozilla.org"], - "expires_in_version": "never", - "kind": "linear", - "high": 200, - "n_buckets": 50, - "description": "Time spent marking GC roots (ms)" - }, - "GC_MARK_GRAY_MS": { - "record_in_processes": ["main", "content"], - "alert_emails": ["dev-telemetry-gc-alerts@mozilla.org"], - "expires_in_version": "never", - "kind": "linear", - "high": 200, - "n_buckets": 50, - "description": "Time spent marking gray GC objects (ms)" - }, "GC_SLICE_MS": { "record_in_processes": ["main", "content"], "alert_emails": ["dev-telemetry-gc-alerts@mozilla.org"], @@ -804,15 +777,6 @@ "bug_numbers": [1528867], "description": "Size of the GC nursery (bytes)" }, - "GC_PRETENURE_COUNT": { - "record_in_processes": ["main", "content"], - "alert_emails": ["dev-telemetry-gc-alerts@mozilla.org"], - "expires_in_version": "never", - "kind": "enumerated", - "n_values": 32, - "bug_numbers": [1293262], - "description": "How many objects groups were selected for pretenuring by a minor GC" - }, "GC_SLICE_DURING_IDLE": { "record_in_processes": ["main", "content"], "alert_emails": ["dev-telemetry-gc-alerts@mozilla.org"], @@ -843,17 +807,6 @@ "bug_numbers": [1485299], "description": "The percentage of nursery objects that were promoted to tenured heap." }, - "GC_MARK_RATE": { - "record_in_processes": ["main", "content"], - "alert_emails": ["dev-telemetry-gc-alerts@mozilla.org", "jcoppeard@mozilla.com"], - "expires_in_version": "never", - "kind": "linear", - "low": 1000, - "high": 1000000, - "n_buckets": 100, - "bug_numbers": [1475896], - "description": "The number of objects marked per ms during GC." - }, "GEOLOCATION_ACCURACY_EXPONENTIAL": { "record_in_processes": ["main", "content"], "expires_in_version": "never", @@ -959,28 +912,6 @@ "n_values": 10, "description": "Count how often we use different fallbacks when the GPU process crashes: None=0, GPUProcessDecodingDisabled=1, GPUProcessDisabled=2" }, - "JS_PRIVILEGED_PARSER_COMPILE_LAZY_AFTER_MS": { - "record_in_processes": ["main", "content"], - "alert_emails": ["dteller@mozilla.com"], - "expires_in_version": "70", - "bug_numbers": [1343483], - "kind": "exponential", - "low": 10, - "high": 10000, - "n_buckets": 10, - "description": "Time elapsed between the moment a function is lazy-parsed (end of parsing of the ScriptSource) and the moment it is recompiled as non-lazy (start of compilation), in milliseconds, for privileged code." - }, - "JS_WEB_PARSER_COMPILE_LAZY_AFTER_MS": { - "record_in_processes": ["main", "content"], - "alert_emails": ["dteller@mozilla.com"], - "expires_in_version": "70", - "bug_numbers": [1343483], - "kind": "exponential", - "low": 10, - "high": 10000, - "n_buckets": 10, - "description": "Time elapsed between the moment a function is lazy-parsed (end of parsing of the ScriptSource) and the moment it is recompiled as non-lazy (start of compilation), in milliseconds, for web code." - }, "XUL_CACHE_DISABLED": { "record_in_processes": ["main", "content"], "expires_in_version": "default", diff --git a/toolkit/components/telemetry/build_scripts/gen_histogram_enum.py b/toolkit/components/telemetry/build_scripts/gen_histogram_enum.py index 17e4b46dde..572e38696d 100644 --- a/toolkit/components/telemetry/build_scripts/gen_histogram_enum.py +++ b/toolkit/components/telemetry/build_scripts/gen_histogram_enum.py @@ -29,6 +29,7 @@ header = """ #define mozilla_TelemetryHistogramEnums_h #include "mozilla/TemplateLib.h" +#include "mozilla/TypeTraits.h" namespace mozilla { namespace Telemetry { diff --git a/toolkit/components/telemetry/core/Telemetry.cpp b/toolkit/components/telemetry/core/Telemetry.cpp index de4811f0fd..71c718d244 100644 --- a/toolkit/components/telemetry/core/Telemetry.cpp +++ b/toolkit/components/telemetry/core/Telemetry.cpp @@ -80,7 +80,6 @@ #include "nsXULAppAPI.h" #include "other/CombinedStacks.h" //#include "other/TelemetryIOInterposeObserver.h" -#include "other/WebrtcTelemetry.h" #include "plstr.h" #if defined(MOZ_GECKO_PROFILER) # include "shared-libraries.h" @@ -200,8 +199,6 @@ class TelemetryImpl final : public nsITelemetry, public nsIMemoryReporter { uint32_t mFailedLockCount; nsCOMArray mCallbacks; friend class nsFetchTelemetryData; - - WebrtcTelemetry mWebrtcTelemetry; }; TelemetryImpl* TelemetryImpl::sTelemetry = nullptr; @@ -224,10 +221,6 @@ TelemetryImpl::CollectReports(nsIHandleReportCallback* aHandleReport, TelemetryScalar::GetMapShallowSizesOfExcludingThis(aMallocSizeOf), "Memory used by the Telemetry Scalar implemenation"); - COLLECT_REPORT("explicit/telemetry/WebRTC", - mWebrtcTelemetry.SizeOfExcludingThis(aMallocSizeOf), - "Memory used by WebRTC Telemetry"); - { // Scope for mHashMutex lock MutexAutoLock lock(mHashMutex); COLLECT_REPORT("explicit/telemetry/PrivateSQL", @@ -673,12 +666,6 @@ TelemetryImpl::GetDebugSlowSQL(JSContext* cx, return NS_ERROR_FAILURE; } -NS_IMETHODIMP -TelemetryImpl::GetWebrtcStats(JSContext* cx, JS::MutableHandle ret) { - if (mWebrtcTelemetry.GetWebrtcStats(cx, ret)) return NS_OK; - return NS_ERROR_FAILURE; -} - NS_IMETHODIMP TelemetryImpl::GetMaximalNumberOfConcurrentThreads(uint32_t* ret) { *ret = nsThreadManager::get().GetHighestNumberOfThreads(); @@ -1487,14 +1474,6 @@ void TelemetryImpl::RecordSlowStatement(const nsACString& sql, StoreSlowSQL(fullSQL, delay, Unsanitized); } -void TelemetryImpl::RecordIceCandidates(const uint32_t iceCandidateBitmask, - const bool success) { - if (!sTelemetry || !TelemetryHistogram::CanRecordExtended()) return; - - sTelemetry->mWebrtcTelemetry.RecordIceCandidateMask(iceCandidateBitmask, - success); -} - #if defined(MOZ_GECKO_PROFILER) void TelemetryImpl::DoStackCapture(const nsACString& aKey) { @@ -2002,11 +1981,6 @@ void RecordSlowSQLStatement(const nsACString& statement, TelemetryImpl::RecordSlowStatement(statement, dbName, delay); } -void RecordWebrtcIceCandidates(const uint32_t iceCandidateBitmask, - const bool success) { - TelemetryImpl::RecordIceCandidates(iceCandidateBitmask, success); -} - void Init() { // Make the service manager hold a long-lived reference to the service nsCOMPtr telemetryService = diff --git a/toolkit/components/telemetry/core/Telemetry.h b/toolkit/components/telemetry/core/Telemetry.h index 10abe72654..60866f21ef 100644 --- a/toolkit/components/telemetry/core/Telemetry.h +++ b/toolkit/components/telemetry/core/Telemetry.h @@ -420,15 +420,6 @@ bool CanRecordPrereleaseData(); void RecordSlowSQLStatement(const nsACString& statement, const nsACString& dbName, uint32_t delay); -/** - * Record Webrtc ICE candidate type combinations in a 17bit bitmask - * - * @param iceCandidateBitmask - the bitmask representing local and remote ICE - * candidate types present for the connection - * @param success - did the peer connection connected - */ -void RecordWebrtcIceCandidates(const uint32_t iceCandidateBitmask, - const bool success); /** * Initialize I/O Reporting * Initially this only records I/O for files in the binary directory. diff --git a/toolkit/components/telemetry/core/TelemetryEvent.cpp b/toolkit/components/telemetry/core/TelemetryEvent.cpp index 607820eb27..f223b3fbc1 100644 --- a/toolkit/components/telemetry/core/TelemetryEvent.cpp +++ b/toolkit/components/telemetry/core/TelemetryEvent.cpp @@ -10,7 +10,6 @@ #include "jsapi.h" #include "js/Array.h" // JS::GetArrayLength, JS::IsArrayObject, JS::NewArrayObject #include "mozilla/Maybe.h" -#include "mozilla/Pair.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" #include "mozilla/StaticMutex.h" @@ -1236,8 +1235,8 @@ nsresult TelemetryEvent::CreateSnapshots(uint32_t aDataset, bool aClear, // from JS recording Telemetry. // (1) Extract the events from storage with a lock held. - nsTArray> processEvents; - nsTArray> leftovers; + nsTArray> processEvents; + nsTArray> leftovers; { StaticMutexAutoLock locker(gTelemetryEventsMutex); @@ -1273,10 +1272,10 @@ nsresult TelemetryEvent::CreateSnapshots(uint32_t aDataset, bool aClear, if (events.Length()) { const char* processName = GetNameForProcessID(ProcessID(iter.Key())); processEvents.AppendElement( - mozilla::MakePair(processName, std::move(events))); + std::make_pair(processName, std::move(events))); if (leftoverEvents.Length()) { leftovers.AppendElement( - mozilla::MakePair(iter.Key(), std::move(leftoverEvents))); + std::make_pair(iter.Key(), std::move(leftoverEvents))); } } } @@ -1287,8 +1286,8 @@ nsresult TelemetryEvent::CreateSnapshots(uint32_t aDataset, bool aClear, if (aClear) { gEventRecords.Clear(); for (auto pair : leftovers) { - gEventRecords.Put(pair.first(), - new EventRecordArray(std::move(pair.second()))); + gEventRecords.Put(pair.first, + new EventRecordArray(std::move(pair.second))); } leftovers.Clear(); } @@ -1303,12 +1302,12 @@ nsresult TelemetryEvent::CreateSnapshots(uint32_t aDataset, bool aClear, const uint32_t processLength = processEvents.Length(); for (uint32_t i = 0; i < processLength; ++i) { JS::RootedObject eventsArray(cx); - if (NS_FAILED(SerializeEventsArray(processEvents[i].second(), cx, + if (NS_FAILED(SerializeEventsArray(processEvents[i].second, cx, &eventsArray, aDataset))) { return NS_ERROR_FAILURE; } - if (!JS_DefineProperty(cx, rootObj, processEvents[i].first(), eventsArray, + if (!JS_DefineProperty(cx, rootObj, processEvents[i].first, eventsArray, JSPROP_ENUMERATE)) { return NS_ERROR_FAILURE; } diff --git a/toolkit/components/telemetry/core/TelemetryHistogram.cpp b/toolkit/components/telemetry/core/TelemetryHistogram.cpp index b7f5c92cf9..8e0dda46df 100644 --- a/toolkit/components/telemetry/core/TelemetryHistogram.cpp +++ b/toolkit/components/telemetry/core/TelemetryHistogram.cpp @@ -11,6 +11,7 @@ #include "jsfriendapi.h" #include "js/Array.h" // JS::GetArrayLength, JS::IsArrayObject, JS::NewArrayObject #include "js/GCAPI.h" +#include "js/Object.h" // JS::GetClass, JS::GetPrivate, JS::SetPrivate #include "mozilla/dom/ToJSValue.h" #include "mozilla/gfx/GPUProcessManager.h" #include "mozilla/Atomics.h" @@ -1788,13 +1789,13 @@ bool internal_JSHistogram_Add(JSContext* cx, unsigned argc, JS::Value* vp) { JS::CallArgs args = CallArgsFromVp(argc, vp); if (!args.thisv().isObject() || - JS_GetClass(&args.thisv().toObject()) != &sJSHistogramClass) { + JS::GetClass(&args.thisv().toObject()) != &sJSHistogramClass) { JS_ReportErrorASCII(cx, "Wrong JS class, expected JSHistogram class"); return false; } JSObject* obj = &args.thisv().toObject(); - JSHistogramData* data = static_cast(JS_GetPrivate(obj)); + JSHistogramData* data = static_cast(JS::GetPrivate(obj)); MOZ_ASSERT(data); HistogramID id = data->histogramId; MOZ_ASSERT(internal_IsHistogramEnumId(id)); @@ -1824,13 +1825,13 @@ bool internal_JSHistogram_Name(JSContext* cx, unsigned argc, JS::Value* vp) { JS::CallArgs args = CallArgsFromVp(argc, vp); if (!args.thisv().isObject() || - JS_GetClass(&args.thisv().toObject()) != &sJSHistogramClass) { + JS::GetClass(&args.thisv().toObject()) != &sJSHistogramClass) { JS_ReportErrorASCII(cx, "Wrong JS class, expected JSHistogram class"); return false; } JSObject* obj = &args.thisv().toObject(); - JSHistogramData* data = static_cast(JS_GetPrivate(obj)); + JSHistogramData* data = static_cast(JS::GetPrivate(obj)); MOZ_ASSERT(data); HistogramID id = data->histogramId; MOZ_ASSERT(internal_IsHistogramEnumId(id)); @@ -1893,13 +1894,13 @@ bool internal_JSHistogram_Snapshot(JSContext* cx, unsigned argc, } if (!args.thisv().isObject() || - JS_GetClass(&args.thisv().toObject()) != &sJSHistogramClass) { + JS::GetClass(&args.thisv().toObject()) != &sJSHistogramClass) { JS_ReportErrorASCII(cx, "Wrong JS class, expected JSHistogram class"); return false; } JSObject* obj = &args.thisv().toObject(); - JSHistogramData* data = static_cast(JS_GetPrivate(obj)); + JSHistogramData* data = static_cast(JS::GetPrivate(obj)); MOZ_ASSERT(data); HistogramID id = data->histogramId; @@ -1956,13 +1957,13 @@ bool internal_JSHistogram_Clear(JSContext* cx, unsigned argc, JS::Value* vp) { JS::CallArgs args = JS::CallArgsFromVp(argc, vp); if (!args.thisv().isObject() || - JS_GetClass(&args.thisv().toObject()) != &sJSHistogramClass) { + JS::GetClass(&args.thisv().toObject()) != &sJSHistogramClass) { JS_ReportErrorASCII(cx, "Wrong JS class, expected JSHistogram class"); return false; } JSObject* obj = &args.thisv().toObject(); - JSHistogramData* data = static_cast(JS_GetPrivate(obj)); + JSHistogramData* data = static_cast(JS::GetPrivate(obj)); MOZ_ASSERT(data); nsAutoString storeName; @@ -2007,19 +2008,19 @@ nsresult internal_WrapAndReturnHistogram(HistogramID id, JSContext* cx, } JSHistogramData* data = new JSHistogramData{id}; - JS_SetPrivate(obj, data); + JS::SetPrivate(obj, data); ret.setObject(*obj); return NS_OK; } void internal_JSHistogram_finalize(JSFreeOp*, JSObject* obj) { - if (!obj || JS_GetClass(obj) != &sJSHistogramClass) { + if (!obj || JS::GetClass(obj) != &sJSHistogramClass) { MOZ_ASSERT_UNREACHABLE("Should have the right JS class."); return; } - JSHistogramData* data = static_cast(JS_GetPrivate(obj)); + JSHistogramData* data = static_cast(JS::GetPrivate(obj)); MOZ_ASSERT(data); delete data; } @@ -2072,13 +2073,13 @@ bool internal_JSKeyedHistogram_Snapshot(JSContext* cx, unsigned argc, JS::CallArgs args = JS::CallArgsFromVp(argc, vp); if (!args.thisv().isObject() || - JS_GetClass(&args.thisv().toObject()) != &sJSKeyedHistogramClass) { + JS::GetClass(&args.thisv().toObject()) != &sJSKeyedHistogramClass) { JS_ReportErrorASCII(cx, "Wrong JS class, expected JSKeyedHistogram class"); return false; } JSObject* obj = &args.thisv().toObject(); - JSHistogramData* data = static_cast(JS_GetPrivate(obj)); + JSHistogramData* data = static_cast(JS::GetPrivate(obj)); MOZ_ASSERT(data); HistogramID id = data->histogramId; MOZ_ASSERT(internal_IsHistogramEnumId(id)); @@ -2133,13 +2134,13 @@ bool internal_JSKeyedHistogram_Add(JSContext* cx, unsigned argc, JS::CallArgs args = JS::CallArgsFromVp(argc, vp); if (!args.thisv().isObject() || - JS_GetClass(&args.thisv().toObject()) != &sJSKeyedHistogramClass) { + JS::GetClass(&args.thisv().toObject()) != &sJSKeyedHistogramClass) { JS_ReportErrorASCII(cx, "Wrong JS class, expected JSKeyedHistogram class"); return false; } JSObject* obj = &args.thisv().toObject(); - JSHistogramData* data = static_cast(JS_GetPrivate(obj)); + JSHistogramData* data = static_cast(JS::GetPrivate(obj)); MOZ_ASSERT(data); HistogramID id = data->histogramId; MOZ_ASSERT(internal_IsHistogramEnumId(id)); @@ -2195,13 +2196,13 @@ bool internal_JSKeyedHistogram_Name(JSContext* cx, unsigned argc, JS::CallArgs args = CallArgsFromVp(argc, vp); if (!args.thisv().isObject() || - JS_GetClass(&args.thisv().toObject()) != &sJSKeyedHistogramClass) { + JS::GetClass(&args.thisv().toObject()) != &sJSKeyedHistogramClass) { JS_ReportErrorASCII(cx, "Wrong JS class, expected JSKeyedHistogram class"); return false; } JSObject* obj = &args.thisv().toObject(); - JSHistogramData* data = static_cast(JS_GetPrivate(obj)); + JSHistogramData* data = static_cast(JS::GetPrivate(obj)); MOZ_ASSERT(data); HistogramID id = data->histogramId; MOZ_ASSERT(internal_IsHistogramEnumId(id)); @@ -2218,13 +2219,13 @@ bool internal_JSKeyedHistogram_Keys(JSContext* cx, unsigned argc, JS::CallArgs args = JS::CallArgsFromVp(argc, vp); if (!args.thisv().isObject() || - JS_GetClass(&args.thisv().toObject()) != &sJSKeyedHistogramClass) { + JS::GetClass(&args.thisv().toObject()) != &sJSKeyedHistogramClass) { JS_ReportErrorASCII(cx, "Wrong JS class, expected JSKeyedHistogram class"); return false; } JSObject* obj = &args.thisv().toObject(); - JSHistogramData* data = static_cast(JS_GetPrivate(obj)); + JSHistogramData* data = static_cast(JS::GetPrivate(obj)); MOZ_ASSERT(data); HistogramID id = data->histogramId; @@ -2290,13 +2291,13 @@ bool internal_JSKeyedHistogram_Clear(JSContext* cx, unsigned argc, JS::CallArgs args = JS::CallArgsFromVp(argc, vp); if (!args.thisv().isObject() || - JS_GetClass(&args.thisv().toObject()) != &sJSKeyedHistogramClass) { + JS::GetClass(&args.thisv().toObject()) != &sJSKeyedHistogramClass) { JS_ReportErrorASCII(cx, "Wrong JS class, expected JSKeyedHistogram class"); return false; } JSObject* obj = &args.thisv().toObject(); - JSHistogramData* data = static_cast(JS_GetPrivate(obj)); + JSHistogramData* data = static_cast(JS::GetPrivate(obj)); MOZ_ASSERT(data); HistogramID id = data->histogramId; @@ -2353,19 +2354,19 @@ nsresult internal_WrapAndReturnKeyedHistogram( } JSHistogramData* data = new JSHistogramData{id}; - JS_SetPrivate(obj, data); + JS::SetPrivate(obj, data); ret.setObject(*obj); return NS_OK; } void internal_JSKeyedHistogram_finalize(JSFreeOp*, JSObject* obj) { - if (!obj || JS_GetClass(obj) != &sJSKeyedHistogramClass) { + if (!obj || JS::GetClass(obj) != &sJSKeyedHistogramClass) { MOZ_ASSERT_UNREACHABLE("Should have the right JS class."); return; } - JSHistogramData* data = static_cast(JS_GetPrivate(obj)); + JSHistogramData* data = static_cast(JS::GetPrivate(obj)); MOZ_ASSERT(data); delete data; } diff --git a/toolkit/components/telemetry/core/TelemetryOrigin.cpp b/toolkit/components/telemetry/core/TelemetryOrigin.cpp index 1b20053d86..2fc672caf1 100644 --- a/toolkit/components/telemetry/core/TelemetryOrigin.cpp +++ b/toolkit/components/telemetry/core/TelemetryOrigin.cpp @@ -16,7 +16,6 @@ #include "mozilla/Atomics.h" #include "mozilla/Base64.h" #include "mozilla/Preferences.h" -#include "mozilla/Pair.h" #include "mozilla/Services.h" #include "mozilla/StaticMutex.h" #include "mozilla/Tuple.h" @@ -26,11 +25,9 @@ #include using mozilla::Get; -using mozilla::MakePair; using mozilla::MakeTuple; using mozilla::MakeUnique; using mozilla::MallocSizeOf; -using mozilla::Pair; using mozilla::StaticMutex; using mozilla::StaticMutexAutoLock; using mozilla::Tuple; @@ -125,7 +122,7 @@ UniquePtr gMetricToOriginBag; mozilla::Atomic gInitDone(false); // Useful for app-encoded data -typedef nsTArray>>> +typedef nsTArray>>> IdBoolsPairArray; // Prio has a maximum supported number of bools it can encode at a time. @@ -196,9 +193,7 @@ nsresult AppEncodeTo(const StaticMutexAutoLock& lock, // // EXTERNALLY VISIBLE FUNCTIONS in namespace TelemetryOrigin:: -void TelemetryOrigin::InitializeGlobalState() { - gInitDone = false; -} +void TelemetryOrigin::InitializeGlobalState() { gInitDone = false; } void TelemetryOrigin::DeInitializeGlobalState() { if (!XRE_IsParentProcess()) { @@ -333,7 +328,7 @@ nsresult TelemetryOrigin::GetOriginSnapshot(bool aClear, JSContext* aCx, nsresult TelemetryOrigin::GetEncodedOriginSnapshot( bool aClear, JSContext* aCx, JS::MutableHandleValue aSnapshot) { - return NS_ERROR_FAILURE; + return NS_ERROR_FAILURE; } /** diff --git a/toolkit/components/telemetry/core/TelemetryScalar.cpp b/toolkit/components/telemetry/core/TelemetryScalar.cpp index 9c9f5f0069..9ebf0524a7 100644 --- a/toolkit/components/telemetry/core/TelemetryScalar.cpp +++ b/toolkit/components/telemetry/core/TelemetryScalar.cpp @@ -822,7 +822,7 @@ ScalarBase* internal_ScalarAllocate(const BaseScalarInfo& aInfo) { */ class KeyedScalar { public: - typedef mozilla::Pair> KeyValuePair; + typedef std::pair> KeyValuePair; explicit KeyedScalar(const BaseScalarInfo& info) : mScalarName(info.name()), mMaximumNumberOfKeys(kMaximumNumberOfKeys){}; @@ -997,8 +997,7 @@ nsresult KeyedScalar::GetValue(const nsACString& aStoreName, bool aClearStorage, } // Append it to value list. - aValues.AppendElement( - mozilla::MakePair(nsCString(iter.Key()), scalarValue)); + aValues.AppendElement(std::make_pair(nsCString(iter.Key()), scalarValue)); } return NS_OK; @@ -3243,13 +3242,13 @@ nsresult TelemetryScalar::CreateKeyedSnapshots( // Convert the value for the key to a JSValue. JS::Rooted keyJsValue(aCx); nsresult rv = nsContentUtils::XPConnect()->VariantToJS( - aCx, keyedScalarObj, keyData.second(), &keyJsValue); + aCx, keyedScalarObj, keyData.second, &keyJsValue); if (NS_FAILED(rv)) { return rv; } // Add the key to the scalar representation. - const NS_ConvertUTF8toUTF16 key(keyData.first()); + const NS_ConvertUTF8toUTF16 key(keyData.first); if (!JS_DefineUCProperty(aCx, keyedScalarObj, key.Data(), key.Length(), keyJsValue, JSPROP_ENUMERATE)) { return NS_ERROR_FAILURE; @@ -3814,8 +3813,8 @@ nsresult TelemetryScalar::SerializeKeyedScalars(mozilla::JSONWriter& aWriter) { for (const KeyedScalar::KeyValuePair& keyData : keyProps) { nsresult rv = WriteVariantToJSONWriter( mozilla::Get<2>(keyedScalarData) /*aScalarType*/, - keyData.second() /*aInputValue*/, - PromiseFlatCString(keyData.first()).get() /*aOutKey*/, + keyData.second /*aInputValue*/, + PromiseFlatCString(keyData.first).get() /*aOutKey*/, aWriter /*aWriter*/); if (NS_FAILED(rv)) { // Skip this scalar if we failed to write it. We don't bail out just @@ -3847,7 +3846,7 @@ nsresult TelemetryScalar::DeserializePersistedScalars(JSContext* aCx, return NS_ERROR_FAILURE; } - typedef mozilla::Pair> PersistedScalarPair; + typedef std::pair> PersistedScalarPair; typedef nsTArray PersistedScalarArray; typedef nsDataHashtable PeristedScalarStorage; @@ -3950,7 +3949,7 @@ nsresult TelemetryScalar::DeserializePersistedScalars(JSContext* aCx, // Add the scalar to the map. PersistedScalarArray& processScalars = scalarsToUpdate.GetOrInsert(static_cast(processID)); - processScalars.AppendElement(mozilla::MakePair( + processScalars.AppendElement(std::make_pair( nsCString(NS_ConvertUTF16toUTF8(scalarName)), unpackedVal)); } } @@ -3964,9 +3963,8 @@ nsresult TelemetryScalar::DeserializePersistedScalars(JSContext* aCx, for (PersistedScalarArray::size_type i = 0; i < processScalars.Length(); i++) { mozilla::Unused << internal_UpdateScalar( - lock, processScalars[i].first(), ScalarActionType::eSet, - processScalars[i].second(), ProcessID(iter.Key()), - true /* aForce */); + lock, processScalars[i].first, ScalarActionType::eSet, + processScalars[i].second, ProcessID(iter.Key()), true /* aForce */); } } } diff --git a/toolkit/components/telemetry/core/components.conf b/toolkit/components/telemetry/core/components.conf index fb7ee858cf..0d766160f1 100644 --- a/toolkit/components/telemetry/core/components.conf +++ b/toolkit/components/telemetry/core/components.conf @@ -8,8 +8,10 @@ UnloadFunc = 'mozilla::Telemetry::ShutdownTelemetry' Classes = [ { + 'js_name': 'telemetry', 'cid': '{aea477f2-b3a2-469c-aa29-0a82d132b829}', 'contract_ids': ['@mozilla.org/base/telemetry;1'], + 'interfaces': ['nsITelemetry'], 'singleton': True, 'type': 'nsITelemetry', 'processes': ProcessSelector.ALLOW_IN_GPU_RDD_VR_AND_SOCKET_PROCESS, diff --git a/toolkit/components/telemetry/core/ipc/TelemetryIPCAccumulator.cpp b/toolkit/components/telemetry/core/ipc/TelemetryIPCAccumulator.cpp index 2015df91e6..50f9d3ec64 100644 --- a/toolkit/components/telemetry/core/ipc/TelemetryIPCAccumulator.cpp +++ b/toolkit/components/telemetry/core/ipc/TelemetryIPCAccumulator.cpp @@ -254,19 +254,19 @@ static void SendAccumulatedData(TActor* ipcActor) { { StaticMutexAutoLock locker(gTelemetryIPCAccumulatorMutex); if (gHistogramAccumulations) { - histogramsToSend.SwapElements(*gHistogramAccumulations); + histogramsToSend = std::move(*gHistogramAccumulations); } if (gKeyedHistogramAccumulations) { - keyedHistogramsToSend.SwapElements(*gKeyedHistogramAccumulations); + keyedHistogramsToSend = std::move(*gKeyedHistogramAccumulations); } if (gChildScalarsActions) { - scalarsToSend.SwapElements(*gChildScalarsActions); + scalarsToSend = std::move(*gChildScalarsActions); } if (gChildKeyedScalarsActions) { - keyedScalarsToSend.SwapElements(*gChildKeyedScalarsActions); + keyedScalarsToSend = std::move(*gChildKeyedScalarsActions); } if (gChildEvents) { - eventsToSend.SwapElements(*gChildEvents); + eventsToSend = std::move(*gChildEvents); } discardedData = gDiscardedData; gDiscardedData = {0}; diff --git a/toolkit/components/telemetry/core/nsITelemetry.idl b/toolkit/components/telemetry/core/nsITelemetry.idl index 72d76d059d..7ad81e651e 100644 --- a/toolkit/components/telemetry/core/nsITelemetry.idl +++ b/toolkit/components/telemetry/core/nsITelemetry.idl @@ -178,14 +178,6 @@ interface nsITelemetry : nsISupports [implicit_jscontext] readonly attribute jsval debugSlowSQL; - /* - * An object containing information about Webrtc related stats. For now it - * only contains local and remote ICE candidates avaiable when a Webrtc - * PeerConnection gets terminated. - */ - [implicit_jscontext] - readonly attribute jsval webrtcStats; - /** * A number representing the highest number of concurrent threads * reached during this session. diff --git a/toolkit/components/telemetry/docs/data/main-ping.rst b/toolkit/components/telemetry/docs/data/main-ping.rst index 27694a4d7d..5aed3c9f3a 100644 --- a/toolkit/components/telemetry/docs/data/main-ping.rst +++ b/toolkit/components/telemetry/docs/data/main-ping.rst @@ -67,7 +67,6 @@ Structure: threadHangStats: [...], // obsolete in firefox 57, use the 'bhr' ping capturedStacks: {...}, log: [...], // obsolete in firefox 61, use Event Telemetry or Scalars - webrtc: {...}, gc: {...}, fileIOReports: {...}, lateWrites: {...}, @@ -399,151 +398,6 @@ At present there is one known users of this section: Telemetry Experiments. Telemetry Experiments uses it to note when experiments are activated and terminated. -webrtc ------- -Contains special statistics gathered by WebRTC related components. - -So far only a bitmask for the ICE candidate type present in a successful or -failed WebRTC connection is getting reported through C++ code as -IceCandidatesStats, because the required bitmask is too big to be represented -in a regular enum histogram. - -Note: in most cases the webrtc dictionary inside of -IceCandidatesStats will simply be empty as the user has not used any WebRTC -PeerConnection at all during the ping report time. - -Structure: - -.. code-block:: js - - "webrtc": { - "IceCandidatesStats": { - "webrtc": { - "34526345": { - "successCount": 5 - }, - "2354353": { - "failureCount": 1 - } - }, - } - }, - -gc --- -Contains statistics about selected garbage collections. To avoid -bloating the ping, only a few GCs are included. There are two -selection strategies. We always save the two GCs with the worst -max_pause time. Additionally, in content processes, two collections -are selected at random. If a GC runs for C milliseconds and the total -time for all GCs since the session began is T milliseconds, then the -GC has a C/T probablility of being selected for one of these "slots". - -Structure: - -.. code-block:: js - - "gc": { - "random": [ - { - // "completed" or "aborted" if an OOM occurred. - "status": "completed", - // Timestamps are in milliseconds since startup. All the times here - // are wall-clock times, which may not be monotonically increasing. - "timestamp": 294872.2, - // All durations are in milliseconds. - "max_pause": 73.629, - "total_time": 364.951, // Sum of all slice times. - "zones_collected": 9, - "total_zones": 9, - "total_compartments": 309, - "minor_gcs": 44, - // Present if non-zero. - "store_buffer_overflows": 19, - "mmu_20ms": 0, - "mmu_50ms": 0, - // Reasons include "None", "NonIncrementalRequested", - // "AbortRequested", "KeepAtomsSet", "IncrementalDisabled", - // "ModeChange", "MallocBytesTrigger", "GCBytesTrigger", - // "ZoneChange", "CompartmentRevived". - // Present for non-incremental GCs only. - "nonincremental_reason": "GCBytesTrigger", - "allocated_bytes": 38853696 // in bytes - - // Present if non-zero. - "added_chunks": 54, - "removed_chunks": 12, - - // Total number of slices (some of which may not appear - // in the "slices" array). - "slices": 15, - // We record at most 4 slices. - "slice_number": 218, // The first slice number for this GC event. - "slices_list": [ - { - "slice": 218, // The global index of this slice. - "pause": 23.221, // How long the slice took (milliseconds). - "reason": "SET_NEW_DOCUMENT", - // GC state when the slice started - "initial_state": "NotActive", - // GC state when the slice ended - "final_state": "Mark", - // Budget is either "Xms", "work(Y)", or - // "unlimited". - "budget": "10ms", - // Number of page faults during the slice. - // optional field, missing means 0. - "page_faults": 1, - // The start time of this slice in seconds. The end time is - // given by the start_timestamp + pause. - "start_timestamp": 294875, - // Time taken by each phase. There are at most 65 possible - // phases, but usually only a few phases run in a given slice. - "times": { - "wait_background_thread": 0.012, - "mark_discard_code": 2.845, - "purge": 0.723, - "mark": 9.831, - "mark_roots": 0.102, - "buffer_gray_roots": 3.095, - "mark_cross_compartment_wrappers": 0.039, - "mark_c_and_js_stacks": 0.005, - "mark_runtime_wide_data": 2.313, - "mark_embedding": 0.117, - "mark_compartments": 2.27, - "unmark": 1.063, - "minor_gcs_to_evict_nursery": 8.701, - ... - } - }, - { ... }, - ], - // Sum of the phase times across all slices, including - // omitted slices. As before, there are <= 65 possible phases. - "totals": { - "wait_background_thread": 0.012, - "mark_discard_code": 2.845, - "purge": 0.723, - "mark": 9.831, - "mark_roots": 0.102, - "buffer_gray_roots": 3.095, - "mark_cross_compartment_wrappers": 0.039, - "mark_c_and_js_stacks": 0.005, - "mark_runtime_wide_data": 2.313, - "mark_embedding": 0.117, - "mark_compartments": 2.27, - "unmark": 1.063, - "minor_gcs_to_evict_nursery": 8.701, - ... - } - }, - ... // Up to four more selected GCs follow. - ], - "worst": [ - ... // Same as above, but the 2 worst GCs by max_pause. - ] - }, - fileIOReports ------------- Contains the statistics of main-thread I/O recorded during the execution. Only the I/O stats for the XRE and the profile directories are currently reported, neither of them disclosing the full local path. @@ -682,3 +536,7 @@ Version History - Firefox 62: - ``events`` are now reported via the :doc:`../data/event-ping` (`bug 1460595 `_). + +- Firefox 80: + + - Stopped reporting ``GCTelemetry`` (`bug 1482089 `_). diff --git a/toolkit/components/telemetry/geckoview/GeckoViewTelemetryController.jsm b/toolkit/components/telemetry/geckoview/GeckoViewTelemetryController.jsm index e4761b698c..3e24565ace 100644 --- a/toolkit/components/telemetry/geckoview/GeckoViewTelemetryController.jsm +++ b/toolkit/components/telemetry/geckoview/GeckoViewTelemetryController.jsm @@ -23,8 +23,6 @@ const { debug, warn } = GeckoViewUtils.initLogging( var EXPORTED_SYMBOLS = ["GeckoViewTelemetryController"]; -/* global debug warn */ - // Persistent data loading topic - see TelemetryGeckoViewPersistence.cpp. const LOAD_COMPLETE_TOPIC = "internal-telemetry-geckoview-load-complete"; diff --git a/toolkit/components/telemetry/histogram-whitelists.json b/toolkit/components/telemetry/histogram-whitelists.json index 62cc232877..be84287411 100644 --- a/toolkit/components/telemetry/histogram-whitelists.json +++ b/toolkit/components/telemetry/histogram-whitelists.json @@ -659,12 +659,9 @@ "FX_THUMBNAILS_STORE_TIME_MS", "FX_TOTAL_TOP_VISITS", "FX_TOUCH_USED", - "GC_BUDGET_MS", "GC_COMPACT_MS", "GC_IS_COMPARTMENTAL", - "GC_MARK_GRAY_MS", "GC_MARK_MS", - "GC_MARK_ROOTS_MS", "GC_MINOR_REASON", "GC_MINOR_REASON_LONG", "GC_MINOR_US", diff --git a/toolkit/components/telemetry/moz.build b/toolkit/components/telemetry/moz.build index 44e5b370a8..dd61d3a1b3 100644 --- a/toolkit/components/telemetry/moz.build +++ b/toolkit/components/telemetry/moz.build @@ -43,7 +43,6 @@ BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini'] XPIDL_SOURCES += [ 'core/nsITelemetry.idl', - 'other/GCTelemetry.idl', ] XPIDL_MODULE = 'telemetry' @@ -79,7 +78,6 @@ SOURCES += [ 'other/CombinedStacks.cpp', 'other/ProcessedStack.cpp', #'other/TelemetryIOInterposeObserver.cpp', - 'other/WebrtcTelemetry.cpp', ] if CONFIG['OS_ARCH'] == 'WINNT': @@ -113,7 +111,6 @@ EXTRA_JS_MODULES += [ 'app/TelemetryStorage.jsm', 'app/TelemetryTimestamps.jsm', 'app/TelemetryUtils.jsm', - 'other/GCTelemetry.jsm', 'other/UITelemetry.jsm', 'pings/CoveragePing.jsm', 'pings/EcosystemTelemetry.jsm', diff --git a/toolkit/components/telemetry/other/GCTelemetry.idl b/toolkit/components/telemetry/other/GCTelemetry.idl deleted file mode 100644 index 1f92b73813..0000000000 --- a/toolkit/components/telemetry/other/GCTelemetry.idl +++ /dev/null @@ -1,17 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsISupports.idl" - -[scriptable, uuid(6ab1c3c1-31cf-4a32-8484-97b5ef0627af)] -interface mozIGCTelemetry : nsISupports { - void init(); - - void shutdown(); -}; - -[scriptable, uuid(93b2a0ca-6306-41c1-b296-c57cad5175c7)] -interface mozIGCTelemetryJSM : nsISupports { - readonly attribute mozIGCTelemetry GCTelemetry; -}; diff --git a/toolkit/components/telemetry/other/GCTelemetry.jsm b/toolkit/components/telemetry/other/GCTelemetry.jsm deleted file mode 100644 index 1a446e03db..0000000000 --- a/toolkit/components/telemetry/other/GCTelemetry.jsm +++ /dev/null @@ -1,251 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -/** - * This module records detailed timing information about selected - * GCs. The data is sent back in the telemetry session ping. To avoid - * bloating the ping, only a few GCs are included. There are two - * selection strategies. We always save the two GCs with the worst - * max_pause time. Additionally, two collections are selected at - * random. If a GC runs for C milliseconds and the total time for all - * GCs since the session began is T milliseconds, then the GC has a - * 2*C/T probablility of being selected (the factor of 2 is because we - * save two of them). - * - * GCs from both the main process and all content processes are - * recorded. The data is cleared for each new subsession. - */ - -ChromeUtils.import("resource://gre/modules/Services.jsm", this); -const { Log } = ChromeUtils.import("resource://gre/modules/Log.jsm"); - -var EXPORTED_SYMBOLS = ["GCTelemetry"]; - -// Names of processes where we record GCs. -const PROCESS_NAMES = ["main", "content"]; - -// Should be the time we started up in milliseconds since the epoch. -const BASE_TIME = Date.now() - Services.telemetry.msSinceProcessStart(); - -// Records selected GCs. There is one instance per process type. -class GCData { - constructor(kind) { - let numRandom = { main: 0, content: 2 }; - let numWorst = { main: 2, content: 2 }; - - this.totalGCTime = 0; - this.randomlySelected = Array(numRandom[kind]).fill(null); - this.worst = Array(numWorst[kind]).fill(null); - } - - // Turn absolute timestamps (in microseconds since the epoch) into - // milliseconds since startup. - rebaseTimes(data) { - function fixup(t) { - return t / 1000.0 - BASE_TIME; - } - - data.timestamp = fixup(data.timestamp); - - for (let i = 0; i < data.slices_list.length; i++) { - let slice = data.slices_list[i]; - slice.start_timestamp = fixup(slice.start_timestamp); - // Slices have no end_timestamp, instead they have a duration. - } - } - - // Records a GC (represented by |data|) in the randomlySelected or - // worst batches depending on the criteria above. - record(data) { - this.rebaseTimes(data); - - let time = data.total_time; - this.totalGCTime += time; - - // Probability that we will replace any one of our - // current randomlySelected GCs with |data|. - let prob = time / this.totalGCTime; - - // Note that we may replace multiple GCs in - // randomlySelected. It's easier to reason about the - // probabilities this way, and it's unlikely to have any effect in - // practice. - for (let i = 0; i < this.randomlySelected.length; i++) { - let r = Math.random(); - if (r <= prob) { - this.randomlySelected[i] = data; - } - } - - // Save the 2 worst GCs based on max_pause. A GC may appear in - // both worst and randomlySelected. - for (let i = 0; i < this.worst.length; i++) { - if (!this.worst[i]) { - this.worst[i] = data; - break; - } - - if (this.worst[i].max_pause < data.max_pause) { - this.worst.splice(i, 0, data); - this.worst.length--; - break; - } - } - } - - entries() { - return { - random: this.randomlySelected.filter(e => e !== null), - worst: this.worst.filter(e => e !== null), - }; - } -} - -// If you adjust any of the constants here (slice limit, number of keys, etc.) -// make sure to update the JSON schema at: -// https://github.com/mozilla-services/mozilla-pipeline-schemas/blob/master/telemetry/main.schema.json -// You should also adjust browser_TelemetryGC.js. -const MAX_GC_KEYS = 24; -const MAX_SLICES = 4; -const MAX_SLICE_KEYS = 12; -const MAX_PHASES = 65; -const LOGGER_NAME = "Toolkit.Telemetry"; - -function limitProperties(name, obj, count, log) { - // If there are too many properties delete all/most of them. We don't - // expect this ever to happen. - let num_properties = Object.keys(obj).length; - if (num_properties > count) { - for (let key of Object.keys(obj)) { - // If this is the main GC object then save some of the critical - // properties. - if ( - name === "data" && - (key === "max_pause" || - key === "slices" || - key === "slices_list" || - key === "status" || - key === "timestamp" || - key === "total_time" || - key === "totals") - ) { - continue; - } - - delete obj[key]; - } - let log_fn; - if (name === "slice.times" || name === "data.totals") { - // This is a bit more likely, but is mostly-okay. - log_fn = s => log.info(s); - } else { - log_fn = s => log.warn(s); - } - log_fn( - `Number of properties exceeded in the GC telemetry ${name} ping, expected ${count} got ${num_properties}` - ); - } -} - -/* - * Reduce the size of the object by limiting the number of slices or times - * etc. - */ -function limitSize(data, log) { - data.slices_list.sort((a, b) => b.pause - a.pause); - - if (data.slices_list.length > MAX_SLICES) { - // Make sure we always keep the first slice since it has the - // reason the GC was started. - let firstSliceIndex = data.slices_list.findIndex(s => s.slice == 0); - if (firstSliceIndex >= MAX_SLICES) { - data.slices_list[MAX_SLICES - 1] = data.slices_list[firstSliceIndex]; - } - - data.slices_list.length = MAX_SLICES; - } - - data.slices_list.sort((a, b) => a.slice - b.slice); - - limitProperties("data", data, MAX_GC_KEYS, log); - - for (let slice of data.slices_list) { - limitProperties("slice", slice, MAX_SLICE_KEYS, log); - limitProperties("slice.times", slice.times, MAX_PHASES, log); - } - - limitProperties("data.totals", data.totals, MAX_PHASES, log); -} - -let processData = new Map(); -for (let name of PROCESS_NAMES) { - processData.set(name, new GCData(name)); -} - -var GCTelemetry = { - initialized: false, - - init() { - if (this.initialized) { - return false; - } - - this.initialized = true; - this._log = Log.repository.getLoggerWithMessagePrefix( - LOGGER_NAME, - "GCTelemetry::" - ); - - Services.obs.addObserver(this, "garbage-collection-statistics"); - - if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_DEFAULT) { - Services.ppmm.addMessageListener("Telemetry:GCStatistics", this); - } - - return true; - }, - - shutdown() { - if (!this.initialized) { - return; - } - - Services.obs.removeObserver(this, "garbage-collection-statistics"); - - if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_DEFAULT) { - Services.ppmm.removeMessageListener("Telemetry:GCStatistics", this); - } - this.initialized = false; - }, - - observe(subject, topic, arg) { - this.observeRaw(JSON.parse(arg)); - }, - - // We expose this method so unit tests can call it, no need to test JSON - // parsing. - observeRaw(data) { - limitSize(data, this._log); - - if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_DEFAULT) { - processData.get("main").record(data); - } else { - Services.cpmm.sendAsyncMessage("Telemetry:GCStatistics", data); - } - }, - - receiveMessage(msg) { - processData.get("content").record(msg.data); - }, - - entries(kind, clear) { - let result = processData.get(kind).entries(); - if (clear) { - processData.set(kind, new GCData(kind)); - } - return result; - }, -}; diff --git a/toolkit/components/telemetry/other/WebrtcTelemetry.cpp b/toolkit/components/telemetry/other/WebrtcTelemetry.cpp deleted file mode 100644 index 475cfec7f0..0000000000 --- a/toolkit/components/telemetry/other/WebrtcTelemetry.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "WebrtcTelemetry.h" - -#include "jsapi.h" -#include "mozilla/Telemetry.h" -#include "nsPrintfCString.h" -#include "nsTHashtable.h" -void WebrtcTelemetry::RecordIceCandidateMask(const uint32_t iceCandidateBitmask, - const bool success) { - WebrtcIceCandidateType* entry = - mWebrtcIceCandidates.GetEntry(iceCandidateBitmask); - if (!entry) { - entry = mWebrtcIceCandidates.PutEntry(iceCandidateBitmask); - if (MOZ_UNLIKELY(!entry)) return; - } - - if (success) { - entry->GetModifiableData()->webrtc.successCount++; - } else { - entry->GetModifiableData()->webrtc.failureCount++; - } -} - -bool ReflectIceEntry(const WebrtcTelemetry::WebrtcIceCandidateType* entry, - const WebrtcTelemetry::WebrtcIceCandidateStats* stat, - JSContext* cx, JS::Handle obj) { - if ((stat->successCount == 0) && (stat->failureCount == 0)) return true; - - const uint32_t& bitmask = entry->GetKey(); - - JS::Rooted statsObj(cx, JS_NewPlainObject(cx)); - if (!statsObj) return false; - if (!JS_DefineProperty(cx, obj, - nsPrintfCString("%" PRIu32, bitmask).BeginReading(), - statsObj, JSPROP_ENUMERATE)) { - return false; - } - if (stat->successCount && - !JS_DefineProperty(cx, statsObj, "successCount", stat->successCount, - JSPROP_ENUMERATE)) { - return false; - } - if (stat->failureCount && - !JS_DefineProperty(cx, statsObj, "failureCount", stat->failureCount, - JSPROP_ENUMERATE)) { - return false; - } - return true; -} - -bool ReflectIceWebrtc(WebrtcTelemetry::WebrtcIceCandidateType* entry, - JSContext* cx, JS::Handle obj) { - return ReflectIceEntry(entry, &entry->GetData().webrtc, cx, obj); -} - -bool WebrtcTelemetry::AddIceInfo(JSContext* cx, JS::Handle iceObj) { - JS::Rooted statsObj(cx, JS_NewPlainObject(cx)); - if (!statsObj) return false; - - if (!mWebrtcIceCandidates.ReflectIntoJS(ReflectIceWebrtc, cx, statsObj)) { - return false; - } - - return JS_DefineProperty(cx, iceObj, "webrtc", statsObj, JSPROP_ENUMERATE); -} - -bool WebrtcTelemetry::GetWebrtcStats(JSContext* cx, - JS::MutableHandle ret) { - JS::Rooted root_obj(cx, JS_NewPlainObject(cx)); - if (!root_obj) return false; - ret.setObject(*root_obj); - - JS::Rooted ice_obj(cx, JS_NewPlainObject(cx)); - if (!ice_obj) return false; - JS_DefineProperty(cx, root_obj, "IceCandidatesStats", ice_obj, - JSPROP_ENUMERATE); - - if (!AddIceInfo(cx, ice_obj)) return false; - - return true; -} - -size_t WebrtcTelemetry::SizeOfExcludingThis( - mozilla::MallocSizeOf aMallocSizeOf) const { - return mWebrtcIceCandidates.ShallowSizeOfExcludingThis(aMallocSizeOf); -} diff --git a/toolkit/components/telemetry/other/WebrtcTelemetry.h b/toolkit/components/telemetry/other/WebrtcTelemetry.h deleted file mode 100644 index 5e2545632a..0000000000 --- a/toolkit/components/telemetry/other/WebrtcTelemetry.h +++ /dev/null @@ -1,38 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef WebrtcTelemetry_h__ -#define WebrtcTelemetry_h__ - -#include "nsBaseHashtable.h" -#include "nsHashKeys.h" -#include "core/TelemetryCommon.h" - -class WebrtcTelemetry { - public: - struct WebrtcIceCandidateStats { - uint32_t successCount; - uint32_t failureCount; - WebrtcIceCandidateStats() : successCount(0), failureCount(0) {} - }; - struct WebrtcIceStatsCategory { - struct WebrtcIceCandidateStats webrtc; - }; - typedef nsBaseHashtableET - WebrtcIceCandidateType; - - void RecordIceCandidateMask(const uint32_t iceCandidateBitmask, bool success); - - bool GetWebrtcStats(JSContext* cx, JS::MutableHandle ret); - - size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; - - private: - bool AddIceInfo(JSContext* cx, JS::Handle rootObj); - - mozilla::Telemetry::Common::AutoHashtable - mWebrtcIceCandidates; -}; - -#endif // WebrtcTelemetry_h__ diff --git a/toolkit/components/telemetry/pings/TelemetrySession.jsm b/toolkit/components/telemetry/pings/TelemetrySession.jsm index ae65d30f69..11e5d6550a 100644 --- a/toolkit/components/telemetry/pings/TelemetrySession.jsm +++ b/toolkit/components/telemetry/pings/TelemetrySession.jsm @@ -17,7 +17,6 @@ XPCOMUtils.defineLazyModuleGetters(this, { TelemetryController: "resource://gre/modules/TelemetryController.jsm", TelemetryStorage: "resource://gre/modules/TelemetryStorage.jsm", UITelemetry: "resource://gre/modules/UITelemetry.jsm", - GCTelemetry: "resource://gre/modules/GCTelemetry.jsm", TelemetryEnvironment: "resource://gre/modules/TelemetryEnvironment.jsm", TelemetryReportingPolicy: "resource://gre/modules/TelemetryReportingPolicy.jsm", @@ -627,7 +626,6 @@ var Impl = { // Add extended set measurements common to chrome & content processes if (Telemetry.canRecordExtended) { payloadObj.log = []; - payloadObj.webrtc = protect(() => Telemetry.webrtcStats); } if (Utils.isContentProcess) { @@ -727,15 +725,6 @@ var Impl = { payloadObj.slowSQLStartup = this._slowSQLStartup; } - if (!this._isClassicReason(reason)) { - payloadObj.processes.parent.gc = protect(() => - GCTelemetry.entries("main", clearSubsession) - ); - payloadObj.processes.content.gc = protect(() => - GCTelemetry.entries("content", clearSubsession) - ); - } - // Adding captured stacks to the payload only if any exist and clearing // captures for this sub-session. let stacks = protect(() => Telemetry.snapshotCapturedStacks(true)); diff --git a/toolkit/components/telemetry/tests/browser/browser.ini b/toolkit/components/telemetry/tests/browser/browser.ini index 3271caf76b..584bf7d39b 100644 --- a/toolkit/components/telemetry/tests/browser/browser.ini +++ b/toolkit/components/telemetry/tests/browser/browser.ini @@ -2,7 +2,6 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -[browser_TelemetryGC.js] [browser_UpdatePingSuccess.js] [browser_DynamicScalars.js] skip-if = !e10s || verify # e10s specific test for definition broadcasting across processes. diff --git a/toolkit/components/telemetry/tests/browser/browser_TelemetryGC.js b/toolkit/components/telemetry/tests/browser/browser_TelemetryGC.js deleted file mode 100644 index 9cc1480beb..0000000000 --- a/toolkit/components/telemetry/tests/browser/browser_TelemetryGC.js +++ /dev/null @@ -1,221 +0,0 @@ -"use strict"; - -/* - ********************************************************************************* - * * - * WARNING * - * * - * If you adjust any of the constants here (slice limit, number of keys, etc.) * - * make sure to update the JSON schema at: * - * https://github.com/mozilla-services/mozilla-pipeline-schemas/blob/master/ * - * telemetry/main.schema.json * - * * - * Otherwise, pings may be dropped by the telemetry backend! * - * * - ********************************************************************************/ - -const { GCTelemetry } = ChromeUtils.import( - "resource://gre/modules/GCTelemetry.jsm" -); - -function check(entries) { - const FIELDS = ["random", "worst"]; - - // Check that all FIELDS are in |entries|. - for (let f of FIELDS) { - ok(f in entries, `${f} found in entries`); - } - - // Check that only FIELDS are in |entries|. - for (let k of Object.keys(entries)) { - ok(FIELDS.includes(k), `${k} found in FIELDS`); - } - - for (let f of FIELDS) { - ok(Array.isArray(entries[f]), "have an array of GCs"); - - ok(entries[f].length <= 2, "not too many GCs"); - - for (let gc of entries[f]) { - isnot(gc, null, "GC is non-null"); - - ok(Object.keys(gc).length <= 24, "number of keys in GC is not too large"); - - // Sanity check the GC data. - ok("status" in gc, "status field present"); - is(gc.status, "completed", "status field correct"); - ok("total_time" in gc, "total_time field present"); - ok("max_pause" in gc, "max_pause field present"); - - ok("slices_list" in gc, "slices_list field present"); - ok(Array.isArray(gc.slices_list), "slices_list is an array"); - ok(gc.slices_list.length > 0, "slices_list array non-empty"); - ok(gc.slices_list.length <= 4, "slices_list array is not too long"); - - ok("totals" in gc, "totals field present"); - is(typeof gc.totals, "object", "totals is an object"); - ok(Object.keys(gc.totals).length <= 65, "totals array is not too long"); - - // Make sure we don't skip any big objects. - for (let key in gc) { - if (key != "slices_list" && key != "totals") { - isnot( - typeof gc[key], - "object", - `${key} property should be primitive` - ); - } - } - - let phases = new Set(); - - for (let slice of gc.slices_list) { - ok(Object.keys(slice).length <= 12, "slice is not too large"); - - ok("pause" in slice, "pause field present in slice"); - ok("reason" in slice, "reason field present in slice"); - ok("times" in slice, "times field present in slice"); - - // Make sure we don't skip any big objects. - for (let key in slice) { - if (key != "times") { - isnot( - typeof slice[key], - "object", - `${key} property should be primitive` - ); - } - } - - ok(Object.keys(slice.times).length <= 65, "no more than 65 phases"); - - for (let phase in slice.times) { - phases.add(phase); - is( - typeof slice.times[phase], - "number", - `${phase} property should be a number` - ); - } - } - - let totals = gc.totals; - // Make sure we don't skip any big objects. - for (let phase in totals) { - is( - typeof totals[phase], - "number", - `${phase} property should be a number` - ); - } - - for (let phase of phases) { - ok(phase in totals, `${phase} is in totals`); - } - } - } -} - -add_task(async function test() { - let multiprocess = Services.appinfo.browserTabsRemoteAutostart; - - // Set these prefs to ensure that we get measurements. - await SpecialPowers.pushPrefEnv({ - set: [["javascript.options.mem.notify", true]], - }); - - function runRemote(f) { - gBrowser.selectedBrowser.messageManager.loadFrameScript( - `data:,(${f})()`, - false - ); - } - - // These are available to frame scripts. - /* global addMessageListener:false, removeMessageListener: false */ - function initScript() { - const { GCTelemetry } = ChromeUtils.import( - "resource://gre/modules/GCTelemetry.jsm" - ); - - /* - * Don't shut down GC telemetry if it was already running before the test! - * Note: We need to use a multiline comment here since this code is turned into a data: URI. - */ - let shutdown = GCTelemetry.init(); - - function listener() { - removeMessageListener("GCTelemTest:Shutdown", listener); - if (shutdown) { - GCTelemetry.shutdown(); - } - } - addMessageListener("GCTelemTest:Shutdown", listener); - } - - if (multiprocess) { - runRemote(initScript); - } - - // Don't shut down GC telemetry if it was already running before the test! - let shutdown = GCTelemetry.init(); - registerCleanupFunction(() => { - if (shutdown) { - GCTelemetry.shutdown(); - } - - gBrowser.selectedBrowser.messageManager.sendAsyncMessage( - "GCTelemTest:Shutdown" - ); - }); - - let localPromise = new Promise(resolve => { - function obs() { - Services.obs.removeObserver(obs, "garbage-collection-statistics"); - resolve(); - } - Services.obs.addObserver(obs, "garbage-collection-statistics"); - }); - - let remotePromise; - if (multiprocess) { - remotePromise = new Promise(resolve => { - function obs() { - Services.ppmm.removeMessageListener("Telemetry:GCStatistics", obs); - resolve(); - } - Services.ppmm.addMessageListener("Telemetry:GCStatistics", obs); - }); - } else { - remotePromise = Promise.resolve(); - } - - // Make sure we have a GC to work with in both processes. - Cu.forceGC(); - if (multiprocess) { - runRemote(() => Cu.forceGC()); - } - - info("Waiting for GCs"); - - await Promise.all([localPromise, remotePromise]); - - let localEntries = GCTelemetry.entries("main", true); - let remoteEntries = multiprocess - ? GCTelemetry.entries("content", true) - : localEntries; - - check(localEntries); - check(remoteEntries); - - localEntries = GCTelemetry.entries("main", false); - remoteEntries = multiprocess - ? GCTelemetry.entries("content", false) - : localEntries; - - is(localEntries.random.length, 0, "no random GCs after reset"); - is(localEntries.worst.length, 0, "no worst GCs after reset"); - - is(remoteEntries.random.length, 0, "no random GCs after reset"); - is(remoteEntries.worst.length, 0, "no worst GCs after reset"); -}); diff --git a/toolkit/components/telemetry/tests/unit/head.js b/toolkit/components/telemetry/tests/unit/head.js index 3250a0391e..eb50b9bd88 100644 --- a/toolkit/components/telemetry/tests/unit/head.js +++ b/toolkit/components/telemetry/tests/unit/head.js @@ -552,7 +552,6 @@ if (runningInParent) { ); // This gets imported via fakeNow(); - /* global TelemetrySend */ registerCleanupFunction(() => TelemetrySend.shutdown()); } diff --git a/toolkit/components/telemetry/tests/unit/test_SocketScalars.js b/toolkit/components/telemetry/tests/unit/test_SocketScalars.js index aa7fc3e33d..1d7c0cebfd 100644 --- a/toolkit/components/telemetry/tests/unit/test_SocketScalars.js +++ b/toolkit/components/telemetry/tests/unit/test_SocketScalars.js @@ -37,7 +37,7 @@ add_task(async function() { do_get_profile(true); await TelemetryController.testSetup(); - Services.netUtils.socketProcessTelemetryPing(); + Services.io.socketProcessTelemetryPing(); // Once scalars are set by the socket process, they don't immediately get // sent to the parent process. Wait for the Telemetry IPC Timer to trigger diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js index be70bf74bd..6f83619ba3 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js @@ -167,11 +167,8 @@ var SysInfo = { if (name in this.overrides) { return this.overrides[name]; } - try { - return this._genuine.getProperty(name); - } catch (ex) { - throw ex; - } + + return this._genuine.getProperty(name); }, getPropertyAsUint32(name) { diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryGC.js b/toolkit/components/telemetry/tests/unit/test_TelemetryGC.js deleted file mode 100644 index e3112cb57a..0000000000 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryGC.js +++ /dev/null @@ -1,170 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ -*/ - -"use strict"; - -ChromeUtils.import("resource://gre/modules/GCTelemetry.jsm", this); - -function do_register_cleanup() { - GCTelemetry.shutdown(); -} - -/* - * These tests are very basic, my goal was to add enough testing to support - * a change made in Bug 1424760. TODO Bug 1429635 for adding more extensive - * tests and deleting this comment. - */ - -function run_test() { - // The limit on the number of fields in a GCMajor object. - const limit = 24; - - // The number of fields that the make_gc() test function generates. - const make_gc_fields = 19; - - // Test initialisation - Assert.ok(GCTelemetry.init(), "Initialize success"); - Assert.ok(!GCTelemetry.init(), "Wont initialize twice"); - - // Test the basic success path. - - // There are currently no entries - assert_num_entries(0, false); - - // Add an entry - GCTelemetry.observeRaw(make_gc()); - // Get it back. - assert_num_entries(1, false); - Assert.equal(make_gc_fields, Object.keys(get_entry()).length); - // "true" will cause the entry to be clared. - assert_num_entries(1, true); - // There are currently no entries. - assert_num_entries(0, false); - - // Test too many fields - let my_big_gc = make_gc(); - for (let i = 0; i < 100; i++) { - my_big_gc["new_property_" + i] = "Data"; - } - GCTelemetry.observeRaw(my_big_gc); - // Assert that it was recorded but has only 7 fields. - Assert.equal(7, Object.keys(get_entry()).length); - assert_num_entries(1, true); - assert_num_entries(0, false); - - let my_gc_exact = make_gc(); - Assert.equal(make_gc_fields, Object.keys(my_gc_exact).length); - - for (let i = 0; i < limit - make_gc_fields; i++) { - my_gc_exact["new_property_" + i] = "Data"; - } - GCTelemetry.observeRaw(my_gc_exact); - // Assert that it was recorded has all the fields. - Assert.equal(limit, Object.keys(get_entry()).length); - assert_num_entries(1, true); - assert_num_entries(0, false); - - // Exactly too many fields. - let my_gc_too_many = make_gc(); - for (let i = 0; i < limit - make_gc_fields + 1; i++) { - my_gc_too_many["new_property_" + i] = "Data"; - } - GCTelemetry.observeRaw(my_gc_too_many); - // Assert that it was recorded but has only 7 fields. - Assert.equal(7, Object.keys(get_entry()).length); - assert_num_entries(1, true); - assert_num_entries(0, false); -} - -function assert_num_entries(expect, clear) { - let entries = GCTelemetry.entries("main", clear); - Assert.equal(expect, entries.worst.length, expect + " worst entries"); - // Randomly sampled GCs are only recorded for content processes - Assert.equal(0, entries.random.length, expect + " random entries"); -} - -function get_entry() { - let entries = GCTelemetry.entries("main", false); - Assert.ok(entries, "Got entries object"); - Assert.ok(entries.random, "Has random property"); - Assert.ok(entries.worst, "Has worst property"); - let entry = entries.worst[0]; - Assert.ok(entry, "Got worst entry"); - return entry; -} - -/* - * These events are not exactly well-formed, but good enough. For example - * there's no guantee that all the pause times add up to total time, or - * that max_pause is correct. - */ -function make_gc() { - // Timestamps are in milliseconds since startup. All the times here - // are wall-clock times, which may not be monotonically increasing. - let timestamp = Math.random() * 1000000; - - let gc = { - status: "completed", - timestamp, - // All durations are in milliseconds. - max_pause: Math.random() * 95 + 5, - total_time: Math.random() * 500 + 500, // Sum of all slice times. - zones_collected: 9, - total_zones: 9, - total_compartments: 309, - minor_gcs: 44, - store_buffer_overflows: 19, - mmu_20ms: 0, - mmu_50ms: 0, - nonincremental_reason: "GCBytesTrigger", - allocated_bytes: 38853696, // in bytes - added_chunks: 54, - removed_chunks: 12, - slices: 15, - slice_number: 218, // The first slice number for this GC event. - slices_list: [ - { - slice: 218, // The global index of this slice. - pause: Math.random() * 2 + 28, - reason: "SET_NEW_DOCUMENT", - initial_state: "NotActive", - final_state: "Mark", - budget: "10ms", - page_faults: 1, - start_timestamp: timestamp + Math.random() * 50000, - times: { - wait_background_thread: 0.012, - mark_discard_code: 2.845, - purge: 0.723, - mark: 9.831, - mark_roots: 0.102, - buffer_gray_roots: 3.095, - mark_cross_compartment_wrappers: 0.039, - mark_c_and_js_stacks: 0.005, - mark_runtime_wide_data: 2.313, - mark_embedding: 0.117, - mark_compartments: 2.27, - unmark: 1.063, - minor_gcs_to_evict_nursery: 8.701, - }, - }, - ], - totals: { - wait_background_thread: 0.012, - mark_discard_code: 2.845, - purge: 0.723, - mark: 9.831, - mark_roots: 0.102, - buffer_gray_roots: 3.095, - mark_cross_compartment_wrappers: 0.039, - mark_c_and_js_stacks: 0.005, - mark_runtime_wide_data: 2.313, - mark_embedding: 0.117, - mark_compartments: 2.27, - unmark: 1.063, - minor_gcs_to_evict_nursery: 8.701, - }, - }; - return gc; -} diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryHistograms.js b/toolkit/components/telemetry/tests/unit/test_TelemetryHistograms.js index 911db0cbe7..b256e125f3 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryHistograms.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryHistograms.js @@ -348,13 +348,6 @@ add_task(async function test_getSlowSQL() { Assert.ok("mainThread" in slow && "otherThreads" in slow); }); -add_task(async function test_getWebrtc() { - var webrtc = Telemetry.webrtcStats; - Assert.ok("IceCandidatesStats" in webrtc); - var icestats = webrtc.IceCandidatesStats; - Assert.ok("webrtc" in icestats); -}); - // Check that telemetry doesn't record in private mode add_task(async function test_privateMode() { var h = Telemetry.getHistogramById("TELEMETRY_TEST_BOOLEAN"); diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js b/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js index 30a2ae3095..6d6c9fd21d 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js @@ -457,11 +457,6 @@ function checkPayload(payload, reason, successfulPings) { "mainThread" in payload.slowSQL && "otherThreads" in payload.slowSQL ); - Assert.ok( - "IceCandidatesStats" in payload.webrtc && - "webrtc" in payload.webrtc.IceCandidatesStats - ); - // Check keyed histogram payload. Assert.ok("keyedHistograms" in payload); @@ -2012,7 +2007,6 @@ add_task(async function test_pingExtendedStats() { "fileIOReports", "lateWrites", "addonDetails", - "webrtc", ]; if (AppConstants.platform == "android") { diff --git a/toolkit/components/telemetry/tests/unit/xpcshell.ini b/toolkit/components/telemetry/tests/unit/xpcshell.ini index ea014da12d..7afa5476c0 100644 --- a/toolkit/components/telemetry/tests/unit/xpcshell.ini +++ b/toolkit/components/telemetry/tests/unit/xpcshell.ini @@ -92,7 +92,6 @@ skip-if = os == "android" # Disabled due to crashes (see bug 1331366) skip-if = (os == "win" && processor == "aarch64") # bug 1530759 [test_PingSender.js] skip-if = (os == "android") || (os == "linux" && bits == 32) -[test_TelemetryGC.js] [test_TelemetryAndroidEnvironment.js] [test_TelemetryUtils.js] [test_UntrustedModulesPing.js] diff --git a/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp index 626575043f..61927d7427 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp +++ b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp @@ -538,7 +538,7 @@ nsresult nsUrlClassifierPrefixSet::WritePrefixes( totalDeltas += deltaLength; indexStarts.AppendElement(totalDeltas); } - indexStarts.RemoveElementAt(indexSize); // we don't use the last element + indexStarts.RemoveLastElement(); // we don't use the last element MOZ_ASSERT(indexStarts.Length() == indexSize); } diff --git a/toolkit/components/urlformatter/components.conf b/toolkit/components/urlformatter/components.conf index 4ad714ed7c..7384ddb629 100644 --- a/toolkit/components/urlformatter/components.conf +++ b/toolkit/components/urlformatter/components.conf @@ -4,8 +4,10 @@ Classes = [ { + 'js_name': 'urlFormatter', 'cid': '{e6156350-2be8-11db-a98b-0800200c9a66}', 'contract_ids': ['@mozilla.org/toolkit/URLFormatterService;1'], + 'interfaces': ['nsIURLFormatter'], 'jsm': 'resource://gre/modules/URLFormatter.jsm', 'constructor': 'nsURLFormatterService', }, diff --git a/toolkit/components/xulstore/components.conf b/toolkit/components/xulstore/components.conf index f74c60b32a..90d8d2ecaf 100644 --- a/toolkit/components/xulstore/components.conf +++ b/toolkit/components/xulstore/components.conf @@ -12,12 +12,21 @@ if defined('MOZ_NEW_XULSTORE'): 'singleton': True, 'constructor': 'mozilla::XULStore::GetService', }, + { + 'js_name': 'xulStore', + 'cid': '{e8e12dba-b942-4c0d-aa21-2843cfc64529}', + 'contract_ids': ['@mozilla.org/xul/js-xulstore;1'], + 'jsm': 'resource://gre/modules/XULStore.jsm', + 'constructor': 'getXULStore', + }, ] else: Classes = [ { + 'js_name': 'xulStore', 'cid': '{6f46b6f4-c8b1-4bd4-a4fa-9ebbed0753ea}', 'contract_ids': ['@mozilla.org/xul/xulstore;1'], + 'interfaces': ['nsIXULStore'], 'jsm': 'resource://gre/modules/XULStore.jsm', 'constructor': 'XULStore', }, diff --git a/toolkit/components/xulstore/new/XULStore.jsm b/toolkit/components/xulstore/new/XULStore.jsm index 63299eed5e..84ef5d7de3 100644 --- a/toolkit/components/xulstore/new/XULStore.jsm +++ b/toolkit/components/xulstore/new/XULStore.jsm @@ -10,7 +10,7 @@ // protocol. It also implements the persist() method. JS consumers should use // this module rather than accessing nsIXULStore directly. -const EXPORTED_SYMBOLS = ["XULStore"]; +const EXPORTED_SYMBOLS = ["XULStore", "getXULStore"]; // Services.xulStore loads this module and returns its `XULStore` symbol // when this implementation of XULStore is enabled, so using it here @@ -99,3 +99,9 @@ class XULStoreEnumerator { } } } + +// Only here for the sake of component registration, which requires a +// callable function. +function getXULStore() { + return XULStore; +} diff --git a/toolkit/content/aboutAbout.xhtml b/toolkit/content/aboutAbout.xhtml index 61ff1c45f3..156bcca0ff 100644 --- a/toolkit/content/aboutAbout.xhtml +++ b/toolkit/content/aboutAbout.xhtml @@ -7,6 +7,7 @@ + diff --git a/toolkit/content/aboutNetworking.xhtml b/toolkit/content/aboutNetworking.xhtml index dc3c51ae52..e620a25c37 100644 --- a/toolkit/content/aboutNetworking.xhtml +++ b/toolkit/content/aboutNetworking.xhtml @@ -8,6 +8,7 @@ + <link rel="stylesheet" href="chrome://mozapps/skin/aboutNetworking.css" type="text/css" /> <script src="chrome://global/content/aboutNetworking.js" /> diff --git a/toolkit/content/aboutProfiles.xhtml b/toolkit/content/aboutProfiles.xhtml index 7411c19ad1..8319831d1e 100644 --- a/toolkit/content/aboutProfiles.xhtml +++ b/toolkit/content/aboutProfiles.xhtml @@ -8,6 +8,7 @@ <html xmlns="http://www.w3.org/1999/xhtml"> <head> + <meta http-equiv="Content-Security-Policy" content="default-src chrome:" /> <title data-l10n-id="profiles-title"> diff --git a/toolkit/content/aboutServiceWorkers.xhtml b/toolkit/content/aboutServiceWorkers.xhtml index 7ec8ef80b0..9aff19ecf2 100644 --- a/toolkit/content/aboutServiceWorkers.xhtml +++ b/toolkit/content/aboutServiceWorkers.xhtml @@ -10,6 +10,7 @@ + diff --git a/toolkit/content/aboutSupport.xhtml b/toolkit/content/aboutSupport.xhtml index d60bb9987f..2962aa91b5 100644 --- a/toolkit/content/aboutSupport.xhtml +++ b/toolkit/content/aboutSupport.xhtml @@ -11,6 +11,7 @@ + <link rel="icon" type="image/png" id="favicon" diff --git a/toolkit/content/aboutTelemetry.xhtml b/toolkit/content/aboutTelemetry.xhtml index fd46162611..452c1efc33 100644 --- a/toolkit/content/aboutTelemetry.xhtml +++ b/toolkit/content/aboutTelemetry.xhtml @@ -8,6 +8,7 @@ <html xmlns="http://www.w3.org/1999/xhtml"> <head> + <meta http-equiv="Content-Security-Policy" content="default-src chrome:" /> <title data-l10n-id="about-telemetry-page-title"> diff --git a/toolkit/content/aboutUrlClassifier.js b/toolkit/content/aboutUrlClassifier.js index 79f6b78bb3..5e0bad4044 100644 --- a/toolkit/content/aboutUrlClassifier.js +++ b/toolkit/content/aboutUrlClassifier.js @@ -8,24 +8,19 @@ const UPDATE_BEGIN = "safebrowsing-update-begin"; const UPDATE_FINISH = "safebrowsing-update-finished"; const JSLOG_PREF = "browser.safebrowsing.debug"; -function unLoad() { - window.removeEventListener("unload", unLoad); - +window.onunload = function() { Search.uninit(); Provider.uninit(); Cache.uninit(); Debug.uninit(); -} - -function onLoad() { - window.removeEventListener("load", onLoad); - window.addEventListener("unload", unLoad); +}; +window.onload = function() { Search.init(); Provider.init(); Cache.init(); Debug.init(); -} +}; /* * Search diff --git a/toolkit/content/aboutUrlClassifier.xhtml b/toolkit/content/aboutUrlClassifier.xhtml index 89aac0032c..f0eea6c71e 100644 --- a/toolkit/content/aboutUrlClassifier.xhtml +++ b/toolkit/content/aboutUrlClassifier.xhtml @@ -10,13 +10,14 @@ + - +