From cbf860dd80d41475bf7cf763e696f4ce0e22f896 Mon Sep 17 00:00:00 2001 From: roytam1 Date: Tue, 11 Jul 2023 15:00:13 +0800 Subject: [PATCH] import changes from `dev' branch of rmottola/Arctic-Fox: - Bug 1207790 - Fix sandbox build for older Linux distributions. r=gdestuynder (33726f14d6) - Bug 1157864 - chromium sandbox: Fix compilation for systems without . r=jld (19789c8f28) - Bug 1181704 - Import chromium SafeSPrintf. r=bobowen (c8f2f34098) - Bug 1196403 - Apply crbug/522201 to support Windows 10 build 10525. r=bobowen (ee9862b408) - Bug 1200336: Apply fix for Chromium issue 482784 for sandbox bug when built with VS2015. r=tabraldes (7f4cf9640b) - Bug 1150765 - Add sandbox rules to allow hardware rendering of OpenGL on Mac. r=smichaud (e23a3d3e89) - Bug 1153809 - Loosen Mac content process sandbox rules for NVidia and Intel HD 3000 graphics hardware. r=areinald (9a3a1fa6ea) - Bug 1229804: Use the correct string length in Windows sandbox logging. r=tabraldes (6a8cb035ed) - Bug 1181704 - Use chromium SafeSPrintf for sandbox logging. r=gdestuynder r=glandium (cff9ae432b) - crash reporter (009341774b) - Bug 1168555 - Work around Nuwa not always being single-threaded when a normal content process is. r=kang (b858b1fd62) - Bug 1199413 - Fix MOZ_DISABLE_GMP_SANDBOX so it disables all the sandboxing. r=kang (49125e07be) - Bug 1176085 - Fix second/nanosecond confusion in Linux sandbox start error case. r=kang (701181c7d2) - Bug 1199481 - Complain more when entering sandboxing code as root. r=kang (baf7b24675) - Bug 1215303 - Part 1 - add permissive mode r=jld (e8237859ac) - Bug 1215303 - Part 2 - automatically enable broker when in permissive mode r=jld (9636c8a956) - Bug 1222500 - Handle unexpected thread creation better on desktop Linux. r=gdestuynder (58e2f81f96) - Bug 1131227 - Make the about:certerror Unknown Issuer string mention missing intermediates and unimported roots. r=keeler (29ae92e655) - Bug 443811 - Use long date format for cert date output. r=keeler (50a31e099c) - namespace (10ccb72736) - Bug 1225682 - Don't use nsAuto{,C}String as class member variables in security/manager/. r=keeler (24d10b66f6) - Bug 1110935 - Part 1 - Assert we're on the main thread on public methods. r=keeler (b9b2b52bf2) - Bug 1110935 - Part 2 - Remove ReentrantMonitor and ReentrantMonitorAutoEnter uses. r=keeler (3fee4d4653) - Bug 1110935 - Part 3 - Remove now unnecessary temp variables. r=keeler (f81461993a) - Bug 1168635 - Add an XPCOM interface to allow RC4. r=keeler (dac5f75e75) - Bug 1136301 - Null check for mCert->slot added in destructorSafeDestroyNSSReference & MarkForPermDeletion. Formatting update in MarkForPermDeletion. r=keeler (daaaf2db22) - Bug 1168048 - Avoid potential null-pointer dereferencing in nsNSSCertificateDB r=keeler (c511046073) - Bug 1224121 - change getRelativeRuleLine to return 0 for line-less rules; r=heycam (7cef25806d) - Bug 1216234 - add inIDOMUtils.getCSSPseudoElementNames; r=heycam,pbrosset (1c94ca86b4) - Bug 1146114 - Make assertion checking additional leading in ruby frame non-crash. r=dholbert (326d82db79) - Bug 1052924 followup - Move the XXX comment to the right field. DONTBUILD (481cb50d0d) - Bug 1136521 - Don't try to use the ascent when placing a frame whose block-direction doesn't match the line's. r=smontagu (eb01f6aa1a) - Bug 1138353 - Correct the directionality of vertical-align dimensions in cases where line- and flow-relative coordinate directions differ. r=smontagu (6e51f4be05) - Bug 1133945 - Fix behavior of vertical-align with a specified length, relative to dominant centered baseline in vertical mode. r=smontagu (a2e611f978) - Bug 1146117 - Downgrade the assertion in FindNearestRubyBaseAncestor to warning. r=dbaron (db29a321f4) - Bug 1191185 - Simplify nsHypotheticalBox, eliminating obsolete/redundant fields, and rename to nsHypotheticalPosition. r=dholbert (3741b6aa49) - Bug 1227099 - [css-grid] Grid container block-size should include a grid-row-gap between each row. r=dholbert (35eed9d00b) - Bug 1225368 - [css-grid] Make CollectGrowable() deal with frozen tracks; i.e. subtract their base size but don't collect them since they aren't growable. r=dholbert (073964a857) - Bug 1224634 - [css-grid] Make grid items shrink-wrap when reflowing them, unless they have justify-self:stretch. r=dholbert (070c2cf6a9) - Bug 1176793 part 1 - [css-grid] Implement margin:auto for grid items. r=dholbert (ff89b80fc2) - Bug 1176793 part 2 - [css-grid] Reftests for margin:auto on grid items. (fe9367e7ca) - Bug 1229999 - [css-grid] Clamp auto-placed lines to where kMaxLine is in the translated grid. r=dholbert (576d9a8384) - Bug 1151243 part 1 - Replace three bool params for nsAbsoluteContainingBlock::Reflow with a flag param (idempotent patch). r=dholbert (7303bb9468) - Bug 978212 - [css-grid] Resolved value of grid-template-{columns,rows} in px units. r=mat (fe4b886917) - Bug 1151243 part 2 - [css-grid] Add a eIsGridContainerCB flag for nsAbsoluteContainingBlock::Reflow to trigger Grid specific code (rather than checking GetType()). r=dholbert (2f5f857b16) - Bug 1215099 part 1 - [css-grid] Backout bug 1206703. r=dholbert (b6af6389ce) - Bug 1123299 - Allow to be displayed in vertical writing mode; but keep the spinner arrows arranged as for horizontal writing mode. r=dholbert (2a2e17ce76) - (no bug) Fix typo in grid style-struct comment: s/grid-columns-rows/grid-template-rows/. No review, DONTBUILD (f362946aed) - fix namespace (70ec283c08) - Bug 1224251 patch 1 - Add reftests. r=mattwoodrow (4f5eed4d0c) - Bug 1224251 patch 2 - Add nsChangeHint_UpdateUsesOpacity to say when opacity changes between 1 and non-1. r=xidorn (0cec051688) - Bug 1224251 patch 3 - Return nsChangeHint_UpdateUsesOpacity when opacity changes between 1 and non-1. r=xidorn (acf5f3514c) - Bug 1224251 patch 4 - Convert UpdateOpacityLayer to RepaintFrame when changing opacity between 1 and non-1 on table parts. r=mattwoodrow (3101e06481) - Tweak a couple of comments; no bug. (DONTBUILD) (6b7d8486ad) - Bug 1219534 - Remove unused nsStyleContext::SetStyle##name_ methods. r=dholbert (4c032b5914) - (no bug) Fix typo in nsStyleContext.h (s/currenlty/currently/). Comment-only, DONTBUILD (3864f8194b) - Bug 1208901 - Fix a typo in nsStyleClipPath::nsStyleClipPath; r=heycam (df5c3b59a6) - Bug 1227766 patch 3 - Make will-change cause creation of a containing block for fixed and absolutely positioned elements when needed. r=dholbert (09c59e07f1) - Bug 1229278 - Fix dynamic changes to text-emphasis-style. r=dbaron (ab0c450f14) - Bug 1159729 - Reftest to check that text decoration is properly offset when needed. r=smontagu (b93bb0ca41) - Bug 196292 - Make table inside align=left reset alignment just like for align=center and align=right. r=bz (3b45a62477) - Bug 1227917 - Update |disp| after we've (potentially) cloned the Display struct so that subsequent tests of 'display' use the new value. r=jfkthame (d3216e7aad) - Bug 1155766 - Fix a bad assertion r=dbaron (d6744220f4) - Bug 1167589 - Mark the members of CSSParsingEnvironment as MOZ_UNSAFE_REF. r=dbaron (ebb9729549) - Bug 1228542 - Resetting AuthorStyleSheets. r=bz (31b27e491d) - Use nsContentUtils::GenerateUUIDInPlace() in nsIDocument::GenerateDocumentId(), no bug (642138ebac) - Bug 1226443 P2 Make service worker fetch and functional events used scheduled timer updates. r=ehsan (a4fc5a9275) - Bug 1227015 P1 Create ServiceWorkerScriptJobBase as parent class to register and install jobs. r=ehsan (214dda4e7d) - Bug 1226443 P1 Add a timer based mechanism for firing service worker updates. r=ehsan (f2a9eae9d2) - Bug 1229052 - Add a telemetry histogram to gather data on the number of top-level pages with scroll-linked positioning effects. r=vladan (6844bfaa2b) - Bug 1229052 - Log a warning when we detect a scroll-linked effect based on the scroll event. r=roc (da8dd5cff2) - bug 1215657 - make AccessibleWrap::get_accFocus work with proxied accessibles (02bfa582d6) - Bug 1227285 part 1 - Add a nsHTMLReflowState ctor flag to request shrink-wrap behavior. r=dholbert (da92253664) - Bug 1227285 part 2 - [css-grid] Request shrink-wrap behavior when doing a measuring reflow to figure out a grid item's block-size. r=dholbert (6bfcf381fe) - Bug 1227285 part 3 - [css-grid] Reftests for testing minmax(min-content,max-content) track sizing with grid item with %-sized descendants. (61b769d0d2) - No bug - [css-grid] Add a small fuzz factor to make this reftest pass on Windows. r=me (3c51c91220) - Bug 1173689: allow column sets in an orthogonal writing mode to their container to expand in the container's block direction, r=roc (88aa32efd9) - Bug 1209994. Take block-wrapper path only for blocks that are wrappers. r=bz (ecb4ae8d7e) - Bug 1191109 - Clean up use of writing-modes in GetHypotheticalBoxContainer, eliminating a redundant ConvertTo call. r=dholbert (f58d0d63c4) - Bug 1183439 - Update the wording of a few assertions to reflect logical-coordinate conversion. r=dholbert (8aacb0f89d) - Bug 1233276 - Make css::Declaration::List more useful again, given that it degraded a bit in bug 978833 (/ bug 1221436). r=heycam (1efda568e7) - Bug 1167665 - Mark css::Loader::mDocument as MOZ_NON_OWNING_REF. r=dbaron (f4d4f4ed1d) - Bug 621596 - Don't assert when a percentage base value overflows to become negative, when getting the computed style of a property that rejects negative values. r=bzbarsky (6076ce7cb6) - Bug 622314 - Add crashtest. (f1d64b943c) - Bug 1230613 - Long-press to trigger text selection should vibrate, r=snorp (f35f37336e) - Bug 1230582 - Always show caret even if input is empty on Fennec. f=capella, r=roc (6cf1258232) - Bug 1183085 - Correct argument name for BuildContainerLayerFor; r=roc (0fd119efd1) - Bug 1183085 - Correct a typo in FrameLayerBuilder.h; r=roc (1fb9a583ed) - Comment typo fix; no bug. (DONTBUILD) (27d7270052) - Bug 1211858 - Add a hint for the Restyle label when the id is unavailable. r=roc,benwa (8eb99a4b45) - bug 1218596 - remove nsPSMInitPanic and other unnecessary things from nsNSSComponent r=Cykesiopka r=jcj (a986e73f0b) - bug 1220223 - don't load PKCS11 modules in safe mode r=mgoodwin r=bsmedberg (5e071955b0) - Bug 921907 - Enable OCSP must-staple. r=keeler (448661431f) - Bug 1215795 - Fix documentation in nsIWeakCryptoOverride.idl. r=keeler IGNORE IDL (3cf051737c) - bug 1222179 - remove unnecessary observation topics in nsNSSComponent r=Cykesiopka (2eaa16d860) - bug 986956 - only ever initialize NSS once per process r=Cykesiopka r=mgoodwin (efdec10cbb) - Bug 1224875 - Enable TLS extended master secret. r=keeler (8f56d54ec1) - Bug 1145893 - Shutdown nsNSSComponent background threads during xpcom-shutdown. r=keeler, a=me (5d513b930d) - namespace (bf40a8f575) - Bug 1224951 - Part 1: Fix nsPresContext::SizeOfExcludingThis() size calculation. r=dholbert (530a54e15d) - Bug 1227666 - Insure that cached plugin geometry configuration cached in ShadowLayerForwarder gets cleared when we reflow and new content has no plugins. r=roc (a44fbce70e) - Bug 1140625 - Part 1: Add recursive call in GetFrameForNodeOffset. r=roc (0b7535cf7b) - Bug 1140625 - Recursive call GetFrameForNodeOffset if text node has no frame. r=roc (9198ab6a20) - Bug 1140625 - Part 3: Fix a bug in bug414526 so we can reopen the test. r=roc (cbe0d3577d) - Bug 1140625 - Part 4: Fix scroll_selection_into_view test to make its function remain. r=roc (97ca749e63) --- accessible/windows/msaa/AccessibleWrap.cpp | 13 +- dom/base/nsDocument.cpp | 38 +- dom/base/nsIDocument.h | 8 +- .../chrome/layout/layout_errors.properties | 1 + dom/workers/ServiceWorkerManager.cpp | 283 ++++++- dom/workers/ServiceWorkerManager.h | 32 +- dom/workers/ServiceWorkerPrivate.cpp | 69 +- editor/libeditor/tests/test_bug414526.html | 7 +- .../apz/util/ScrollLinkedEffectDetector.cpp | 48 ++ .../apz/util/ScrollLinkedEffectDetector.h | 43 + gfx/layers/moz.build | 2 + layout/base/AccessibleCaretManager.cpp | 24 +- layout/base/AccessibleCaretManager.h | 9 +- layout/base/FrameLayerBuilder.h | 4 +- layout/base/RestyleManager.cpp | 32 +- layout/base/nsChangeHint.h | 16 +- layout/base/nsDisplayList.cpp | 11 +- layout/base/nsPresContext.cpp | 9 +- .../scroll_selection_into_view_window.html | 14 +- layout/generic/crashtests/1146114.html | 6 + layout/generic/nsAbsoluteContainingBlock.cpp | 27 +- layout/generic/nsAbsoluteContainingBlock.h | 20 +- layout/generic/nsBlockFrame.cpp | 11 +- layout/generic/nsColumnSetFrame.cpp | 10 +- layout/generic/nsFrame.cpp | 9 +- layout/generic/nsGfxScrollFrame.cpp | 2 + layout/generic/nsGridContainerFrame.cpp | 95 ++- layout/generic/nsGridContainerFrame.h | 38 +- layout/generic/nsHTMLReflowState.cpp | 244 +++--- layout/generic/nsHTMLReflowState.h | 25 +- layout/generic/nsLineLayout.cpp | 31 +- layout/generic/nsLineLayout.h | 4 +- layout/generic/nsRubyFrame.cpp | 7 +- layout/generic/nsSelection.cpp | 44 +- layout/generic/nsViewportFrame.cpp | 7 +- layout/inspector/inDOMUtils.cpp | 25 +- layout/inspector/inIDOMUtils.idl | 13 +- layout/inspector/tests/mochitest.ini | 1 + .../tests/test_getCSSPseudoElementNames.html | 57 ++ .../tests/test_getRelativeRuleLine.html | 2 + layout/reftests/bugs/1209994-1-ref.html | 17 + layout/reftests/bugs/1209994-1.html | 21 + layout/reftests/bugs/1209994-2-ref.html | 17 + layout/reftests/bugs/1209994-2.html | 21 + layout/reftests/bugs/1209994-3-ref.html | 17 + layout/reftests/bugs/1209994-3.html | 21 + layout/reftests/bugs/1209994-4-ref.html | 17 + layout/reftests/bugs/1209994-4.html | 21 + layout/reftests/bugs/reftest.list | 4 + .../css-grid/grid-abspos-items-001-ref.html | 12 +- .../css-grid/grid-abspos-items-002-ref.html | 8 +- .../css-grid/grid-abspos-items-011-ref.html | 54 -- .../css-grid/grid-abspos-items-011.html | 61 -- .../css-grid/grid-abspos-items-012-ref.html | 45 ++ .../css-grid/grid-abspos-items-012.html | 47 ++ .../grid-item-margin-left-auto-001-ref.html | 117 +++ .../grid-item-margin-left-auto-001.html | 116 +++ .../grid-item-margin-left-auto-002-ref.html | 115 +++ .../grid-item-margin-left-auto-002.html | 116 +++ .../grid-item-margin-left-auto-003-ref.html | 116 +++ .../grid-item-margin-left-auto-003.html | 115 +++ .../grid-item-margin-left-auto-004-ref.html | 117 +++ .../grid-item-margin-left-auto-004.html | 116 +++ ...d-item-margin-left-right-auto-001-ref.html | 117 +++ .../grid-item-margin-left-right-auto-001.html | 117 +++ ...d-item-margin-left-right-auto-002-ref.html | 115 +++ .../grid-item-margin-left-right-auto-002.html | 117 +++ ...d-item-margin-left-right-auto-003-ref.html | 116 +++ .../grid-item-margin-left-right-auto-003.html | 116 +++ ...d-item-margin-left-right-auto-004-ref.html | 115 +++ .../grid-item-margin-left-right-auto-004.html | 117 +++ .../grid-item-margin-right-auto-001-ref.html | 117 +++ .../grid-item-margin-right-auto-001.html | 116 +++ .../grid-item-margin-right-auto-002-ref.html | 118 +++ .../grid-item-margin-right-auto-002.html | 116 +++ .../grid-item-margin-right-auto-003-ref.html | 116 +++ .../grid-item-margin-right-auto-003.html | 115 +++ .../grid-item-margin-right-auto-004-ref.html | 117 +++ .../grid-item-margin-right-auto-004.html | 116 +++ .../grid-min-max-content-sizing-001-ref.html | 2 +- .../grid-min-max-content-sizing-002-ref.html | 82 ++ .../grid-min-max-content-sizing-002.html | 85 ++ layout/reftests/css-grid/reftest.list | 17 +- ...ilar-to-text-unthemed-vertical-lr-ref.html | 8 + ...-similar-to-text-unthemed-vertical-lr.html | 8 + ...ilar-to-text-unthemed-vertical-rl-ref.html | 8 + ...-similar-to-text-unthemed-vertical-rl.html | 8 + .../reftests/forms/input/number/reftest.list | 2 + layout/reftests/reftest.list | 3 + layout/reftests/table-background/reftest.list | 2 + .../table-row-opacity-dynamic-1-ref.html | 28 + .../table-row-opacity-dynamic-1.html | 55 ++ .../table-row-opacity-dynamic-2-ref.html | 28 + .../table-row-opacity-dynamic-2.html | 54 ++ ...l-align-stopped-at-table-1-quirks-ref.html | 31 + .../cell-align-stopped-at-table-1-quirks.html | 31 + ...lign-stopped-at-table-1-standards-ref.html | 32 + ...ll-align-stopped-at-table-1-standards.html | 32 + layout/reftests/table-html/reftest.list | 2 + .../1159729-offset-adjustment-notref.html | 19 + .../1159729-offset-adjustment.html | 21 + .../emphasis-style-dynamic-ref.html | 12 + .../emphasis-style-dynamic.html | 20 + layout/reftests/text-decoration/reftest.list | 2 + layout/style/Declaration.cpp | 7 + layout/style/Declaration.h | 4 +- layout/style/Loader.h | 5 +- layout/style/crashtests/622314-1.xhtml | 26 + layout/style/crashtests/crashtests.list | 2 + layout/style/forms.css | 13 +- layout/style/nsComputedDOMStyle.cpp | 72 +- layout/style/nsComputedDOMStyle.h | 3 +- layout/style/nsDOMCSSDeclaration.cpp | 18 + layout/style/nsDOMCSSDeclaration.h | 14 +- layout/style/nsHTMLCSSStyleSheet.cpp | 2 +- layout/style/nsStyleContext.cpp | 17 +- layout/style/nsStyleContext.h | 22 +- layout/style/nsStyleStruct.cpp | 27 +- layout/style/nsStyleStruct.h | 3 +- layout/style/nsStyleStructInlines.h | 1 + layout/style/nsStyleTransformMatrix.h | 2 +- layout/style/number-control.css | 1 - .../test/chrome/test_additional_sheets.html | 6 + layout/style/test/mochitest.ini | 1 + .../style/test/test_grid_computed_values.html | 83 ++ modules/libpref/init/all.js | 6 + .../certverifier/NSSCertDBTrustDomain.cpp | 5 +- security/certverifier/NSSCertDBTrustDomain.h | 2 +- .../en-US/chrome/pipnss/pipnss.properties | 2 + security/manager/ssl/SharedSSLState.cpp | 5 +- .../manager/ssl/TransportSecurityInfo.cpp | 35 +- security/manager/ssl/WeakCryptoOverride.cpp | 62 ++ security/manager/ssl/WeakCryptoOverride.h | 35 + security/manager/ssl/moz.build | 2 + .../manager/ssl/nsCertVerificationThread.cpp | 2 +- .../manager/ssl/nsIWeakCryptoOverride.idl | 45 ++ security/manager/ssl/nsKeygenHandler.cpp | 1 + security/manager/ssl/nsNSSCallbacks.cpp | 3 + security/manager/ssl/nsNSSCertHelper.cpp | 17 +- security/manager/ssl/nsNSSCertValidity.cpp | 20 +- security/manager/ssl/nsNSSCertificate.cpp | 11 +- security/manager/ssl/nsNSSCertificateDB.cpp | 2 + security/manager/ssl/nsNSSComponent.cpp | 296 ++----- security/manager/ssl/nsNSSComponent.h | 58 +- security/manager/ssl/nsNSSIOLayer.cpp | 108 ++- security/manager/ssl/nsNSSIOLayer.h | 4 + security/manager/ssl/nsNSSModule.cpp | 72 +- .../manager/ssl/nsSecureBrowserUIImpl.cpp | 320 +++----- security/manager/ssl/nsSecureBrowserUIImpl.h | 10 +- security/manager/ssl/nsSecurityHeaderParser.h | 6 +- .../manager/ssl/nsSiteSecurityService.cpp | 1 + security/manager/ssl/tests/unit/head_psm.js | 1 + .../ssl/tests/unit/test_pkcs11_safe_mode.js | 57 ++ .../ssl/tests/unit/test_weak_crypto.js | 43 +- .../ssl/tests/unit/xpcshell-smartcards.ini | 3 + .../sandbox/win/sandboxLogging.cpp | 8 +- .../sandbox/win/sandboxLogging.h | 4 +- .../chromium/base/strings/safe_sprintf.cc | 685 ++++++++++++++++ .../chromium/base/strings/safe_sprintf.h | 247 ++++++ .../base/strings/safe_sprintf_unittest.cc | 759 ++++++++++++++++++ .../sandbox/linux/seccomp-bpf/linux_seccomp.h | 2 +- .../sandbox/win/src/service_resolver_64.cc | 61 +- .../sandbox/win/src/target_services.cc | 24 +- .../sandbox/win/src/target_services.h | 14 +- security/sandbox/linux/LinuxCapabilities.h | 9 + security/sandbox/linux/Sandbox.cpp | 44 +- security/sandbox/linux/SandboxFilter.cpp | 1 + security/sandbox/linux/SandboxFilterUtil.cpp | 7 +- security/sandbox/linux/SandboxLogging.cpp | 63 ++ security/sandbox/linux/SandboxLogging.h | 46 +- .../sandbox/linux/broker/SandboxBroker.cpp | 35 +- security/sandbox/linux/broker/SandboxBroker.h | 1 + .../broker/SandboxBrokerPolicyFactory.cpp | 7 + security/sandbox/linux/common/SandboxInfo.cpp | 68 +- security/sandbox/linux/common/SandboxInfo.h | 15 +- security/sandbox/linux/common/moz.build | 3 +- security/sandbox/linux/glue/SandboxCrash.cpp | 6 + security/sandbox/linux/glue/moz.build | 7 + .../sandbox/linux/gtest/TestSandboxUtil.cpp | 5 + security/sandbox/linux/moz.build | 7 + security/sandbox/mac/Sandbox.mm | 24 +- .../sandbox/moz-chromium-commit-status.txt | 3 + .../win/src/sandboxtarget/sandboxTarget.h | 3 - toolkit/components/telemetry/Histograms.json | 7 + toolkit/xre/nsAppRunner.cpp | 8 + 185 files changed, 7656 insertions(+), 1185 deletions(-) create mode 100644 gfx/layers/apz/util/ScrollLinkedEffectDetector.cpp create mode 100644 gfx/layers/apz/util/ScrollLinkedEffectDetector.h create mode 100644 layout/generic/crashtests/1146114.html create mode 100644 layout/inspector/tests/test_getCSSPseudoElementNames.html create mode 100644 layout/reftests/bugs/1209994-1-ref.html create mode 100644 layout/reftests/bugs/1209994-1.html create mode 100644 layout/reftests/bugs/1209994-2-ref.html create mode 100644 layout/reftests/bugs/1209994-2.html create mode 100644 layout/reftests/bugs/1209994-3-ref.html create mode 100644 layout/reftests/bugs/1209994-3.html create mode 100644 layout/reftests/bugs/1209994-4-ref.html create mode 100644 layout/reftests/bugs/1209994-4.html delete mode 100644 layout/reftests/css-grid/grid-abspos-items-011-ref.html delete mode 100644 layout/reftests/css-grid/grid-abspos-items-011.html create mode 100644 layout/reftests/css-grid/grid-abspos-items-012-ref.html create mode 100644 layout/reftests/css-grid/grid-abspos-items-012.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-auto-001-ref.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-auto-001.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-auto-002-ref.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-auto-002.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-auto-003-ref.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-auto-003.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-auto-004-ref.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-auto-004.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-right-auto-001-ref.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-right-auto-001.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-right-auto-002-ref.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-right-auto-002.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-right-auto-003-ref.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-right-auto-003.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-right-auto-004-ref.html create mode 100644 layout/reftests/css-grid/grid-item-margin-left-right-auto-004.html create mode 100644 layout/reftests/css-grid/grid-item-margin-right-auto-001-ref.html create mode 100644 layout/reftests/css-grid/grid-item-margin-right-auto-001.html create mode 100644 layout/reftests/css-grid/grid-item-margin-right-auto-002-ref.html create mode 100644 layout/reftests/css-grid/grid-item-margin-right-auto-002.html create mode 100644 layout/reftests/css-grid/grid-item-margin-right-auto-003-ref.html create mode 100644 layout/reftests/css-grid/grid-item-margin-right-auto-003.html create mode 100644 layout/reftests/css-grid/grid-item-margin-right-auto-004-ref.html create mode 100644 layout/reftests/css-grid/grid-item-margin-right-auto-004.html create mode 100644 layout/reftests/css-grid/grid-min-max-content-sizing-002-ref.html create mode 100644 layout/reftests/css-grid/grid-min-max-content-sizing-002.html create mode 100644 layout/reftests/forms/input/number/number-similar-to-text-unthemed-vertical-lr-ref.html create mode 100644 layout/reftests/forms/input/number/number-similar-to-text-unthemed-vertical-lr.html create mode 100644 layout/reftests/forms/input/number/number-similar-to-text-unthemed-vertical-rl-ref.html create mode 100644 layout/reftests/forms/input/number/number-similar-to-text-unthemed-vertical-rl.html create mode 100644 layout/reftests/table-background/table-row-opacity-dynamic-1-ref.html create mode 100644 layout/reftests/table-background/table-row-opacity-dynamic-1.html create mode 100644 layout/reftests/table-background/table-row-opacity-dynamic-2-ref.html create mode 100644 layout/reftests/table-background/table-row-opacity-dynamic-2.html create mode 100644 layout/reftests/table-html/cell-align-stopped-at-table-1-quirks-ref.html create mode 100644 layout/reftests/table-html/cell-align-stopped-at-table-1-quirks.html create mode 100644 layout/reftests/table-html/cell-align-stopped-at-table-1-standards-ref.html create mode 100644 layout/reftests/table-html/cell-align-stopped-at-table-1-standards.html create mode 100644 layout/reftests/table-html/reftest.list create mode 100644 layout/reftests/text-decoration/1159729-offset-adjustment-notref.html create mode 100644 layout/reftests/text-decoration/1159729-offset-adjustment.html create mode 100644 layout/reftests/text-decoration/emphasis-style-dynamic-ref.html create mode 100644 layout/reftests/text-decoration/emphasis-style-dynamic.html create mode 100644 layout/style/crashtests/622314-1.xhtml create mode 100644 layout/style/test/test_grid_computed_values.html create mode 100644 security/manager/ssl/WeakCryptoOverride.cpp create mode 100644 security/manager/ssl/WeakCryptoOverride.h create mode 100644 security/manager/ssl/nsIWeakCryptoOverride.idl create mode 100644 security/manager/ssl/tests/unit/test_pkcs11_safe_mode.js create mode 100644 security/sandbox/chromium/base/strings/safe_sprintf.cc create mode 100644 security/sandbox/chromium/base/strings/safe_sprintf.h create mode 100644 security/sandbox/chromium/base/strings/safe_sprintf_unittest.cc create mode 100644 security/sandbox/linux/SandboxLogging.cpp diff --git a/accessible/windows/msaa/AccessibleWrap.cpp b/accessible/windows/msaa/AccessibleWrap.cpp index dbfcf7ff62..53292ff531 100644 --- a/accessible/windows/msaa/AccessibleWrap.cpp +++ b/accessible/windows/msaa/AccessibleWrap.cpp @@ -658,12 +658,15 @@ AccessibleWrap::get_accFocus( if (IsDefunct()) return CO_E_OBJNOTCONNECTED; - // TODO make this work with proxies. - if (IsProxy()) - return E_NOTIMPL; - // Return the current IAccessible child that has focus - Accessible* focusedAccessible = FocusedChild(); + Accessible* focusedAccessible; + if (IsProxy()) { + ProxyAccessible* proxy = Proxy()->FocusedChild(); + focusedAccessible = proxy ? WrapperFor(proxy) : nullptr; + } else { + focusedAccessible = FocusedChild(); + } + if (focusedAccessible == this) { pvarChild->vt = VT_I4; pvarChild->lVal = CHILDID_SELF; diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 2ede3c41a0..8d69317dcf 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -1448,6 +1448,7 @@ nsIDocument::nsIDocument() mPostedFlushUserFontSet(false), mPartID(0), mDidFireDOMContentLoaded(true), + mHasScrollLinkedEffect(false), mUserHasInteracted(false) { SetInDocument(); @@ -1568,6 +1569,8 @@ nsDocument::~nsDocument() mixedContentLevel = MIXED_DISPLAY_CONTENT; } Accumulate(Telemetry::MIXED_CONTENT_PAGE_LOAD, mixedContentLevel); + + Accumulate(Telemetry::SCROLL_LINKED_EFFECT_FOUND, mHasScrollLinkedEffect); } } @@ -2334,6 +2337,11 @@ nsDocument::ResetStylesheetsToURI(nsIURI* aURI) RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eUserSheet], SheetType::User); RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAuthorSheet], SheetType::Doc); + nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance(); + if (sheetService) { + RemoveStyleSheetsFromStyleSets(*sheetService->AuthorStyleSheets(), SheetType::Doc); + } + // Release all the sheets mStyleSheets.Clear(); mOnDemandBuiltInUASheets.Clear(); @@ -5119,6 +5127,14 @@ nsDocument::DispatchContentLoadedEvents() true, true); } + if (mMaybeServiceWorkerControlled) { + using mozilla::dom::workers::ServiceWorkerManager; + RefPtr swm = ServiceWorkerManager::GetInstance(); + if (swm) { + swm->MaybeCheckNavigationUpdate(this); + } + } + UnblockOnload(true); } @@ -13115,14 +13131,8 @@ nsIDocument::CreateHTMLElement(nsIAtom* aTag) nsresult nsIDocument::GenerateDocumentId(nsAString& aId) { - nsresult rv; - nsCOMPtr uuidgen = do_GetService("@mozilla.org/uuid-generator;1", &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - nsID id; - rv = uuidgen->GenerateUUIDInPlace(&id); + nsresult rv = nsContentUtils::GenerateUUIDInPlace(id); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -13314,6 +13324,20 @@ nsIDocument::Fonts() return mFontFaceSet; } +void +nsIDocument::ReportHasScrollLinkedEffect() +{ + if (mHasScrollLinkedEffect) { + // We already did this once for this document, don't do it again. + return; + } + mHasScrollLinkedEffect = true; + nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, + NS_LITERAL_CSTRING("Async Pan/Zoom"), + this, nsContentUtils::eLAYOUT_PROPERTIES, + "ScrollLinkedEffectFound"); +} + Selection* nsIDocument::GetSelection(ErrorResult& aRv) { diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index 639b87f9b6..2bd23b255f 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -161,8 +161,8 @@ struct FullScreenOptions { } // namespace mozilla #define NS_IDOCUMENT_IID \ -{ 0x13011a82, 0x46cd, 0x4c33, \ - { 0x9d, 0x4e, 0x31, 0x41, 0xbb, 0x3f, 0x18, 0xe9 } } +{ 0xce1f7627, 0x7109, 0x4977, \ + { 0xba, 0x77, 0x49, 0x0f, 0xfd, 0xe0, 0x7a, 0xaa } } // Enum for requesting a particular type of document when creating a doc enum DocumentFlavor { @@ -2706,6 +2706,8 @@ public: bool InlineScriptAllowedByCSP(); + void ReportHasScrollLinkedEffect(); + protected: bool GetUseCounter(mozilla::UseCounter aUseCounter) { @@ -3102,6 +3104,8 @@ protected: uint32_t mBlockDOMContentLoaded; bool mDidFireDOMContentLoaded:1; + bool mHasScrollLinkedEffect:1; + // Our live MediaQueryLists PRCList mDOMMediaQueryLists; diff --git a/dom/locales/en-US/chrome/layout/layout_errors.properties b/dom/locales/en-US/chrome/layout/layout_errors.properties index b1ca72171d..b276ea27a7 100644 --- a/dom/locales/en-US/chrome/layout/layout_errors.properties +++ b/dom/locales/en-US/chrome/layout/layout_errors.properties @@ -9,3 +9,4 @@ ImageMapPolyWrongNumberOfCoords=The "coords" attribute of the tag is missing the last "y" coordinate (the correct format is "x1,y1,x2,y2 …"). TablePartRelPosWarning=Relative positioning of table rows and row groups is now supported. This site may need to be updated because it may depend on this feature having no effect. +ScrollLinkedEffectFound=This site appears to use a scroll-linked positioning effect. This may not work well with asynchronous panning; see https://developers.mozilla.org/docs/Mozilla/Performance/ScrollLinkedEffects for further details and to join the discussion on related tools and features! diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp index 0a5cfa6e6c..85be059ab6 100644 --- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -21,6 +21,7 @@ #include "nsIMutableArray.h" #include "nsIScriptError.h" #include "nsISimpleEnumerator.h" +#include "nsITimer.h" #include "nsIUploadChannel2.h" #include "nsPIDOMWindow.h" #include "nsScriptLoader.h" @@ -135,6 +136,9 @@ struct ServiceWorkerManager::RegistrationDataPerPrincipal final // Maps scopes to job queues. nsClassHashtable mJobQueues; + + // Map scopes to scheduled update timers. + nsInterfaceHashtable mUpdateTimers; }; struct ServiceWorkerManager::PendingOperation final @@ -422,9 +426,10 @@ ServiceWorkerRegistrationInfo::Clear() ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(const nsACString& aScope, nsIPrincipal* aPrincipal) : mControlledDocumentsCounter(0) + , mUpdateState(NoUpdate) + , mLastUpdateCheckTime(0) , mScope(aScope) , mPrincipal(aPrincipal) - , mLastUpdateCheckTime(0) , mUpdating(false) , mPendingUninstall(false) {} @@ -952,6 +957,32 @@ protected: ~ServiceWorkerJobBase() { } + void + Succeed() + { + AssertIsOnMainThread(); + // We don't have a callback for soft updates. + if (mCallback) { + mCallback->UpdateSucceeded(mRegistration); + mCallback = nullptr; + } + } +}; + +// Base type for jobs that work with a specific service worker script. +class ServiceWorkerScriptJobBase : public ServiceWorkerJobBase +{ +protected: + ServiceWorkerScriptJobBase(ServiceWorkerJobQueue* aQueue, + ServiceWorkerJob::Type aJobType, + ServiceWorkerUpdateFinishCallback* aCallback, + ServiceWorkerRegistrationInfo* aRegistration, + ServiceWorkerInfo* aServiceWorkerInfo) + : ServiceWorkerJobBase(aQueue, aJobType, aCallback, aRegistration, + aServiceWorkerInfo) + { + } + // This MUST only be called when the job is still performing actions related // to registration or update. After the spec resolves the update promise, use // Done() with the failure code instead. @@ -1013,20 +1044,9 @@ protected: ErrorResult rv(aRv); Fail(rv); } - - void - Succeed() - { - AssertIsOnMainThread(); - // We don't have a callback for soft updates. - if (mCallback) { - mCallback->UpdateSucceeded(mRegistration); - mCallback = nullptr; - } - } }; -class ServiceWorkerInstallJob final : public ServiceWorkerJobBase +class ServiceWorkerInstallJob final : public ServiceWorkerScriptJobBase { friend class ContinueInstallTask; @@ -1035,8 +1055,8 @@ public: ServiceWorkerUpdateFinishCallback* aCallback, ServiceWorkerRegistrationInfo* aRegistration, ServiceWorkerInfo* aServiceWorkerInfo) - : ServiceWorkerJobBase(aQueue, Type::InstallJob, aCallback, - aRegistration, aServiceWorkerInfo) + : ServiceWorkerScriptJobBase(aQueue, Type::InstallJob, aCallback, + aRegistration, aServiceWorkerInfo) { MOZ_ASSERT(aRegistration); } @@ -1161,7 +1181,7 @@ public: } }; -class ServiceWorkerRegisterJob final : public ServiceWorkerJobBase, +class ServiceWorkerRegisterJob final : public ServiceWorkerScriptJobBase, public serviceWorkerScriptCache::CompareCallback { friend class ContinueUpdateRunnable; @@ -1184,7 +1204,8 @@ public: ServiceWorkerUpdateFinishCallback* aCallback, nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup) - : ServiceWorkerJobBase(aQueue, Type::RegisterJob, aCallback) + : ServiceWorkerScriptJobBase(aQueue, Type::RegisterJob, aCallback, nullptr, + nullptr) , mScope(aScope) , mScriptSpec(aScriptSpec) , mPrincipal(aPrincipal) @@ -1199,8 +1220,8 @@ public: ServiceWorkerRegisterJob(ServiceWorkerJobQueue* aQueue, ServiceWorkerRegistrationInfo* aRegistration, ServiceWorkerUpdateFinishCallback* aCallback) - : ServiceWorkerJobBase(aQueue, Type::UpdateJob, aCallback, - aRegistration, nullptr) + : ServiceWorkerScriptJobBase(aQueue, Type::UpdateJob, aCallback, + aRegistration, nullptr) { AssertIsOnMainThread(); } @@ -2698,6 +2719,54 @@ ServiceWorkerRegistrationInfo::NotifyListenersOnChange() } } +void +ServiceWorkerRegistrationInfo::MaybeScheduleTimeCheckAndUpdate() +{ + AssertIsOnMainThread(); + + RefPtr swm = ServiceWorkerManager::GetInstance(); + if (!swm) { + // shutting down, do nothing + return; + } + + if (mUpdateState == NoUpdate) { + mUpdateState = NeedTimeCheckAndUpdate; + } + + swm->ScheduleUpdateTimer(mPrincipal, mScope); +} + +void +ServiceWorkerRegistrationInfo::MaybeScheduleUpdate() +{ + AssertIsOnMainThread(); + + RefPtr swm = ServiceWorkerManager::GetInstance(); + if (!swm) { + // shutting down, do nothing + return; + } + + mUpdateState = NeedUpdate; + + swm->ScheduleUpdateTimer(mPrincipal, mScope); +} + +bool +ServiceWorkerRegistrationInfo::CheckAndClearIfUpdateNeeded() +{ + AssertIsOnMainThread(); + + bool result = mUpdateState == NeedUpdate || + (mUpdateState == NeedTimeCheckAndUpdate && + IsLastUpdateCheckTimeOverOneDay()); + + mUpdateState = NoUpdate; + + return result; +} + void ServiceWorkerManager::LoadRegistration( const ServiceWorkerRegistrationData& aRegistration) @@ -3024,6 +3093,12 @@ ServiceWorkerManager::RemoveScopeAndRegistration(ServiceWorkerRegistrationInfo* return; } + nsCOMPtr timer = data->mUpdateTimers.Get(aRegistration->mScope); + if (timer) { + timer->Cancel(); + data->mUpdateTimers.Remove(aRegistration->mScope); + } + RefPtr info; data->mInfos.Get(aRegistration->mScope, getter_AddRefs(info)); @@ -3083,6 +3158,27 @@ ServiceWorkerManager::MaybeStopControlling(nsIDocument* aDoc) mAllDocuments.RemoveEntry(aDoc); } +void +ServiceWorkerManager::MaybeCheckNavigationUpdate(nsIDocument* aDoc) +{ + AssertIsOnMainThread(); + MOZ_ASSERT(aDoc); + // We perform these success path navigation update steps when the + // document tells us its more or less done loading. This avoids + // slowing down page load and also lets pages consistently get + // updatefound events when they fire. + // + // 9.8.20 If respondWithEntered is false, then: + // 9.8.22 Else: (respondWith was entered and succeeded) + // If request is a non-subresource request, then: Invoke Soft Update + // algorithm. + RefPtr registration; + mControlledDocuments.Get(aDoc, getter_AddRefs(registration)); + if (registration) { + registration->MaybeScheduleUpdate(); + } +} + void ServiceWorkerManager::StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration, nsIDocument* aDoc, @@ -4123,6 +4219,13 @@ ServiceWorkerManager::ForceUnregister(RegistrationDataPerPrincipal* aRegistratio queue->CancelJobs(); } + nsCOMPtr timer = + aRegistrationData->mUpdateTimers.Get(aRegistration->mScope); + if (timer) { + timer->Cancel(); + aRegistrationData->mUpdateTimers.Remove(aRegistration->mScope); + } + // Since Unregister is async, it is ok to call it in an enumeration. Unregister(aRegistration->mPrincipal, nullptr, NS_ConvertUTF8toUTF16(aRegistration->mScope)); } @@ -4452,6 +4555,14 @@ ServiceWorkerManager::Observe(nsISupports* aSubject, if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) { mShuttingDown = true; + for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) { + for (auto it2 = it1.UserData()->mUpdateTimers.Iter(); !it2.Done(); it2.Next()) { + nsCOMPtr timer = it2.UserData(); + timer->Cancel(); + } + it1.UserData()->mUpdateTimers.Clear(); + } + nsCOMPtr obs = mozilla::services::GetObserverService(); if (obs) { obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID); @@ -4656,6 +4767,140 @@ ServiceWorkerManager::RemoveNavigationInterception(const nsACString& aScope, } } +class UpdateTimerCallback final : public nsITimerCallback +{ + nsCOMPtr mPrincipal; + const nsCString mScope; + + ~UpdateTimerCallback() + { + } + +public: + UpdateTimerCallback(nsIPrincipal* aPrincipal, const nsACString& aScope) + : mPrincipal(aPrincipal) + , mScope(aScope) + { + AssertIsOnMainThread(); + MOZ_ASSERT(mPrincipal); + MOZ_ASSERT(!mScope.IsEmpty()); + } + + NS_IMETHOD + Notify(nsITimer* aTimer) override + { + AssertIsOnMainThread(); + + RefPtr swm = ServiceWorkerManager::GetInstance(); + if (!swm) { + // shutting down, do nothing + return NS_OK; + } + + swm->UpdateTimerFired(mPrincipal, mScope); + return NS_OK; + } + + NS_DECL_ISUPPORTS +}; + +NS_IMPL_ISUPPORTS(UpdateTimerCallback, nsITimerCallback) + +void +ServiceWorkerManager::ScheduleUpdateTimer(nsIPrincipal* aPrincipal, + const nsACString& aScope) +{ + AssertIsOnMainThread(); + MOZ_ASSERT(aPrincipal); + MOZ_ASSERT(!aScope.IsEmpty()); + + if (mShuttingDown) { + return; + } + + nsAutoCString scopeKey; + nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + RegistrationDataPerPrincipal* data; + if (!mRegistrationInfos.Get(scopeKey, &data)) { + return; + } + + nsCOMPtr timer = data->mUpdateTimers.Get(aScope); + if (timer) { + timer->Cancel(); + data->mUpdateTimers.Remove(aScope); + } else { + timer = do_CreateInstance("@mozilla.org/timer;1", &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + } + + nsCOMPtr callback = new UpdateTimerCallback(aPrincipal, + aScope); + + const uint32_t UPDATE_DELAY_MS = 1000; + + rv = timer->InitWithCallback(callback, UPDATE_DELAY_MS, + nsITimer::TYPE_ONE_SHOT); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + data->mUpdateTimers.Put(aScope, timer); +} + +void +ServiceWorkerManager::UpdateTimerFired(nsIPrincipal* aPrincipal, + const nsACString& aScope) +{ + AssertIsOnMainThread(); + MOZ_ASSERT(aPrincipal); + MOZ_ASSERT(!aScope.IsEmpty()); + + if (mShuttingDown) { + return; + } + + // First cleanup the timer. + nsAutoCString scopeKey; + nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + RegistrationDataPerPrincipal* data; + if (!mRegistrationInfos.Get(scopeKey, &data)) { + return; + } + + nsCOMPtr timer = data->mUpdateTimers.Get(aScope); + if (timer) { + timer->Cancel(); + data->mUpdateTimers.Remove(aScope); + } + + RefPtr registration; + data->mInfos.Get(aScope, getter_AddRefs(registration)); + if (!registration) { + return; + } + + if (!registration->CheckAndClearIfUpdateNeeded()) { + return; + } + + OriginAttributes attrs = + mozilla::BasePrincipal::Cast(aPrincipal)->OriginAttributesRef(); + + // Then trigger an update to fire asynchronously now. + PropagateSoftUpdate(attrs, NS_ConvertUTF8toUTF16(aScope)); +} + NS_IMPL_ISUPPORTS(ServiceWorkerInfo, nsIServiceWorkerInfo) NS_IMETHODIMP diff --git a/dom/workers/ServiceWorkerManager.h b/dom/workers/ServiceWorkerManager.h index 3e0ad565e8..0bfe1c9c0d 100644 --- a/dom/workers/ServiceWorkerManager.h +++ b/dom/workers/ServiceWorkerManager.h @@ -57,6 +57,15 @@ class ServiceWorkerRegistrationInfo final { uint32_t mControlledDocumentsCounter; + enum + { + NoUpdate, + NeedTimeCheckAndUpdate, + NeedUpdate + } mUpdateState; + + uint64_t mLastUpdateCheckTime; + virtual ~ServiceWorkerRegistrationInfo(); public: @@ -76,8 +85,6 @@ public: nsTArray> mListeners; - uint64_t mLastUpdateCheckTime; - // According to the spec, Soft Update shouldn't queue an update job // if the registration queue is not empty. Because our job queue // works slightly different, we use a flag to determine if the registration @@ -152,6 +159,15 @@ public: void NotifyListenersOnChange(); + + void + MaybeScheduleTimeCheckAndUpdate(); + + void + MaybeScheduleUpdate(); + + bool + CheckAndClearIfUpdateNeeded(); }; class ServiceWorkerUpdateFinishCallback @@ -316,8 +332,10 @@ class ServiceWorkerManager final friend class ServiceWorkerInstallJob; friend class ServiceWorkerRegisterJob; friend class ServiceWorkerJobBase; + friend class ServiceWorkerScriptJobBase; friend class ServiceWorkerRegistrationInfo; friend class ServiceWorkerUnregisterJob; + friend class UpdateTimerCallback; public: NS_DECL_ISUPPORTS @@ -480,6 +498,10 @@ public: NS_IMETHOD RemoveRegistrationEventListener(const nsAString& aScope, ServiceWorkerRegistrationListener* aListener); + + void + MaybeCheckNavigationUpdate(nsIDocument* aDoc); + private: ServiceWorkerManager(); ~ServiceWorkerManager(); @@ -658,6 +680,12 @@ private: void RemoveNavigationInterception(const nsACString& aScope, nsIInterceptedChannel* aChannel); + + void + ScheduleUpdateTimer(nsIPrincipal* aPrincipal, const nsACString& aScope); + + void + UpdateTimerFired(nsIPrincipal* aPrincipal, const nsACString& aScope); }; } // namespace workers diff --git a/dom/workers/ServiceWorkerPrivate.cpp b/dom/workers/ServiceWorkerPrivate.cpp index 1361e154cf..1ea25ac8d2 100644 --- a/dom/workers/ServiceWorkerPrivate.cpp +++ b/dom/workers/ServiceWorkerPrivate.cpp @@ -212,46 +212,26 @@ public: NS_IMPL_ISUPPORTS0(KeepAliveHandler) -class SoftUpdateRequest : public nsRunnable +class RegistrationUpdateRunnable : public nsRunnable { -protected: nsMainThreadPtrHandle mRegistration; + const bool mNeedTimeCheck; + public: - explicit SoftUpdateRequest(nsMainThreadPtrHandle& aRegistration) + RegistrationUpdateRunnable(nsMainThreadPtrHandle& aRegistration, + bool aNeedTimeCheck) : mRegistration(aRegistration) + , mNeedTimeCheck(aNeedTimeCheck) { - MOZ_ASSERT(aRegistration); } - NS_IMETHOD Run() + NS_IMETHOD + Run() override { - AssertIsOnMainThread(); - - RefPtr swm = ServiceWorkerManager::GetInstance(); - MOZ_ASSERT(swm); - - OriginAttributes attrs = - mozilla::BasePrincipal::Cast(mRegistration->mPrincipal)->OriginAttributesRef(); - - swm->PropagateSoftUpdate(attrs, - NS_ConvertUTF8toUTF16(mRegistration->mScope)); - return NS_OK; - } -}; - -class CheckLastUpdateTimeRequest final : public SoftUpdateRequest -{ -public: - explicit CheckLastUpdateTimeRequest( - nsMainThreadPtrHandle& aRegistration) - : SoftUpdateRequest(aRegistration) - {} - - NS_IMETHOD Run() - { - AssertIsOnMainThread(); - if (mRegistration->IsLastUpdateCheckTimeOverOneDay()) { - SoftUpdateRequest::Run(); + if (mNeedTimeCheck) { + mRegistration->MaybeScheduleTimeCheckAndUpdate(); + } else { + mRegistration->MaybeScheduleUpdate(); } return NS_OK; } @@ -335,9 +315,9 @@ public: void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult) { - nsCOMPtr runnable = new CheckLastUpdateTimeRequest(mRegistration); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable.forget()))); + nsCOMPtr runnable = + new RegistrationUpdateRunnable(mRegistration, true /* time check */); + NS_DispatchToMainThread(runnable.forget()); } }; @@ -1281,16 +1261,21 @@ private: nsCOMPtr runnable; if (event->DefaultPrevented(aCx)) { event->ReportCanceled(); - runnable = new CancelChannelRunnable(mInterceptedChannel, - NS_ERROR_INTERCEPTION_FAILED); } else if (event->GetInternalNSEvent()->mFlags.mExceptionHasBeenRisen) { // Exception logged via the WorkerPrivate ErrorReporter - runnable = new CancelChannelRunnable(mInterceptedChannel, - NS_ERROR_INTERCEPTION_FAILED); } else { runnable = new ResumeRequest(mInterceptedChannel); } + if (!runnable) { + nsCOMPtr updateRunnable = + new RegistrationUpdateRunnable(mRegistration, false /* time check */); + NS_DispatchToMainThread(runnable.forget()); + + runnable = new CancelChannelRunnable(mInterceptedChannel, + NS_ERROR_INTERCEPTION_FAILED); + } + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); } @@ -1301,12 +1286,6 @@ private: waitUntilPromise->AppendNativeHandler(keepAliveHandler); } - // 9.8.22 If request is a non-subresource request, then: Invoke Soft Update algorithm - if (internalReq->IsNavigationRequest()) { - nsCOMPtr runnable= new SoftUpdateRequest(mRegistration); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable.forget()))); - } return true; } diff --git a/editor/libeditor/tests/test_bug414526.html b/editor/libeditor/tests/test_bug414526.html index 4b6ede11cb..0975b6a5ab 100644 --- a/editor/libeditor/tests/test_bug414526.html +++ b/editor/libeditor/tests/test_bug414526.html @@ -126,12 +126,7 @@ function runTests() editor3.focus(); moveCaretToEndOf(editor3); synthesizeKey("VK_DELETE", { }); - // Because of a bug in nsFrameSelection::CharacterExtendForDelete, pressing Delete - // here puts the selection in the text node inside editor3's inner div, which - // causes us to delete the contents of that text node. This situation shouldn't - // happen by the normal caret navigation functions that the user can invoke, so - // we can fix it later. - todo_is(container.innerHTML, kTestCase1, + is(container.innerHTML, kTestCase1, "Pressing delete key at end of editor3 changes the content"); reset(); diff --git a/gfx/layers/apz/util/ScrollLinkedEffectDetector.cpp b/gfx/layers/apz/util/ScrollLinkedEffectDetector.cpp new file mode 100644 index 0000000000..56b593f300 --- /dev/null +++ b/gfx/layers/apz/util/ScrollLinkedEffectDetector.cpp @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ScrollLinkedEffectDetector.h" + +#include "nsIDocument.h" + +namespace mozilla { +namespace layers { + +uint32_t ScrollLinkedEffectDetector::sDepth = 0; +bool ScrollLinkedEffectDetector::sFoundScrollLinkedEffect = false; + +/* static */ void +ScrollLinkedEffectDetector::PositioningPropertyMutated() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (sDepth > 0) { + // We are inside a scroll event dispatch + sFoundScrollLinkedEffect = true; + } +} + +ScrollLinkedEffectDetector::ScrollLinkedEffectDetector(nsIDocument* aDoc) + : mDocument(aDoc) +{ + MOZ_ASSERT(NS_IsMainThread()); + sDepth++; +} + +ScrollLinkedEffectDetector::~ScrollLinkedEffectDetector() +{ + sDepth--; + if (sDepth == 0) { + // We have exited all (possibly-nested) scroll event dispatches, + // record whether or not we found an effect, and reset state + if (sFoundScrollLinkedEffect) { + mDocument->ReportHasScrollLinkedEffect(); + sFoundScrollLinkedEffect = false; + } + } +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/apz/util/ScrollLinkedEffectDetector.h b/gfx/layers/apz/util/ScrollLinkedEffectDetector.h new file mode 100644 index 0000000000..f792586cfa --- /dev/null +++ b/gfx/layers/apz/util/ScrollLinkedEffectDetector.h @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_layers_ScrollLinkedEffectDetector_h +#define mozilla_layers_ScrollLinkedEffectDetector_h + +#include "mozilla/RefPtr.h" + +class nsIDocument; + +namespace mozilla { +namespace layers { + +// ScrollLinkedEffectDetector is used to detect the existence of a scroll-linked +// effect on a webpage. Generally speaking, a scroll-linked effect is something +// on the page that animates or changes with respect to the scroll position. +// Content authors usually rely on running some JS in response to the scroll +// event in order to implement such effects, and therefore it tends to be laggy +// or work improperly with APZ enabled. This class helps us detect such an +// effect so that we can warn the author and/or take other preventative +// measures. +class MOZ_STACK_CLASS ScrollLinkedEffectDetector +{ +private: + static uint32_t sDepth; + static bool sFoundScrollLinkedEffect; + +public: + static void PositioningPropertyMutated(); + + explicit ScrollLinkedEffectDetector(nsIDocument* aDoc); + ~ScrollLinkedEffectDetector(); + +private: + RefPtr mDocument; +}; + +} // namespace layers +} // namespace mozilla + +#endif /* mozilla_layers_ScrollLinkedEffectDetector_h */ diff --git a/gfx/layers/moz.build b/gfx/layers/moz.build index 1dd32ef083..8ced817e21 100644 --- a/gfx/layers/moz.build +++ b/gfx/layers/moz.build @@ -109,6 +109,7 @@ EXPORTS.mozilla.layers += [ 'apz/util/ChromeProcessController.h', 'apz/util/DoubleTapToZoom.h', 'apz/util/InputAPZContext.h', + 'apz/util/ScrollLinkedEffectDetector.h', 'AsyncCanvasRenderer.h', 'AtomicRefCountedWithFinalize.h', 'AxisPhysicsModel.h', @@ -258,6 +259,7 @@ UNIFIED_SOURCES += [ 'apz/util/ChromeProcessController.cpp', 'apz/util/DoubleTapToZoom.cpp', 'apz/util/InputAPZContext.cpp', + 'apz/util/ScrollLinkedEffectDetector.cpp', 'AsyncCanvasRenderer.cpp', 'AxisPhysicsModel.cpp', 'AxisPhysicsMSDModel.cpp', diff --git a/layout/base/AccessibleCaretManager.cpp b/layout/base/AccessibleCaretManager.cpp index f862555bd8..51376ee29e 100644 --- a/layout/base/AccessibleCaretManager.cpp +++ b/layout/base/AccessibleCaretManager.cpp @@ -20,6 +20,7 @@ #include "nsFrame.h" #include "nsFrameSelection.h" #include "nsGenericHTMLElement.h" +#include "nsIHapticFeedback.h" namespace mozilla { @@ -63,7 +64,8 @@ std::ostream& operator<<(std::ostream& aStream, /*static*/ bool AccessibleCaretManager::sCaretsExtendedVisibility = false; - +/*static*/ bool +AccessibleCaretManager::sHapticFeedback = false; AccessibleCaretManager::AccessibleCaretManager(nsIPresShell* aPresShell) : mPresShell(aPresShell) @@ -81,6 +83,8 @@ AccessibleCaretManager::AccessibleCaretManager(nsIPresShell* aPresShell) if (!addedPrefs) { Preferences::AddBoolVarCache(&sCaretsExtendedVisibility, "layout.accessiblecaret.extendedvisibility"); + Preferences::AddBoolVarCache(&sHapticFeedback, + "layout.accessiblecaret.hapticfeedback"); addedPrefs = true; } } @@ -259,7 +263,10 @@ AccessibleCaretManager::UpdateCaretsForCursorMode(UpdateCaretsHint aHint) case PositionChangedResult::Changed: switch (aHint) { case UpdateCaretsHint::Default: - if (HasNonEmptyTextContent(GetEditingHostForFrame(frame))) { + // On Fennec, always show accessiblecaret even if the input is empty + // to make ActionBar visible. + if (sCaretsExtendedVisibility || + HasNonEmptyTextContent(GetEditingHostForFrame(frame))) { mFirstCaret->SetAppearance(Appearance::Normal); } else { mFirstCaret->SetAppearance(Appearance::NormalNotShown); @@ -366,6 +373,16 @@ AccessibleCaretManager::UpdateCaretsForTilt() } } +void +AccessibleCaretManager::ProvideHapticFeedback() +{ + if (sHapticFeedback) { + nsCOMPtr haptic = + do_GetService("@mozilla.org/widget/hapticfeedback;1"); + haptic->PerformSimpleAction(haptic->LongPress); + } +} + nsresult AccessibleCaretManager::PressCaret(const nsPoint& aPoint) { @@ -466,6 +483,7 @@ AccessibleCaretManager::SelectWordOrShortcut(const nsPoint& aPoint) // We need to update carets to get correct information before dispatching // CaretStateChangedEvent. UpdateCarets(); + ProvideHapticFeedback(); DispatchCaretStateChangedEvent(CaretChangedReason::Longpressonemptycontent); return NS_OK; } @@ -496,6 +514,8 @@ AccessibleCaretManager::SelectWordOrShortcut(const nsPoint& aPoint) nsresult rv = SelectWord(ptFrame, ptInFrame); UpdateCarets(); + ProvideHapticFeedback(); + return rv; } diff --git a/layout/base/AccessibleCaretManager.h b/layout/base/AccessibleCaretManager.h index a94606f449..863e0a96e3 100644 --- a/layout/base/AccessibleCaretManager.h +++ b/layout/base/AccessibleCaretManager.h @@ -137,6 +137,9 @@ protected: void UpdateCaretsForCursorMode(UpdateCaretsHint aHint); void UpdateCaretsForSelectionMode(UpdateCaretsHint aHint); + // Provide haptic / touch feedback, primarily for select on longpress. + void ProvideHapticFeedback(); + // Get the nearest enclosing focusable frame of aFrame. // @return focusable frame if there is any; nullptr otherwise. nsIFrame* GetFocusableFrame(nsIFrame* aFrame) const; @@ -246,8 +249,12 @@ protected: // AccessibleCaret visibility preference. Used to avoid hiding caret during // (NO_REASON) selection change notifications generated by keyboard IME, and to - // maintain a visible ActionBar while carets NotShown during scroll. + // maintain a visible ActionBar while carets NotShown during scroll and while + // cursor is on an empty input. static bool sCaretsExtendedVisibility; + + // AccessibleCaret pref for haptic feedback behaviour on longPress. + static bool sHapticFeedback; }; std::ostream& operator<<(std::ostream& aStream, diff --git a/layout/base/FrameLayerBuilder.h b/layout/base/FrameLayerBuilder.h index f1e9af057b..7cc51244ad 100644 --- a/layout/base/FrameLayerBuilder.h +++ b/layout/base/FrameLayerBuilder.h @@ -216,7 +216,7 @@ public: * sets the container layer children to layers which together render * the contents of the display list. It reuses existing layers from * the retained layer manager if possible. - * aContainer may be null, in which case we construct a root layer. + * aContainerItem may be null, in which case we construct a root layer. * This gets called by display list code. It calls BuildLayer on the * items in the display list, making items with their own layers * children of the new container, and assigning all other items to @@ -474,7 +474,7 @@ public: * longer than the transaction. * * Updates the geometry, frame list and clip. - * For items within a PaintedLayer, a geometry object must be specifed to retain + * For items within a PaintedLayer, a geometry object must be specified to retain * until the next transaction. * */ diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp index 294a089a9e..9e318ae420 100644 --- a/layout/base/RestyleManager.cpp +++ b/layout/base/RestyleManager.cpp @@ -854,6 +854,21 @@ RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList) didReflowThisFrame = true; } + if ((hint & nsChangeHint_UpdateUsesOpacity) && + frame->IsFrameOfType(nsIFrame::eTablePart)) { + NS_ASSERTION(hint & nsChangeHint_UpdateOpacityLayer, + "should only return UpdateUsesOpacity hint " + "when also returning UpdateOpacityLayer hint"); + // When an internal table part (including cells) changes between + // having opacity 1 and non-1, it changes whether its + // backgrounds (and those of table parts inside of it) are + // painted as part of the table's nsDisplayTableBorderBackground + // display item, or part of its own display item. That requires + // invalidation, so change UpdateOpacityLayer to RepaintFrame. + hint &= ~nsChangeHint_UpdateOpacityLayer; + hint |= nsChangeHint_RepaintFrame; + } + if (hint & (nsChangeHint_RepaintFrame | nsChangeHint_SyncFrameView | nsChangeHint_UpdateOpacityLayer | nsChangeHint_UpdateTransformLayer | nsChangeHint_ChildrenOnlyTransform | nsChangeHint_SchedulePaint)) { @@ -1332,7 +1347,7 @@ RestyleManager::GetMaxAnimationGenerationForFrame(nsIFrame* aFrame) void RestyleManager::RestyleForEmptyChange(Element* aContainer) { - // In some cases (:empty + E, :empty ~ E), a change if the content of + // In some cases (:empty + E, :empty ~ E), a change in the content of // an element requires restyling its parent's siblings. nsRestyleHint hint = eRestyle_Subtree; nsIContent* grandparent = aContainer->GetParent(); @@ -4438,20 +4453,16 @@ ElementRestyler::ComputeStyleChangeFor(nsIFrame* aFrame, aSwappedStructOwners) { nsIContent* content = aFrame->GetContent(); - nsAutoCString idStr; + nsAutoCString localDescriptor; if (profiler_is_active() && content) { - nsIAtom* id = content->GetID(); - if (id) { - id->ToUTF8String(idStr); - } else { - idStr.AssignLiteral("?"); - } + std::string elemDesc = ToString(*content); + localDescriptor.Assign(elemDesc.c_str()); } PROFILER_LABEL_PRINTF("ElementRestyler", "ComputeStyleChangeFor", js::ProfileEntry::Category::CSS, content ? "Element: %s" : "%s", - content ? idStr.get() : ""); + content ? localDescriptor.get() : ""); if (aMinChange) { aChangeList->AppendChange(aFrame, content, aMinChange); } @@ -5089,7 +5100,8 @@ RestyleManager::ChangeHintToString(nsChangeHint aHint) "ChildrenOnlyTransform", "RecomputePosition", "AddOrRemoveTransform", "BorderStyleNoneChange", "UpdateTextPath", "SchedulePaint", "NeutralChange", "InvalidateRenderingObservers", - "ReflowChangesSizeOrPosition", "UpdateComputedBSize" + "ReflowChangesSizeOrPosition", "UpdateComputedBSize", + "UpdateUsesOpacity" }; uint32_t hint = aHint & ((1 << ArrayLength(names)) - 1); uint32_t rest = aHint & ~((1 << ArrayLength(names)) - 1); diff --git a/layout/base/nsChangeHint.h b/layout/base/nsChangeHint.h index 14b7fc7a4b..c015013fac 100644 --- a/layout/base/nsChangeHint.h +++ b/layout/base/nsChangeHint.h @@ -184,6 +184,16 @@ enum nsChangeHint { */ nsChangeHint_UpdateComputedBSize = 0x1000000, + /** + * Indicates that the 'opacity' property changed between 1 and non-1. + * + * Used as extra data for handling UpdateOpacityLayer hints. + * + * Note that we do not send this hint if the non-1 value was 0.99 or + * greater, since in that case we send a RepaintFrame hint instead. + */ + nsChangeHint_UpdateUsesOpacity = 0x2000000, + // IMPORTANT NOTE: When adding new hints, consider whether you need to // add them to NS_HintsNotHandledForDescendantsIn() below. Please also // add them to RestyleManager::ChangeHintToString. @@ -292,7 +302,8 @@ inline nsChangeHint operator^=(nsChangeHint& aLeft, nsChangeHint aRight) nsChangeHint_NeedReflow | \ nsChangeHint_ReflowChangesSizeOrPosition | \ nsChangeHint_ClearAncestorIntrinsics | \ - nsChangeHint_UpdateComputedBSize) + nsChangeHint_UpdateComputedBSize | \ + nsChangeHint_UpdateUsesOpacity) inline nsChangeHint NS_HintsNotHandledForDescendantsIn(nsChangeHint aChangeHint) { nsChangeHint result = nsChangeHint(aChangeHint & ( @@ -307,7 +318,8 @@ inline nsChangeHint NS_HintsNotHandledForDescendantsIn(nsChangeHint aChangeHint) nsChangeHint_RecomputePosition | nsChangeHint_UpdateContainingBlock | nsChangeHint_BorderStyleNoneChange | - nsChangeHint_UpdateComputedBSize)); + nsChangeHint_UpdateComputedBSize | + nsChangeHint_UpdateUsesOpacity)); if (!NS_IsHintSubset(nsChangeHint_NeedDirtyReflow, aChangeHint)) { if (NS_IsHintSubset(nsChangeHint_NeedReflow, aChangeHint)) { diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index b8e767fd42..8bb30d6ff9 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -1727,10 +1727,13 @@ already_AddRefed nsDisplayList::PaintRoot(nsDisplayListBuilder* aB // If this is the content process, we ship plugin geometry updates over with layer // updates, so calculate that now before we call EndTransaction. - if (rootPresContext && - aBuilder->WillComputePluginGeometry() && - XRE_IsContentProcess()) { - rootPresContext->ComputePluginGeometryUpdates(aBuilder->RootReferenceFrame(), aBuilder, this); + if (rootPresContext && XRE_IsContentProcess()) { + if (aBuilder->WillComputePluginGeometry()) { + rootPresContext->ComputePluginGeometryUpdates(aBuilder->RootReferenceFrame(), aBuilder, this); + } + // The layer system caches plugin configuration information for forwarding + // with layer updates which needs to get set during reflow. This must be + // called even if there are no windowed plugins in the page. rootPresContext->CollectPluginGeometryUpdates(layerManager); } diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp index 932cad23af..2a3cac6929 100644 --- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -2852,11 +2852,10 @@ nsPresContext::GetPrimaryFrameFor(nsIContent* aContent) return nullptr; } - size_t nsPresContext::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const { - return mPropertyTable.SizeOfExcludingThis(aMallocSizeOf); + return mPropertyTable.SizeOfExcludingThis(aMallocSizeOf) + mLangGroupFontPrefs.SizeOfExcludingThis(aMallocSizeOf); // Measurement of other members may be added later if DMD finds it is @@ -3209,6 +3208,12 @@ nsRootPresContext::CollectPluginGeometryUpdates(LayerManager* aLayerManager) mozilla::layers::ClientLayerManager* clm = aLayerManager->AsClientLayerManager(); nsTArray configurations; + // If there aren't any plugins to configure, clear the plugin data cache + // in the layer system. + if (!mRegisteredPlugins.Count() && clm) { + clm->StorePluginWidgetConfigurations(configurations); + return; + } PluginGetGeometryUpdate(mRegisteredPlugins, &configurations); if (configurations.IsEmpty()) { PluginDidSetGeometry(mRegisteredPlugins); diff --git a/layout/base/tests/chrome/scroll_selection_into_view_window.html b/layout/base/tests/chrome/scroll_selection_into_view_window.html index 53b2f5cb6f..46fbb0d19a 100644 --- a/layout/base/tests/chrome/scroll_selection_into_view_window.html +++ b/layout/base/tests/chrome/scroll_selection_into_view_window.html @@ -8,21 +8,21 @@
target + style="display:inline-block; vertical-align:top; height:20px;">
target2 + style="display:inline-block; vertical-align:top; height:20px;">
target3 + style="display:inline-block; vertical-align:top; height:300px;">
@@ -32,21 +32,21 @@
target4 + style='display:inline-block; vertical-align:top; height:300px;'>
">
- target +
- target +
@@ -54,7 +54,7 @@
- target +
diff --git a/layout/generic/crashtests/1146114.html b/layout/generic/crashtests/1146114.html new file mode 100644 index 0000000000..5f148a1679 --- /dev/null +++ b/layout/generic/crashtests/1146114.html @@ -0,0 +1,6 @@ + + + +
+ + diff --git a/layout/generic/nsAbsoluteContainingBlock.cpp b/layout/generic/nsAbsoluteContainingBlock.cpp index 521a0cbc03..604128067f 100644 --- a/layout/generic/nsAbsoluteContainingBlock.cpp +++ b/layout/generic/nsAbsoluteContainingBlock.cpp @@ -113,36 +113,27 @@ nsAbsoluteContainingBlock::Reflow(nsContainerFrame* aDelegatingFrame, const nsHTMLReflowState& aReflowState, nsReflowStatus& aReflowStatus, const nsRect& aContainingBlock, - bool aConstrainHeight, - bool aCBWidthChanged, - bool aCBHeightChanged, + AbsPosReflowFlags aFlags, nsOverflowAreas* aOverflowAreas) { nsReflowStatus reflowStatus = NS_FRAME_COMPLETE; - bool reflowAll = aReflowState.ShouldReflowAllKids(); - - // The 'width' check below is an optimization to avoid the virtual GetType() - // call in most cases. 'aContainingBlock' isn't used for grid items, - // each item has its own CB on a frame property instead. - // @see nsGridContainerFrame::ReflowChildren - const bool isGrid = - aContainingBlock.width == nsGridContainerFrame::VERY_LIKELY_A_GRID_CONTAINER && - aDelegatingFrame->GetType() == nsGkAtoms::gridContainerFrame; - + const bool reflowAll = aReflowState.ShouldReflowAllKids(); + const bool isGrid = !!(aFlags & AbsPosReflowFlags::eIsGridContainerCB); nsIFrame* kidFrame; nsOverflowContinuationTracker tracker(aDelegatingFrame, true); for (kidFrame = mAbsoluteFrames.FirstChild(); kidFrame; kidFrame = kidFrame->GetNextSibling()) { bool kidNeedsReflow = reflowAll || NS_SUBTREE_DIRTY(kidFrame) || - FrameDependsOnContainer(kidFrame, aCBWidthChanged, aCBHeightChanged); + FrameDependsOnContainer(kidFrame, + !!(aFlags & AbsPosReflowFlags::eCBWidthChanged), + !!(aFlags & AbsPosReflowFlags::eCBHeightChanged)); if (kidNeedsReflow && !aPresContext->HasPendingInterrupt()) { // Reflow the frame nsReflowStatus kidStatus = NS_FRAME_COMPLETE; const nsRect& cb = isGrid ? nsGridContainerFrame::GridItemCB(kidFrame) : aContainingBlock; ReflowAbsoluteFrame(aDelegatingFrame, aPresContext, aReflowState, cb, - aConstrainHeight, kidFrame, kidStatus, - aOverflowAreas); + aFlags, kidFrame, kidStatus, aOverflowAreas); nsIFrame* nextFrame = kidFrame->GetNextInFlow(); if (!NS_FRAME_IS_FULLY_COMPLETE(kidStatus) && aDelegatingFrame->IsFrameOfType(nsIFrame::eCanContainOverflowContainers)) { @@ -354,7 +345,7 @@ nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegat nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, const nsRect& aContainingBlock, - bool aConstrainBSize, + AbsPosReflowFlags aFlags, nsIFrame* aKidFrame, nsReflowStatus& aStatus, nsOverflowAreas* aOverflowAreas) @@ -404,7 +395,7 @@ nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegat const LogicalMargin margin = kidReflowState.ComputedLogicalMargin().ConvertTo(outerWM, wm); bool constrainBSize = (aReflowState.AvailableBSize() != NS_UNCONSTRAINEDSIZE) - && aConstrainBSize + && (aFlags & AbsPosReflowFlags::eConstrainHeight) // Don't split if told not to (e.g. for fixed frames) && (aDelegatingFrame->GetType() != nsGkAtoms::inlineFrame) //XXX we don't handle splitting frames for inline absolute containing blocks yet diff --git a/layout/generic/nsAbsoluteContainingBlock.h b/layout/generic/nsAbsoluteContainingBlock.h index b061abd223..15e733be04 100644 --- a/layout/generic/nsAbsoluteContainingBlock.h +++ b/layout/generic/nsAbsoluteContainingBlock.h @@ -13,6 +13,7 @@ #include "nsFrameList.h" #include "nsIFrame.h" +#include "mozilla/TypedEnumBits.h" class nsContainerFrame; struct nsHTMLReflowState; @@ -68,6 +69,14 @@ public: ChildListID aListID, nsIFrame* aOldFrame); + enum class AbsPosReflowFlags { + eConstrainHeight = 0x1, + eCBWidthChanged = 0x2, + eCBHeightChanged = 0x4, + eCBWidthAndHeightChanged = eCBWidthChanged | eCBHeightChanged, + eIsGridContainerCB = 0x8, + }; + /** * Called by the delegating frame after it has done its reflow first. This * function will reflow any absolutely positioned child frames that need to @@ -81,15 +90,15 @@ public: * @param aReflowStatus is assumed to be already-initialized, e.g. with the * status of the delegating frame's main reflow. This function merges in the * statuses of the absolutely positioned children's reflows. + * + * @param aFlags zero or more AbsPosReflowFlags */ void Reflow(nsContainerFrame* aDelegatingFrame, nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, nsReflowStatus& aReflowStatus, const nsRect& aContainingBlock, - bool aConstrainHeight, - bool aCBWidthChanged, - bool aCBHeightChanged, + AbsPosReflowFlags aFlags, nsOverflowAreas* aOverflowAreas); void DestroyFrames(nsIFrame* aDelegatingFrame, @@ -121,7 +130,7 @@ protected: nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, const nsRect& aContainingBlockRect, - bool aConstrainHeight, + AbsPosReflowFlags aFlags, nsIFrame* aKidFrame, nsReflowStatus& aStatus, nsOverflowAreas* aOverflowAreas); @@ -142,4 +151,7 @@ protected: #endif }; +namespace mozilla { + MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(nsAbsoluteContainingBlock::AbsPosReflowFlags) +} #endif /* nsnsAbsoluteContainingBlock_h___ */ diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index c44881eef0..1e76525762 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -63,6 +63,7 @@ static const char16_t kDiscCharacter = 0x2022; using namespace mozilla; using namespace mozilla::css; using namespace mozilla::layout; +typedef nsAbsoluteContainingBlock::AbsPosReflowFlags AbsPosReflowFlags; static void MarkAllDescendantLinesDirty(nsBlockFrame* aBlock) { @@ -1399,10 +1400,16 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, nsRect containingBlock(nsPoint(0, 0), containingBlockSize.GetPhysicalSize(parentWM)); + AbsPosReflowFlags flags = AbsPosReflowFlags::eConstrainHeight; + if (cbWidthChanged) { + flags |= AbsPosReflowFlags::eCBWidthChanged; + } + if (cbHeightChanged) { + flags |= AbsPosReflowFlags::eCBHeightChanged; + } absoluteContainer->Reflow(this, aPresContext, *reflowState, state.mReflowStatus, - containingBlock, true, - cbWidthChanged, cbHeightChanged, + containingBlock, flags, &aMetrics.mOverflowAreas); } } diff --git a/layout/generic/nsColumnSetFrame.cpp b/layout/generic/nsColumnSetFrame.cpp index 9985c2a817..1ded814082 100644 --- a/layout/generic/nsColumnSetFrame.cpp +++ b/layout/generic/nsColumnSetFrame.cpp @@ -1061,8 +1061,14 @@ nsColumnSetFrame::Reflow(nsPresContext* aPresContext, //------------ Handle Incremental Reflow ----------------- - ReflowConfig config = ChooseColumnStrategy(aReflowState); - + // If inline size is unconstrained, set aForceAuto to true to allow + // the columns to expand in the inline direction. (This typically + // happens in orthogonal flows where the inline direction is the + // container's block direction). + ReflowConfig config = + ChooseColumnStrategy(aReflowState, + aReflowState.ComputedISize() == NS_UNCONSTRAINEDSIZE); + // If balancing, then we allow the last column to grow to unbounded // height during the first reflow. This gives us a way to estimate // what the average column height should be, because we can measure diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index efa6d221a2..bd93a54727 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -109,6 +109,7 @@ using namespace mozilla::dom; using namespace mozilla::gfx; using namespace mozilla::layers; using namespace mozilla::layout; +typedef nsAbsoluteContainingBlock::AbsPosReflowFlags AbsPosReflowFlags; namespace mozilla { namespace gfx { @@ -4660,9 +4661,13 @@ nsFrame::ReflowAbsoluteFrames(nsPresContext* aPresContext, NS_ASSERTION(container, "Abs-pos children only supported on container frames for now"); nsRect containingBlock(0, 0, containingBlockWidth, containingBlockHeight); + AbsPosReflowFlags flags = + AbsPosReflowFlags::eCBWidthAndHeightChanged; // XXX could be optimized + if (aConstrainBSize) { + flags |= AbsPosReflowFlags::eConstrainHeight; + } absoluteContainer->Reflow(container, aPresContext, aReflowState, aStatus, - containingBlock, - aConstrainBSize, true, true, // XXX could be optimized + containingBlock, flags, &aDesiredSize.mOverflowAreas); } } diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index fbd5c68eff..64869a1a21 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -61,6 +61,7 @@ #include "nsPluginFrame.h" #include #include +#include "mozilla/layers/ScrollLinkedEffectDetector.h" #include "mozilla/unused.h" #include #include // for std::abs(int/long) @@ -4246,6 +4247,7 @@ ScrollFrameHelper::FireScrollEvent() nsPresContext* prescontext = mOuter->PresContext(); // Fire viewport scroll events at the document (where they // will bubble to the window) + mozilla::layers::ScrollLinkedEffectDetector detector(content->GetComposedDoc()); if (mIsRoot) { nsIDocument* doc = content->GetCurrentDoc(); if (doc) { diff --git a/layout/generic/nsGridContainerFrame.cpp b/layout/generic/nsGridContainerFrame.cpp index 1411e250ab..ea0cd2e9bd 100644 --- a/layout/generic/nsGridContainerFrame.cpp +++ b/layout/generic/nsGridContainerFrame.cpp @@ -27,9 +27,10 @@ #include "nsStyleContext.h" using namespace mozilla; +typedef nsAbsoluteContainingBlock::AbsPosReflowFlags AbsPosReflowFlags; typedef nsGridContainerFrame::TrackSize TrackSize; const uint32_t nsGridContainerFrame::kTranslatedMaxLine = - uint32_t(nsStyleGridLine::kMaxLine - nsStyleGridLine::kMinLine - 1); + uint32_t(nsStyleGridLine::kMaxLine - nsStyleGridLine::kMinLine); const uint32_t nsGridContainerFrame::kAutoLine = kTranslatedMaxLine + 3457U; MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(TrackSize::StateBits) @@ -271,9 +272,11 @@ struct MOZ_STACK_CLASS nsGridContainerFrame::Tracks const LineRange& aRange, nsIFrame* aGridItem); /** - * Collect the tracks which are growable (matching aSelector) and return - * aAvailableSpace minus the sum of mBase's in aPlan for the tracks - * in aRange, or 0 if this subtraction goes below 0. + * Collect the tracks which are growable (matching aSelector) into + * aGrowableTracks, and return the amount of space that can be used + * to grow those tracks. Specifically, we return aAvailableSpace minus + * the sum of mBase's in aPlan (clamped to 0) for the tracks in aRange, + * or zero when there are no growable tracks. * @note aPlan[*].mBase represents a planned new base or limit. */ static nscoord CollectGrowable(nscoord aAvailableSpace, @@ -288,16 +291,15 @@ struct MOZ_STACK_CLASS nsGridContainerFrame::Tracks const uint32_t end = aRange.mEnd; for (uint32_t i = start; i < end; ++i) { const TrackSize& sz = aPlan[i]; - MOZ_ASSERT(!sz.IsFrozen()); space -= sz.mBase; if (space <= 0) { return 0; } - if (sz.mState & aSelector) { + if ((sz.mState & aSelector) && !sz.IsFrozen()) { aGrowableTracks.AppendElement(i); } } - return space; + return aGrowableTracks.IsEmpty() ? 0 : space; } void SetupGrowthPlan(nsTArray& aPlan, @@ -908,15 +910,36 @@ AlignJustifySelf(uint8_t aAlignment, bool aOverflowSafe, LogicalAxis aAxis, } } + const auto& styleMargin = aRS.mStyleMargin->mMargin; + bool hasAutoMarginStart; + bool hasAutoMarginEnd; + if (aAxis == eLogicalAxisBlock) { + hasAutoMarginStart = styleMargin.GetBStartUnit(wm) == eStyleUnit_Auto; + hasAutoMarginEnd = styleMargin.GetBEndUnit(wm) == eStyleUnit_Auto; + } else { + hasAutoMarginStart = styleMargin.GetIStartUnit(wm) == eStyleUnit_Auto; + hasAutoMarginEnd = styleMargin.GetIEndUnit(wm) == eStyleUnit_Auto; + } + // https://drafts.csswg.org/css-align-3/#overflow-values // This implements = 'safe'. - if (MOZ_UNLIKELY(aOverflowSafe) && aAlignment != NS_STYLE_ALIGN_START) { + // And auto-margins: https://drafts.csswg.org/css-grid/#auto-margins + if ((MOZ_UNLIKELY(aOverflowSafe) && aAlignment != NS_STYLE_ALIGN_START) || + hasAutoMarginStart || hasAutoMarginEnd) { nscoord space = SpaceToFill(wm, aChildSize, marginStart + marginEnd, aAxis, aCBSize); // XXX we might want to include == 0 here as an optimization - // I need to see what the baseline/last-baseline code looks like first. if (space < 0) { + // "Overflowing elements ignore their auto margins and overflow + // in the end directions" aAlignment = NS_STYLE_ALIGN_START; + } else if (hasAutoMarginEnd) { + aAlignment = hasAutoMarginStart ? NS_STYLE_ALIGN_CENTER + : (aSameSide ? NS_STYLE_ALIGN_START + : NS_STYLE_ALIGN_END); + } else if (hasAutoMarginStart) { + aAlignment = aSameSide ? NS_STYLE_ALIGN_END : NS_STYLE_ALIGN_START; } } @@ -941,15 +964,11 @@ AlignJustifySelf(uint8_t aAlignment, bool aOverflowSafe, LogicalAxis aAxis, aAxis, aCBSize) / 2; break; case NS_STYLE_ALIGN_STRETCH: { + MOZ_ASSERT(!hasAutoMarginStart && !hasAutoMarginEnd); offset = marginStart; - const auto& styleMargin = aRS.mStyleMargin->mMargin; if (aAxis == eLogicalAxisBlock - ? (aRS.mStylePosition->BSize(wm).GetUnit() == eStyleUnit_Auto && - styleMargin.GetBStartUnit(wm) != eStyleUnit_Auto && - styleMargin.GetBEndUnit(wm) != eStyleUnit_Auto) - : (aRS.mStylePosition->ISize(wm).GetUnit() == eStyleUnit_Auto && - styleMargin.GetIStartUnit(wm) != eStyleUnit_Auto && - styleMargin.GetIEndUnit(wm) != eStyleUnit_Auto)) { + ? (aRS.mStylePosition->BSize(wm).GetUnit() == eStyleUnit_Auto) + : (aRS.mStylePosition->ISize(wm).GetUnit() == eStyleUnit_Auto)) { nscoord size = aAxis == eLogicalAxisBlock ? aChildSize.BSize(wm) : aChildSize.ISize(wm); nscoord gap = aCBSize - (size + marginStart + marginEnd); @@ -1563,7 +1582,7 @@ nsGridContainerFrame::PlaceAutoCol(uint32_t aStartCol, GridArea* aArea) const { MOZ_ASSERT(aArea->mRows.IsDefinite() && aArea->mCols.IsAuto()); uint32_t col = FindAutoCol(aStartCol, aArea->mRows.mStart, aArea); - aArea->mCols.ResolveAutoPosition(col); + aArea->mCols.ResolveAutoPosition(col, mExplicitGridOffsetCol); MOZ_ASSERT(aArea->IsDefinite()); } @@ -1601,7 +1620,7 @@ nsGridContainerFrame::PlaceAutoRow(uint32_t aStartRow, GridArea* aArea) const { MOZ_ASSERT(aArea->mCols.IsDefinite() && aArea->mRows.IsAuto()); uint32_t row = FindAutoRow(aArea->mCols.mStart, aStartRow, aArea); - aArea->mRows.ResolveAutoPosition(row); + aArea->mRows.ResolveAutoPosition(row, mExplicitGridOffsetRow); MOZ_ASSERT(aArea->IsDefinite()); } @@ -1625,8 +1644,8 @@ nsGridContainerFrame::PlaceAutoAutoInRowOrder(uint32_t aStartCol, } MOZ_ASSERT(row < gridRowEnd || col == 0, "expected column 0 for placing in a new row"); - aArea->mCols.ResolveAutoPosition(col); - aArea->mRows.ResolveAutoPosition(row); + aArea->mCols.ResolveAutoPosition(col, mExplicitGridOffsetCol); + aArea->mRows.ResolveAutoPosition(row, mExplicitGridOffsetRow); MOZ_ASSERT(aArea->IsDefinite()); } @@ -1650,8 +1669,8 @@ nsGridContainerFrame::PlaceAutoAutoInColOrder(uint32_t aStartCol, } MOZ_ASSERT(col < gridColEnd || row == 0, "expected row 0 for placing in a new column"); - aArea->mCols.ResolveAutoPosition(col); - aArea->mRows.ResolveAutoPosition(row); + aArea->mCols.ResolveAutoPosition(col, mExplicitGridOffsetCol); + aArea->mRows.ResolveAutoPosition(row, mExplicitGridOffsetRow); MOZ_ASSERT(aArea->IsDefinite()); } @@ -1993,7 +2012,8 @@ ContentContribution(nsIFrame* aChild, #endif // XXX this will give mostly correct results for now (until bug 1174569). LogicalSize availableSize(wm, INFINITE_ISIZE_COORD, NS_UNCONSTRAINEDSIZE); - nsHTMLReflowState childRS(pc, *rs, aChild, availableSize); + nsHTMLReflowState childRS(pc, *rs, aChild, availableSize, nullptr, + nsHTMLReflowState::COMPUTE_SIZE_SHRINK_WRAP); nsHTMLReflowMetrics childSize(childRS); nsReflowStatus childStatus; const uint32_t flags = NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_SIZE_VIEW; @@ -2933,13 +2953,16 @@ nsGridContainerFrame::ReflowChildren(GridReflowState& aState, } *cb = itemCB.GetPhysicalRect(wm, gridCBPhysicalSize); } - // This rect isn't used at all for layout so we use it to optimize - // away the virtual GetType() call in the callee in most cases. - // @see nsAbsoluteContainingBlock::Reflow - nsRect dummyRect(0, 0, VERY_LIKELY_A_GRID_CONTAINER, 0); + // We pass a dummy rect as CB because each child has its own CB rect. + // The eIsGridContainerCB flag tells nsAbsoluteContainingBlock::Reflow to + // use those instead. + nsRect dummyRect; + AbsPosReflowFlags flags = + AbsPosReflowFlags::eCBWidthAndHeightChanged; // XXX could be optimized + flags |= AbsPosReflowFlags::eConstrainHeight; + flags |= AbsPosReflowFlags::eIsGridContainerCB; GetAbsoluteContainingBlock()->Reflow(this, pc, *aState.mReflowState, - aStatus, dummyRect, true, - true, true, // XXX could be optimized + aStatus, dummyRect, flags, &aDesiredSize.mOverflowAreas); } } @@ -2979,11 +3002,27 @@ nsGridContainerFrame::Reflow(nsPresContext* aPresContext, LogicalSize(wm, computedISize, computedBSize), nsLayoutUtils::PREF_ISIZE); + // FIXME bug 1229180: Instead of doing this on every reflow, we should only + // set these properties if they are needed. + nsTArray colTrackSizes(gridReflowState.mCols.mSizes.Length()); + for (const TrackSize& sz : gridReflowState.mCols.mSizes) { + colTrackSizes.AppendElement(sz.mBase); + } + Properties().Set(GridColTrackSizes(), + new nsTArray(mozilla::Move(colTrackSizes))); + nsTArray rowTrackSizes(gridReflowState.mRows.mSizes.Length()); + for (const TrackSize& sz : gridReflowState.mRows.mSizes) { + rowTrackSizes.AppendElement(sz.mBase); + } + Properties().Set(GridRowTrackSizes(), + new nsTArray(mozilla::Move(rowTrackSizes))); + nscoord bSize = 0; if (computedBSize == NS_AUTOHEIGHT) { for (uint32_t i = 0; i < mGridRowEnd; ++i) { bSize += gridReflowState.mRows.mSizes[i].mBase; } + bSize += gridReflowState.mRows.SumOfGridGaps(); } else { bSize = computedBSize; } diff --git a/layout/generic/nsGridContainerFrame.h b/layout/generic/nsGridContainerFrame.h index d2ebb0021d..d886dd8e50 100644 --- a/layout/generic/nsGridContainerFrame.h +++ b/layout/generic/nsGridContainerFrame.h @@ -86,11 +86,22 @@ public: StateBits mState; }; - // @see nsAbsoluteContainingBlock::Reflow about this magic number - static const nscoord VERY_LIKELY_A_GRID_CONTAINER = -123456789; - NS_DECLARE_FRAME_PROPERTY(GridItemContainingBlockRect, DeleteValue) + NS_DECLARE_FRAME_PROPERTY(GridColTrackSizes, DeleteValue>) + + const nsTArray* GetComputedTemplateColumns() + { + return static_cast*>(Properties().Get(GridColTrackSizes())); + } + + NS_DECLARE_FRAME_PROPERTY(GridRowTrackSizes, DeleteValue>) + + const nsTArray* GetComputedTemplateRows() + { + return static_cast*>(Properties().Get(GridRowTrackSizes())); + } + protected: static const uint32_t kAutoLine; // The maximum line number, in the zero-based translated grid. @@ -161,17 +172,19 @@ protected: * Resolve this auto range to start at aStart, making it definite. * Precondition: this range IsAuto() */ - void ResolveAutoPosition(uint32_t aStart) + void ResolveAutoPosition(uint32_t aStart, uint32_t aExplicitGridOffset) { MOZ_ASSERT(IsAuto(), "Why call me?"); mStart = aStart; mEnd += aStart; - // Clamping per http://dev.w3.org/csswg/css-grid/#overlarge-grids : - if (MOZ_UNLIKELY(mStart >= kTranslatedMaxLine)) { - mEnd = kTranslatedMaxLine; + // Clamping to where kMaxLine is in the explicit grid, per + // http://dev.w3.org/csswg/css-grid/#overlarge-grids : + uint32_t translatedMax = aExplicitGridOffset + nsStyleGridLine::kMaxLine; + if (MOZ_UNLIKELY(mStart >= translatedMax)) { + mEnd = translatedMax; mStart = mEnd - 1; - } else if (MOZ_UNLIKELY(mEnd > kTranslatedMaxLine)) { - mEnd = kTranslatedMaxLine; + } else if (MOZ_UNLIKELY(mEnd > translatedMax)) { + mEnd = translatedMax; } } /** @@ -233,15 +246,14 @@ protected: }; /** - * Return aLine if it's inside the aMin..aMax range (inclusive), otherwise - * return kAutoLine. If the range is empty (aMin == aMax, i.e. there are - * no tracks in the grid) then aLine is outside. + * Return aLine if it's inside the aMin..aMax range (inclusive), + * otherwise return kAutoLine. */ static int32_t AutoIfOutside(int32_t aLine, int32_t aMin, int32_t aMax) { MOZ_ASSERT(aMin <= aMax); - if (aLine < aMin || aLine > aMax || aMin == aMax) { + if (aLine < aMin || aLine > aMax) { return kAutoLine; } return aLine; diff --git a/layout/generic/nsHTMLReflowState.cpp b/layout/generic/nsHTMLReflowState.cpp index 6fedba7fbc..f63a12b55b 100644 --- a/layout/generic/nsHTMLReflowState.cpp +++ b/layout/generic/nsHTMLReflowState.cpp @@ -79,6 +79,9 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext, if (aFlags & DUMMY_PARENT_REFLOW_STATE) { mFlags.mDummyParentReflowState = true; } + if (aFlags & COMPUTE_SIZE_SHRINK_WRAP) { + mFlags.mShrinkWrap = true; + } if (!(aFlags & CALLER_WILL_INIT)) { Init(aPresContext); @@ -219,6 +222,7 @@ nsHTMLReflowState::nsHTMLReflowState( mFlags.mIsColumnBalancing = false; mFlags.mIsFlexContainerMeasuringHeight = false; mFlags.mDummyParentReflowState = false; + mFlags.mShrinkWrap = !!(aFlags & COMPUTE_SIZE_SHRINK_WRAP); mDiscoveredClearance = nullptr; mPercentBSizeObserver = (aParentReflowState.mPercentBSizeObserver && @@ -444,11 +448,19 @@ nsHTMLReflowState::Init(nsPresContext* aPresContext, } } - if (AvailableBSize() != NS_UNCONSTRAINEDSIZE && parentReflowState && + if (parentReflowState && parentReflowState->GetWritingMode().IsOrthogonalTo(mWritingMode)) { - // Orthogonal frames are always reflowed with unconstrained block-size, - // to avoid incomplete reflow across an orthogonal boundary. - AvailableBSize() = NS_UNCONSTRAINEDSIZE; + // Orthogonal frames are always reflowed with an unconstrained + // dimension to avoid incomplete reflow across an orthogonal + // boundary. Normally this is the block-size, but for column sets + // with auto-height it's the inline-size, so that they can add + // columns in the container's block direction + if (type == nsGkAtoms::columnSetFrame && + eStyleUnit_Auto == mStylePosition->ISize(mWritingMode).GetUnit()) { + ComputedISize() = NS_UNCONSTRAINEDSIZE; + } else { + AvailableBSize() = NS_UNCONSTRAINEDSIZE; + } } LAYOUT_WARN_IF_FALSE((mFrameType == NS_CSS_FRAME_TYPE_INLINE && @@ -621,7 +633,7 @@ nsHTMLReflowState::InitResizeFlags(nsPresContext* aPresContext, nsIAtom* aFrameT // mCBReflowState->IsBResize() is set correctly below when // reflowing descendant. SetBResize(true); - } else if (mCBReflowState && !nsLayoutUtils::IsNonWrapperBlock(frame)) { + } else if (mCBReflowState && frame->IsBlockWrapper()) { // XXX Is this problematic for relatively positioned inlines acting // as containing block for absolutely positioned elements? // Possibly; in that case we should at least be checking @@ -1017,11 +1029,10 @@ nsHTMLReflowState::GetHypotheticalBoxContainer(nsIFrame* aFrame, state = nullptr; } - WritingMode wm = aFrame->GetWritingMode(); if (state) { - WritingMode stateWM = state->GetWritingMode(); - aCBIStartEdge = - state->ComputedLogicalBorderPadding().ConvertTo(wm, stateWM).IStart(wm); + WritingMode wm = state->GetWritingMode(); + NS_ASSERTION(wm == aFrame->GetWritingMode(), "unexpected writing mode"); + aCBIStartEdge = state->ComputedLogicalBorderPadding().IStart(wm); aCBSize = state->ComputedSize(wm); } else { /* Didn't find a reflow state for aFrame. Just compute the information we @@ -1029,6 +1040,7 @@ nsHTMLReflowState::GetHypotheticalBoxContainer(nsIFrame* aFrame, ought to be true by now. */ NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_IN_REFLOW), "aFrame shouldn't be in reflow; we'll lie if it is"); + WritingMode wm = aFrame->GetWritingMode(); LogicalMargin borderPadding = aFrame->GetLogicalUsedBorderAndPadding(wm); aCBIStartEdge = borderPadding.IStart(wm); aCBSize = aFrame->GetLogicalSize(wm) - borderPadding.Size(wm); @@ -1037,27 +1049,12 @@ nsHTMLReflowState::GetHypotheticalBoxContainer(nsIFrame* aFrame, return aFrame; } -// When determining the hypothetical box that would have been if the element -// had been in the flow we may not be able to exactly determine both the IStart -// and IEnd edges. For example, if the element is a non-replaced inline-level -// element we would have to reflow it in order to determine its desired ISize. -// In that case depending on the progression direction either the IStart or -// IEnd edge would be marked as not being exact. -struct nsHypotheticalBox { - // offsets from inline-start edge of containing block (which is a padding edge) - nscoord mIStart, mIEnd; +struct nsHypotheticalPosition { + // offset from inline-start edge of containing block (which is a padding edge) + nscoord mIStart; // offset from block-start edge of containing block (which is a padding edge) nscoord mBStart; WritingMode mWritingMode; -#ifdef DEBUG - bool mIStartIsExact, mIEndIsExact; -#endif - - nsHypotheticalBox() { -#ifdef DEBUG - mIStartIsExact = mIEndIsExact = false; -#endif - } }; static bool @@ -1191,20 +1188,22 @@ static bool AreAllEarlierInFlowFramesEmpty(nsIFrame* aFrame, return true; } -// Calculate the hypothetical box that the element would have if it were in -// the flow. The values returned are relative to the padding edge of the -// absolute containing block. The writing-mode of the hypothetical box will +// Calculate the position of the hypothetical box that the element would have +// if it were in the flow. +// The values returned are relative to the padding edge of the absolute +// containing block. The writing-mode of the hypothetical box position will // have the same block direction as the absolute containing block, but may // differ in inline-bidi direction. // In the code below, |cbrs->frame| is the absolute containing block, while // |containingBlock| is the nearest block container of the placeholder frame, // which may be different from the absolute containing block. void -nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, - nsIFrame* aPlaceholderFrame, - const nsHTMLReflowState* cbrs, - nsHypotheticalBox& aHypotheticalBox, - nsIAtom* aFrameType) +nsHTMLReflowState::CalculateHypotheticalPosition + (nsPresContext* aPresContext, + nsIFrame* aPlaceholderFrame, + const nsHTMLReflowState* cbrs, + nsHypotheticalPosition& aHypotheticalPos, + nsIAtom* aFrameType) { NS_ASSERTION(mStyleDisplay->mOriginalDisplay != NS_STYLE_DISPLAY_NONE, "mOriginalDisplay has not been properly initialized"); @@ -1311,7 +1310,7 @@ nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, if (!isValid) { // Give up. We're probably dealing with somebody using // position:absolute inside native-anonymous content anyway. - aHypotheticalBox.mBStart = placeholderOffset.B(wm); + aHypotheticalPos.mBStart = placeholderOffset.B(wm); } else { NS_ASSERTION(iter.GetContainer() == blockFrame, "Found placeholder in wrong block!"); @@ -1325,7 +1324,7 @@ nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, if (mStyleDisplay->IsOriginalDisplayInlineOutsideStyle()) { // Use the block-start of the inline box which the placeholder lives in // as the hypothetical box's block-start. - aHypotheticalBox.mBStart = lineBounds.BStart(wm) + blockOffset.B(wm); + aHypotheticalPos.mBStart = lineBounds.BStart(wm) + blockOffset.B(wm); } else { // The element would have been block-level which means it would // be below the line containing the placeholder frame, unless @@ -1350,15 +1349,17 @@ nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, // The top of the hypothetical box is the top of the line // containing the placeholder, since there is nothing in the // line before our placeholder except empty frames. - aHypotheticalBox.mBStart = lineBounds.BStart(wm) + blockOffset.B(wm); + aHypotheticalPos.mBStart = + lineBounds.BStart(wm) + blockOffset.B(wm); } else { // The top of the hypothetical box is just below the line // containing the placeholder. - aHypotheticalBox.mBStart = lineBounds.BEnd(wm) + blockOffset.B(wm); + aHypotheticalPos.mBStart = + lineBounds.BEnd(wm) + blockOffset.B(wm); } } else { // Just use the placeholder's block-offset wrt the containing block - aHypotheticalBox.mBStart = placeholderOffset.B(wm); + aHypotheticalPos.mBStart = placeholderOffset.B(wm); } } } @@ -1366,36 +1367,17 @@ nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, // The containing block is not a block, so it's probably something // like a XUL box, etc. // Just use the placeholder's block-offset - aHypotheticalBox.mBStart = placeholderOffset.B(wm); + aHypotheticalPos.mBStart = placeholderOffset.B(wm); } - // Second, determine the hypothetical box's mIStart & mIEnd. + // Second, determine the hypothetical box's mIStart. // How we determine the hypothetical box depends on whether the element // would have been inline-level or block-level if (mStyleDisplay->IsOriginalDisplayInlineOutsideStyle()) { // The placeholder represents the left edge of the hypothetical box - aHypotheticalBox.mIStart = placeholderOffset.I(wm); + aHypotheticalPos.mIStart = placeholderOffset.I(wm); } else { - aHypotheticalBox.mIStart = blockIStartContentEdge; - } -#ifdef DEBUG - aHypotheticalBox.mIStartIsExact = true; -#endif - - if (knowBoxISize) { - aHypotheticalBox.mIEnd = aHypotheticalBox.mIStart + boxISize; -#ifdef DEBUG - aHypotheticalBox.mIEndIsExact = true; -#endif - } else { - // We can't compute the inline-end edge because we don't know the desired - // inline-size. So instead use the end content edge of the block parent, - // but remember it's not exact - aHypotheticalBox.mIEnd = - blockIStartContentEdge + blockContentSize.ISize(wm); -#ifdef DEBUG - aHypotheticalBox.mIEndIsExact = false; -#endif + aHypotheticalPos.mIStart = blockIStartContentEdge; } // The current coordinate space is that of the nearest block to the placeholder. @@ -1437,9 +1419,8 @@ nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, } nsSize cbrsSize = cbrs->ComputedSizeAsContainerIfConstrained(); LogicalPoint logCBOffs(wm, cbOffset, cbrsSize - containerSize); - aHypotheticalBox.mIStart += logCBOffs.I(wm); - aHypotheticalBox.mIEnd += logCBOffs.I(wm); - aHypotheticalBox.mBStart += logCBOffs.B(wm); + aHypotheticalPos.mIStart += logCBOffs.I(wm); + aHypotheticalPos.mBStart += logCBOffs.B(wm); // The specified offsets are relative to the absolute containing block's // padding edge and our current values are relative to the border edge, so @@ -1447,20 +1428,19 @@ nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, LogicalMargin border = cbrs->ComputedLogicalBorderPadding() - cbrs->ComputedLogicalPadding(); border = border.ConvertTo(wm, cbrs->GetWritingMode()); - aHypotheticalBox.mIStart -= border.IStart(wm); - aHypotheticalBox.mIEnd -= border.IStart(wm); - aHypotheticalBox.mBStart -= border.BStart(wm); + aHypotheticalPos.mIStart -= border.IStart(wm); + aHypotheticalPos.mBStart -= border.BStart(wm); - // At this point, we have computed aHypotheticalBox using the writing mode + // At this point, we have computed aHypotheticalPos using the writing mode // of the placeholder's containing block. if (cbwm.GetBlockDir() != wm.GetBlockDir()) { - // If the block direction we used in calculating aHypotheticalBox does not + // If the block direction we used in calculating aHypotheticalPos does not // match the absolute containing block's, we need to convert here so that - // aHypotheticalBox is usable in relation to the absolute containing block. + // aHypotheticalPos is usable in relation to the absolute containing block. // This requires computing or measuring the abspos frame's block-size, - // which is not otherwise required/used here (as aHypotheticalBox records - // only the block-start coordinate). + // which is not otherwise required/used here (as aHypotheticalPos + // records only the block-start coordinate). // This is similar to the inline-size calculation for a replaced // inline-level element or a block-level element (above), except that @@ -1502,21 +1482,16 @@ nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, LogicalSize boxSize(wm, knowBoxISize ? boxISize : 0, boxBSize); - LogicalPoint origin(wm, aHypotheticalBox.mIStart, - aHypotheticalBox.mBStart); + LogicalPoint origin(wm, aHypotheticalPos.mIStart, + aHypotheticalPos.mBStart); origin = origin.ConvertTo(cbwm, wm, cbrsSize - boxSize.GetPhysicalSize(wm)); - aHypotheticalBox.mIStart = origin.I(cbwm); - aHypotheticalBox.mIEnd = aHypotheticalBox.mIStart + - boxSize.ConvertTo(cbwm, wm).ISize(cbwm); -#ifdef DEBUG - aHypotheticalBox.mIEndIsExact = false; // it may be fake -#endif - aHypotheticalBox.mBStart = origin.B(cbwm); - aHypotheticalBox.mWritingMode = cbwm; + aHypotheticalPos.mIStart = origin.I(cbwm); + aHypotheticalPos.mBStart = origin.B(cbwm); + aHypotheticalPos.mWritingMode = cbwm; } else { - aHypotheticalBox.mWritingMode = wm; + aHypotheticalPos.mWritingMode = wm; } } @@ -1543,15 +1518,15 @@ nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext, NS_ASSERTION(nullptr != placeholderFrame, "no placeholder frame"); // If both 'left' and 'right' are 'auto' or both 'top' and 'bottom' are - // 'auto', then compute the hypothetical box of where the element would + // 'auto', then compute the hypothetical box position where the element would // have been if it had been in the flow - nsHypotheticalBox hypotheticalBox; + nsHypotheticalPosition hypotheticalPos; if (((eStyleUnit_Auto == mStylePosition->mOffset.GetLeftUnit()) && (eStyleUnit_Auto == mStylePosition->mOffset.GetRightUnit())) || ((eStyleUnit_Auto == mStylePosition->mOffset.GetTopUnit()) && (eStyleUnit_Auto == mStylePosition->mOffset.GetBottomUnit()))) { - CalculateHypotheticalBox(aPresContext, placeholderFrame, cbrs, - hypotheticalBox, aFrameType); + CalculateHypotheticalPosition(aPresContext, placeholderFrame, cbrs, + hypotheticalPos, aFrameType); } // Initialize the 'left' and 'right' computed offsets @@ -1583,13 +1558,11 @@ nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext, } if (iStartIsAuto && iEndIsAuto) { - NS_ASSERTION(hypotheticalBox.mIStartIsExact, "should always have " - "exact value on containing block's start side"); - if (cbwm.IsBidiLTR() != hypotheticalBox.mWritingMode.IsBidiLTR()) { - offsets.IEnd(cbwm) = hypotheticalBox.mIStart; + if (cbwm.IsBidiLTR() != hypotheticalPos.mWritingMode.IsBidiLTR()) { + offsets.IEnd(cbwm) = hypotheticalPos.mIStart; iEndIsAuto = false; } else { - offsets.IStart(cbwm) = hypotheticalBox.mIStart; + offsets.IStart(cbwm) = hypotheticalPos.mIStart; iStartIsAuto = false; } } @@ -1613,7 +1586,7 @@ nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext, if (bStartIsAuto && bEndIsAuto) { // Treat 'top' like 'static-position' - offsets.BStart(cbwm) = hypotheticalBox.mBStart; + offsets.BStart(cbwm) = hypotheticalPos.mBStart; bStartIsAuto = false; } @@ -1624,6 +1597,10 @@ nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext, typedef nsIFrame::ComputeSizeFlags ComputeSizeFlags; ComputeSizeFlags computeSizeFlags = ComputeSizeFlags::eDefault; + if (mFlags.mShrinkWrap) { + computeSizeFlags = + ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap); + } if (wm.IsOrthogonalTo(cbwm)) { if (bStartIsAuto || bEndIsAuto) { computeSizeFlags = @@ -2026,11 +2003,11 @@ nsHTMLReflowState::ComputeContainingBlockRectangle( cbSize.ISize(wm) = aContainingBlockRS->frame->ISize(wm) - computedBorder.IStartEnd(wm); NS_ASSERTION(cbSize.ISize(wm) >= 0, - "Negative containing block width!"); + "Negative containing block isize!"); cbSize.BSize(wm) = aContainingBlockRS->frame->BSize(wm) - computedBorder.BStartEnd(wm); NS_ASSERTION(cbSize.BSize(wm) >= 0, - "Negative containing block height!"); + "Negative containing block bsize!"); } else { // If the ancestor is block-level, the containing block is formed by the // padding edge of the ancestor @@ -2254,11 +2231,12 @@ nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext, // this MUST come after we've computed our border and padding. ComputeMinMaxValues(cbSize); - // Calculate the computed width and blockSize. This varies by frame type + // Calculate the computed inlineSize and blockSize. + // This varies by frame type. if (NS_CSS_FRAME_TYPE_INTERNAL_TABLE == mFrameType) { // Internal table elements. The rules vary depending on the type. - // Calculate the computed width + // Calculate the computed isize bool rowOrRowGroup = false; const nsStyleCoord &inlineSize = mStylePosition->ISize(wm); nsStyleUnit inlineSizeUnit = inlineSize.GetUnit(); @@ -2281,11 +2259,11 @@ nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext, if (ComputedISize() < 0) ComputedISize() = 0; } - NS_ASSERTION(ComputedISize() >= 0, "Bogus computed width"); + NS_ASSERTION(ComputedISize() >= 0, "Bogus computed isize"); } else { NS_ASSERTION(inlineSizeUnit == inlineSize.GetUnit(), - "unexpected width unit change"); + "unexpected inline size unit change"); ComputedISize() = ComputeISizeValue(cbSize.ISize(wm), mStylePosition->mBoxSizing, inlineSize); @@ -2323,37 +2301,57 @@ nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext, typedef nsIFrame::ComputeSizeFlags ComputeSizeFlags; ComputeSizeFlags computeSizeFlags = isBlock ? ComputeSizeFlags::eDefault : ComputeSizeFlags::eShrinkWrap; - - // Make sure legend frames with display:block and width:auto still - // shrink-wrap. - // Also shrink-wrap blocks that are orthogonal to their container. - if (isBlock && - ((aFrameType == nsGkAtoms::legendFrame && - frame->StyleContext()->GetPseudo() != nsCSSAnonBoxes::scrolledContent) || - (aFrameType == nsGkAtoms::scrollFrame && - frame->GetContentInsertionFrame()->GetType() == nsGkAtoms::legendFrame) || - (mCBReflowState && - mCBReflowState->GetWritingMode().IsOrthogonalTo(mWritingMode)))) { + if (mFlags.mShrinkWrap) { computeSizeFlags = ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap); } nsIFrame* parent = frame->GetParent(); nsIAtom* parentFrameType = parent ? parent->GetType() : nullptr; - if (parentFrameType == nsGkAtoms::flexContainerFrame) { - computeSizeFlags = - ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap); - - // If we're inside of a flex container that needs to measure our - // auto height, pass that information along to ComputeSize(). - if (mFlags.mIsFlexContainerMeasuringHeight) { + if (parentFrameType == nsGkAtoms::gridContainerFrame) { + // Shrink-wrap grid items that will be aligned (rather than stretched) + // in its inline axis. + auto inlineAxisAlignment = wm.IsOrthogonalTo(cbwm) ? + mStylePosition->ComputedAlignSelf(mStyleDisplay, + frame->StyleContext()->GetParent()) : + mStylePosition->ComputedJustifySelf(mStyleDisplay, + frame->StyleContext()->GetParent()); + if (inlineAxisAlignment != NS_STYLE_ALIGN_STRETCH || + mStyleMargin->mMargin.GetIStartUnit(wm) == eStyleUnit_Auto || + mStyleMargin->mMargin.GetIEndUnit(wm) == eStyleUnit_Auto) { computeSizeFlags = - ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eUseAutoHeight); + ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap); } } else { - MOZ_ASSERT(!mFlags.mIsFlexContainerMeasuringHeight, - "We're not in a flex container, so the flag " - "'mIsFlexContainerMeasuringHeight' shouldn't be set"); + // Make sure legend frames with display:block and width:auto still + // shrink-wrap. + // Also shrink-wrap blocks that are orthogonal to their container. + if (isBlock && + ((aFrameType == nsGkAtoms::legendFrame && + frame->StyleContext()->GetPseudo() != nsCSSAnonBoxes::scrolledContent) || + (aFrameType == nsGkAtoms::scrollFrame && + frame->GetContentInsertionFrame()->GetType() == nsGkAtoms::legendFrame) || + (mCBReflowState && + mCBReflowState->GetWritingMode().IsOrthogonalTo(mWritingMode)))) { + computeSizeFlags = + ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap); + } + + if (parentFrameType == nsGkAtoms::flexContainerFrame) { + computeSizeFlags = + ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap); + + // If we're inside of a flex container that needs to measure our + // auto height, pass that information along to ComputeSize(). + if (mFlags.mIsFlexContainerMeasuringHeight) { + computeSizeFlags = + ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eUseAutoHeight); + } + } else { + MOZ_ASSERT(!mFlags.mIsFlexContainerMeasuringHeight, + "We're not in a flex container, so the flag " + "'mIsFlexContainerMeasuringHeight' shouldn't be set"); + } } if (cbSize.ISize(wm) == NS_UNCONSTRAINEDSIZE) { diff --git a/layout/generic/nsHTMLReflowState.h b/layout/generic/nsHTMLReflowState.h index 1c8ca63e8f..f8a02593ad 100644 --- a/layout/generic/nsHTMLReflowState.h +++ b/layout/generic/nsHTMLReflowState.h @@ -19,7 +19,7 @@ class nsRenderingContext; class nsFloatManager; class nsLineLayout; class nsIPercentBSizeObserver; -struct nsHypotheticalBox; +struct nsHypotheticalPosition; /** * @return aValue clamped to [aMinValue, aMaxValue]. @@ -583,6 +583,7 @@ public: // but its in a paginated environment // (e.g. columns), it should always // reflow its placeholder children. + uint16_t mShrinkWrap:1; // stores the COMPUTE_SIZE_SHRINK_WRAP ctor flag } mFlags; // Logical and physical accessors for the resize flags. All users should go @@ -673,7 +674,11 @@ public: // Indicates that the calling function will initialize the reflow state, and // that the constructor should not call Init(). - CALLER_WILL_INIT = (1<<1) + CALLER_WILL_INIT = (1<<1), + + // The caller wants shrink-wrap behavior (i.e. ComputeSizeFlags::eShrinkWrap + // will be passed to ComputeSize()). + COMPUTE_SIZE_SHRINK_WRAP = (1<<2), }; // This method initializes various data members. It is automatically @@ -918,14 +923,14 @@ protected: // Calculate a "hypothetical box" position where the placeholder frame // (for a position:fixed/absolute element) would have been placed if it were - // positioned statically. The hypothetical box will have a writing mode with - // the same block direction as the absolute containing block (cbrs->frame), - // though it may differ in inline-bidi direction. - void CalculateHypotheticalBox(nsPresContext* aPresContext, - nsIFrame* aPlaceholderFrame, - const nsHTMLReflowState* cbrs, - nsHypotheticalBox& aHypotheticalBox, - nsIAtom* aFrameType); + // positioned statically. The hypothetical box position will have a writing + // mode with the same block direction as the absolute containing block + // (cbrs->frame), though it may differ in inline direction. + void CalculateHypotheticalPosition(nsPresContext* aPresContext, + nsIFrame* aPlaceholderFrame, + const nsHTMLReflowState* cbrs, + nsHypotheticalPosition& aHypotheticalPos, + nsIAtom* aFrameType); void InitAbsoluteConstraints(nsPresContext* aPresContext, const nsHTMLReflowState* cbrs, diff --git a/layout/generic/nsLineLayout.cpp b/layout/generic/nsLineLayout.cpp index 31cf0ea5ba..18bcf0b260 100644 --- a/layout/generic/nsLineLayout.cpp +++ b/layout/generic/nsLineLayout.cpp @@ -1435,11 +1435,17 @@ nsLineLayout::PlaceFrame(PerFrameData* pfd, nsHTMLReflowMetrics& aMetrics) { WritingMode lineWM = mRootSpan->mWritingMode; - // Record ascent and update max-ascent and max-descent values - if (aMetrics.BlockStartAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) { - pfd->mAscent = pfd->mFrame->GetLogicalBaseline(lineWM); + // If the frame's block direction does not match the line's, we can't use + // its ascent; instead, treat it as a block with baseline at the block-end + // edge (or block-begin in the case of an "inverted" line). + if (pfd->mFrame->GetWritingMode().GetBlockDir() != lineWM.GetBlockDir()) { + pfd->mAscent = lineWM.IsLineInverted() ? 0 : aMetrics.BSize(lineWM); } else { - pfd->mAscent = aMetrics.BlockStartAscent(); + if (aMetrics.BlockStartAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) { + pfd->mAscent = pfd->mFrame->GetLogicalBaseline(lineWM); + } else { + pfd->mAscent = aMetrics.BlockStartAscent(); + } } // Advance to next inline coordinate @@ -2199,9 +2205,18 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd) // "raises" the box by the given distance while a negative value // "lowers" the box by the given distance (with zero being the // baseline). Since Y coordinates increase towards the bottom of - // the screen we reverse the sign. - nscoord revisedBaselineBCoord = baselineBCoord - offset; - pfd->mBounds.BStart(lineWM) = revisedBaselineBCoord - pfd->mAscent; + // the screen we reverse the sign, unless the line orientation is + // inverted relative to block direction. + nscoord revisedBaselineBCoord = baselineBCoord - offset * + lineWM.FlowRelativeToLineRelativeFactor(); + if (lineWM.IsVertical() && !lineWM.IsSideways()) { + // If we're using a dominant center baseline, we align with the center + // of the frame being placed (bug 1133945). + pfd->mBounds.BStart(lineWM) = + revisedBaselineBCoord - pfd->mBounds.BSize(lineWM)/2; + } else { + pfd->mBounds.BStart(lineWM) = revisedBaselineBCoord - pfd->mAscent; + } pfd->mBlockDirAlign = VALIGN_OTHER; } @@ -2958,7 +2973,7 @@ FindNearestRubyBaseAncestor(nsIFrame* aFrame) // XXX It is possible that no ruby base ancestor is found because of // some edge cases like form control or canvas inside ruby text. // See bug 1138092 comment 4. - NS_ASSERTION(aFrame, "No ruby base ancestor?"); + NS_WARN_IF_FALSE(aFrame, "no ruby base ancestor?"); return aFrame; } diff --git a/layout/generic/nsLineLayout.h b/layout/generic/nsLineLayout.h index 8c799d3668..3fa369c614 100644 --- a/layout/generic/nsLineLayout.h +++ b/layout/generic/nsLineLayout.h @@ -404,12 +404,12 @@ protected: // XXX remove this when landing bug 154892 (splitting absolute positioned frames) friend class nsInlineFrame; - nsBlockReflowState* mBlockRS;/* XXX hack! */ - // XXX Take care that nsRubyBaseContainer would give nullptr to this // member. It should not be a problem currently, since the only // code use it is handling float, which does not affect ruby. // See comment in nsLineLayout::AddFloat + nsBlockReflowState* mBlockRS;/* XXX hack! */ + nsLineList::iterator mLineBox; // Per-frame data recorded by the line-layout reflow logic. This diff --git a/layout/generic/nsRubyFrame.cpp b/layout/generic/nsRubyFrame.cpp index 0b6409f48c..9b55ae973d 100644 --- a/layout/generic/nsRubyFrame.cpp +++ b/layout/generic/nsRubyFrame.cpp @@ -340,9 +340,10 @@ nsRubyFrame::ReflowSegment(nsPresContext* aPresContext, // Set block leadings of the base container nscoord startLeading = baseRect.BStart(lineWM) - offsetRect.BStart(lineWM); nscoord endLeading = offsetRect.BEnd(lineWM) - baseRect.BEnd(lineWM); - NS_ASSERTION(startLeading >= 0 && endLeading >= 0, - "Leadings should be non-negative (because adding " - "ruby annotation can only increase the size)"); + // XXX When bug 765861 gets fixed, this warning should be upgraded. + NS_WARN_IF_FALSE(startLeading >= 0 && endLeading >= 0, + "Leadings should be non-negative (because adding " + "ruby annotation can only increase the size)"); mBStartLeading = std::max(mBStartLeading, startLeading); mBEndLeading = std::max(mBEndLeading, endLeading); } diff --git a/layout/generic/nsSelection.cpp b/layout/generic/nsSelection.cpp index 78fe794ddf..801af00010 100644 --- a/layout/generic/nsSelection.cpp +++ b/layout/generic/nsSelection.cpp @@ -1914,29 +1914,21 @@ nsFrameSelection::GetFrameForNodeOffset(nsIContent* aNode, theNode = childNode; } -#ifdef DONT_DO_THIS_YET - // XXX: We can't use this code yet because the hinting - // can cause us to attach to the wrong line frame. - // Now that we have the child node, check if it too // can contain children. If so, call this method again! - - if (theNode->IsElement()) + if (theNode->IsElement() && + theNode->GetChildCount() && + !theNode->HasIndependentSelection()) { int32_t newOffset = 0; - if (aOffset > childIndex) - { + if (aOffset > childIndex) { numChildren = theNode->GetChildCount(); - newOffset = numChildren; } return GetFrameForNodeOffset(theNode, newOffset, aHint, aReturnOffset); - } - else -#endif // DONT_DO_THIS_YET - { + } else { // Check to see if theNode is a text node. If it is, translate // aOffset into an offset into the text node. @@ -1958,13 +1950,25 @@ nsFrameSelection::GetFrameForNodeOffset(nsIContent* aNode, } else *aReturnOffset = 0; - } - else - { - // If we're at a collapsed whitespace content node (which - // does not have a primary frame), just use the original node - // to get the frame on which we should put the caret. - theNode = aNode; + } else { + int32_t numChildren = aNode->GetChildCount(); + int32_t newChildIndex = + aHint == CARET_ASSOCIATE_BEFORE ? childIndex - 1 : childIndex + 1; + + if (newChildIndex >= 0 && newChildIndex < numChildren) { + nsCOMPtr newChildNode = aNode->GetChildAt(newChildIndex); + if (!newChildNode) + return nullptr; + + theNode = newChildNode; + int32_t newOffset = + aHint == CARET_ASSOCIATE_BEFORE ? theNode->GetChildCount() : 0; + return GetFrameForNodeOffset(theNode, newOffset, aHint, aReturnOffset); + } else { + // newChildIndex is illegal which means we're at first or last + // child. Just use original node to get the frame. + theNode = aNode; + } } } } diff --git a/layout/generic/nsViewportFrame.cpp b/layout/generic/nsViewportFrame.cpp index de0a5c3f01..15f80eee7a 100644 --- a/layout/generic/nsViewportFrame.cpp +++ b/layout/generic/nsViewportFrame.cpp @@ -18,6 +18,7 @@ #include "nsIMozBrowserFrame.h" using namespace mozilla; +typedef nsAbsoluteContainingBlock::AbsPosReflowFlags AbsPosReflowFlags; ViewportFrame* NS_NewViewportFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) @@ -350,10 +351,10 @@ ViewportFrame::Reflow(nsPresContext* aPresContext, if (rootScrollFrame && !rootScrollFrame->IsIgnoringViewportClipping()) { overflowAreas = nullptr; } + AbsPosReflowFlags flags = + AbsPosReflowFlags::eCBWidthAndHeightChanged; // XXX could be optimized GetAbsoluteContainingBlock()->Reflow(this, aPresContext, reflowState, aStatus, - rect, - false, true, true, // XXX could be optimized - overflowAreas); + rect, flags, overflowAreas); } if (mFrames.NotEmpty()) { diff --git a/layout/inspector/inDOMUtils.cpp b/layout/inspector/inDOMUtils.cpp index 11188b3260..ee977a0ab0 100644 --- a/layout/inspector/inDOMUtils.cpp +++ b/layout/inspector/inDOMUtils.cpp @@ -318,7 +318,7 @@ inDOMUtils::GetRelativeRuleLine(nsIDOMCSSRule* aRule, uint32_t* _retval) uint32_t lineNumber = rule->GetLineNumber(); CSSStyleSheet* sheet = rule->GetStyleSheet(); - if (sheet) { + if (sheet && lineNumber != 0) { nsINode* owningNode = sheet->GetOwnerNode(); if (owningNode) { nsCOMPtr link = @@ -1187,6 +1187,29 @@ GetStatesForPseudoClass(const nsAString& aStatePseudo) return sPseudoClassStates[nsCSSPseudoClasses::GetPseudoType(atom)]; } +NS_IMETHODIMP +inDOMUtils::GetCSSPseudoElementNames(uint32_t* aLength, char16_t*** aNames) +{ + nsTArray array; + + for (int i = 0; i < nsCSSPseudoElements::ePseudo_PseudoElementCount; ++i) { + nsCSSPseudoElements::Type type = static_cast(i); + if (!nsCSSPseudoElements::PseudoElementIsUASheetOnly(type)) { + nsIAtom* atom = nsCSSPseudoElements::GetPseudoAtom(type); + array.AppendElement(atom); + } + } + + *aLength = array.Length(); + char16_t** ret = + static_cast(moz_xmalloc(*aLength * sizeof(char16_t*))); + for (uint32_t i = 0; i < *aLength; ++i) { + ret[i] = ToNewUnicode(nsDependentAtomString(array[i])); + } + *aNames = ret; + return NS_OK; +} + NS_IMETHODIMP inDOMUtils::AddPseudoClassLock(nsIDOMElement *aElement, const nsAString &aPseudoClass) diff --git a/layout/inspector/inIDOMUtils.idl b/layout/inspector/inIDOMUtils.idl index da6c1c03ac..ddc79710ad 100644 --- a/layout/inspector/inIDOMUtils.idl +++ b/layout/inspector/inIDOMUtils.idl @@ -17,7 +17,7 @@ interface nsIDOMFontFaceList; interface nsIDOMRange; interface nsIDOMCSSStyleSheet; -[scriptable, uuid(60b4cbf7-2a08-4419-8937-6ef495417824)] +[scriptable, uuid(ec3dc3d5-41d1-4d08-ace5-7e944de6614d)] interface inIDOMUtils : nsISupports { // CSS utilities @@ -162,6 +162,17 @@ interface inIDOMUtils : nsISupports nsIDOMFontFaceList getUsedFontFaces(in nsIDOMRange aRange); + /** + * Get the names of all the supported pseudo-elements. + * Pseudo-elements which are only accepted in UA style sheets are + * not included. + * + * @param {unsigned long} aCount the number of items returned + * @param {wstring[]} aNames the names + */ + void getCSSPseudoElementNames([optional] out unsigned long aCount, + [retval, array, size_is(aCount)] out wstring aNames); + // pseudo-class style locking methods. aPseudoClass must be a valid pseudo-class // selector string, e.g. ":hover". ":-moz-any-link" and non-event-state // pseudo-classes are ignored. diff --git a/layout/inspector/tests/mochitest.ini b/layout/inspector/tests/mochitest.ini index 4dd9acdd3a..25209f57a6 100644 --- a/layout/inspector/tests/mochitest.ini +++ b/layout/inspector/tests/mochitest.ini @@ -19,6 +19,7 @@ support-files = [test_color_to_rgba.html] [test_css_property_is_shorthand.html] [test_css_property_is_valid.html] +[test_getCSSPseudoElementNames.html] [test_getRelativeRuleLine.html] [test_get_all_style_sheets.html] [test_is_valid_css_color.html] diff --git a/layout/inspector/tests/test_getCSSPseudoElementNames.html b/layout/inspector/tests/test_getCSSPseudoElementNames.html new file mode 100644 index 0000000000..6085a018c9 --- /dev/null +++ b/layout/inspector/tests/test_getCSSPseudoElementNames.html @@ -0,0 +1,57 @@ + + + + + Test inDOMUtils::getCSSPseudoElementNames + + + + + +

Test inDOMUtils::getCSSPseudoElementNames

+

+ +
+
+ + diff --git a/layout/inspector/tests/test_getRelativeRuleLine.html b/layout/inspector/tests/test_getRelativeRuleLine.html index ba44f3a7fe..a1481a49d0 100644 --- a/layout/inspector/tests/test_getRelativeRuleLine.html +++ b/layout/inspector/tests/test_getRelativeRuleLine.html @@ -34,10 +34,12 @@ { sheetNo: 1, ruleNo: 0, lineNo: 2, columnNo: 15 }, { sheetNo: 1, ruleNo: 1, lineNo: 8, columnNo: 5 }, { sheetNo: 2, ruleNo: 0, lineNo: 1, columnNo: 1 }, + { sheetNo: 2, ruleNo: 1, lineNo: 0, columnNo: 1 }, { sheetNo: 3, ruleNo: 0, lineNo: 5, columnNo: 6 }, ]; function doTest() { + document.styleSheets[2].insertRule("body{}", 1); for (let test of tests) { let sheet = document.styleSheets[test.sheetNo]; let rule = sheet.cssRules[test.ruleNo]; diff --git a/layout/reftests/bugs/1209994-1-ref.html b/layout/reftests/bugs/1209994-1-ref.html new file mode 100644 index 0000000000..1b6f7cdd46 --- /dev/null +++ b/layout/reftests/bugs/1209994-1-ref.html @@ -0,0 +1,17 @@ + + +There should be a visible fieldset below: +
+
This is fieldset
+
+ diff --git a/layout/reftests/bugs/1209994-1.html b/layout/reftests/bugs/1209994-1.html new file mode 100644 index 0000000000..d362c00872 --- /dev/null +++ b/layout/reftests/bugs/1209994-1.html @@ -0,0 +1,21 @@ + + +There should be a visible fieldset below: +
+
This is fieldset
+
+ + diff --git a/layout/reftests/bugs/1209994-2-ref.html b/layout/reftests/bugs/1209994-2-ref.html new file mode 100644 index 0000000000..8858616b69 --- /dev/null +++ b/layout/reftests/bugs/1209994-2-ref.html @@ -0,0 +1,17 @@ + + +There should be a visible button below: +
+ +
+ diff --git a/layout/reftests/bugs/1209994-2.html b/layout/reftests/bugs/1209994-2.html new file mode 100644 index 0000000000..485b0fe585 --- /dev/null +++ b/layout/reftests/bugs/1209994-2.html @@ -0,0 +1,21 @@ + + +There should be a visible button below: +
+ +
+ + diff --git a/layout/reftests/bugs/1209994-3-ref.html b/layout/reftests/bugs/1209994-3-ref.html new file mode 100644 index 0000000000..bd219e03ae --- /dev/null +++ b/layout/reftests/bugs/1209994-3-ref.html @@ -0,0 +1,17 @@ + + +There should be a visible table below: +
+ This is a table
+
+ diff --git a/layout/reftests/bugs/1209994-3.html b/layout/reftests/bugs/1209994-3.html new file mode 100644 index 0000000000..94df02fabb --- /dev/null +++ b/layout/reftests/bugs/1209994-3.html @@ -0,0 +1,21 @@ + + +There should be a visible table below: +
+ This is a table
+
+ + diff --git a/layout/reftests/bugs/1209994-4-ref.html b/layout/reftests/bugs/1209994-4-ref.html new file mode 100644 index 0000000000..2f15740fc1 --- /dev/null +++ b/layout/reftests/bugs/1209994-4-ref.html @@ -0,0 +1,17 @@ + + +There should be a visible select below: +
+ +
+ diff --git a/layout/reftests/bugs/1209994-4.html b/layout/reftests/bugs/1209994-4.html new file mode 100644 index 0000000000..95e2d062fa --- /dev/null +++ b/layout/reftests/bugs/1209994-4.html @@ -0,0 +1,21 @@ + + +There should be a visible select below: +
+ +
+ + diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index 3ad74f1c81..c2832ebd9c 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1935,4 +1935,8 @@ fuzzy(1,74) fuzzy-if(gtkWidget,6,79) == 1174332-1.html 1174332-1-ref.html == 1190635-1.html 1190635-1-ref.html != 1207326-1.html about:blank == 1209603-1.html 1209603-1-ref.html +== 1209994-1.html 1209994-1-ref.html +== 1209994-2.html 1209994-2-ref.html +== 1209994-3.html 1209994-3-ref.html +== 1209994-4.html 1209994-4-ref.html == 1222226-1.html 1222226-1-ref.html diff --git a/layout/reftests/css-grid/grid-abspos-items-001-ref.html b/layout/reftests/css-grid/grid-abspos-items-001-ref.html index f4a05afe7c..0fc6afed8d 100644 --- a/layout/reftests/css-grid/grid-abspos-items-001-ref.html +++ b/layout/reftests/css-grid/grid-abspos-items-001-ref.html @@ -163,17 +163,17 @@ span {
-a -b -c -d +a +b +c +d
- +
- +
diff --git a/layout/reftests/css-grid/grid-abspos-items-002-ref.html b/layout/reftests/css-grid/grid-abspos-items-002-ref.html index c031bd734b..f084d630ca 100644 --- a/layout/reftests/css-grid/grid-abspos-items-002-ref.html +++ b/layout/reftests/css-grid/grid-abspos-items-002-ref.html @@ -164,10 +164,10 @@ span {
-a -b -c -d +a +b +c +d
diff --git a/layout/reftests/css-grid/grid-abspos-items-011-ref.html b/layout/reftests/css-grid/grid-abspos-items-011-ref.html deleted file mode 100644 index 92e540563e..0000000000 --- a/layout/reftests/css-grid/grid-abspos-items-011-ref.html +++ /dev/null @@ -1,54 +0,0 @@ - - - - - CSS Grid Test: abs pos areas in empty grid - - - - - -There should be no red areas. -
- -
-
-
-
- -
-
-
-
-
-
- -
-
-
-
- -
- - - diff --git a/layout/reftests/css-grid/grid-abspos-items-011.html b/layout/reftests/css-grid/grid-abspos-items-011.html deleted file mode 100644 index cc514b7a4b..0000000000 --- a/layout/reftests/css-grid/grid-abspos-items-011.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - CSS Grid Test: abs pos areas in empty grid - - - - - - - -There should be no red areas. -
- -
-
-
-
- -
-
-
-
-
-
- -
-
-
-
- -
- - - diff --git a/layout/reftests/css-grid/grid-abspos-items-012-ref.html b/layout/reftests/css-grid/grid-abspos-items-012-ref.html new file mode 100644 index 0000000000..e0feef2051 --- /dev/null +++ b/layout/reftests/css-grid/grid-abspos-items-012-ref.html @@ -0,0 +1,45 @@ + + + + + Reference: margin:auto on grid abs.pos. child + + + + + +
+
+
+
+
+
+
+ + + diff --git a/layout/reftests/css-grid/grid-abspos-items-012.html b/layout/reftests/css-grid/grid-abspos-items-012.html new file mode 100644 index 0000000000..69c96640c1 --- /dev/null +++ b/layout/reftests/css-grid/grid-abspos-items-012.html @@ -0,0 +1,47 @@ + + + + + CSS Grid Test: margin:auto on grid abs.pos. child + + + + + + + +
+
+
+
+
+
+
+ + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-auto-001-ref.html b/layout/reftests/css-grid/grid-item-margin-left-auto-001-ref.html new file mode 100644 index 0000000000..a7892746df --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-auto-001-ref.html @@ -0,0 +1,117 @@ + + + + + Reference: margin-left:auto + justify-self + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-auto-001.html b/layout/reftests/css-grid/grid-item-margin-left-auto-001.html new file mode 100644 index 0000000000..663f0ddec7 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-auto-001.html @@ -0,0 +1,116 @@ + + + + + CSS Grid Test: margin-left:auto + justify-self + + + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-auto-002-ref.html b/layout/reftests/css-grid/grid-item-margin-left-auto-002-ref.html new file mode 100644 index 0000000000..6319ef310b --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-auto-002-ref.html @@ -0,0 +1,115 @@ + + + + + Reference: margin-left:auto + justify-self + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-auto-002.html b/layout/reftests/css-grid/grid-item-margin-left-auto-002.html new file mode 100644 index 0000000000..e68ede0132 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-auto-002.html @@ -0,0 +1,116 @@ + + + + + CSS Grid Test: margin-left:auto + justify-self + + + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-auto-003-ref.html b/layout/reftests/css-grid/grid-item-margin-left-auto-003-ref.html new file mode 100644 index 0000000000..c7f1979279 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-auto-003-ref.html @@ -0,0 +1,116 @@ + + + + + Reference: margin-left:auto + align-self + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-auto-003.html b/layout/reftests/css-grid/grid-item-margin-left-auto-003.html new file mode 100644 index 0000000000..353f8f02e0 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-auto-003.html @@ -0,0 +1,115 @@ + + + + + CSS Grid Test: margin-left:auto + align-self + + + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-auto-004-ref.html b/layout/reftests/css-grid/grid-item-margin-left-auto-004-ref.html new file mode 100644 index 0000000000..4501f52fbc --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-auto-004-ref.html @@ -0,0 +1,117 @@ + + + + + Reference: margin-left:auto + align-self + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-auto-004.html b/layout/reftests/css-grid/grid-item-margin-left-auto-004.html new file mode 100644 index 0000000000..3eef662a43 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-auto-004.html @@ -0,0 +1,116 @@ + + + + + CSS Grid Test: margin-left:auto + align-self + + + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-right-auto-001-ref.html b/layout/reftests/css-grid/grid-item-margin-left-right-auto-001-ref.html new file mode 100644 index 0000000000..b48f2e7156 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-right-auto-001-ref.html @@ -0,0 +1,117 @@ + + + + + Reference: margin-left/right:auto + justify-self + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-right-auto-001.html b/layout/reftests/css-grid/grid-item-margin-left-right-auto-001.html new file mode 100644 index 0000000000..fb198f796f --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-right-auto-001.html @@ -0,0 +1,117 @@ + + + + + CSS Grid Test: margin-left/right:auto + justify-self + + + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-right-auto-002-ref.html b/layout/reftests/css-grid/grid-item-margin-left-right-auto-002-ref.html new file mode 100644 index 0000000000..dd2006d96c --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-right-auto-002-ref.html @@ -0,0 +1,115 @@ + + + + + Reference: margin-left/right:auto + justify-self + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-right-auto-002.html b/layout/reftests/css-grid/grid-item-margin-left-right-auto-002.html new file mode 100644 index 0000000000..3dc049dbe9 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-right-auto-002.html @@ -0,0 +1,117 @@ + + + + + CSS Grid Test: margin-left/right:auto + justify-self + + + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-right-auto-003-ref.html b/layout/reftests/css-grid/grid-item-margin-left-right-auto-003-ref.html new file mode 100644 index 0000000000..7391f9e0fc --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-right-auto-003-ref.html @@ -0,0 +1,116 @@ + + + + + Reference: margin-left/right:auto + align-self + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-right-auto-003.html b/layout/reftests/css-grid/grid-item-margin-left-right-auto-003.html new file mode 100644 index 0000000000..4de40bbcc5 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-right-auto-003.html @@ -0,0 +1,116 @@ + + + + + CSS Grid Test: margin-left/right:auto + align-self + + + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-right-auto-004-ref.html b/layout/reftests/css-grid/grid-item-margin-left-right-auto-004-ref.html new file mode 100644 index 0000000000..7463065f83 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-right-auto-004-ref.html @@ -0,0 +1,115 @@ + + + + + Reference: margin-left/right:auto + align-self + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-left-right-auto-004.html b/layout/reftests/css-grid/grid-item-margin-left-right-auto-004.html new file mode 100644 index 0000000000..b4799ab2fc --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-left-right-auto-004.html @@ -0,0 +1,117 @@ + + + + + CSS Grid Test: margin-left/right:auto + align-self + + + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-right-auto-001-ref.html b/layout/reftests/css-grid/grid-item-margin-right-auto-001-ref.html new file mode 100644 index 0000000000..bfdb07aba0 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-right-auto-001-ref.html @@ -0,0 +1,117 @@ + + + + + Reference: margin-right:auto + justify-self + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-right-auto-001.html b/layout/reftests/css-grid/grid-item-margin-right-auto-001.html new file mode 100644 index 0000000000..6803478ba7 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-right-auto-001.html @@ -0,0 +1,116 @@ + + + + + CSS Grid Test: margin-right:auto + justify-self + + + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-right-auto-002-ref.html b/layout/reftests/css-grid/grid-item-margin-right-auto-002-ref.html new file mode 100644 index 0000000000..4b2bd9788b --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-right-auto-002-ref.html @@ -0,0 +1,118 @@ + + + + + Reference: margin-right:auto + justify-self + + + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-right-auto-002.html b/layout/reftests/css-grid/grid-item-margin-right-auto-002.html new file mode 100644 index 0000000000..839ffd8399 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-right-auto-002.html @@ -0,0 +1,116 @@ + + + + + CSS Grid Test: margin-right:auto + justify-self + + + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-right-auto-003-ref.html b/layout/reftests/css-grid/grid-item-margin-right-auto-003-ref.html new file mode 100644 index 0000000000..08a01afca4 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-right-auto-003-ref.html @@ -0,0 +1,116 @@ + + + + + Reference: margin-right:auto + align-self + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-right-auto-003.html b/layout/reftests/css-grid/grid-item-margin-right-auto-003.html new file mode 100644 index 0000000000..ccec8f7dab --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-right-auto-003.html @@ -0,0 +1,115 @@ + + + + + CSS Grid Test: margin-right:auto + align-self + + + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-right-auto-004-ref.html b/layout/reftests/css-grid/grid-item-margin-right-auto-004-ref.html new file mode 100644 index 0000000000..d8efb6a018 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-right-auto-004-ref.html @@ -0,0 +1,117 @@ + + + + + Reference: margin-right:auto + align-self + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-item-margin-right-auto-004.html b/layout/reftests/css-grid/grid-item-margin-right-auto-004.html new file mode 100644 index 0000000000..fc3a2973b5 --- /dev/null +++ b/layout/reftests/css-grid/grid-item-margin-right-auto-004.html @@ -0,0 +1,116 @@ + + + + + CSS Grid Test: margin-right:auto + align-self + + + + + + + + + + + + diff --git a/layout/reftests/css-grid/grid-min-max-content-sizing-001-ref.html b/layout/reftests/css-grid/grid-min-max-content-sizing-001-ref.html index 366ca349ce..608dc25f50 100644 --- a/layout/reftests/css-grid/grid-min-max-content-sizing-001-ref.html +++ b/layout/reftests/css-grid/grid-min-max-content-sizing-001-ref.html @@ -5,7 +5,7 @@ --> - CSS Grid Test: Testing grid minmax(min-content,max-content) column/rows + Reference: Testing grid minmax(min-content,max-content) column/rows + + + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + + diff --git a/layout/reftests/css-grid/grid-min-max-content-sizing-002.html b/layout/reftests/css-grid/grid-min-max-content-sizing-002.html new file mode 100644 index 0000000000..759eb30508 --- /dev/null +++ b/layout/reftests/css-grid/grid-min-max-content-sizing-002.html @@ -0,0 +1,85 @@ + + + + + CSS Grid Test: Testing grid minmax(min-content,max-content) column/rows + + + + + + + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + + diff --git a/layout/reftests/css-grid/reftest.list b/layout/reftests/css-grid/reftest.list index bc380719a4..2122cbae02 100644 --- a/layout/reftests/css-grid/reftest.list +++ b/layout/reftests/css-grid/reftest.list @@ -21,7 +21,7 @@ fails == grid-whitespace-handling-1b.xhtml grid-whitespace-handling-1-ref.xhtml == grid-abspos-items-008.html grid-abspos-items-008-ref.html == grid-abspos-items-009.html grid-abspos-items-009-ref.html == grid-abspos-items-010.html grid-abspos-items-010-ref.html -== grid-abspos-items-011.html grid-abspos-items-011-ref.html +== grid-abspos-items-012.html grid-abspos-items-012-ref.html == grid-order-abspos-items-001.html grid-order-abspos-items-001-ref.html == grid-order-placement-auto-001.html grid-order-placement-auto-001-ref.html == grid-order-placement-definite-001.html grid-order-placement-definite-001-ref.html @@ -40,7 +40,8 @@ pref(layout.css.vertical-text.enabled,true) == vrl-grid-placement-auto-row-spars fuzzy-if(winWidget,70,130) fuzzy-if(cocoaWidget,85,180) == grid-col-max-sizing-max-content-001.html grid-col-max-sizing-max-content-001-ref.html fuzzy-if(winWidget,70,130) fuzzy-if(cocoaWidget,85,180) == grid-col-max-sizing-max-content-002.html grid-col-max-sizing-max-content-002-ref.html == grid-min-max-content-sizing-001.html grid-min-max-content-sizing-001-ref.html -== grid-auto-min-sizing-definite-001.html grid-auto-min-sizing-definite-001-ref.html +== grid-min-max-content-sizing-002.html grid-min-max-content-sizing-002-ref.html +fuzzy-if(winWidget,1,36) == grid-auto-min-sizing-definite-001.html grid-auto-min-sizing-definite-001-ref.html == grid-auto-min-sizing-intrinsic-001.html grid-auto-min-sizing-intrinsic-001-ref.html == grid-auto-min-sizing-intrinsic-002.html grid-auto-min-sizing-intrinsic-002-ref.html == grid-auto-min-sizing-intrinsic-003.html grid-auto-min-sizing-intrinsic-003-ref.html @@ -72,3 +73,15 @@ fuzzy-if(winWidget,70,130) fuzzy-if(cocoaWidget,85,180) == grid-col-max-sizing-m == grid-column-gap-002.html grid-column-gap-002-ref.html == grid-column-gap-003.html grid-column-gap-003-ref.html == grid-row-gap-001-ref.html grid-row-gap-001-ref.html +== grid-item-margin-left-auto-001.html grid-item-margin-left-auto-001-ref.html +== grid-item-margin-left-auto-002.html grid-item-margin-left-auto-002-ref.html +== grid-item-margin-left-auto-003.html grid-item-margin-left-auto-003-ref.html +== grid-item-margin-left-auto-004.html grid-item-margin-left-auto-004-ref.html +== grid-item-margin-left-right-auto-001.html grid-item-margin-left-right-auto-001-ref.html +== grid-item-margin-left-right-auto-002.html grid-item-margin-left-right-auto-002-ref.html +== grid-item-margin-left-right-auto-003.html grid-item-margin-left-right-auto-003-ref.html +== grid-item-margin-left-right-auto-004.html grid-item-margin-left-right-auto-004-ref.html +== grid-item-margin-right-auto-001.html grid-item-margin-right-auto-001-ref.html +== grid-item-margin-right-auto-002.html grid-item-margin-right-auto-002-ref.html +== grid-item-margin-right-auto-003.html grid-item-margin-right-auto-003-ref.html +== grid-item-margin-right-auto-004.html grid-item-margin-right-auto-004-ref.html diff --git a/layout/reftests/forms/input/number/number-similar-to-text-unthemed-vertical-lr-ref.html b/layout/reftests/forms/input/number/number-similar-to-text-unthemed-vertical-lr-ref.html new file mode 100644 index 0000000000..6deed48916 --- /dev/null +++ b/layout/reftests/forms/input/number/number-similar-to-text-unthemed-vertical-lr-ref.html @@ -0,0 +1,8 @@ + + + + + +
+ + diff --git a/layout/reftests/forms/input/number/number-similar-to-text-unthemed-vertical-lr.html b/layout/reftests/forms/input/number/number-similar-to-text-unthemed-vertical-lr.html new file mode 100644 index 0000000000..7a78830629 --- /dev/null +++ b/layout/reftests/forms/input/number/number-similar-to-text-unthemed-vertical-lr.html @@ -0,0 +1,8 @@ + + + + + +
+ + diff --git a/layout/reftests/forms/input/number/number-similar-to-text-unthemed-vertical-rl-ref.html b/layout/reftests/forms/input/number/number-similar-to-text-unthemed-vertical-rl-ref.html new file mode 100644 index 0000000000..73a5373877 --- /dev/null +++ b/layout/reftests/forms/input/number/number-similar-to-text-unthemed-vertical-rl-ref.html @@ -0,0 +1,8 @@ + + + + + +
+ + diff --git a/layout/reftests/forms/input/number/number-similar-to-text-unthemed-vertical-rl.html b/layout/reftests/forms/input/number/number-similar-to-text-unthemed-vertical-rl.html new file mode 100644 index 0000000000..3bc3a388a1 --- /dev/null +++ b/layout/reftests/forms/input/number/number-similar-to-text-unthemed-vertical-rl.html @@ -0,0 +1,8 @@ + + + + + +
+ + diff --git a/layout/reftests/forms/input/number/reftest.list b/layout/reftests/forms/input/number/reftest.list index 3d726c7425..83d734d1fb 100644 --- a/layout/reftests/forms/input/number/reftest.list +++ b/layout/reftests/forms/input/number/reftest.list @@ -10,6 +10,8 @@ skip-if(!Android&&!B2G&&!Mulet) == number-same-as-text-unthemed.html number-same # should look the same as type=text, except for the spin box == number-similar-to-text-unthemed.html number-similar-to-text-unthemed-ref.html == number-similar-to-text-unthemed-rtl.html number-similar-to-text-unthemed-rtl-ref.html +pref(layout.css.vertical-text.enabled,true) == number-similar-to-text-unthemed-vertical-lr.html number-similar-to-text-unthemed-vertical-lr-ref.html +pref(layout.css.vertical-text.enabled,true) == number-similar-to-text-unthemed-vertical-rl.html number-similar-to-text-unthemed-vertical-rl-ref.html # dynamic type changes: fuzzy-if(/^Windows\x20NT\x205\.1/.test(http.oscpu),64,4) fuzzy-if(cocoaWidget,63,4) == to-number-from-other-type-unthemed-1.html to-number-from-other-type-unthemed-1-ref.html diff --git a/layout/reftests/reftest.list b/layout/reftests/reftest.list index 06539424ca..104f0fed51 100644 --- a/layout/reftests/reftest.list +++ b/layout/reftests/reftest.list @@ -311,6 +311,9 @@ include table-bordercollapse/reftest.list # table-dom/ include table-dom/reftest.list +# table-html/ +include table-html/reftest.list + include table-overflow/reftest.list # table-width/ diff --git a/layout/reftests/table-background/reftest.list b/layout/reftests/table-background/reftest.list index 8ad1890efa..e38308551d 100644 --- a/layout/reftests/table-background/reftest.list +++ b/layout/reftests/table-background/reftest.list @@ -58,3 +58,5 @@ skip-if(B2G||Mulet) == border-separate-opacity-table.html border-separate-opacit == scrollable-rowgroup-separate-border.html scrollable-rowgroup-separate-notref.html # scrolling rowgroups were removed in bug 28800 == empty-cells-default-1.html empty-cells-default-1-ref.html == empty-cells-default-2.html empty-cells-default-2-ref.html +fuzzy-if(OSX,1,113) fuzzy-if(winWidget,1,12) fuzzy-if(Android,1,39) == table-row-opacity-dynamic-1.html table-row-opacity-dynamic-1-ref.html +== table-row-opacity-dynamic-2.html table-row-opacity-dynamic-2-ref.html diff --git a/layout/reftests/table-background/table-row-opacity-dynamic-1-ref.html b/layout/reftests/table-background/table-row-opacity-dynamic-1-ref.html new file mode 100644 index 0000000000..e69c3005c1 --- /dev/null +++ b/layout/reftests/table-background/table-row-opacity-dynamic-1-ref.html @@ -0,0 +1,28 @@ + + +Test for bug 1224253 + + + + + +
cell
diff --git a/layout/reftests/table-background/table-row-opacity-dynamic-1.html b/layout/reftests/table-background/table-row-opacity-dynamic-1.html new file mode 100644 index 0000000000..a92b2f8ac4 --- /dev/null +++ b/layout/reftests/table-background/table-row-opacity-dynamic-1.html @@ -0,0 +1,55 @@ + + +Test for bug 1224253 + + + + + +
cell
+ + diff --git a/layout/reftests/table-background/table-row-opacity-dynamic-2-ref.html b/layout/reftests/table-background/table-row-opacity-dynamic-2-ref.html new file mode 100644 index 0000000000..a2faff6fde --- /dev/null +++ b/layout/reftests/table-background/table-row-opacity-dynamic-2-ref.html @@ -0,0 +1,28 @@ + + +Test for bug 1224253 + + + + + +
cell
diff --git a/layout/reftests/table-background/table-row-opacity-dynamic-2.html b/layout/reftests/table-background/table-row-opacity-dynamic-2.html new file mode 100644 index 0000000000..cf9a5fd485 --- /dev/null +++ b/layout/reftests/table-background/table-row-opacity-dynamic-2.html @@ -0,0 +1,54 @@ + + +Test for bug 1224251 + + + + + +
cell
+ + diff --git a/layout/reftests/table-html/cell-align-stopped-at-table-1-quirks-ref.html b/layout/reftests/table-html/cell-align-stopped-at-table-1-quirks-ref.html new file mode 100644 index 0000000000..2b84dcf3b7 --- /dev/null +++ b/layout/reftests/table-html/cell-align-stopped-at-table-1-quirks-ref.html @@ -0,0 +1,31 @@ +Testcase, bug 196292 + + + + + + + + + + + + diff --git a/layout/reftests/table-html/cell-align-stopped-at-table-1-quirks.html b/layout/reftests/table-html/cell-align-stopped-at-table-1-quirks.html new file mode 100644 index 0000000000..2d5a75f49f --- /dev/null +++ b/layout/reftests/table-html/cell-align-stopped-at-table-1-quirks.html @@ -0,0 +1,31 @@ +Testcase, bug 196292 + + +
+

Left

+ + + +
Center
Left
+
+

Center

+ + + +
Center
Left
+
+

Right

+ + + +
Center
Left
+
+ + + + + + + + + diff --git a/layout/reftests/table-html/cell-align-stopped-at-table-1-standards-ref.html b/layout/reftests/table-html/cell-align-stopped-at-table-1-standards-ref.html new file mode 100644 index 0000000000..4b9088d35d --- /dev/null +++ b/layout/reftests/table-html/cell-align-stopped-at-table-1-standards-ref.html @@ -0,0 +1,32 @@ + +Testcase, bug 196292 + + +
+

Left

+ + + +
Center
Left
+
+

Center

+ + + +
Center
Left
+
+

Right

+ + + +
Center
Left
+
+ + + + + + + + + diff --git a/layout/reftests/table-html/cell-align-stopped-at-table-1-standards.html b/layout/reftests/table-html/cell-align-stopped-at-table-1-standards.html new file mode 100644 index 0000000000..2b687cd29c --- /dev/null +++ b/layout/reftests/table-html/cell-align-stopped-at-table-1-standards.html @@ -0,0 +1,32 @@ + +Testcase, bug 196292 + + +
+

Left

+ + + +
Center
Left
+
+

Center

+ + + +
Center
Left
+
+

Right

+ + + +
Center
Left
+
+ + + + + + + + + diff --git a/layout/reftests/table-html/reftest.list b/layout/reftests/table-html/reftest.list new file mode 100644 index 0000000000..362a96096d --- /dev/null +++ b/layout/reftests/table-html/reftest.list @@ -0,0 +1,2 @@ +== cell-align-stopped-at-table-1-standards.html cell-align-stopped-at-table-1-standards-ref.html +== cell-align-stopped-at-table-1-quirks.html cell-align-stopped-at-table-1-quirks-ref.html diff --git a/layout/reftests/text-decoration/1159729-offset-adjustment-notref.html b/layout/reftests/text-decoration/1159729-offset-adjustment-notref.html new file mode 100644 index 0000000000..90431b9a82 --- /dev/null +++ b/layout/reftests/text-decoration/1159729-offset-adjustment-notref.html @@ -0,0 +1,19 @@ + + + + + + + +

+abcxyz +

+ + diff --git a/layout/reftests/text-decoration/1159729-offset-adjustment.html b/layout/reftests/text-decoration/1159729-offset-adjustment.html new file mode 100644 index 0000000000..cf05dd1880 --- /dev/null +++ b/layout/reftests/text-decoration/1159729-offset-adjustment.html @@ -0,0 +1,21 @@ + + + + + + + +

+abcxyz +

+ + diff --git a/layout/reftests/text-decoration/emphasis-style-dynamic-ref.html b/layout/reftests/text-decoration/emphasis-style-dynamic-ref.html new file mode 100644 index 0000000000..786be94023 --- /dev/null +++ b/layout/reftests/text-decoration/emphasis-style-dynamic-ref.html @@ -0,0 +1,12 @@ + + + +Bug 1229278 - Dynamic change to text-emphasis-style + +
テスト
+ diff --git a/layout/reftests/text-decoration/emphasis-style-dynamic.html b/layout/reftests/text-decoration/emphasis-style-dynamic.html new file mode 100644 index 0000000000..3e9b920eb0 --- /dev/null +++ b/layout/reftests/text-decoration/emphasis-style-dynamic.html @@ -0,0 +1,20 @@ + + + +Bug 1229278 - Dynamic change to text-emphasis-style + +
テスト
+ + diff --git a/layout/reftests/text-decoration/reftest.list b/layout/reftests/text-decoration/reftest.list index e5b71be8f1..9de1f4defe 100644 --- a/layout/reftests/text-decoration/reftest.list +++ b/layout/reftests/text-decoration/reftest.list @@ -108,3 +108,5 @@ fuzzy-if(OSX==1010,1,2) == underline-button-2.html underline-button-2-ref.html == underline-select-1.html underline-select-1-ref.html == underline-select-2.html underline-select-2-ref.html == 1133392.html 1133392-ref.html +!= 1159729-offset-adjustment.html 1159729-offset-adjustment-notref.html +pref(layout.css.text-emphasis.enabled,true) == emphasis-style-dynamic.html emphasis-style-dynamic-ref.html diff --git a/layout/style/Declaration.cpp b/layout/style/Declaration.cpp index 422f9ef268..40a5fd71c5 100644 --- a/layout/style/Declaration.cpp +++ b/layout/style/Declaration.cpp @@ -1469,6 +1469,13 @@ Declaration::ToString(nsAString& aString) const /* virtual */ void Declaration::List(FILE* out, int32_t aIndent) const { + const Rule* owningRule = GetOwningRule(); + if (owningRule) { + // More useful to print the selector and sheet URI too. + owningRule->List(out, aIndent); + return; + } + nsAutoCString str; for (int32_t index = aIndent; --index >= 0; ) { str.AppendLiteral(" "); diff --git a/layout/style/Declaration.h b/layout/style/Declaration.h index 6b93a4a43c..b044c53943 100644 --- a/layout/style/Declaration.h +++ b/layout/style/Declaration.h @@ -325,7 +325,7 @@ public: mContainer.mOwningRule = aRule; } - Rule* GetOwningRule() { + Rule* GetOwningRule() const { if (mContainer.mRaw & 0x1) { return nullptr; } @@ -341,7 +341,7 @@ public: } } - nsHTMLCSSStyleSheet* GetHTMLCSSStyleSheet() { + nsHTMLCSSStyleSheet* GetHTMLCSSStyleSheet() const { if (!(mContainer.mRaw & 0x1)) { return nullptr; } diff --git a/layout/style/Loader.h b/layout/style/Loader.h index 9d1c321b98..11062dea72 100644 --- a/layout/style/Loader.h +++ b/layout/style/Loader.h @@ -594,8 +594,9 @@ private: // Our array of "global" observers nsTObserverArray > mObservers; - // the load data needs access to the document... - nsIDocument* mDocument; // the document we live for + // This reference is nulled by the Document in it's destructor through + // DropDocumentReference(). + nsIDocument* MOZ_NON_OWNING_REF mDocument; // the document we live for // Number of datas still waiting to be notified on if we're notifying on a diff --git a/layout/style/crashtests/622314-1.xhtml b/layout/style/crashtests/622314-1.xhtml new file mode 100644 index 0000000000..4daf23169f --- /dev/null +++ b/layout/style/crashtests/622314-1.xhtml @@ -0,0 +1,26 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ diff --git a/layout/style/crashtests/crashtests.list b/layout/style/crashtests/crashtests.list index 1fde46fd86..a05b10859e 100644 --- a/layout/style/crashtests/crashtests.list +++ b/layout/style/crashtests/crashtests.list @@ -70,6 +70,8 @@ load 601437-1.html load 601439-1.html load 605689-1.html load 611922-1.html +load 621596-1.html +load 622314-1.xhtml load 637242.xhtml load 645142.html == 645951-1.html 645951-1-ref.html diff --git a/layout/style/forms.css b/layout/style/forms.css index 9b1527fe61..432e136446 100644 --- a/layout/style/forms.css +++ b/layout/style/forms.css @@ -1043,17 +1043,24 @@ input[type=number]::-moz-number-text { } input[type=number]::-moz-number-spin-box { + writing-mode: horizontal-tb; display: flex; flex-direction: column; %ifdef XP_WIN /* The Window's Theme's spin buttons have a very narrow minimum width, so * make it something reasonable: - * XXX What about vertical mode? How will the spin buttons be arranged? - * This may need to be adjusted when bug 1123299 is implemented. */ width: 16px; %endif - height: 0; + /* If the spin-box has auto height, it ends up enlarging the default height + * of the control, so we limit it to 1em here. The height doesn't affect + * the rendering of the spinner-buttons; it's only for layout purposes. + * + * This is a temporary hack until we implement better positioning for the + * spin-box in vertical mode; it works OK at default size but less well + * if the font-size is made substantially larger or smaller. (Bug 1175074.) + */ + max-height: 1em; align-self: center; justify-content: center; } diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index b2e7380966..dc79426817 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -25,6 +25,7 @@ #include "nsDOMCSSRGBColor.h" #include "nsDOMCSSValueList.h" #include "nsFlexContainerFrame.h" +#include "nsGridContainerFrame.h" #include "nsGkAtoms.h" #include "nsHTMLReflowState.h" #include "nsStyleUtil.h" @@ -2387,10 +2388,6 @@ CSSValue* nsComputedDOMStyle::GetGridTrackSize(const nsStyleCoord& aMinValue, const nsStyleCoord& aMaxValue) { - // FIXME bug 978212: for grid-template-columns and grid-template-rows - // (not grid-auto-columns and grid-auto-rows), if we have frame, - // every should be resolved into 'px' here, - // based on layout results. if (aMinValue == aMaxValue) { nsROCSSPrimitiveValue *val = new nsROCSSPrimitiveValue; SetValueToCoord(val, aMinValue, true, @@ -2420,7 +2417,8 @@ nsComputedDOMStyle::GetGridTrackSize(const nsStyleCoord& aMinValue, } CSSValue* -nsComputedDOMStyle::GetGridTemplateColumnsRows(const nsStyleGridTemplate& aTrackList) +nsComputedDOMStyle::GetGridTemplateColumnsRows(const nsStyleGridTemplate& aTrackList, + const nsTArray* aTrackSizes) { if (aTrackList.mIsSubgrid) { NS_ASSERTION(aTrackList.mMinTrackSizingFunctions.IsEmpty() && @@ -2453,16 +2451,31 @@ nsComputedDOMStyle::GetGridTemplateColumnsRows(const nsStyleGridTemplate& aTrack // one before each track, plus one at the very end. MOZ_ASSERT(aTrackList.mLineNameLists.Length() == numSizes + 1, "Unexpected number of line name lists"); - for (uint32_t i = 0;; i++) { - const nsTArray& lineNames = aTrackList.mLineNameLists[i]; - if (!lineNames.IsEmpty()) { - valueList->AppendCSSValue(GetGridLineNames(lineNames)); + if (aTrackSizes) { + for (uint32_t i = 0;; i++) { + const nsTArray& lineNames = aTrackList.mLineNameLists[i]; + if (!lineNames.IsEmpty()) { + valueList->AppendCSSValue(GetGridLineNames(lineNames)); + } + if (i == numSizes) { + break; + } + nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue; + val->SetAppUnits(aTrackSizes->ElementAt(i)); + valueList->AppendCSSValue(val); } - if (i == numSizes) { - break; + } else { + for (uint32_t i = 0;; i++) { + const nsTArray& lineNames = aTrackList.mLineNameLists[i]; + if (!lineNames.IsEmpty()) { + valueList->AppendCSSValue(GetGridLineNames(lineNames)); + } + if (i == numSizes) { + break; + } + valueList->AppendCSSValue(GetGridTrackSize(aTrackList.mMinTrackSizingFunctions[i], + aTrackList.mMaxTrackSizingFunctions[i])); } - valueList->AppendCSSValue(GetGridTrackSize(aTrackList.mMinTrackSizingFunctions[i], - aTrackList.mMaxTrackSizingFunctions[i])); } return valueList; @@ -2499,13 +2512,31 @@ nsComputedDOMStyle::DoGetGridAutoRows() CSSValue* nsComputedDOMStyle::DoGetGridTemplateColumns() { - return GetGridTemplateColumnsRows(StylePosition()->mGridTemplateColumns); + const nsTArray* trackSizes = nullptr; + if (mInnerFrame) { + nsIFrame* gridContainerCandidate = mInnerFrame->GetContentInsertionFrame(); + if (gridContainerCandidate && + gridContainerCandidate->GetType() == nsGkAtoms::gridContainerFrame) { + auto gridContainer = static_cast(gridContainerCandidate); + trackSizes = gridContainer->GetComputedTemplateColumns(); + } + } + return GetGridTemplateColumnsRows(StylePosition()->mGridTemplateColumns, trackSizes); } CSSValue* nsComputedDOMStyle::DoGetGridTemplateRows() { - return GetGridTemplateColumnsRows(StylePosition()->mGridTemplateRows); + const nsTArray* trackSizes = nullptr; + if (mInnerFrame) { + nsIFrame* gridContainerCandidate = mInnerFrame->GetContentInsertionFrame(); + if (gridContainerCandidate && + gridContainerCandidate->GetType() == nsGkAtoms::gridContainerFrame) { + auto gridContainer = static_cast(gridContainerCandidate); + trackSizes = gridContainer->GetComputedTemplateRows(); + } + } + return GetGridTemplateColumnsRows(StylePosition()->mGridTemplateRows, trackSizes); } CSSValue* @@ -5077,7 +5108,16 @@ nsComputedDOMStyle::StyleCoordToNSCoord(const nsStyleCoord& aCoord, nscoord result = nsRuleNode::ComputeCoordPercentCalc(aCoord, percentageBase); if (aClampNegativeCalc && result < 0) { - MOZ_ASSERT(aCoord.IsCalcUnit(), + // It's expected that we can get a negative value here with calc(). + // We can also get a negative value with a percentage value if + // percentageBase is negative; this isn't expected, but can happen + // when large length values overflow. + NS_WARN_IF_FALSE(percentageBase >= 0, + "percentage base value overflowed to become " + "negative for a property that disallows negative " + "values"); + MOZ_ASSERT(aCoord.IsCalcUnit() || + (aCoord.HasPercent() && percentageBase < 0), "parser should have rejected value"); result = 0; } diff --git a/layout/style/nsComputedDOMStyle.h b/layout/style/nsComputedDOMStyle.h index fef3bd73d3..ce7a6d02b4 100644 --- a/layout/style/nsComputedDOMStyle.h +++ b/layout/style/nsComputedDOMStyle.h @@ -179,7 +179,8 @@ private: mozilla::dom::CSSValue* GetGridLineNames(const nsTArray& aLineNames); mozilla::dom::CSSValue* GetGridTrackSize(const nsStyleCoord& aMinSize, const nsStyleCoord& aMaxSize); - mozilla::dom::CSSValue* GetGridTemplateColumnsRows(const nsStyleGridTemplate& aTrackList); + mozilla::dom::CSSValue* GetGridTemplateColumnsRows(const nsStyleGridTemplate& aTrackList, + const nsTArray* aTrackSizes); mozilla::dom::CSSValue* GetGridLine(const nsStyleGridLine& aGridLine); bool GetLineHeightCoord(nscoord& aCoord); diff --git a/layout/style/nsDOMCSSDeclaration.cpp b/layout/style/nsDOMCSSDeclaration.cpp index 56ce8319d0..a6b7299cbd 100644 --- a/layout/style/nsDOMCSSDeclaration.cpp +++ b/layout/style/nsDOMCSSDeclaration.cpp @@ -19,6 +19,7 @@ #include "mozilla/dom/BindingUtils.h" #include "nsContentUtils.h" #include "nsQueryObject.h" +#include "mozilla/layers/ScrollLinkedEffectDetector.h" using namespace mozilla; @@ -77,6 +78,23 @@ NS_IMETHODIMP nsDOMCSSDeclaration::SetPropertyValue(const nsCSSProperty aPropID, const nsAString& aValue) { + switch (aPropID) { + case eCSSProperty_background_position: + case eCSSProperty_transform: + case eCSSProperty_top: + case eCSSProperty_left: + case eCSSProperty_bottom: + case eCSSProperty_right: + case eCSSProperty_margin_top: + case eCSSProperty_margin_left: + case eCSSProperty_margin_bottom: + case eCSSProperty_margin_right: + mozilla::layers::ScrollLinkedEffectDetector::PositioningPropertyMutated(); + break; + default: + break; + } + if (aValue.IsEmpty()) { // If the new value of the property is an empty string we remove the // property. diff --git a/layout/style/nsDOMCSSDeclaration.h b/layout/style/nsDOMCSSDeclaration.h index ced905e4b1..6a3c3a9e31 100644 --- a/layout/style/nsDOMCSSDeclaration.h +++ b/layout/style/nsDOMCSSDeclaration.h @@ -134,12 +134,18 @@ protected: // fly. This is why we don't use CSSParsingEnvironment as a return // value, to avoid multiple-refcounting of mBaseURI. struct CSSParsingEnvironment { - nsIURI* mSheetURI; + nsIURI* MOZ_UNSAFE_REF("user of CSSParsingEnviroment must hold an owning " + "reference; reference counting here has unacceptable " + "performance overhead (see bug 649163)") mSheetURI; nsCOMPtr mBaseURI; - nsIPrincipal* mPrincipal; - mozilla::css::Loader* mCSSLoader; + nsIPrincipal* MOZ_UNSAFE_REF("user of CSSParsingEnviroment must hold an owning " + "reference; reference counting here has unacceptable " + "performance overhead (see bug 649163)") mPrincipal; + mozilla::css::Loader* MOZ_UNSAFE_REF("user of CSSParsingEnviroment must hold an owning " + "reference; reference counting here has unacceptable " + "performance overhead (see bug 649163)") mCSSLoader; }; - + // On failure, mPrincipal should be set to null in aCSSParseEnv. // If mPrincipal is null, the other members may not be set to // anything meaningful. diff --git a/layout/style/nsHTMLCSSStyleSheet.cpp b/layout/style/nsHTMLCSSStyleSheet.cpp index 4ddb1951da..51dd7db55f 100644 --- a/layout/style/nsHTMLCSSStyleSheet.cpp +++ b/layout/style/nsHTMLCSSStyleSheet.cpp @@ -195,7 +195,7 @@ nsHTMLCSSStyleSheet::EvictStyleAttr(const nsAString& aSerialized, { #ifdef DEBUG { - NS_ASSERTION(aValue = mCachedStyleAttrs.Get(aSerialized), + NS_ASSERTION(aValue == mCachedStyleAttrs.Get(aSerialized), "Cached value does not match?!"); } #endif diff --git a/layout/style/nsStyleContext.cpp b/layout/style/nsStyleContext.cpp index 294f061f37..95fad64090 100644 --- a/layout/style/nsStyleContext.cpp +++ b/layout/style/nsStyleContext.cpp @@ -600,8 +600,9 @@ nsStyleContext::ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup) // This is covering the
+

Left

+ + + +
Center
Left
+
+

Center

+ + + +
Center
Left
+
+

Right

+ + + +
Center
Left
+
...
case. // In this case, we don't want to inherit the text alignment into the table. const nsStyleText* text = StyleText(); - - if (text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER || + + if (text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_LEFT || + text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER || text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT) { nsStyleText* uniqueText = (nsStyleText*)GetUniqueStyleData(eStyleStruct_Text); @@ -627,8 +628,9 @@ nsStyleContext::ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup) displayVal = NS_STYLE_DISPLAY_BLOCK; } if (displayVal != disp->mDisplay) { - nsStyleDisplay *mutable_display = + nsStyleDisplay* mutable_display = static_cast(GetUniqueStyleData(eStyleStruct_Display)); + disp = mutable_display; // If we're in this code, then mOriginalDisplay doesn't matter // for purposes of the cascade (because this nsStyleDisplay @@ -691,8 +693,9 @@ nsStyleContext::ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup) "We shouldn't be changing the display value of " "positioned content (and we should have already " "converted its display value to be block-level...)"); - nsStyleDisplay *mutable_display = + nsStyleDisplay* mutable_display = static_cast(GetUniqueStyleData(eStyleStruct_Display)); + disp = mutable_display; mutable_display->mDisplay = displayVal; } } @@ -703,8 +706,9 @@ nsStyleContext::ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup) uint8_t displayVal = disp->mDisplay; nsRuleNode::EnsureInlineDisplay(displayVal); if (displayVal != disp->mDisplay) { - nsStyleDisplay *mutable_display = + nsStyleDisplay* mutable_display = static_cast(GetUniqueStyleData(eStyleStruct_Display)); + disp = mutable_display; mutable_display->mDisplay = displayVal; } } @@ -754,8 +758,9 @@ nsStyleContext::ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup) // and text-orientation) here; just the writing-mode property is enough. if (StyleVisibility()->mWritingMode != mParent->StyleVisibility()->mWritingMode) { - nsStyleDisplay *mutable_display = + nsStyleDisplay* mutable_display = static_cast(GetUniqueStyleData(eStyleStruct_Display)); + disp = mutable_display; mutable_display->mOriginalDisplay = mutable_display->mDisplay = NS_STYLE_DISPLAY_INLINE_BLOCK; } diff --git a/layout/style/nsStyleContext.h b/layout/style/nsStyleContext.h index db7dd608ec..7e0a1f1196 100644 --- a/layout/style/nsStyleContext.h +++ b/layout/style/nsStyleContext.h @@ -163,7 +163,7 @@ public: // true, the line should not be broken inside, which means inlines act // as if nowrap is set,
is suppressed, and blocks are inlinized. // This bit is propogated to all children of line partitipants. It is - // currenlty used by ruby to make its content frames unbreakable. + // currently used by ruby to make its content frames unbreakable. // NOTE: for nsTextFrame, use nsTextFrame::ShouldSuppressLineBreak() // instead of this method. bool ShouldSuppressLineBreak() const @@ -260,22 +260,6 @@ public: // Tell this style context to cache aStruct as the struct for aSID void SetStyle(nsStyleStructID aSID, void* aStruct); - // Setters for inherit structs only, since rulenode only sets those eagerly. - #define STYLE_STRUCT_INHERITED(name_, checkdata_cb_) \ - void SetStyle##name_ (nsStyle##name_ * aStruct) { \ - void *& slot = \ - mCachedInheritedData.mStyleStructs[eStyleStruct_##name_]; \ - NS_ASSERTION(!slot || \ - (mBits & \ - nsCachedStyleData::GetBitForSID(eStyleStruct_##name_)), \ - "Going to leak styledata"); \ - slot = aStruct; \ - } -#define STYLE_STRUCT_RESET(name_, checkdata_cb_) /* nothing */ - #include "nsStyleStructList.h" - #undef STYLE_STRUCT_RESET - #undef STYLE_STRUCT_INHERITED - /** * Returns whether this style context has cached style data for a * given style struct and it does NOT own that struct. This can @@ -297,7 +281,7 @@ public: /* * Get the style data for a style struct. This is the most important - * member function of nsIStyleContext. It fills in a const pointer + * member function of nsStyleContext. It fills in a const pointer * to a style data struct that is appropriate for the style context's * frame. This struct may be shared with other contexts (either in * the rule tree or the style context tree), so it should not be @@ -328,7 +312,7 @@ public: #undef STYLE_STRUCT /** - * PeekStyle* is like GetStyle* but doesn't trigger style + * PeekStyle* is like Style* but doesn't trigger style * computation if the data is not cached on either the style context * or the rule node. * diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index 7a573a47a1..29a45a3ddc 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -1031,7 +1031,7 @@ nsStyleClipPath::nsStyleClipPath(const nsStyleClipPath& aSource) SetURL(aSource.mURL); } else if (aSource.mType == NS_STYLE_CLIP_PATH_SHAPE) { SetBasicShape(aSource.mBasicShape, aSource.mSizingBox); - } else if (aSource.mType == NS_STYLE_CLIP_PATH_SHAPE) { + } else if (aSource.mType == NS_STYLE_CLIP_PATH_BOX) { SetSizingBox(aSource.mSizingBox); } } @@ -2916,6 +2916,9 @@ nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const NS_UpdateHint(hint, nsChangeHint_RepaintFrame); } else { NS_UpdateHint(hint, nsChangeHint_UpdateOpacityLayer); + if ((mOpacity == 1.0f) != (aOther.mOpacity == 1.0f)) { + NS_UpdateHint(hint, nsChangeHint_UpdateUsesOpacity); + } } } @@ -3001,14 +3004,19 @@ nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const // test above handles relevant changes in the // NS_STYLE_WILL_CHANGE_TRANSFORM bit, which in turn handles frame // reconstruction for changes in the containing block of - // fixed-positioned elements. Other than that, all changes to - // 'will-change' can be handled by a repaint. + // fixed-positioned elements. uint8_t willChangeBitsChanged = mWillChangeBitField ^ aOther.mWillChangeBitField; - if (willChangeBitsChanged) { + if (willChangeBitsChanged & (NS_STYLE_WILL_CHANGE_STACKING_CONTEXT | + NS_STYLE_WILL_CHANGE_SCROLL | + NS_STYLE_WILL_CHANGE_OPACITY)) { NS_UpdateHint(hint, nsChangeHint_RepaintFrame); } + if (willChangeBitsChanged & NS_STYLE_WILL_CHANGE_FIXPOS_CB) { + NS_UpdateHint(hint, nsChangeHint_UpdateContainingBlock); + } + // Note: Our current behavior for handling changes to the // transition-duration, transition-delay, and transition-timing-function // properties is to do nothing. In other words, the transition @@ -3632,15 +3640,10 @@ nsChangeHint nsStyleText::CalcDifference(const nsStyleText& aOther) const nsChangeHint_RepaintFrame; } - if (!AreShadowArraysEqual(mTextShadow, aOther.mTextShadow)) { - return nsChangeHint_UpdateSubtreeOverflow | - nsChangeHint_SchedulePaint | - nsChangeHint_RepaintFrame; - } - - if (mTextEmphasisStyle != aOther.mTextEmphasisStyle || + if (!AreShadowArraysEqual(mTextShadow, aOther.mTextShadow) || + mTextEmphasisStyle != aOther.mTextEmphasisStyle || mTextEmphasisStyleString != aOther.mTextEmphasisStyleString) { - return nsChangeHint_UpdateOverflow | + return nsChangeHint_UpdateSubtreeOverflow | nsChangeHint_SchedulePaint | nsChangeHint_RepaintFrame; } diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index e677289982..c2f6f9849c 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -1261,7 +1261,7 @@ public: nsRect mImageRegion; // [inherited] the rect to use within an image }; -// Computed value of the grid-template-columns or grid-columns-rows property +// Computed value of the grid-template-columns or grid-template-rows property // (but *not* grid-template-areas.) // http://dev.w3.org/csswg/css-grid/#track-sizing // @@ -2005,6 +2005,7 @@ struct nsStyleDisplay { // All the parts of FRAMECHANGE are present in CalcDifference. return nsChangeHint(NS_STYLE_HINT_FRAMECHANGE | nsChangeHint_UpdateOpacityLayer | + nsChangeHint_UpdateUsesOpacity | nsChangeHint_UpdateTransformLayer | nsChangeHint_UpdateOverflow | nsChangeHint_UpdatePostTransformOverflow | diff --git a/layout/style/nsStyleStructInlines.h b/layout/style/nsStyleStructInlines.h index 544414d7f9..6e845646ad 100644 --- a/layout/style/nsStyleStructInlines.h +++ b/layout/style/nsStyleStructInlines.h @@ -144,6 +144,7 @@ nsStyleDisplay::IsFixedPosContainingBlock(const nsIFrame* aContextFrame) const "unexpected aContextFrame"); return (IsContainPaint() || HasTransform(aContextFrame) || HasPerspectiveStyle() || + (mWillChangeBitField & NS_STYLE_WILL_CHANGE_FIXPOS_CB) || aContextFrame->StyleSVGReset()->HasFilters()) && !aContextFrame->IsSVGText(); } diff --git a/layout/style/nsStyleTransformMatrix.h b/layout/style/nsStyleTransformMatrix.h index e20ea9e575..0c2580cfc1 100644 --- a/layout/style/nsStyleTransformMatrix.h +++ b/layout/style/nsStyleTransformMatrix.h @@ -18,7 +18,7 @@ class nsPresContext; struct nsRect; namespace mozilla { class RuleNodeCacheConditions; -} +} // namespace mozilla /** * A helper to generate gfxMatrixes from css transform functions. diff --git a/layout/style/number-control.css b/layout/style/number-control.css index 0ea55f6508..b4c784cf27 100644 --- a/layout/style/number-control.css +++ b/layout/style/number-control.css @@ -8,7 +8,6 @@ */ input[type="number"] { - writing-mode: horizontal-tb !important; /* XXX remove when bug 1123299 is done */ -moz-appearance: number-input; /* Has to revert some properties applied by the generic input rule. */ -moz-binding: none; diff --git a/layout/style/test/chrome/test_additional_sheets.html b/layout/style/test/chrome/test_additional_sheets.html index c4b7b75e7a..00f5337aa6 100644 --- a/layout/style/test/chrome/test_additional_sheets.html +++ b/layout/style/test/chrome/test_additional_sheets.html @@ -301,6 +301,12 @@ function run() testStyleVsStyle(win, additionalAuthor, additionalUser, {AB:{rr:0, ii:1, ri:1, ir:0}, BA:{rr:0, ii:1, ri:1, ir:0}}); + // Bug 1228542 + loadAndRegisterAuthorSheet(win, getStyle('rgb(255, 0, 0)')); + // Avoiding security exception... + (new win.Function("document.open()"))(); + (new win.Function("document.close()"))(); + SimpleTest.finish(); } diff --git a/layout/style/test/mochitest.ini b/layout/style/test/mochitest.ini index b1273ca0d1..25ea4ce851 100644 --- a/layout/style/test/mochitest.ini +++ b/layout/style/test/mochitest.ini @@ -178,6 +178,7 @@ support-files = [test_grid_container_shorthands.html] [test_grid_item_shorthands.html] [test_grid_shorthand_serialization.html] +[test_grid_computed_values.html] [test_group_insertRule.html] [test_hover_quirk.html] [test_html_attribute_computed_values.html] diff --git a/layout/style/test/test_grid_computed_values.html b/layout/style/test/test_grid_computed_values.html new file mode 100644 index 0000000000..0ee0689a08 --- /dev/null +++ b/layout/style/test/test_grid_computed_values.html @@ -0,0 +1,83 @@ + + + + + Test computed grid values + + + + + + + + +
+
+
+
+
+
+ + + + diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index b89e9fabf0..f9ae1aa392 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -2305,6 +2305,9 @@ pref("security.mixed_content.block_display_content", false); // Sub-resource integrity pref("security.sri.enable", false); +// OCSP must-staple +pref("security.ssl.enable_ocsp_must_staple", true); + // Enable pinning checks by default. pref("security.cert_pinning.enforcement_level", 2); @@ -5156,6 +5159,9 @@ pref("layout.accessiblecaret.use_long_tap_injector", true); // events hide AccessibleCarets by default. pref("layout.accessiblecaret.extendedvisibility", false); +// Optionally provide haptic feedback on longPress selection events. +pref("layout.accessiblecaret.hapticfeedback", false); + // Wakelock is disabled by default. pref("dom.wakelock.enabled", false); diff --git a/security/certverifier/NSSCertDBTrustDomain.cpp b/security/certverifier/NSSCertDBTrustDomain.cpp index 896017208e..80359513dd 100644 --- a/security/certverifier/NSSCertDBTrustDomain.cpp +++ b/security/certverifier/NSSCertDBTrustDomain.cpp @@ -974,7 +974,7 @@ nss_addEscape(const char* string, char quote) } // unnamed namespace SECStatus -InitializeNSS(const char* dir, bool readOnly) +InitializeNSS(const char* dir, bool readOnly, bool loadPKCS11Modules) { MOZ_ASSERT(NS_IsMainThread()); @@ -987,6 +987,9 @@ InitializeNSS(const char* dir, bool readOnly) if (readOnly) { flags |= NSS_INIT_READONLY; } + if (!loadPKCS11Modules) { + flags |= NSS_INIT_NOMODDB; + } nsAutoCString dbTypeAndDirectory; dbTypeAndDirectory.Assign("dbm:"); dbTypeAndDirectory.Append(dir); diff --git a/security/certverifier/NSSCertDBTrustDomain.h b/security/certverifier/NSSCertDBTrustDomain.h index 7814cb055b..831ef17cab 100644 --- a/security/certverifier/NSSCertDBTrustDomain.h +++ b/security/certverifier/NSSCertDBTrustDomain.h @@ -19,7 +19,7 @@ enum class ValidityCheckingMode { CheckForEV = 1, }; -SECStatus InitializeNSS(const char* dir, bool readOnly); +SECStatus InitializeNSS(const char* dir, bool readOnly, bool loadPKCS11Modules); void DisableMD5(); diff --git a/security/manager/locales/en-US/chrome/pipnss/pipnss.properties b/security/manager/locales/en-US/chrome/pipnss/pipnss.properties index afb5b980c6..7f9eb05e82 100755 --- a/security/manager/locales/en-US/chrome/pipnss/pipnss.properties +++ b/security/manager/locales/en-US/chrome/pipnss/pipnss.properties @@ -272,6 +272,8 @@ certErrorIntro=%S uses an invalid security certificate. certErrorTrust_SelfSigned=The certificate is not trusted because it is self-signed. certErrorTrust_UnknownIssuer=The certificate is not trusted because the issuer certificate is unknown. +certErrorTrust_UnknownIssuer2=The server might not be sending the appropriate intermediate certificates. +certErrorTrust_UnknownIssuer3=An additional root certificate may need to be imported. certErrorTrust_CaInvalid=The certificate is not trusted because it was issued by an invalid CA certificate. certErrorTrust_Issuer=The certificate is not trusted because the issuer certificate is not trusted. certErrorTrust_SignatureAlgorithmDisabled=The certificate is not trusted because it was signed using a signature algorithm that was disabled because that algorithm is not secure. diff --git a/security/manager/ssl/SharedSSLState.cpp b/security/manager/ssl/SharedSSLState.cpp index bb22929b5e..0e879a0fb6 100644 --- a/security/manager/ssl/SharedSSLState.cpp +++ b/security/manager/ssl/SharedSSLState.cpp @@ -57,7 +57,7 @@ public: bool mShouldClearSessionCache; }; -} // anonymous namespace +} // namespace namespace mozilla { @@ -102,7 +102,7 @@ private: SharedSSLState* gPublicState; SharedSSLState* gPrivateState; -} // anonymous namespace +} // namespace NS_IMPL_ISUPPORTS(PrivateBrowsingObserver, nsIObserver) @@ -122,6 +122,7 @@ SharedSSLState::SharedSSLState() , mMutex("SharedSSLState::mMutex") , mSocketCreated(false) , mOCSPStaplingEnabled(false) +, mOCSPMustStapleEnabled(false) { mIOLayerHelpers.Init(); mClientAuthRemember->Init(); diff --git a/security/manager/ssl/TransportSecurityInfo.cpp b/security/manager/ssl/TransportSecurityInfo.cpp index f356073207..88a6625832 100644 --- a/security/manager/ssl/TransportSecurityInfo.cpp +++ b/security/manager/ssl/TransportSecurityInfo.cpp @@ -571,7 +571,9 @@ AppendErrorTextUntrusted(PRErrorCode errTrust, nsINSSComponent *component, nsString &returnedMessage) { - const char *errorID = nullptr; + const char* errorID = nullptr; + const char* errorID2 = nullptr; + const char* errorID3 = nullptr; bool isSelfSigned; if (NS_SUCCEEDED(ix509->GetIsSelfSigned(&isSelfSigned)) && isSelfSigned) { errorID = "certErrorTrust_SelfSigned"; @@ -581,6 +583,8 @@ AppendErrorTextUntrusted(PRErrorCode errTrust, switch (errTrust) { case SEC_ERROR_UNKNOWN_ISSUER: errorID = "certErrorTrust_UnknownIssuer"; + errorID2 = "certErrorTrust_UnknownIssuer2"; + errorID3 = "certErrorTrust_UnknownIssuer3"; break; case SEC_ERROR_CA_CERT_INVALID: errorID = "certErrorTrust_CaInvalid"; @@ -601,13 +605,18 @@ AppendErrorTextUntrusted(PRErrorCode errTrust, } } - nsString formattedString; - nsresult rv = component->GetPIPNSSBundleString(errorID, - formattedString); - if (NS_SUCCEEDED(rv)) - { - returnedMessage.Append(formattedString); - returnedMessage.Append('\n'); + const char* errorIDs[] = { errorID, errorID2, errorID3 }; + for (size_t i = 0; i < ArrayLength(errorIDs); i++) { + if (!errorIDs[i]) { + break; + } + + nsString formattedString; + nsresult rv = component->GetPIPNSSBundleString(errorIDs[i], formattedString); + if (NS_SUCCEEDED(rv)) { + returnedMessage.Append(formattedString); + returnedMessage.Append('\n'); + } } } @@ -832,12 +841,10 @@ GetDateBoundary(nsIX509Cert* ix509, if (NS_FAILED(rv)) return; - dateTimeFormat->FormatPRTime(nullptr, kDateFormatShort, - kTimeFormatNoSeconds, timeToUse, - formattedDate); - dateTimeFormat->FormatPRTime(nullptr, kDateFormatShort, - kTimeFormatNoSeconds, now, - nowDate); + dateTimeFormat->FormatPRTime(nullptr, kDateFormatLong, kTimeFormatNoSeconds, + timeToUse, formattedDate); + dateTimeFormat->FormatPRTime(nullptr, kDateFormatLong, kTimeFormatNoSeconds, + now, nowDate); } static void diff --git a/security/manager/ssl/WeakCryptoOverride.cpp b/security/manager/ssl/WeakCryptoOverride.cpp new file mode 100644 index 0000000000..e747a33008 --- /dev/null +++ b/security/manager/ssl/WeakCryptoOverride.cpp @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "WeakCryptoOverride.h" + +#include "MainThreadUtils.h" +#include "SharedSSLState.h" + +using namespace mozilla; +using namespace mozilla::psm; + +NS_IMPL_ISUPPORTS(WeakCryptoOverride, + nsIWeakCryptoOverride) + +WeakCryptoOverride::WeakCryptoOverride() +{ +} + +WeakCryptoOverride::~WeakCryptoOverride() +{ +} + +NS_IMETHODIMP +WeakCryptoOverride::AddWeakCryptoOverride(const nsACString& aHostName, + bool aPrivate, bool aTemporary) +{ + if (!NS_IsMainThread()) { + return NS_ERROR_NOT_SAME_THREAD; + } + + SharedSSLState* sharedState = aPrivate ? PrivateSSLState() + : PublicSSLState(); + if (!sharedState) { + return NS_ERROR_NOT_AVAILABLE; + } + const nsPromiseFlatCString& host = PromiseFlatCString(aHostName); + sharedState->IOLayerHelpers().addInsecureFallbackSite(host, aTemporary); + + return NS_OK; +} + +NS_IMETHODIMP +WeakCryptoOverride::RemoveWeakCryptoOverride(const nsACString& aHostName, + int32_t aPort, bool aPrivate) +{ + if (!NS_IsMainThread()) { + return NS_ERROR_NOT_SAME_THREAD; + } + + SharedSSLState* sharedState = aPrivate ? PrivateSSLState() + : PublicSSLState(); + if (!sharedState) { + return NS_ERROR_NOT_AVAILABLE; + } + const nsPromiseFlatCString& host = PromiseFlatCString(aHostName); + sharedState->IOLayerHelpers().removeInsecureFallbackSite(host, aPort); + + return NS_OK; +} diff --git a/security/manager/ssl/WeakCryptoOverride.h b/security/manager/ssl/WeakCryptoOverride.h new file mode 100644 index 0000000000..fefee75ef9 --- /dev/null +++ b/security/manager/ssl/WeakCryptoOverride.h @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef WEAKCRYPTOOVERRIDE_H +#define WEAKCRYPTOOVERRIDE_H + +#include "nsIWeakCryptoOverride.h" +#include "nsWeakReference.h" + +namespace mozilla { +namespace psm { + +class WeakCryptoOverride final : public nsIWeakCryptoOverride +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIWEAKCRYPTOOVERRIDE + + WeakCryptoOverride(); + +protected: + ~WeakCryptoOverride(); +}; + +} // psm +} // mozilla + +#define NS_WEAKCRYPTOOVERRIDE_CID /* ffb06724-3c20-447c-8328-ae71513dd618 */ \ +{ 0xffb06724, 0x3c20, 0x447c, \ + { 0x83, 0x28, 0xae, 0x71, 0x51, 0x3d, 0xd6, 0x18 } } + +#endif diff --git a/security/manager/ssl/moz.build b/security/manager/ssl/moz.build index 4528480308..f8bbc99e82 100644 --- a/security/manager/ssl/moz.build +++ b/security/manager/ssl/moz.build @@ -37,6 +37,7 @@ XPIDL_SOURCES += [ 'nsITokenDialogs.idl', 'nsITokenPasswordDialogs.idl', 'nsIUserCertPicker.idl', + 'nsIWeakCryptoOverride.idl', 'nsIX509Cert.idl', 'nsIX509CertDB.idl', 'nsIX509CertList.idl', @@ -130,6 +131,7 @@ UNIFIED_SOURCES += [ 'SharedSSLState.cpp', 'SSLServerCertVerification.cpp', 'TransportSecurityInfo.cpp', + 'WeakCryptoOverride.cpp', ] # nsNSSCertificateDB.cpp needs to include nscert.h before everything else. diff --git a/security/manager/ssl/nsCertVerificationThread.cpp b/security/manager/ssl/nsCertVerificationThread.cpp index 1698b9be1d..6f26d6b0b8 100644 --- a/security/manager/ssl/nsCertVerificationThread.cpp +++ b/security/manager/ssl/nsCertVerificationThread.cpp @@ -34,7 +34,7 @@ private: nsCOMPtr mCert; nsCOMPtr mResult; }; -} // anonymous namespace +} // namespace void nsCertVerificationJob::Run() { diff --git a/security/manager/ssl/nsIWeakCryptoOverride.idl b/security/manager/ssl/nsIWeakCryptoOverride.idl new file mode 100644 index 0000000000..7e3aa663fd --- /dev/null +++ b/security/manager/ssl/nsIWeakCryptoOverride.idl @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +%{C++ +#define NS_WEAKCRYPTOOVERRIDE_CONTRACTID "@mozilla.org/security/weakcryptooverride;1" +%} + +/** + * This represents the fallback whitelist for + * weak crypto servers such as RC4-only. + */ +[scriptable, uuid(27b4d3df-8f15-4eb4-a35f-474e911b61e7)] +interface nsIWeakCryptoOverride : nsISupports { + /** + * Add a weak crypto override for the given hostname. + * Main thread only. + * + * @param aHostName The host (punycode) this mapping belongs to + * @param aPrivate The override info will used for the private browsing + * session and no information will be written to the disk. + * @param aTemporary The override info will not persist between sessions. + * Ignored if aPrivate is true. + */ + void addWeakCryptoOverride(in ACString aHostName, + in boolean aPrivate, + [optional] in boolean aTemporary); + + /** + * Remove a weak crypto override for the given hostname:port. + * Main thread only. + * + * @param aHostName The host (punycode) whose entry should be cleared. + * @param aPort The port whose entry should be cleared. + * @param aPrivate The override info will used for the private browsing + * session. + */ + void removeWeakCryptoOverride(in ACString aHostName, + in int32_t aPort, + in boolean aPrivate); +}; diff --git a/security/manager/ssl/nsKeygenHandler.cpp b/security/manager/ssl/nsKeygenHandler.cpp index 14d669c1f5..f58c89608f 100644 --- a/security/manager/ssl/nsKeygenHandler.cpp +++ b/security/manager/ssl/nsKeygenHandler.cpp @@ -18,6 +18,7 @@ #include "nsIDOMHTMLSelectElement.h" #include "nsIContent.h" #include "nsKeygenThread.h" +#include "nsNSSHelper.h" #include "nsReadableUtils.h" #include "nsUnicharUtils.h" #include "nsCRT.h" diff --git a/security/manager/ssl/nsNSSCallbacks.cpp b/security/manager/ssl/nsNSSCallbacks.cpp index 896fa0410d..13abbd7ecd 100644 --- a/security/manager/ssl/nsNSSCallbacks.cpp +++ b/security/manager/ssl/nsNSSCallbacks.cpp @@ -1056,6 +1056,9 @@ void HandshakeCallback(PRFileDesc* fd, void* client_data) { } else { state = nsIWebProgressListener::STATE_IS_SECURE | nsIWebProgressListener::STATE_SECURE_HIGH; + // we know this site no longer requires a weak cipher + ioLayerHelpers.removeInsecureFallbackSite(infoObject->GetHostName(), + infoObject->GetPort()); } infoObject->SetSecurityState(state); diff --git a/security/manager/ssl/nsNSSCertHelper.cpp b/security/manager/ssl/nsNSSCertHelper.cpp index 5cb4b01d4a..0c19e32cd3 100644 --- a/security/manager/ssl/nsNSSCertHelper.cpp +++ b/security/manager/ssl/nsNSSCertHelper.cpp @@ -1660,14 +1660,15 @@ ProcessSECAlgorithmID(SECAlgorithmID *algID, } static nsresult -ProcessTime(PRTime dispTime, const char16_t *displayName, - nsIASN1Sequence *parentSequence) +ProcessTime(PRTime dispTime, const char16_t* displayName, + nsIASN1Sequence* parentSequence) { nsresult rv; nsCOMPtr dateFormatter = do_CreateInstance(NS_DATETIMEFORMAT_CONTRACTID, &rv); - if (NS_FAILED(rv)) + if (NS_FAILED(rv)) { return rv; + } nsString text; nsString tempString; @@ -1675,8 +1676,9 @@ ProcessTime(PRTime dispTime, const char16_t *displayName, PRExplodedTime explodedTime; PR_ExplodeTime(dispTime, PR_LocalTimeParameters, &explodedTime); - dateFormatter->FormatPRExplodedTime(nullptr, kDateFormatShort, kTimeFormatSecondsForce24Hour, - &explodedTime, tempString); + dateFormatter->FormatPRExplodedTime(nullptr, kDateFormatLong, + kTimeFormatSeconds, &explodedTime, + tempString); text.Append(tempString); text.AppendLiteral("\n("); @@ -1684,8 +1686,9 @@ ProcessTime(PRTime dispTime, const char16_t *displayName, PRExplodedTime explodedTimeGMT; PR_ExplodeTime(dispTime, PR_GMTParameters, &explodedTimeGMT); - dateFormatter->FormatPRExplodedTime(nullptr, kDateFormatShort, kTimeFormatSecondsForce24Hour, - &explodedTimeGMT, tempString); + dateFormatter->FormatPRExplodedTime(nullptr, kDateFormatLong, + kTimeFormatSeconds, &explodedTimeGMT, + tempString); text.Append(tempString); text.AppendLiteral(" GMT)"); diff --git a/security/manager/ssl/nsNSSCertValidity.cpp b/security/manager/ssl/nsNSSCertValidity.cpp index 3e7b4719b4..fea0e939ea 100644 --- a/security/manager/ssl/nsNSSCertValidity.cpp +++ b/security/manager/ssl/nsNSSCertValidity.cpp @@ -60,8 +60,8 @@ NS_IMETHODIMP nsX509CertValidity::GetNotBeforeLocalTime(nsAString &aNotBeforeLoc nsAutoString date; PRExplodedTime explodedTime; PR_ExplodeTime(mNotBefore, PR_LocalTimeParameters, &explodedTime); - dateFormatter->FormatPRExplodedTime(nullptr, kDateFormatShort, kTimeFormatSecondsForce24Hour, - &explodedTime, date); + dateFormatter->FormatPRExplodedTime(nullptr, kDateFormatLong, + kTimeFormatSeconds, &explodedTime, date); aNotBeforeLocalTime = date; return NS_OK; } @@ -79,7 +79,7 @@ NS_IMETHODIMP nsX509CertValidity::GetNotBeforeLocalDay(nsAString &aNotBeforeLoca nsAutoString date; PRExplodedTime explodedTime; PR_ExplodeTime(mNotBefore, PR_LocalTimeParameters, &explodedTime); - dateFormatter->FormatPRExplodedTime(nullptr, kDateFormatShort, kTimeFormatNone, + dateFormatter->FormatPRExplodedTime(nullptr, kDateFormatLong, kTimeFormatNone, &explodedTime, date); aNotBeforeLocalDay = date; return NS_OK; @@ -99,8 +99,8 @@ NS_IMETHODIMP nsX509CertValidity::GetNotBeforeGMT(nsAString &aNotBeforeGMT) nsAutoString date; PRExplodedTime explodedTime; PR_ExplodeTime(mNotBefore, PR_GMTParameters, &explodedTime); - dateFormatter->FormatPRExplodedTime(nullptr, kDateFormatShort, kTimeFormatSecondsForce24Hour, - &explodedTime, date); + dateFormatter->FormatPRExplodedTime(nullptr, kDateFormatLong, + kTimeFormatSeconds, &explodedTime, date); aNotBeforeGMT = date; return NS_OK; } @@ -130,8 +130,8 @@ NS_IMETHODIMP nsX509CertValidity::GetNotAfterLocalTime(nsAString &aNotAfterLocal nsAutoString date; PRExplodedTime explodedTime; PR_ExplodeTime(mNotAfter, PR_LocalTimeParameters, &explodedTime); - dateFormatter->FormatPRExplodedTime(nullptr, kDateFormatShort, kTimeFormatSecondsForce24Hour, - &explodedTime, date); + dateFormatter->FormatPRExplodedTime(nullptr, kDateFormatLong, + kTimeFormatSeconds, &explodedTime, date); aNotAfterLocaltime = date; return NS_OK; } @@ -149,7 +149,7 @@ NS_IMETHODIMP nsX509CertValidity::GetNotAfterLocalDay(nsAString &aNotAfterLocalD nsAutoString date; PRExplodedTime explodedTime; PR_ExplodeTime(mNotAfter, PR_LocalTimeParameters, &explodedTime); - dateFormatter->FormatPRExplodedTime(nullptr, kDateFormatShort, kTimeFormatNone, + dateFormatter->FormatPRExplodedTime(nullptr, kDateFormatLong, kTimeFormatNone, &explodedTime, date); aNotAfterLocalDay = date; return NS_OK; @@ -168,8 +168,8 @@ NS_IMETHODIMP nsX509CertValidity::GetNotAfterGMT(nsAString &aNotAfterGMT) nsAutoString date; PRExplodedTime explodedTime; PR_ExplodeTime(mNotAfter, PR_GMTParameters, &explodedTime); - dateFormatter->FormatPRExplodedTime(nullptr, kDateFormatShort, kTimeFormatSecondsForce24Hour, - &explodedTime, date); + dateFormatter->FormatPRExplodedTime(nullptr, kDateFormatLong, + kTimeFormatSeconds, &explodedTime, date); aNotAfterGMT = date; return NS_OK; } diff --git a/security/manager/ssl/nsNSSCertificate.cpp b/security/manager/ssl/nsNSSCertificate.cpp index e53e10783b..caa3af7928 100644 --- a/security/manager/ssl/nsNSSCertificate.cpp +++ b/security/manager/ssl/nsNSSCertificate.cpp @@ -180,7 +180,7 @@ void nsNSSCertificate::destructorSafeDestroyNSSReference() if (mCertType == nsNSSCertificate::USER_CERT) { nsCOMPtr cxt = new PipUIContext(); PK11_DeleteTokenCertAndKey(mCert.get(), cxt); - } else if (!PK11_IsReadOnly(mCert->slot)) { + } else if (mCert->slot && !PK11_IsReadOnly(mCert->slot)) { // If the list of built-ins does contain a non-removable // copy of this certificate, our call will not remove // the certificate permanently, but rather remove all trust. @@ -225,12 +225,9 @@ nsNSSCertificate::MarkForPermDeletion() // make sure user is logged in to the token nsCOMPtr ctx = new PipUIContext(); - if (PK11_NeedLogin(mCert->slot) - && !PK11_NeedUserInit(mCert->slot) - && !PK11_IsInternal(mCert->slot)) - { - if (SECSuccess != PK11_Authenticate(mCert->slot, true, ctx)) - { + if (mCert->slot && PK11_NeedLogin(mCert->slot) && + !PK11_NeedUserInit(mCert->slot) && !PK11_IsInternal(mCert->slot)) { + if (SECSuccess != PK11_Authenticate(mCert->slot, true, ctx)) { return NS_ERROR_FAILURE; } } diff --git a/security/manager/ssl/nsNSSCertificateDB.cpp b/security/manager/ssl/nsNSSCertificateDB.cpp index 82c55ba11f..4234f59791 100644 --- a/security/manager/ssl/nsNSSCertificateDB.cpp +++ b/security/manager/ssl/nsNSSCertificateDB.cpp @@ -924,6 +924,7 @@ loser: NS_IMETHODIMP nsNSSCertificateDB::DeleteCertificate(nsIX509Cert *aCert) { + NS_ENSURE_ARG_POINTER(aCert); nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; @@ -962,6 +963,7 @@ nsNSSCertificateDB::SetCertTrust(nsIX509Cert *cert, uint32_t type, uint32_t trusted) { + NS_ENSURE_ARG_POINTER(cert); nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; diff --git a/security/manager/ssl/nsNSSComponent.cpp b/security/manager/ssl/nsNSSComponent.cpp index 262cb869eb..d6ac86be3e 100644 --- a/security/manager/ssl/nsNSSComponent.cpp +++ b/security/manager/ssl/nsNSSComponent.cpp @@ -8,54 +8,49 @@ #include "ExtendedValidation.h" #include "NSSCertDBTrustDomain.h" -#include "nsAppDirectoryServiceDefs.h" -#include "nsCertVerificationThread.h" -#include "nsAppDirectoryServiceDefs.h" -#include "nsComponentManagerUtils.h" -#include "nsDirectoryServiceDefs.h" -#include "nsICertOverrideService.h" -#include "NSSCertDBTrustDomain.h" -#include "nsThreadUtils.h" +#include "SharedSSLState.h" #include "mozilla/Preferences.h" -#include "nsThreadUtils.h" #include "mozilla/PublicSSL.h" #include "mozilla/Services.h" #include "mozilla/StaticPtr.h" - +#include "nsAppDirectoryServiceDefs.h" +#include "nsCRT.h" +#include "nsCertVerificationThread.h" +#include "nsClientAuthRemember.h" +#include "nsComponentManagerUtils.h" +#include "nsDirectoryServiceDefs.h" +#include "nsIBufEntropyCollector.h" +#include "nsICertOverrideService.h" +#include "nsIFile.h" +#include "nsIObserverService.h" +#include "nsIPrompt.h" +#include "nsIProperties.h" +#include "nsISiteSecurityService.h" +#include "nsITokenPasswordDialogs.h" +#include "nsIWindowWatcher.h" +#include "nsIXULRuntime.h" +#include "nsNSSHelper.h" +#include "nsNSSShutDown.h" +#include "nsServiceManagerUtils.h" +#include "nsThreadUtils.h" +#include "nsXULAppAPI.h" +#include "nss.h" +#include "p12plcy.h" +#include "pkix/pkixnss.h" +#include "secerr.h" +#include "secmod.h" +#include "ssl.h" +#include "sslerr.h" +#include "sslproto.h" + #ifndef MOZ_NO_SMART_CARDS #include "nsSmartCardMonitor.h" #endif -#include "nsCRT.h" -#include "nsNTLMAuthModule.h" -#include "nsIFile.h" -#include "nsIProperties.h" -#include "nsIWindowWatcher.h" -#include "nsIPrompt.h" -#include "nsIBufEntropyCollector.h" -#include "nsITokenPasswordDialogs.h" -#include "nsISiteSecurityService.h" -#include "nsServiceManagerUtils.h" -#include "nsNSSShutDown.h" -#include "SharedSSLState.h" -#include "NSSErrorsService.h" - -#include "nss.h" -#include "pkix/pkixnss.h" -#include "ssl.h" -#include "sslproto.h" -#include "secmod.h" -#include "secerr.h" -#include "sslerr.h" - -#include "nsXULAppAPI.h" - #ifdef XP_WIN #include "nsILocalFileWin.h" #endif -#include "p12plcy.h" - using namespace mozilla; using namespace mozilla::psm; @@ -63,8 +58,6 @@ PRLogModuleInfo* gPIPNSSLog = nullptr; int nsNSSComponent::mInstanceCount = 0; -bool nsPSMInitPanic::isPanic = false; - // This function can be called from chrome or content processes // to ensure that NSS is initialized. bool EnsureNSSInitializedChromeOrContent() @@ -103,9 +96,6 @@ bool EnsureNSSInitializedChromeOrContent() // creating any other components. bool EnsureNSSInitialized(EnsureNSSOperator op) { - if (nsPSMInitPanic::GetPanic()) - return false; - if (GeckoProcessType_Default != XRE_GetProcessType()) { if (op == nssEnsureOnChromeOnly) @@ -233,12 +223,10 @@ nsNSSComponent::nsNSSComponent() if (!gPIPNSSLog) gPIPNSSLog = PR_NewLogModule("pipnss"); MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsNSSComponent::ctor\n")); - mObserversRegistered = false; NS_ASSERTION( (0 == mInstanceCount), "nsNSSComponent is a singleton, but instantiated multiple times!"); ++mInstanceCount; mShutdownObjectList = nsNSSShutDownList::construct(); - mIsNetworkDown = false; } void @@ -271,6 +259,8 @@ nsNSSComponent::createBackgroundThreads() nsNSSComponent::~nsNSSComponent() { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsNSSComponent::dtor\n")); + NS_ASSERTION(!mCertVerificationThread, + "Cert verification thread should have been cleaned up."); deleteBackgroundThreads(); @@ -328,26 +318,6 @@ nsNSSComponent::GetPIPNSSBundleString(const char* name, nsAString& outString) return rv; } -NS_IMETHODIMP -nsNSSComponent::NSSBundleFormatStringFromName(const char* name, - const char16_t** params, - uint32_t numParams, - nsAString& outString) -{ - nsresult rv = NS_ERROR_FAILURE; - - if (mNSSErrorsBundle && name) { - nsXPIDLString result; - rv = mNSSErrorsBundle->FormatStringFromName(NS_ConvertASCIItoUTF16(name).get(), - params, numParams, - getter_Copies(result)); - if (NS_SUCCEEDED(rv)) { - outString = result; - } - } - return rv; -} - NS_IMETHODIMP nsNSSComponent::GetNSSBundleString(const char* name, nsAString& outString) { @@ -900,7 +870,7 @@ CipherSuiteChangeObserver::Observe(nsISupports* aSubject, return NS_OK; } -} // anonymous namespace +} // namespace // Caller must hold a lock on nsNSSComponent::mutex when calling this function void nsNSSComponent::setValidationOptions(bool isInitialSetting, @@ -921,7 +891,7 @@ void nsNSSComponent::setValidationOptions(bool isInitialSetting, PrivateSSLState()->SetOCSPStaplingEnabled(ocspStaplingEnabled); bool ocspMustStapleEnabled = Preferences::GetBool("security.ssl.enable_ocsp_must_staple", - false); + true); PublicSSLState()->SetOCSPMustStapleEnabled(ocspMustStapleEnabled); PrivateSSLState()->SetOCSPMustStapleEnabled(ocspMustStapleEnabled); @@ -1000,8 +970,8 @@ GetNSSProfilePath(nsAutoCString& aProfilePath) nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(profileFile)); if (NS_FAILED(rv)) { - MOZ_LOG(gPIPNSSLog, LogLevel::Error, - ("Unable to get profile directory - continuing with no NSS DB\n")); + NS_WARNING("NSS will be initialized without a profile directory. " + "Some things may not work as expected."); return NS_OK; } @@ -1024,6 +994,8 @@ GetNSSProfilePath(nsAutoCString& aProfilePath) return rv; } + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("NSS profile at '%s'\n", aProfilePath.get())); return NS_OK; } @@ -1063,20 +1035,32 @@ nsNSSComponent::InitializeNSS() nsAutoCString profileStr; nsresult rv = GetNSSProfilePath(profileStr); if (NS_FAILED(rv)) { - nsPSMInitPanic::SetPanic(); return NS_ERROR_NOT_AVAILABLE; } SECStatus init_rv = SECFailure; bool nocertdb = Preferences::GetBool("security.nocertdb", false); + bool inSafeMode = true; + nsCOMPtr runtime(do_GetService("@mozilla.org/xre/runtime;1")); + // There might not be an nsIXULRuntime in embedded situations. This will + // default to assuming we are in safe mode (as a result, no external PKCS11 + // modules will be loaded). + if (runtime) { + rv = runtime->GetInSafeMode(&inSafeMode); + if (NS_FAILED(rv)) { + return rv; + } + } + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("inSafeMode: %u\n", inSafeMode)); if (!nocertdb && !profileStr.IsEmpty()) { // First try to initialize the NSS DB in read/write mode. - init_rv = ::mozilla::psm::InitializeNSS(profileStr.get(), false); + // Only load PKCS11 modules if we're not in safe mode. + init_rv = ::mozilla::psm::InitializeNSS(profileStr.get(), false, !inSafeMode); // If that fails, attempt read-only mode. if (init_rv != SECSuccess) { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("could not init NSS r/w in %s\n", profileStr.get())); - init_rv = ::mozilla::psm::InitializeNSS(profileStr.get(), true); + init_rv = ::mozilla::psm::InitializeNSS(profileStr.get(), true, !inSafeMode); } if (init_rv != SECSuccess) { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("could not init in r/o either\n")); @@ -1090,7 +1074,6 @@ nsNSSComponent::InitializeNSS() } if (init_rv != SECSuccess) { MOZ_LOG(gPIPNSSLog, LogLevel::Error, ("could not initialize NSS - panicking\n")); - nsPSMInitPanic::SetPanic(); return NS_ERROR_NOT_AVAILABLE; } @@ -1108,7 +1091,6 @@ nsNSSComponent::InitializeNSS() rv = setEnabledTLSVersions(); if (NS_FAILED(rv)) { - nsPSMInitPanic::SetPanic(); return NS_ERROR_UNEXPECTED; } @@ -1126,6 +1108,8 @@ nsNSSComponent::InitializeNSS() SSL_OptionSetDefault(SSL_ENABLE_RENEGOTIATION, SSL_RENEGOTIATE_REQUIRES_XTN); + SSL_OptionSetDefault(SSL_ENABLE_EXTENDED_MASTER_SECRET, true); + SSL_OptionSetDefault(SSL_ENABLE_FALSE_START, Preferences::GetBool("security.ssl.enable_false_start", FALSE_START_ENABLED_DEFAULT)); @@ -1217,7 +1201,7 @@ nsNSSComponent::ShutdownNSS() } } -NS_IMETHODIMP +nsresult nsNSSComponent::Init() { // No mutex protection. @@ -1252,54 +1236,42 @@ nsNSSComponent::Init() getter_Copies(result)); } - // Do that before NSS init, to make sure we won't get unloaded. - RegisterObservers(); rv = InitializeNSS(); if (NS_FAILED(rv)) { - MOZ_LOG(gPIPNSSLog, LogLevel::Error, ("Unable to Initialize NSS.\n")); - - DeregisterObservers(); - mPIPNSSBundle = nullptr; + MOZ_LOG(gPIPNSSLog, LogLevel::Error, + ("nsNSSComponent::InitializeNSS() failed\n")); return rv; } RememberCertErrorsTable::Init(); createBackgroundThreads(); - if (!mCertVerificationThread) - { - MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("NSS init, could not create threads\n")); - - DeregisterObservers(); - mPIPNSSBundle = nullptr; + if (!mCertVerificationThread) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("nsNSSComponent::createBackgroundThreads() failed\n")); return NS_ERROR_OUT_OF_MEMORY; } - nsCOMPtr ec - = do_GetService(NS_ENTROPYCOLLECTOR_CONTRACTID); - - nsCOMPtr bec; - - if (ec) { - bec = do_QueryInterface(ec); + nsCOMPtr ec( + do_GetService(NS_ENTROPYCOLLECTOR_CONTRACTID)); + if (!ec) { + return NS_ERROR_FAILURE; } - - NS_ASSERTION(bec, "No buffering entropy collector. " - "This means no entropy will be collected."); - if (bec) { - bec->ForwardTo(this); + nsCOMPtr bec(do_QueryInterface(ec)); + if (!bec) { + return NS_ERROR_FAILURE; } + bec->ForwardTo(this); - return rv; + return RegisterObservers(); } // nsISupports Implementation for the class NS_IMPL_ISUPPORTS(nsNSSComponent, nsIEntropyCollector, nsINSSComponent, - nsIObserver, - nsISupportsWeakReference) + nsIObserver) NS_IMETHODIMP nsNSSComponent::RandomUpdate(void* entropy, int32_t bufLen) @@ -1318,12 +1290,7 @@ nsNSSComponent::RandomUpdate(void* entropy, int32_t bufLen) return NS_OK; } -static const char* const PROFILE_CHANGE_NET_TEARDOWN_TOPIC - = "profile-change-net-teardown"; -static const char* const PROFILE_CHANGE_NET_RESTORE_TOPIC - = "profile-change-net-restore"; static const char* const PROFILE_BEFORE_CHANGE_TOPIC = "profile-before-change"; -static const char* const PROFILE_DO_CHANGE_TOPIC = "profile-do-change"; NS_IMETHODIMP nsNSSComponent::Observe(nsISupports* aSubject, const char* aTopic, @@ -1331,41 +1298,8 @@ nsNSSComponent::Observe(nsISupports* aSubject, const char* aTopic, { if (nsCRT::strcmp(aTopic, PROFILE_BEFORE_CHANGE_TOPIC) == 0) { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("receiving profile change topic\n")); - DoProfileBeforeChange(aSubject); - } - else if (nsCRT::strcmp(aTopic, PROFILE_DO_CHANGE_TOPIC) == 0) { - if (someData && NS_LITERAL_STRING("startup").Equals(someData)) { - // The application is initializing against a known profile directory for - // the first time during process execution. - // However, earlier code execution might have already triggered NSS init. - // We must ensure that NSS gets shut down prior to any attempt to init - // it again. We use the same cleanup functionality used when switching - // profiles. The order of function calls must correspond to the order - // of notifications sent by Profile Manager (nsProfile). - DoProfileChangeNetTeardown(); - DoProfileBeforeChange(aSubject); - DoProfileChangeNetRestore(); - } - - bool needsInit = true; - - { - MutexAutoLock lock(mutex); - - if (mNSSInitialized) { - // We have already initialized NSS before the profile came up, - // no need to do it again - needsInit = false; - } - } - - if (needsInit) { - if (NS_FAILED(InitializeNSS())) { - MOZ_LOG(gPIPNSSLog, LogLevel::Error, ("Unable to Initialize NSS after profile switch.\n")); - } - } - } - else if (nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) { + DoProfileBeforeChange(); + } else if (nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsNSSComponent: XPCom shutdown observed\n")); @@ -1381,6 +1315,8 @@ nsNSSComponent::Observe(nsISupports* aSubject, const char* aTopic, bec->DontForward(); } } + + deleteBackgroundThreads(); } else if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) { nsNSSShutDownPreventionLock locker; @@ -1429,14 +1365,6 @@ nsNSSComponent::Observe(nsISupports* aSubject, const char* aTopic, if (clearSessionCache) SSL_ClearSessionCache(); } - else if (nsCRT::strcmp(aTopic, PROFILE_CHANGE_NET_TEARDOWN_TOPIC) == 0) { - MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("receiving network teardown topic\n")); - DoProfileChangeNetTeardown(); - } - else if (nsCRT::strcmp(aTopic, PROFILE_CHANGE_NET_RESTORE_TOPIC) == 0) { - MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("receiving network restore topic\n")); - DoProfileChangeNetRestore(); - } return NS_OK; } @@ -1508,64 +1436,27 @@ nsNSSComponent::RegisterObservers() { // Happens once during init only, no mutex protection. - nsCOMPtr observerService(do_GetService("@mozilla.org/observer-service;1")); - NS_ASSERTION(observerService, "could not get observer service"); - if (observerService) { - mObserversRegistered = true; - MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsNSSComponent: adding observers\n")); - - // We are a service. - // Once we are loaded, don't allow being removed from memory. - // This makes sense, as initializing NSS is expensive. - - // By using false for parameter ownsWeak in AddObserver, - // we make sure that we won't get unloaded until the application shuts down. - - observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); - - observerService->AddObserver(this, PROFILE_BEFORE_CHANGE_TOPIC, false); - observerService->AddObserver(this, PROFILE_DO_CHANGE_TOPIC, false); - observerService->AddObserver(this, PROFILE_CHANGE_NET_TEARDOWN_TOPIC, false); - observerService->AddObserver(this, PROFILE_CHANGE_NET_RESTORE_TOPIC, false); + nsCOMPtr observerService( + do_GetService("@mozilla.org/observer-service;1")); + if (!observerService) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("nsNSSComponent: couldn't get observer service\n")); + return NS_ERROR_FAILURE; } - return NS_OK; -} -nsresult -nsNSSComponent::DeregisterObservers() -{ - if (!mObserversRegistered) - return NS_OK; + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsNSSComponent: adding observers\n")); + // Using false for the ownsweak parameter means the observer service will + // keep a strong reference to this component. As a result, this will live at + // least as long as the observer service. + observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); + observerService->AddObserver(this, PROFILE_BEFORE_CHANGE_TOPIC, false); - nsCOMPtr observerService(do_GetService("@mozilla.org/observer-service;1")); - NS_ASSERTION(observerService, "could not get observer service"); - if (observerService) { - mObserversRegistered = false; - MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsNSSComponent: removing observers\n")); - - observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID); - - observerService->RemoveObserver(this, PROFILE_BEFORE_CHANGE_TOPIC); - observerService->RemoveObserver(this, PROFILE_DO_CHANGE_TOPIC); - observerService->RemoveObserver(this, PROFILE_CHANGE_NET_TEARDOWN_TOPIC); - observerService->RemoveObserver(this, PROFILE_CHANGE_NET_RESTORE_TOPIC); - } return NS_OK; } void -nsNSSComponent::DoProfileChangeNetTeardown() +nsNSSComponent::DoProfileBeforeChange() { - if (mCertVerificationThread) - mCertVerificationThread->requestExit(); - mIsNetworkDown = true; -} - -void -nsNSSComponent::DoProfileBeforeChange(nsISupports* aSubject) -{ - NS_ASSERTION(mIsNetworkDown, "nsNSSComponent relies on profile manager to wait for synchronous shutdown of all network activity"); - bool needsCleanup = true; { @@ -1584,15 +1475,6 @@ nsNSSComponent::DoProfileBeforeChange(nsISupports* aSubject) } } -void -nsNSSComponent::DoProfileChangeNetRestore() -{ - // XXX this doesn't work well, since nothing expects null pointers - deleteBackgroundThreads(); - createBackgroundThreads(); - mIsNetworkDown = false; -} - NS_IMETHODIMP nsNSSComponent::IsNSSInitialized(bool* initialized) { diff --git a/security/manager/ssl/nsNSSComponent.h b/security/manager/ssl/nsNSSComponent.h index fa1d877bdb..26017e20b8 100644 --- a/security/manager/ssl/nsNSSComponent.h +++ b/security/manager/ssl/nsNSSComponent.h @@ -13,12 +13,8 @@ #include "nsIEntropyCollector.h" #include "nsIStringBundle.h" #include "nsIObserver.h" -#include "nsIObserverService.h" -#include "nsINSSErrorsService.h" #include "nsNSSCallbacks.h" #include "SharedCertVerifier.h" -#include "nsNSSHelper.h" -#include "nsClientAuthRemember.h" #include "prerror.h" #include "sslt.h" @@ -40,13 +36,9 @@ MOZ_WARN_UNUSED_RESULT #define PSM_COMPONENT_CONTRACTID "@mozilla.org/psm;1" -//Define an interface that we can use to look up from the -//callbacks passed to NSS. - -#define NS_INSSCOMPONENT_IID_STR "e60602a8-97a3-4fe7-b5b7-56bc6ce87ab4" #define NS_INSSCOMPONENT_IID \ - { 0xe60602a8, 0x97a3, 0x4fe7, \ - { 0xb5, 0xb7, 0x56, 0xbc, 0x6c, 0xe8, 0x7a, 0xb4 } } + { 0xa0a8f52b, 0xea18, 0x4abc, \ + { 0xa3, 0xca, 0xec, 0xcf, 0x70, 0x4f, 0xfe, 0x63 } } enum EnsureNSSOperator { @@ -63,10 +55,9 @@ extern bool EnsureNSSInitializedChromeOrContent(); extern bool EnsureNSSInitialized(EnsureNSSOperator op); -class nsNSSComponent; - -class NS_NO_VTABLE nsINSSComponent : public nsISupports { - public: +class NS_NO_VTABLE nsINSSComponent : public nsISupports +{ +public: NS_DECLARE_STATIC_IID_ACCESSOR(NS_INSSCOMPONENT_IID) NS_IMETHOD ShowAlertFromStringBundle(const char* messageID) = 0; @@ -80,10 +71,6 @@ class NS_NO_VTABLE nsINSSComponent : public nsISupports { NS_IMETHOD GetNSSBundleString(const char* name, nsAString& outString) = 0; - NS_IMETHOD NSSBundleFormatStringFromName(const char* name, - const char16_t** params, - uint32_t numParams, - nsAString& outString) = 0; NS_IMETHOD LogoutAuthenticatedPK11() = 0; @@ -105,13 +92,10 @@ class nsNSSShutDownList; class nsCertVerificationThread; // Implementation of the PSM component interface. -class nsNSSComponent final : public nsIEntropyCollector, - public nsINSSComponent, - public nsIObserver, - public nsSupportsWeakReference +class nsNSSComponent final : public nsIEntropyCollector + , public nsINSSComponent + , public nsIObserver { - typedef mozilla::Mutex Mutex; - public: NS_DEFINE_STATIC_CID_ACCESSOR( NS_NSSCOMPONENT_CID ) @@ -121,7 +105,7 @@ public: NS_DECL_NSIENTROPYCOLLECTOR NS_DECL_NSIOBSERVER - NS_METHOD Init(); + nsresult Init(); static nsresult GetNewPrompter(nsIPrompt** result); static nsresult ShowAlertWithConstructedString(const nsString& message); @@ -134,10 +118,6 @@ public: uint32_t numParams, nsAString& outString) override; NS_IMETHOD GetNSSBundleString(const char* name, nsAString& outString) override; - NS_IMETHOD NSSBundleFormatStringFromName(const char* name, - const char16_t** params, - uint32_t numParams, - nsAString& outString) override; NS_IMETHOD LogoutAuthenticatedPK11() override; #ifndef MOZ_NO_SMART_CARDS @@ -179,26 +159,19 @@ private: nsresult InitializePIPNSSBundle(); nsresult ConfigureInternalPKCS11Token(); nsresult RegisterObservers(); - nsresult DeregisterObservers(); - // Methods that we use to handle the profile change notifications (and to - // synthesize a full profile change when we're just doing a profile startup): - void DoProfileChangeNetTeardown(); - void DoProfileBeforeChange(nsISupports* aSubject); - void DoProfileChangeNetRestore(); + void DoProfileBeforeChange(); - Mutex mutex; + mozilla::Mutex mutex; nsCOMPtr mPIPNSSBundle; nsCOMPtr mNSSErrorsBundle; bool mNSSInitialized; - bool mObserversRegistered; static int mInstanceCount; nsNSSShutDownList* mShutdownObjectList; #ifndef MOZ_NO_SMART_CARDS SmartCardThreadList* mThreadList; #endif - bool mIsNetworkDown; void deleteBackgroundThreads(); void createBackgroundThreads(); @@ -220,13 +193,4 @@ public: nsString& returnedMessage); }; -class nsPSMInitPanic -{ -private: - static bool isPanic; -public: - static void SetPanic() {isPanic = true;} - static bool GetPanic() {return isPanic;} -}; - #endif // _nsNSSComponent_h_ diff --git a/security/manager/ssl/nsNSSIOLayer.cpp b/security/manager/ssl/nsNSSIOLayer.cpp index 25b4819585..36e777b5b2 100644 --- a/security/manager/ssl/nsNSSIOLayer.cpp +++ b/security/manager/ssl/nsNSSIOLayer.cpp @@ -1506,9 +1506,11 @@ PrefObserver::Observe(nsISupports* aSubject, const char* aTopic, } else if (prefName.EqualsLiteral("security.tls.version.fallback-limit")) { mOwner->loadVersionFallbackLimit(); } else if (prefName.EqualsLiteral("security.tls.insecure_fallback_hosts")) { - nsCString insecureFallbackHosts; - Preferences::GetCString("security.tls.insecure_fallback_hosts", &insecureFallbackHosts); - mOwner->setInsecureFallbackSites(insecureFallbackHosts); + // Changes to the whitelist on the public side will update the pref. + // Don't propagate the changes to the private side. + if (mOwner->isPublic()) { + mOwner->initInsecureFallbackSites(); + } } else if (prefName.EqualsLiteral("security.tls.insecure_fallback_hosts.use_static_list")) { mOwner->mUseStaticFallbackList = Preferences::GetBool("security.tls.insecure_fallback_hosts.use_static_list", true); @@ -1609,9 +1611,7 @@ nsSSLIOLayerHelpers::Init() Preferences::GetBool("security.ssl.false_start.require-npn", FALSE_START_REQUIRE_NPN_DEFAULT); loadVersionFallbackLimit(); - nsCString insecureFallbackHosts; - Preferences::GetCString("security.tls.insecure_fallback_hosts", &insecureFallbackHosts); - setInsecureFallbackSites(insecureFallbackHosts); + initInsecureFallbackSites(); mUseStaticFallbackList = Preferences::GetBool("security.tls.insecure_fallback_hosts.use_static_list", true); mUnrestrictedRC4Fallback = @@ -1674,6 +1674,102 @@ nsSSLIOLayerHelpers::setInsecureFallbackSites(const nsCString& str) } } +void +nsSSLIOLayerHelpers::initInsecureFallbackSites() +{ + MOZ_ASSERT(NS_IsMainThread()); + nsCString insecureFallbackHosts; + Preferences::GetCString("security.tls.insecure_fallback_hosts", + &insecureFallbackHosts); + setInsecureFallbackSites(insecureFallbackHosts); +} + +bool +nsSSLIOLayerHelpers::isPublic() const +{ + return this == &PublicSSLState()->IOLayerHelpers(); +} + +void +nsSSLIOLayerHelpers::addInsecureFallbackSite(const nsCString& hostname, + bool temporary) +{ + MOZ_ASSERT(NS_IsMainThread()); + { + MutexAutoLock lock(mutex); + if (mInsecureFallbackSites.Contains(hostname)) { + return; + } + mInsecureFallbackSites.PutEntry(hostname); + } + if (!isPublic() || temporary) { + return; + } + nsCString value; + Preferences::GetCString("security.tls.insecure_fallback_hosts", &value); + if (!value.IsEmpty()) { + value.Append(','); + } + value.Append(hostname); + Preferences::SetCString("security.tls.insecure_fallback_hosts", value); +} + +class FallbackPrefRemover final : public nsRunnable +{ +public: + explicit FallbackPrefRemover(const nsACString& aHost) + : mHost(aHost) + {} + NS_IMETHOD Run() override; +private: + nsCString mHost; +}; + +NS_IMETHODIMP +FallbackPrefRemover::Run() +{ + MOZ_ASSERT(NS_IsMainThread()); + nsCString oldValue; + Preferences::GetCString("security.tls.insecure_fallback_hosts", &oldValue); + nsCCharSeparatedTokenizer toker(oldValue, ','); + nsCString newValue; + while (toker.hasMoreTokens()) { + const nsCSubstring& host = toker.nextToken(); + if (host.Equals(mHost)) { + continue; + } + if (!newValue.IsEmpty()) { + newValue.Append(','); + } + newValue.Append(host); + } + Preferences::SetCString("security.tls.insecure_fallback_hosts", newValue); + return NS_OK; +} + +void +nsSSLIOLayerHelpers::removeInsecureFallbackSite(const nsACString& hostname, + uint16_t port) +{ + forgetIntolerance(hostname, port); + { + MutexAutoLock lock(mutex); + if (!mInsecureFallbackSites.Contains(hostname)) { + return; + } + mInsecureFallbackSites.RemoveEntry(hostname); + } + if (!isPublic()) { + return; + } + RefPtr runnable = new FallbackPrefRemover(hostname); + if (NS_IsMainThread()) { + runnable->Run(); + } else { + NS_DispatchToMainThread(runnable); + } +} + struct FallbackListComparator { explicit FallbackListComparator(const char* aTarget) diff --git a/security/manager/ssl/nsNSSIOLayer.h b/security/manager/ssl/nsNSSIOLayer.h index 13b8abad4c..0116827933 100644 --- a/security/manager/ssl/nsNSSIOLayer.h +++ b/security/manager/ssl/nsNSSIOLayer.h @@ -229,6 +229,10 @@ public: void clearStoredData(); void loadVersionFallbackLimit(); void setInsecureFallbackSites(const nsCString& str); + void initInsecureFallbackSites(); + bool isPublic() const; + void addInsecureFallbackSite(const nsCString& hostname, bool temporary); + void removeInsecureFallbackSite(const nsACString& hostname, uint16_t port); bool isInsecureFallbackSite(const nsACString& hostname); bool mFalseStartRequireNPN; diff --git a/security/manager/ssl/nsNSSModule.cpp b/security/manager/ssl/nsNSSModule.cpp index f0f8476503..ea74d1f62a 100644 --- a/security/manager/ssl/nsNSSModule.cpp +++ b/security/manager/ssl/nsNSSModule.cpp @@ -4,48 +4,44 @@ * 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/ModuleUtils.h" - -#include "nsNSSComponent.h" -#include "nsSSLSocketProvider.h" -#include "nsTLSSocketProvider.h" +#include "CertBlocklist.h" +#include "nsCertOverrideService.h" +#include "nsCertPicker.h" +#include "nsCrypto.h" +#include "nsCryptoHash.h" +#include "nsCURILoader.h" +#include "nsDataSignatureVerifier.h" +#include "nsDOMCID.h" //For the NS_CRYPTO_CONTRACTID define +#include "nsEntropyCollector.h" +#include "nsICategoryManager.h" #include "nsKeygenHandler.h" - -#include "nsSDR.h" - +#include "nsKeyModule.h" +#include "mozilla/ModuleUtils.h" +#include "nsNetCID.h" +#include "nsNSSCertificate.h" +#include "nsNSSCertificateDB.h" +#include "nsNSSCertificateFakeTransport.h" +#include "nsNSSComponent.h" +#include "NSSErrorsService.h" +#include "nsNSSVersion.h" +#include "nsNTLMAuthModule.h" #include "nsPK11TokenDB.h" #include "nsPKCS11Slot.h" -#include "nsNSSCertificate.h" -#include "nsNSSCertificateFakeTransport.h" -#include "nsNSSCertificateDB.h" +#include "PSMContentListener.h" +#include "nsRandomGenerator.h" +#include "nsSDR.h" +#include "nsSecureBrowserUIImpl.h" +#include "nsSiteSecurityService.h" +#include "nsSSLSocketProvider.h" +#include "nsSSLStatus.h" +#include "nsTLSSocketProvider.h" +#include "TransportSecurityInfo.h" +#include "WeakCryptoOverride.h" +#include "nsXULAppAPI.h" + #ifdef MOZ_XUL #include "nsCertTree.h" #endif -#include "nsCrypto.h" -#include "nsCryptoHash.h" -//For the NS_CRYPTO_CONTRACTID define -#include "nsDOMCID.h" -#include "nsNetCID.h" -#include "nsCertPicker.h" -#include "nsCURILoader.h" -#include "nsICategoryManager.h" -#include "nsNTLMAuthModule.h" -#include "nsKeyModule.h" -#include "nsDataSignatureVerifier.h" -#include "nsCertOverrideService.h" -#include "nsRandomGenerator.h" -#include "nsSSLStatus.h" -#include "TransportSecurityInfo.h" -#include "NSSErrorsService.h" -#include "nsNSSVersion.h" -#include "CertBlocklist.h" -#include "nsEntropyCollector.h" -#include "nsSecureBrowserUIImpl.h" -#include "nsSiteSecurityService.h" - -#include "nsXULAppAPI.h" - -#include "PSMContentListener.h" #define NS_IS_PROCESS_DEFAULT \ (GeckoProcessType_Default == XRE_GetProcessType()) @@ -223,6 +219,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsEntropyCollector) NS_GENERIC_FACTORY_CONSTRUCTOR(nsSecureBrowserUIImpl) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(CertBlocklist, Init) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsSiteSecurityService, Init) +NS_GENERIC_FACTORY_CONSTRUCTOR(WeakCryptoOverride) NS_DEFINE_NAMED_CID(NS_NSSCOMPONENT_CID); NS_DEFINE_NAMED_CID(NS_SSLSOCKETPROVIDER_CID); @@ -256,6 +253,7 @@ NS_DEFINE_NAMED_CID(NS_ENTROPYCOLLECTOR_CID); NS_DEFINE_NAMED_CID(NS_SECURE_BROWSER_UI_CID); NS_DEFINE_NAMED_CID(NS_SITE_SECURITY_SERVICE_CID); NS_DEFINE_NAMED_CID(NS_CERT_BLOCKLIST_CID); +NS_DEFINE_NAMED_CID(NS_WEAKCRYPTOOVERRIDE_CID); static const mozilla::Module::CIDEntry kNSSCIDs[] = { { &kNS_NSSCOMPONENT_CID, false, nullptr, nsNSSComponentConstructor }, @@ -290,6 +288,7 @@ static const mozilla::Module::CIDEntry kNSSCIDs[] = { { &kNS_SECURE_BROWSER_UI_CID, false, nullptr, nsSecureBrowserUIImplConstructor }, { &kNS_SITE_SECURITY_SERVICE_CID, false, nullptr, nsSiteSecurityServiceConstructor }, { &kNS_CERT_BLOCKLIST_CID, false, nullptr, CertBlocklistConstructor}, + { &kNS_WEAKCRYPTOOVERRIDE_CID, false, nullptr, WeakCryptoOverrideConstructor }, { nullptr } }; @@ -325,6 +324,7 @@ static const mozilla::Module::ContractIDEntry kNSSContracts[] = { { NS_SECURE_BROWSER_UI_CONTRACTID, &kNS_SECURE_BROWSER_UI_CID }, { NS_SSSERVICE_CONTRACTID, &kNS_SITE_SECURITY_SERVICE_CID }, { NS_CERTBLOCKLIST_CONTRACTID, &kNS_CERT_BLOCKLIST_CID }, + { NS_WEAKCRYPTOOVERRIDE_CONTRACTID, &kNS_WEAKCRYPTOOVERRIDE_CID }, { nullptr } }; diff --git a/security/manager/ssl/nsSecureBrowserUIImpl.cpp b/security/manager/ssl/nsSecureBrowserUIImpl.cpp index 1e26f3e0ea..25514f95ec 100644 --- a/security/manager/ssl/nsSecureBrowserUIImpl.cpp +++ b/security/manager/ssl/nsSecureBrowserUIImpl.cpp @@ -31,6 +31,7 @@ #include "imgIRequest.h" #include "nsThreadUtils.h" #include "nsNetCID.h" +#include "nsNetUtil.h" #include "nsCRT.h" #include "nsIInterfaceRequestorUtils.h" #include "nsIProtocolHandler.h" @@ -98,8 +99,7 @@ class nsAutoAtomic { #endif nsSecureBrowserUIImpl::nsSecureBrowserUIImpl() - : mReentrantMonitor("nsSecureBrowserUIImpl.mReentrantMonitor") - , mNotifiedSecurityState(lis_no_security) + : mNotifiedSecurityState(lis_no_security) , mNotifiedToplevelIsEV(false) , mNewToplevelSecurityState(STATE_IS_INSECURE) , mNewToplevelIsEV(false) @@ -114,6 +114,8 @@ nsSecureBrowserUIImpl::nsSecureBrowserUIImpl() #endif , mTransferringRequests(&gMapOps, sizeof(RequestHashEntry)) { + MOZ_ASSERT(NS_IsMainThread()); + ResetStateTracking(); if (!gSecureDocLog) @@ -127,8 +129,10 @@ NS_IMPL_ISUPPORTS(nsSecureBrowserUIImpl, nsISSLStatusProvider) NS_IMETHODIMP -nsSecureBrowserUIImpl::Init(nsIDOMWindow *aWindow) +nsSecureBrowserUIImpl::Init(nsIDOMWindow* aWindow) { + MOZ_ASSERT(NS_IsMainThread()); + if (MOZ_LOG_TEST(gSecureDocLog, LogLevel::Debug)) { nsCOMPtr window(do_QueryReferent(mWindow)); @@ -185,8 +189,9 @@ nsSecureBrowserUIImpl::Init(nsIDOMWindow *aWindow) NS_IMETHODIMP nsSecureBrowserUIImpl::GetState(uint32_t* aState) { - ReentrantMonitorAutoEnter lock(mReentrantMonitor); - return MapInternalToExternalState(aState, mNotifiedSecurityState, mNotifiedToplevelIsEV); + MOZ_ASSERT(NS_IsMainThread()); + return MapInternalToExternalState(aState, mNotifiedSecurityState, + mNotifiedToplevelIsEV); } // static @@ -302,8 +307,9 @@ nsSecureBrowserUIImpl::MapInternalToExternalState(uint32_t* aState, lockIconStat } NS_IMETHODIMP -nsSecureBrowserUIImpl::SetDocShell(nsIDocShell *aDocShell) +nsSecureBrowserUIImpl::SetDocShell(nsIDocShell* aDocShell) { + MOZ_ASSERT(NS_IsMainThread()); nsresult rv; mDocShell = do_GetWeakReference(aDocShell, &rv); return rv; @@ -375,77 +381,63 @@ nsSecureBrowserUIImpl::OnProgressChange(nsIWebProgress* aWebProgress, return NS_OK; } -void nsSecureBrowserUIImpl::ResetStateTracking() +void +nsSecureBrowserUIImpl::ResetStateTracking() { - ReentrantMonitorAutoEnter lock(mReentrantMonitor); - mDocumentRequestsInProgress = 0; mTransferringRequests.Clear(); } void nsSecureBrowserUIImpl::EvaluateAndUpdateSecurityState(nsIRequest* aRequest, - nsISupports *info, + nsISupports* info, bool withNewLocation, bool withNewSink) { - /* I explicitly ignore the camelCase variable naming style here, - I want to make it clear these are temp variables that relate to the - member variables with the same suffix.*/ - - uint32_t temp_NewToplevelSecurityState = nsIWebProgressListener::STATE_IS_INSECURE; - bool temp_NewToplevelIsEV = false; + mNewToplevelIsEV = false; bool updateStatus = false; nsCOMPtr temp_SSLStatus; - temp_NewToplevelSecurityState = - GetSecurityStateFromSecurityInfoAndRequest(info, aRequest); + mNewToplevelSecurityState = + GetSecurityStateFromSecurityInfoAndRequest(info, aRequest); - MOZ_LOG(gSecureDocLog, LogLevel::Debug, - ("SecureUI:%p: OnStateChange: remember mNewToplevelSecurityState => %x\n", this, - temp_NewToplevelSecurityState)); + MOZ_LOG(gSecureDocLog, LogLevel::Debug, + ("SecureUI:%p: OnStateChange: remember mNewToplevelSecurityState => %x\n", + this, mNewToplevelSecurityState)); - nsCOMPtr sp = do_QueryInterface(info); - if (sp) { - // Ignore result - updateStatus = true; - (void) sp->GetSSLStatus(getter_AddRefs(temp_SSLStatus)); - if (temp_SSLStatus) { - bool aTemp; - if (NS_SUCCEEDED(temp_SSLStatus->GetIsExtendedValidation(&aTemp))) { - temp_NewToplevelIsEV = aTemp; - } + nsCOMPtr sp(do_QueryInterface(info)); + if (sp) { + // Ignore result + updateStatus = true; + (void) sp->GetSSLStatus(getter_AddRefs(temp_SSLStatus)); + if (temp_SSLStatus) { + bool aTemp; + if (NS_SUCCEEDED(temp_SSLStatus->GetIsExtendedValidation(&aTemp))) { + mNewToplevelIsEV = aTemp; } } - - // assume temp_NewToplevelSecurityState was set in this scope! - // see code that is directly above - - { - ReentrantMonitorAutoEnter lock(mReentrantMonitor); - mNewToplevelSecurityStateKnown = true; - mNewToplevelSecurityState = temp_NewToplevelSecurityState; - mNewToplevelIsEV = temp_NewToplevelIsEV; - if (updateStatus) { - mSSLStatus = temp_SSLStatus; - } - MOZ_LOG(gSecureDocLog, LogLevel::Debug, - ("SecureUI:%p: remember securityInfo %p\n", this, - info)); - nsCOMPtr associatedContentSecurityFromRequest = - do_QueryInterface(aRequest); - if (associatedContentSecurityFromRequest) - mCurrentToplevelSecurityInfo = aRequest; - else - mCurrentToplevelSecurityInfo = info; - - // The subrequest counters are now in sync with - // mCurrentToplevelSecurityInfo, don't restore after top level - // document load finishes. - mRestoreSubrequests = false; } + mNewToplevelSecurityStateKnown = true; + if (updateStatus) { + mSSLStatus = temp_SSLStatus; + } + MOZ_LOG(gSecureDocLog, LogLevel::Debug, + ("SecureUI:%p: remember securityInfo %p\n", this, + info)); + nsCOMPtr associatedContentSecurityFromRequest( + do_QueryInterface(aRequest)); + if (associatedContentSecurityFromRequest) { + mCurrentToplevelSecurityInfo = aRequest; + } else { + mCurrentToplevelSecurityInfo = info; + } + + // The subrequest counters are now in sync with mCurrentToplevelSecurityInfo, + // don't restore after top level document load finishes. + mRestoreSubrequests = false; + UpdateSecurityState(aRequest, withNewLocation, withNewSink || updateStatus); } @@ -458,9 +450,6 @@ nsSecureBrowserUIImpl::UpdateSubrequestMembers(nsISupports* securityInfo, uint32_t reqState = GetSecurityStateFromSecurityInfoAndRequest(securityInfo, request); - // the code above this line should run without a lock - ReentrantMonitorAutoEnter lock(mReentrantMonitor); - if (reqState & STATE_IS_SECURE) { // do nothing } else if (reqState & STATE_IS_BROKEN) { @@ -480,6 +469,7 @@ nsSecureBrowserUIImpl::OnStateChange(nsIWebProgress* aWebProgress, uint32_t aProgressStateFlags, nsresult aStatus) { + MOZ_ASSERT(NS_IsMainThread()); #ifdef DEBUG nsAutoAtomic atomic(mOnStateLocationChangeReentranceDetection); NS_ASSERTION(mOnStateLocationChangeReentranceDetection == 1, @@ -578,27 +568,11 @@ nsSecureBrowserUIImpl::OnStateChange(nsIWebProgress* aWebProgress, nsCOMPtr windowForProgress; aWebProgress->GetDOMWindow(getter_AddRefs(windowForProgress)); - nsCOMPtr window; - bool isViewSource; + nsCOMPtr window(do_QueryReferent(mWindow)); + NS_ASSERTION(window, "Window has gone away?!"); - nsCOMPtr ioService; - - { - ReentrantMonitorAutoEnter lock(mReentrantMonitor); - window = do_QueryReferent(mWindow); - NS_ASSERTION(window, "Window has gone away?!"); - isViewSource = mIsViewSource; - ioService = mIOService; - } - - if (!ioService) - { - ioService = do_GetService(NS_IOSERVICE_CONTRACTID); - if (ioService) - { - ReentrantMonitorAutoEnter lock(mReentrantMonitor); - mIOService = ioService; - } + if (!mIOService) { + mIOService = do_GetService(NS_IOSERVICE_CONTRACTID); } bool isNoContentResponse = false; @@ -633,8 +607,9 @@ nsSecureBrowserUIImpl::OnStateChange(nsIWebProgress* aWebProgress, MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI:%p: OnStateChange\n", this)); - if (isViewSource) + if (mIsViewSource) { return NS_OK; + } if (!aRequest) { @@ -725,12 +700,12 @@ nsSecureBrowserUIImpl::OnStateChange(nsIWebProgress* aWebProgress, // This will ignore all resource, chrome, data, file, moz-icon, and anno // protocols. Local resources are treated as trusted. - if (uri && ioService) { + if (uri && mIOService) { bool hasFlag; - nsresult rv = - ioService->URIChainHasFlags(uri, - nsIProtocolHandler::URI_IS_LOCAL_RESOURCE, - &hasFlag); + nsresult rv = + mIOService->URIChainHasFlags(uri, + nsIProtocolHandler::URI_IS_LOCAL_RESOURCE, + &hasFlag); if (NS_SUCCEEDED(rv) && hasFlag) { isSubDocumentRelevant = false; } @@ -857,8 +832,6 @@ nsSecureBrowserUIImpl::OnStateChange(nsIWebProgress* aWebProgress, { // The listing of a request in mTransferringRequests // means, there has already been data transfered. - - ReentrantMonitorAutoEnter lock(mReentrantMonitor); mTransferringRequests.Add(aRequest, fallible); return NS_OK; @@ -870,13 +843,10 @@ nsSecureBrowserUIImpl::OnStateChange(nsIWebProgress* aWebProgress, && aProgressStateFlags & STATE_IS_REQUEST) { - { /* scope for the ReentrantMonitorAutoEnter */ - ReentrantMonitorAutoEnter lock(mReentrantMonitor); - PLDHashEntryHdr* entry = mTransferringRequests.Search(aRequest); - if (entry) { - mTransferringRequests.RemoveEntry(entry); - requestHasTransferedData = true; - } + PLDHashEntryHdr* entry = mTransferringRequests.Search(aRequest); + if (entry) { + mTransferringRequests.RemoveEntry(entry); + requestHasTransferedData = true; } if (!requestHasTransferedData) { @@ -913,8 +883,6 @@ nsSecureBrowserUIImpl::OnStateChange(nsIWebProgress* aWebProgress, && loadFlags & nsIChannel::LOAD_DOCUMENT_URI) { - bool inProgress; - int32_t saveSubBroken; int32_t saveSubNo; nsCOMPtr prevContentSecurity; @@ -922,16 +890,12 @@ nsSecureBrowserUIImpl::OnStateChange(nsIWebProgress* aWebProgress, int32_t newSubBroken = 0; int32_t newSubNo = 0; - { - ReentrantMonitorAutoEnter lock(mReentrantMonitor); - inProgress = (mDocumentRequestsInProgress!=0); + bool inProgress = (mDocumentRequestsInProgress != 0); - if (allowSecurityStateChange && !inProgress) - { - saveSubBroken = mSubRequestsBrokenSecurity; - saveSubNo = mSubRequestsNoSecurity; - prevContentSecurity = do_QueryInterface(mCurrentToplevelSecurityInfo); - } + if (allowSecurityStateChange && !inProgress) { + saveSubBroken = mSubRequestsBrokenSecurity; + saveSubNo = mSubRequestsNoSecurity; + prevContentSecurity = do_QueryInterface(mCurrentToplevelSecurityInfo); } if (allowSecurityStateChange && !inProgress) @@ -999,26 +963,21 @@ nsSecureBrowserUIImpl::OnStateChange(nsIWebProgress* aWebProgress, } } - { - ReentrantMonitorAutoEnter lock(mReentrantMonitor); - - if (allowSecurityStateChange && !inProgress) - { - ResetStateTracking(); - mSubRequestsBrokenSecurity = newSubBroken; - mSubRequestsNoSecurity = newSubNo; - mNewToplevelSecurityStateKnown = false; - } - - // By using a counter, this code also works when the toplevel - // document get's redirected, but the STOP request for the - // previous toplevel document has not yet have been received. - MOZ_LOG(gSecureDocLog, LogLevel::Debug, - ("SecureUI:%p: OnStateChange: ++mDocumentRequestsInProgress\n", this - )); - ++mDocumentRequestsInProgress; + if (allowSecurityStateChange && !inProgress) { + ResetStateTracking(); + mSubRequestsBrokenSecurity = newSubBroken; + mSubRequestsNoSecurity = newSubNo; + mNewToplevelSecurityStateKnown = false; } + // By using a counter, this code also works when the toplevel + // document get's redirected, but the STOP request for the + // previous toplevel document has not yet have been received. + MOZ_LOG(gSecureDocLog, LogLevel::Debug, + ("SecureUI:%p: OnStateChange: ++mDocumentRequestsInProgress\n", this + )); + ++mDocumentRequestsInProgress; + return NS_OK; } @@ -1030,20 +989,13 @@ nsSecureBrowserUIImpl::OnStateChange(nsIWebProgress* aWebProgress, && loadFlags & nsIChannel::LOAD_DOCUMENT_URI) { - int32_t temp_DocumentRequestsInProgress; nsCOMPtr temp_ToplevelEventSink; - { - ReentrantMonitorAutoEnter lock(mReentrantMonitor); - temp_DocumentRequestsInProgress = mDocumentRequestsInProgress; - if (allowSecurityStateChange) - { - temp_ToplevelEventSink = mToplevelEventSink; - } + if (allowSecurityStateChange) { + temp_ToplevelEventSink = mToplevelEventSink; } - if (temp_DocumentRequestsInProgress <= 0) - { + if (mDocumentRequestsInProgress <= 0) { // Ignore stop requests unless a document load is in progress // Unfortunately on application start, see some stops without having seen any starts... return NS_OK; @@ -1063,16 +1015,12 @@ nsSecureBrowserUIImpl::OnStateChange(nsIWebProgress* aWebProgress, bool sinkChanged = false; bool inProgress; - { - ReentrantMonitorAutoEnter lock(mReentrantMonitor); - if (allowSecurityStateChange) - { - sinkChanged = (mToplevelEventSink != temp_ToplevelEventSink); - mToplevelEventSink = temp_ToplevelEventSink; - } - --mDocumentRequestsInProgress; - inProgress = mDocumentRequestsInProgress > 0; + if (allowSecurityStateChange) { + sinkChanged = (mToplevelEventSink != temp_ToplevelEventSink); + mToplevelEventSink = temp_ToplevelEventSink; } + --mDocumentRequestsInProgress; + inProgress = mDocumentRequestsInProgress > 0; if (allowSecurityStateChange && requestHasTransferedData) { // Data has been transferred for the single toplevel @@ -1099,18 +1047,14 @@ nsSecureBrowserUIImpl::OnStateChange(nsIWebProgress* aWebProgress, // app handler. Restore mSubRequests* members to what the current security // state info holds (it was reset to all zero in OnStateChange(START) // before). - nsCOMPtr currentContentSecurity; - { - ReentrantMonitorAutoEnter lock(mReentrantMonitor); - currentContentSecurity = do_QueryInterface(mCurrentToplevelSecurityInfo); + nsCOMPtr currentContentSecurity( + do_QueryInterface(mCurrentToplevelSecurityInfo)); - // Drop this indication flag, the restore opration is just being - // done. - mRestoreSubrequests = false; + // Drop this indication flag, the restore operation is just being done. + mRestoreSubrequests = false; - // We can do this since the state didn't actually change. - mNewToplevelSecurityStateKnown = true; - } + // We can do this since the state didn't actually change. + mNewToplevelSecurityStateKnown = true; int32_t subBroken = 0; int32_t subNo = 0; @@ -1123,31 +1067,28 @@ nsSecureBrowserUIImpl::OnStateChange(nsIWebProgress* aWebProgress, this, currentContentSecurity.get(), subBroken, subNo)); } - { - ReentrantMonitorAutoEnter lock(mReentrantMonitor); - mSubRequestsBrokenSecurity = subBroken; - mSubRequestsNoSecurity = subNo; - } + mSubRequestsBrokenSecurity = subBroken; + mSubRequestsNoSecurity = subNo; } - + return NS_OK; } - + if (aProgressStateFlags & STATE_STOP && aProgressStateFlags & STATE_IS_REQUEST) { if (!isSubDocumentRelevant) return NS_OK; - + // if we arrive here, LOAD_DOCUMENT_URI is not set - + // We only care for the security state of sub requests which have actually transfered data. if (allowSecurityStateChange && requestHasTransferedData) { UpdateSubrequestMembers(securityInfo, aRequest); - + // Care for the following scenario: // A new top level document load might have already started, // but the security state of the new top level document might not yet been known. @@ -1158,13 +1099,7 @@ nsSecureBrowserUIImpl::OnStateChange(nsIWebProgress* aWebProgress, // // We skip updating the security state in this case. - bool temp_NewToplevelSecurityStateKnown; - { - ReentrantMonitorAutoEnter lock(mReentrantMonitor); - temp_NewToplevelSecurityStateKnown = mNewToplevelSecurityStateKnown; - } - - if (temp_NewToplevelSecurityStateKnown) { + if (mNewToplevelSecurityStateKnown) { UpdateSecurityState(aRequest, false, false); } } @@ -1232,20 +1167,15 @@ nsSecureBrowserUIImpl::UpdateSecurityState(nsIRequest* aRequest, void nsSecureBrowserUIImpl::TellTheWorld(nsIRequest* aRequest) { - nsCOMPtr toplevelEventSink; uint32_t state = STATE_IS_INSECURE; - { - ReentrantMonitorAutoEnter lock(mReentrantMonitor); - toplevelEventSink = mToplevelEventSink; - GetState(&state); - } + GetState(&state); - if (toplevelEventSink) { + if (mToplevelEventSink) { MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI:%p: UpdateSecurityState: calling OnSecurityChange\n", this)); - toplevelEventSink->OnSecurityChange(aRequest, state); + mToplevelEventSink->OnSecurityChange(aRequest, state); } else { MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI:%p: UpdateSecurityState: NO mToplevelEventSink!\n", @@ -1260,6 +1190,7 @@ nsSecureBrowserUIImpl::OnLocationChange(nsIWebProgress* aWebProgress, nsIURI* aLocation, uint32_t aFlags) { + MOZ_ASSERT(NS_IsMainThread()); #ifdef DEBUG nsAutoAtomic atomic(mOnStateLocationChangeReentranceDetection); NS_ASSERTION(mOnStateLocationChangeReentranceDetection == 1, @@ -1288,15 +1219,12 @@ nsSecureBrowserUIImpl::OnLocationChange(nsIWebProgress* aWebProgress, temp_IsViewSource = vs; } - { - ReentrantMonitorAutoEnter lock(mReentrantMonitor); - if (updateIsViewSource) { - mIsViewSource = temp_IsViewSource; - } - mCurrentURI = aLocation; - window = do_QueryReferent(mWindow); - NS_ASSERTION(window, "Window has gone away?!"); + if (updateIsViewSource) { + mIsViewSource = temp_IsViewSource; } + mCurrentURI = aLocation; + window = do_QueryReferent(mWindow); + NS_ASSERTION(window, "Window has gone away?!"); // When |aRequest| is null, basically we don't trust that document. But if // docshell insists that the document has not changed at all, we will reuse @@ -1338,13 +1266,7 @@ nsSecureBrowserUIImpl::OnLocationChange(nsIWebProgress* aWebProgress, // // We skip updating the security state in this case. - bool temp_NewToplevelSecurityStateKnown; - { - ReentrantMonitorAutoEnter lock(mReentrantMonitor); - temp_NewToplevelSecurityStateKnown = mNewToplevelSecurityStateKnown; - } - - if (temp_NewToplevelSecurityStateKnown) { + if (mNewToplevelSecurityStateKnown) { UpdateSecurityState(aRequest, true, false); } @@ -1362,10 +1284,11 @@ nsSecureBrowserUIImpl::OnStatusChange(nsIWebProgress* aWebProgress, } nsresult -nsSecureBrowserUIImpl::OnSecurityChange(nsIWebProgress *aWebProgress, - nsIRequest *aRequest, +nsSecureBrowserUIImpl::OnSecurityChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, uint32_t state) { + MOZ_ASSERT(NS_IsMainThread()); #if defined(DEBUG) nsCOMPtr channel(do_QueryInterface(aRequest)); if (!channel) @@ -1373,7 +1296,7 @@ nsSecureBrowserUIImpl::OnSecurityChange(nsIWebProgress *aWebProgress, nsCOMPtr aURI; channel->GetURI(getter_AddRefs(aURI)); - + if (aURI) { nsAutoCString temp; aURI->GetSpec(temp); @@ -1391,8 +1314,7 @@ NS_IMETHODIMP nsSecureBrowserUIImpl::GetSSLStatus(nsISSLStatus** _result) { NS_ENSURE_ARG_POINTER(_result); - - ReentrantMonitorAutoEnter lock(mReentrantMonitor); + MOZ_ASSERT(NS_IsMainThread()); switch (mNotifiedSecurityState) { @@ -1407,7 +1329,7 @@ nsSecureBrowserUIImpl::GetSSLStatus(nsISSLStatus** _result) *_result = nullptr; return NS_OK; } - + *_result = mSSLStatus; NS_IF_ADDREF(*_result); diff --git a/security/manager/ssl/nsSecureBrowserUIImpl.h b/security/manager/ssl/nsSecureBrowserUIImpl.h index 54afc63afa..f40319a172 100644 --- a/security/manager/ssl/nsSecureBrowserUIImpl.h +++ b/security/manager/ssl/nsSecureBrowserUIImpl.h @@ -39,10 +39,9 @@ class nsSecureBrowserUIImpl : public nsISecureBrowserUI, public nsISSLStatusProvider { public: - nsSecureBrowserUIImpl(); - - NS_DECL_THREADSAFE_ISUPPORTS + + NS_DECL_ISUPPORTS NS_DECL_NSIWEBPROGRESSLISTENER NS_DECL_NSISECUREBROWSERUI NS_DECL_NSISSLSTATUSPROVIDER @@ -50,14 +49,12 @@ public: protected: virtual ~nsSecureBrowserUIImpl() {}; - mozilla::ReentrantMonitor mReentrantMonitor; - nsWeakPtr mWindow; nsWeakPtr mDocShell; nsCOMPtr mIOService; nsCOMPtr mCurrentURI; nsCOMPtr mToplevelEventSink; - + enum lockIconState { lis_no_security, lis_broken_security, @@ -80,7 +77,6 @@ protected: bool mRestoreSubrequests; bool mOnLocationChangeSeen; #ifdef DEBUG - /* related to mReentrantMonitor */ mozilla::Atomic mOnStateLocationChangeReentranceDetection; #endif diff --git a/security/manager/ssl/nsSecurityHeaderParser.h b/security/manager/ssl/nsSecurityHeaderParser.h index 0237895cfa..eee29127e6 100644 --- a/security/manager/ssl/nsSecurityHeaderParser.h +++ b/security/manager/ssl/nsSecurityHeaderParser.h @@ -12,8 +12,8 @@ // Utility class for handing back parsed directives and (optional) values class nsSecurityHeaderDirective : public mozilla::LinkedListElement { public: - nsAutoCString mName; - nsAutoCString mValue; + nsCString mName; + nsCString mValue; }; // This class parses security-related HTTP headers like @@ -67,7 +67,7 @@ private: const char *mCursor; nsSecurityHeaderDirective *mDirective; - nsAutoCString mOutput; + nsCString mOutput; bool mError; }; diff --git a/security/manager/ssl/nsSiteSecurityService.cpp b/security/manager/ssl/nsSiteSecurityService.cpp index 6b9c8ec5cb..8cfaa73ee9 100644 --- a/security/manager/ssl/nsSiteSecurityService.cpp +++ b/security/manager/ssl/nsSiteSecurityService.cpp @@ -13,6 +13,7 @@ #include "nsISSLStatus.h" #include "nsISocketProvider.h" #include "nsIURI.h" +#include "nsIX509Cert.h" #include "nsNetUtil.h" #include "nsNSSComponent.h" #include "nsSecurityHeaderParser.h" diff --git a/security/manager/ssl/tests/unit/head_psm.js b/security/manager/ssl/tests/unit/head_psm.js index 9f7b9b9fd9..54a5eca9cd 100644 --- a/security/manager/ssl/tests/unit/head_psm.js +++ b/security/manager/ssl/tests/unit/head_psm.js @@ -63,6 +63,7 @@ const SEC_ERROR_POLICY_VALIDATION_FAILED = SEC_ERROR_BASE + 160; const SEC_ERROR_OCSP_BAD_SIGNATURE = SEC_ERROR_BASE + 157; const SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED = SEC_ERROR_BASE + 176; +const SSL_ERROR_NO_CYPHER_OVERLAP = SSL_ERROR_BASE + 2; const SSL_ERROR_BAD_CERT_DOMAIN = SSL_ERROR_BASE + 12; const SSL_ERROR_BAD_CERT_ALERT = SSL_ERROR_BASE + 17; const SSL_ERROR_WEAK_SERVER_CERT_KEY = SSL_ERROR_BASE + 132; diff --git a/security/manager/ssl/tests/unit/test_pkcs11_safe_mode.js b/security/manager/ssl/tests/unit/test_pkcs11_safe_mode.js new file mode 100644 index 0000000000..e3ac01509f --- /dev/null +++ b/security/manager/ssl/tests/unit/test_pkcs11_safe_mode.js @@ -0,0 +1,57 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +// In safe mode, PKCS#11 modules should not be loaded. This test tests this by +// simulating starting in safe mode and then attempting to load a module. + +var { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {}); + +function run_test() { + do_get_profile(); + + // Simulate starting in safe mode. + let xulRuntime = { + inSafeMode: true, + logConsoleErrors: true, + OS: "XPCShell", + XPCOMABI: "noarch-spidermonkey", + invalidateCachesOnRestart: function invalidateCachesOnRestart() { + // Do nothing + }, + QueryInterface: XPCOMUtils.generateQI([Ci.nsIXULRuntime]) + }; + + let xulRuntimeFactory = { + createInstance: function (outer, iid) { + if (outer != null) { + throw Cr.NS_ERROR_NO_AGGREGATION; + } + return xulRuntime.QueryInterface(iid); + } + }; + + let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); + const XULRUNTIME_CONTRACTID = "@mozilla.org/xre/runtime;1"; + const XULRUNTIME_CID = Components.ID("{f0f0b230-5525-4127-98dc-7bca39059e70}"); + registrar.registerFactory(XULRUNTIME_CID, "XULRuntime", XULRUNTIME_CONTRACTID, + xulRuntimeFactory); + + // When starting in safe mode, the test module should fail to load. + let pkcs11 = Cc["@mozilla.org/security/pkcs11;1"].getService(Ci.nsIPKCS11); + let libraryName = ctypes.libraryName("pkcs11testmodule"); + let libraryFile = Services.dirsvc.get("CurWorkD", Ci.nsILocalFile); + libraryFile.append("pkcs11testmodule"); + libraryFile.append(libraryName); + ok(libraryFile.exists(), "The pkcs11testmodule file should exist"); + let exceptionCaught = false; + try { + pkcs11.addModule("PKCS11 Test Module", libraryFile.path, 0, 0); + ok(false, "addModule should have thrown an exception"); + } catch (e) { + exceptionCaught = true; + } + ok(exceptionCaught, "addModule should have thrown an exception"); +} diff --git a/security/manager/ssl/tests/unit/test_weak_crypto.js b/security/manager/ssl/tests/unit/test_weak_crypto.js index ab3bfaca18..d93fe7c621 100644 --- a/security/manager/ssl/tests/unit/test_weak_crypto.js +++ b/security/manager/ssl/tests/unit/test_weak_crypto.js @@ -103,6 +103,8 @@ function storeCertOverride(port, cert) { } function startClient(port, expectedResult, options = {}) { + let SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE; + let SSL_ERROR_BAD_CERT_ALERT = SSL_ERROR_BASE + 17; let transport = socketTransportService.createTransport(["ssl"], 1, "127.0.0.1", port, null); if (options.isPrivate) { @@ -111,7 +113,8 @@ function startClient(port, expectedResult, options = {}) { let input; let output; - let deferred = Promise.defer(); + let inputDeferred = Promise.defer(); + let outputDeferred = Promise.defer(); let handler = { @@ -122,48 +125,46 @@ function startClient(port, expectedResult, options = {}) { }, onInputStreamReady: function(input) { + let errorCode = Cr.NS_OK; try { let data = NetUtil.readInputStreamToString(input, input.available()); equal(data, "HELLO", "Echoed data received"); input.close(); output.close(); - deferred.resolve(); } catch (e) { - deferred.reject(e); + errorCode = e.result; + } + try { + equal(errorCode, expectedResult, + "Actual and expected connection result should match"); + inputDeferred.resolve(); + } catch (e) { + inputDeferred.reject(e); } }, onOutputStreamReady: function(output) { try { - try { - output.write("HELLO", 5); - } catch (e) { - if (e.result == Cr.NS_BASE_STREAM_WOULD_BLOCK) { - output.asyncWait(handler, 0, 0, Services.tm.currentThread); - return; - } - equal(e.result, expectedResult, - "Actual and expected connection result should match"); - output.close(); - deferred.resolve(); - return; - } - equal(Cr.NS_OK, expectedResult, "Connection should succeed"); + output.write("HELLO", 5); do_print("Output to server written"); + outputDeferred.resolve(); input = transport.openInputStream(0, 0, 0); input.asyncWait(handler, 0, 0, Services.tm.currentThread); } catch (e) { - deferred.reject(e); + let errorCode = -1 * (e.result & 0xFFFF); + if (errorCode == SSL_ERROR_BAD_CERT_ALERT) { + do_print("Server doesn't like client cert"); + } + outputDeferred.reject(e); } } }; transport.setEventSink(handler, Services.tm.currentThread); - output = transport.openOutputStream(Ci.nsITransport.OPEN_UNBUFFERED, 0, 0); - output.QueryInterface(Ci.nsIAsyncOutputStream); + output = transport.openOutputStream(0, 0, 0); - return deferred.promise; + return Promise.all([inputDeferred.promise, outputDeferred.promise]); } function run_test() { diff --git a/security/manager/ssl/tests/unit/xpcshell-smartcards.ini b/security/manager/ssl/tests/unit/xpcshell-smartcards.ini index a5a420d5c1..349df9e96a 100644 --- a/security/manager/ssl/tests/unit/xpcshell-smartcards.ini +++ b/security/manager/ssl/tests/unit/xpcshell-smartcards.ini @@ -11,3 +11,6 @@ skip-if = os == "win" [test_pkcs11_no_events_after_removal.js] # Bug 1049969: this test doesn't work on windows skip-if = os == "win" +[test_pkcs11_safe_mode.js] +# Bug 1049969: this test doesn't work on windows +skip-if = os == "win" diff --git a/security/sandbox/chromium-shim/sandbox/win/sandboxLogging.cpp b/security/sandbox/chromium-shim/sandbox/win/sandboxLogging.cpp index ea0c09730f..6db62d6d3e 100644 --- a/security/sandbox/chromium-shim/sandbox/win/sandboxLogging.cpp +++ b/security/sandbox/chromium-shim/sandbox/win/sandboxLogging.cpp @@ -61,12 +61,12 @@ LogBlocked(const char* aFunctionName, const wchar_t* aContext) void LogBlocked(const char* aFunctionName, const wchar_t* aContext, - uint16_t aLength) + uint16_t aLengthInBytes) { if (sLogFunction) { // Skip an extra frame to allow for this function. LogBlocked(aFunctionName, - base::WideToUTF8(std::wstring(aContext, aLength)).c_str(), + base::WideToUTF8(std::wstring(aContext, aLengthInBytes / sizeof(wchar_t))).c_str(), /* aFramesToSkip */ 3); } } @@ -90,11 +90,11 @@ LogAllowed(const char* aFunctionName, const wchar_t* aContext) void LogAllowed(const char* aFunctionName, const wchar_t* aContext, - uint16_t aLength) + uint16_t aLengthInBytes) { if (sLogFunction) { LogAllowed(aFunctionName, - base::WideToUTF8(std::wstring(aContext, aLength)).c_str()); + base::WideToUTF8(std::wstring(aContext, aLengthInBytes / sizeof(wchar_t))).c_str()); } } diff --git a/security/sandbox/chromium-shim/sandbox/win/sandboxLogging.h b/security/sandbox/chromium-shim/sandbox/win/sandboxLogging.h index dc9fff8712..d5ebdffd7b 100644 --- a/security/sandbox/chromium-shim/sandbox/win/sandboxLogging.h +++ b/security/sandbox/chromium-shim/sandbox/win/sandboxLogging.h @@ -43,7 +43,7 @@ void LogBlocked(const char* aFunctionName, const char* aContext = nullptr, // Convenience functions to convert to char*. void LogBlocked(const char* aFunctionName, const wchar_t* aContext); void LogBlocked(const char* aFunctionName, const wchar_t* aContext, - uint16_t aLength); + uint16_t aLengthInBytes); // Log a "ALLOWED" msg to the browser console and, if DEBUG build, stderr. void LogAllowed(const char* aFunctionName, const char* aContext = nullptr); @@ -51,7 +51,7 @@ void LogAllowed(const char* aFunctionName, const char* aContext = nullptr); // Convenience functions to convert to char*. void LogAllowed(const char* aFunctionName, const wchar_t* aContext); void LogAllowed(const char* aFunctionName, const wchar_t* aContext, - uint16_t aLength); + uint16_t aLengthInBytes); } // sandboxing diff --git a/security/sandbox/chromium/base/strings/safe_sprintf.cc b/security/sandbox/chromium/base/strings/safe_sprintf.cc new file mode 100644 index 0000000000..5b57563551 --- /dev/null +++ b/security/sandbox/chromium/base/strings/safe_sprintf.cc @@ -0,0 +1,685 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/strings/safe_sprintf.h" + +#include + +#if !defined(NDEBUG) +// In debug builds, we use RAW_CHECK() to print useful error messages, if +// SafeSPrintf() is called with broken arguments. +// As our contract promises that SafeSPrintf() can be called from any +// restricted run-time context, it is not actually safe to call logging +// functions from it; and we only ever do so for debug builds and hope for the +// best. We should _never_ call any logging function other than RAW_CHECK(), +// and we should _never_ include any logging code that is active in production +// builds. Most notably, we should not include these logging functions in +// unofficial release builds, even though those builds would otherwise have +// DCHECKS() enabled. +// In other words; please do not remove the #ifdef around this #include. +// Instead, in production builds we opt for returning a degraded result, +// whenever an error is encountered. +// E.g. The broken function call +// SafeSPrintf("errno = %d (%x)", errno, strerror(errno)) +// will print something like +// errno = 13, (%x) +// instead of +// errno = 13 (Access denied) +// In most of the anticipated use cases, that's probably the preferred +// behavior. +#include "base/logging.h" +#define DEBUG_CHECK RAW_CHECK +#else +#define DEBUG_CHECK(x) do { if (x) { } } while (0) +#endif + +namespace base { +namespace strings { + +// The code in this file is extremely careful to be async-signal-safe. +// +// Most obviously, we avoid calling any code that could dynamically allocate +// memory. Doing so would almost certainly result in bugs and dead-locks. +// We also avoid calling any other STL functions that could have unintended +// side-effects involving memory allocation or access to other shared +// resources. +// +// But on top of that, we also avoid calling other library functions, as many +// of them have the side-effect of calling getenv() (in order to deal with +// localization) or accessing errno. The latter sounds benign, but there are +// several execution contexts where it isn't even possible to safely read let +// alone write errno. +// +// The stated design goal of the SafeSPrintf() function is that it can be +// called from any context that can safely call C or C++ code (i.e. anything +// that doesn't require assembly code). +// +// For a brief overview of some but not all of the issues with async-signal- +// safety, refer to: +// http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html + +namespace { +const size_t kSSizeMaxConst = ((size_t)(ssize_t)-1) >> 1; + +const char kUpCaseHexDigits[] = "0123456789ABCDEF"; +const char kDownCaseHexDigits[] = "0123456789abcdef"; +} + +#if defined(NDEBUG) +// We would like to define kSSizeMax as std::numeric_limits::max(), +// but C++ doesn't allow us to do that for constants. Instead, we have to +// use careful casting and shifting. We later use a COMPILE_ASSERT to +// verify that this worked correctly. +namespace { +const size_t kSSizeMax = kSSizeMaxConst; +} +#else // defined(NDEBUG) +// For efficiency, we really need kSSizeMax to be a constant. But for unit +// tests, it should be adjustable. This allows us to verify edge cases without +// having to fill the entire available address space. As a compromise, we make +// kSSizeMax adjustable in debug builds, and then only compile that particular +// part of the unit test in debug builds. +namespace { +static size_t kSSizeMax = kSSizeMaxConst; +} + +namespace internal { +void SetSafeSPrintfSSizeMaxForTest(size_t max) { + kSSizeMax = max; +} + +size_t GetSafeSPrintfSSizeMaxForTest() { + return kSSizeMax; +} +} +#endif // defined(NDEBUG) + +namespace { +class Buffer { + public: + // |buffer| is caller-allocated storage that SafeSPrintf() writes to. It + // has |size| bytes of writable storage. It is the caller's responsibility + // to ensure that the buffer is at least one byte in size, so that it fits + // the trailing NUL that will be added by the destructor. The buffer also + // must be smaller or equal to kSSizeMax in size. + Buffer(char* buffer, size_t size) + : buffer_(buffer), + size_(size - 1), // Account for trailing NUL byte + count_(0) { +// The following assertion does not build on Mac and Android. This is because +// static_assert only works with compile-time constants, but mac uses +// libstdc++4.2 and android uses stlport, which both don't mark +// numeric_limits::max() as constexp. Likewise, MSVS2013's standard library +// also doesn't mark max() as constexpr yet. cl.exe supports static_cast but +// doesn't really implement constexpr yet so it doesn't complain, but clang +// does. +#if __cplusplus >= 201103 && !defined(OS_ANDROID) && !defined(OS_MACOSX) && \ + !defined(OS_IOS) && !(defined(__clang__) && defined(OS_WIN)) + COMPILE_ASSERT(kSSizeMaxConst == \ + static_cast(std::numeric_limits::max()), + kSSizeMax_is_the_max_value_of_an_ssize_t); +#endif + DEBUG_CHECK(size > 0); + DEBUG_CHECK(size <= kSSizeMax); + } + + ~Buffer() { + // The code calling the constructor guaranteed that there was enough space + // to store a trailing NUL -- and in debug builds, we are actually + // verifying this with DEBUG_CHECK()s in the constructor. So, we can + // always unconditionally write the NUL byte in the destructor. We do not + // need to adjust the count_, as SafeSPrintf() copies snprintf() in not + // including the NUL byte in its return code. + *GetInsertionPoint() = '\000'; + } + + // Returns true, iff the buffer is filled all the way to |kSSizeMax-1|. The + // caller can now stop adding more data, as GetCount() has reached its + // maximum possible value. + inline bool OutOfAddressableSpace() const { + return count_ == static_cast(kSSizeMax - 1); + } + + // Returns the number of bytes that would have been emitted to |buffer_| + // if it was sized sufficiently large. This number can be larger than + // |size_|, if the caller provided an insufficiently large output buffer. + // But it will never be bigger than |kSSizeMax-1|. + inline ssize_t GetCount() const { + DEBUG_CHECK(count_ < kSSizeMax); + return static_cast(count_); + } + + // Emits one |ch| character into the |buffer_| and updates the |count_| of + // characters that are currently supposed to be in the buffer. + // Returns "false", iff the buffer was already full. + // N.B. |count_| increases even if no characters have been written. This is + // needed so that GetCount() can return the number of bytes that should + // have been allocated for the |buffer_|. + inline bool Out(char ch) { + if (size_ >= 1 && count_ < size_) { + buffer_[count_] = ch; + return IncrementCountByOne(); + } + // |count_| still needs to be updated, even if the buffer has been + // filled completely. This allows SafeSPrintf() to return the number of + // bytes that should have been emitted. + IncrementCountByOne(); + return false; + } + + // Inserts |padding|-|len| bytes worth of padding into the |buffer_|. + // |count_| will also be incremented by the number of bytes that were meant + // to be emitted. The |pad| character is typically either a ' ' space + // or a '0' zero, but other non-NUL values are legal. + // Returns "false", iff the the |buffer_| filled up (i.e. |count_| + // overflowed |size_|) at any time during padding. + inline bool Pad(char pad, size_t padding, size_t len) { + DEBUG_CHECK(pad); + DEBUG_CHECK(padding <= kSSizeMax); + for (; padding > len; --padding) { + if (!Out(pad)) { + if (--padding) { + IncrementCount(padding-len); + } + return false; + } + } + return true; + } + + // POSIX doesn't define any async-signal-safe function for converting + // an integer to ASCII. Define our own version. + // + // This also gives us the ability to make the function a little more + // powerful and have it deal with |padding|, with truncation, and with + // predicting the length of the untruncated output. + // + // IToASCII() converts an integer |i| to ASCII. + // + // Unlike similar functions in the standard C library, it never appends a + // NUL character. This is left for the caller to do. + // + // While the function signature takes a signed int64_t, the code decides at + // run-time whether to treat the argument as signed (int64_t) or as unsigned + // (uint64_t) based on the value of |sign|. + // + // It supports |base|s 2 through 16. Only a |base| of 10 is allowed to have + // a |sign|. Otherwise, |i| is treated as unsigned. + // + // For bases larger than 10, |upcase| decides whether lower-case or upper- + // case letters should be used to designate digits greater than 10. + // + // Padding can be done with either '0' zeros or ' ' spaces. Padding has to + // be positive and will always be applied to the left of the output. + // + // Prepends a |prefix| to the number (e.g. "0x"). This prefix goes to + // the left of |padding|, if |pad| is '0'; and to the right of |padding| + // if |pad| is ' '. + // + // Returns "false", if the |buffer_| overflowed at any time. + bool IToASCII(bool sign, bool upcase, int64_t i, int base, + char pad, size_t padding, const char* prefix); + + private: + // Increments |count_| by |inc| unless this would cause |count_| to + // overflow |kSSizeMax-1|. Returns "false", iff an overflow was detected; + // it then clamps |count_| to |kSSizeMax-1|. + inline bool IncrementCount(size_t inc) { + // "inc" is either 1 or a "padding" value. Padding is clamped at + // run-time to at most kSSizeMax-1. So, we know that "inc" is always in + // the range 1..kSSizeMax-1. + // This allows us to compute "kSSizeMax - 1 - inc" without incurring any + // integer overflows. + DEBUG_CHECK(inc <= kSSizeMax - 1); + if (count_ > kSSizeMax - 1 - inc) { + count_ = kSSizeMax - 1; + return false; + } else { + count_ += inc; + return true; + } + } + + // Convenience method for the common case of incrementing |count_| by one. + inline bool IncrementCountByOne() { + return IncrementCount(1); + } + + // Return the current insertion point into the buffer. This is typically + // at |buffer_| + |count_|, but could be before that if truncation + // happened. It always points to one byte past the last byte that was + // successfully placed into the |buffer_|. + inline char* GetInsertionPoint() const { + size_t idx = count_; + if (idx > size_) { + idx = size_; + } + return buffer_ + idx; + } + + // User-provided buffer that will receive the fully formatted output string. + char* buffer_; + + // Number of bytes that are available in the buffer excluding the trailing + // NUL byte that will be added by the destructor. + const size_t size_; + + // Number of bytes that would have been emitted to the buffer, if the buffer + // was sufficiently big. This number always excludes the trailing NUL byte + // and it is guaranteed to never grow bigger than kSSizeMax-1. + size_t count_; + + DISALLOW_COPY_AND_ASSIGN(Buffer); +}; + + +bool Buffer::IToASCII(bool sign, bool upcase, int64_t i, int base, + char pad, size_t padding, const char* prefix) { + // Sanity check for parameters. None of these should ever fail, but see + // above for the rationale why we can't call CHECK(). + DEBUG_CHECK(base >= 2); + DEBUG_CHECK(base <= 16); + DEBUG_CHECK(!sign || base == 10); + DEBUG_CHECK(pad == '0' || pad == ' '); + DEBUG_CHECK(padding <= kSSizeMax); + DEBUG_CHECK(!(sign && prefix && *prefix)); + + // Handle negative numbers, if the caller indicated that |i| should be + // treated as a signed number; otherwise treat |i| as unsigned (even if the + // MSB is set!) + // Details are tricky, because of limited data-types, but equivalent pseudo- + // code would look like: + // if (sign && i < 0) + // prefix = "-"; + // num = abs(i); + int minint = 0; + uint64_t num; + if (sign && i < 0) { + prefix = "-"; + + // Turn our number positive. + if (i == std::numeric_limits::min()) { + // The most negative integer needs special treatment. + minint = 1; + num = static_cast(-(i + 1)); + } else { + // "Normal" negative numbers are easy. + num = static_cast(-i); + } + } else { + num = static_cast(i); + } + + // If padding with '0' zero, emit the prefix or '-' character now. Otherwise, + // make the prefix accessible in reverse order, so that we can later output + // it right between padding and the number. + // We cannot choose the easier approach of just reversing the number, as that + // fails in situations where we need to truncate numbers that have padding + // and/or prefixes. + const char* reverse_prefix = NULL; + if (prefix && *prefix) { + if (pad == '0') { + while (*prefix) { + if (padding) { + --padding; + } + Out(*prefix++); + } + prefix = NULL; + } else { + for (reverse_prefix = prefix; *reverse_prefix; ++reverse_prefix) { + } + } + } else + prefix = NULL; + const size_t prefix_length = reverse_prefix - prefix; + + // Loop until we have converted the entire number. Output at least one + // character (i.e. '0'). + size_t start = count_; + size_t discarded = 0; + bool started = false; + do { + // Make sure there is still enough space left in our output buffer. + if (count_ >= size_) { + if (start < size_) { + // It is rare that we need to output a partial number. But if asked + // to do so, we will still make sure we output the correct number of + // leading digits. + // Since we are generating the digits in reverse order, we actually + // have to discard digits in the order that we have already emitted + // them. This is essentially equivalent to: + // memmove(buffer_ + start, buffer_ + start + 1, size_ - start - 1) + for (char* move = buffer_ + start, *end = buffer_ + size_ - 1; + move < end; + ++move) { + *move = move[1]; + } + ++discarded; + --count_; + } else if (count_ - size_ > 1) { + // Need to increment either |count_| or |discarded| to make progress. + // The latter is more efficient, as it eventually triggers fast + // handling of padding. But we have to ensure we don't accidentally + // change the overall state (i.e. switch the state-machine from + // discarding to non-discarding). |count_| needs to always stay + // bigger than |size_|. + --count_; + ++discarded; + } + } + + // Output the next digit and (if necessary) compensate for the most + // negative integer needing special treatment. This works because, + // no matter the bit width of the integer, the lowest-most decimal + // integer always ends in 2, 4, 6, or 8. + if (!num && started) { + if (reverse_prefix > prefix) { + Out(*--reverse_prefix); + } else { + Out(pad); + } + } else { + started = true; + Out((upcase ? kUpCaseHexDigits : kDownCaseHexDigits)[num%base + minint]); + } + + minint = 0; + num /= base; + + // Add padding, if requested. + if (padding > 0) { + --padding; + + // Performance optimization for when we are asked to output excessive + // padding, but our output buffer is limited in size. Even if we output + // a 64bit number in binary, we would never write more than 64 plus + // prefix non-padding characters. So, once this limit has been passed, + // any further state change can be computed arithmetically; we know that + // by this time, our entire final output consists of padding characters + // that have all already been output. + if (discarded > 8*sizeof(num) + prefix_length) { + IncrementCount(padding); + padding = 0; + } + } + } while (num || padding || (reverse_prefix > prefix)); + + // Conversion to ASCII actually resulted in the digits being in reverse + // order. We can't easily generate them in forward order, as we can't tell + // the number of characters needed until we are done converting. + // So, now, we reverse the string (except for the possible '-' sign). + char* front = buffer_ + start; + char* back = GetInsertionPoint(); + while (--back > front) { + char ch = *back; + *back = *front; + *front++ = ch; + } + + IncrementCount(discarded); + return !discarded; +} + +} // anonymous namespace + +namespace internal { + +ssize_t SafeSNPrintf(char* buf, size_t sz, const char* fmt, const Arg* args, + const size_t max_args) { + // Make sure that at least one NUL byte can be written, and that the buffer + // never overflows kSSizeMax. Not only does that use up most or all of the + // address space, it also would result in a return code that cannot be + // represented. + if (static_cast(sz) < 1) { + return -1; + } else if (sz > kSSizeMax) { + sz = kSSizeMax; + } + + // Iterate over format string and interpret '%' arguments as they are + // encountered. + Buffer buffer(buf, sz); + size_t padding; + char pad; + for (unsigned int cur_arg = 0; *fmt && !buffer.OutOfAddressableSpace(); ) { + if (*fmt++ == '%') { + padding = 0; + pad = ' '; + char ch = *fmt++; + format_character_found: + switch (ch) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + // Found a width parameter. Convert to an integer value and store in + // "padding". If the leading digit is a zero, change the padding + // character from a space ' ' to a zero '0'. + pad = ch == '0' ? '0' : ' '; + for (;;) { + // The maximum allowed padding fills all the available address + // space and leaves just enough space to insert the trailing NUL. + const size_t max_padding = kSSizeMax - 1; + if (padding > max_padding/10 || + 10*padding > max_padding - (ch - '0')) { + DEBUG_CHECK(padding <= max_padding/10 && + 10*padding <= max_padding - (ch - '0')); + // Integer overflow detected. Skip the rest of the width until + // we find the format character, then do the normal error handling. + padding_overflow: + padding = max_padding; + while ((ch = *fmt++) >= '0' && ch <= '9') { + } + if (cur_arg < max_args) { + ++cur_arg; + } + goto fail_to_expand; + } + padding = 10*padding + ch - '0'; + if (padding > max_padding) { + // This doesn't happen for "sane" values of kSSizeMax. But once + // kSSizeMax gets smaller than about 10, our earlier range checks + // are incomplete. Unittests do trigger this artificial corner + // case. + DEBUG_CHECK(padding <= max_padding); + goto padding_overflow; + } + ch = *fmt++; + if (ch < '0' || ch > '9') { + // Reached the end of the width parameter. This is where the format + // character is found. + goto format_character_found; + } + } + break; + case 'c': { // Output an ASCII character. + // Check that there are arguments left to be inserted. + if (cur_arg >= max_args) { + DEBUG_CHECK(cur_arg < max_args); + goto fail_to_expand; + } + + // Check that the argument has the expected type. + const Arg& arg = args[cur_arg++]; + if (arg.type != Arg::INT && arg.type != Arg::UINT) { + DEBUG_CHECK(arg.type == Arg::INT || arg.type == Arg::UINT); + goto fail_to_expand; + } + + // Apply padding, if needed. + buffer.Pad(' ', padding, 1); + + // Convert the argument to an ASCII character and output it. + char ch = static_cast(arg.integer.i); + if (!ch) { + goto end_of_output_buffer; + } + buffer.Out(ch); + break; } + case 'd': // Output a possibly signed decimal value. + case 'o': // Output an unsigned octal value. + case 'x': // Output an unsigned hexadecimal value. + case 'X': + case 'p': { // Output a pointer value. + // Check that there are arguments left to be inserted. + if (cur_arg >= max_args) { + DEBUG_CHECK(cur_arg < max_args); + goto fail_to_expand; + } + + const Arg& arg = args[cur_arg++]; + int64_t i; + const char* prefix = NULL; + if (ch != 'p') { + // Check that the argument has the expected type. + if (arg.type != Arg::INT && arg.type != Arg::UINT) { + DEBUG_CHECK(arg.type == Arg::INT || arg.type == Arg::UINT); + goto fail_to_expand; + } + i = arg.integer.i; + + if (ch != 'd') { + // The Arg() constructor automatically performed sign expansion on + // signed parameters. This is great when outputting a %d decimal + // number, but can result in unexpected leading 0xFF bytes when + // outputting a %x hexadecimal number. Mask bits, if necessary. + // We have to do this here, instead of in the Arg() constructor, as + // the Arg() constructor cannot tell whether we will output a %d + // or a %x. Only the latter should experience masking. + if (arg.integer.width < sizeof(int64_t)) { + i &= (1LL << (8*arg.integer.width)) - 1; + } + } + } else { + // Pointer values require an actual pointer or a string. + if (arg.type == Arg::POINTER) { + i = reinterpret_cast(arg.ptr); + } else if (arg.type == Arg::STRING) { + i = reinterpret_cast(arg.str); + } else if (arg.type == Arg::INT && + arg.integer.width == sizeof(NULL) && + arg.integer.i == 0) { // Allow C++'s version of NULL + i = 0; + } else { + DEBUG_CHECK(arg.type == Arg::POINTER || arg.type == Arg::STRING); + goto fail_to_expand; + } + + // Pointers always include the "0x" prefix. + prefix = "0x"; + } + + // Use IToASCII() to convert to ASCII representation. For decimal + // numbers, optionally print a sign. For hexadecimal numbers, + // distinguish between upper and lower case. %p addresses are always + // printed as upcase. Supports base 8, 10, and 16. Prints padding + // and/or prefixes, if so requested. + buffer.IToASCII(ch == 'd' && arg.type == Arg::INT, + ch != 'x', i, + ch == 'o' ? 8 : ch == 'd' ? 10 : 16, + pad, padding, prefix); + break; } + case 's': { + // Check that there are arguments left to be inserted. + if (cur_arg >= max_args) { + DEBUG_CHECK(cur_arg < max_args); + goto fail_to_expand; + } + + // Check that the argument has the expected type. + const Arg& arg = args[cur_arg++]; + const char *s; + if (arg.type == Arg::STRING) { + s = arg.str ? arg.str : ""; + } else if (arg.type == Arg::INT && arg.integer.width == sizeof(NULL) && + arg.integer.i == 0) { // Allow C++'s version of NULL + s = ""; + } else { + DEBUG_CHECK(arg.type == Arg::STRING); + goto fail_to_expand; + } + + // Apply padding, if needed. This requires us to first check the + // length of the string that we are outputting. + if (padding) { + size_t len = 0; + for (const char* src = s; *src++; ) { + ++len; + } + buffer.Pad(' ', padding, len); + } + + // Printing a string involves nothing more than copying it into the + // output buffer and making sure we don't output more bytes than + // available space; Out() takes care of doing that. + for (const char* src = s; *src; ) { + buffer.Out(*src++); + } + break; } + case '%': + // Quoted percent '%' character. + goto copy_verbatim; + fail_to_expand: + // C++ gives us tools to do type checking -- something that snprintf() + // could never really do. So, whenever we see arguments that don't + // match up with the format string, we refuse to output them. But + // since we have to be extremely conservative about being async- + // signal-safe, we are limited in the type of error handling that we + // can do in production builds (in debug builds we can use + // DEBUG_CHECK() and hope for the best). So, all we do is pass the + // format string unchanged. That should eventually get the user's + // attention; and in the meantime, it hopefully doesn't lose too much + // data. + default: + // Unknown or unsupported format character. Just copy verbatim to + // output. + buffer.Out('%'); + DEBUG_CHECK(ch); + if (!ch) { + goto end_of_format_string; + } + buffer.Out(ch); + break; + } + } else { + copy_verbatim: + buffer.Out(fmt[-1]); + } + } + end_of_format_string: + end_of_output_buffer: + return buffer.GetCount(); +} + +} // namespace internal + +ssize_t SafeSNPrintf(char* buf, size_t sz, const char* fmt) { + // Make sure that at least one NUL byte can be written, and that the buffer + // never overflows kSSizeMax. Not only does that use up most or all of the + // address space, it also would result in a return code that cannot be + // represented. + if (static_cast(sz) < 1) { + return -1; + } else if (sz > kSSizeMax) { + sz = kSSizeMax; + } + + Buffer buffer(buf, sz); + + // In the slow-path, we deal with errors by copying the contents of + // "fmt" unexpanded. This means, if there are no arguments passed, the + // SafeSPrintf() function always degenerates to a version of strncpy() that + // de-duplicates '%' characters. + const char* src = fmt; + for (; *src; ++src) { + buffer.Out(*src); + DEBUG_CHECK(src[0] != '%' || src[1] == '%'); + if (src[0] == '%' && src[1] == '%') { + ++src; + } + } + return buffer.GetCount(); +} + +} // namespace strings +} // namespace base diff --git a/security/sandbox/chromium/base/strings/safe_sprintf.h b/security/sandbox/chromium/base/strings/safe_sprintf.h new file mode 100644 index 0000000000..2d173202d3 --- /dev/null +++ b/security/sandbox/chromium/base/strings/safe_sprintf.h @@ -0,0 +1,247 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_STRINGS_SAFE_SPRINTF_H_ +#define BASE_STRINGS_SAFE_SPRINTF_H_ + +#include "build/build_config.h" + +#include +#include +#include + +#if defined(OS_POSIX) +// For ssize_t +#include +#endif + +#include "base/base_export.h" +#include "base/basictypes.h" + +namespace base { +namespace strings { + +#if defined(_MSC_VER) +// Define ssize_t inside of our namespace. +#if defined(_WIN64) +typedef __int64 ssize_t; +#else +typedef long ssize_t; +#endif +#endif + +// SafeSPrintf() is a type-safe and completely self-contained version of +// snprintf(). +// +// SafeSNPrintf() is an alternative function signature that can be used when +// not dealing with fixed-sized buffers. When possible, SafeSPrintf() should +// always be used instead of SafeSNPrintf() +// +// These functions allow for formatting complicated messages from contexts that +// require strict async-signal-safety. In fact, it is safe to call them from +// any low-level execution context, as they are guaranteed to make no library +// or system calls. It deliberately never touches "errno", either. +// +// The only exception to this rule is that in debug builds the code calls +// RAW_CHECK() to help diagnose problems when the format string does not +// match the rest of the arguments. In release builds, no CHECK()s are used, +// and SafeSPrintf() instead returns an output string that expands only +// those arguments that match their format characters. Mismatched arguments +// are ignored. +// +// The code currently only supports a subset of format characters: +// %c, %o, %d, %x, %X, %p, and %s. +// +// SafeSPrintf() aims to be as liberal as reasonably possible. Integer-like +// values of arbitrary width can be passed to all of the format characters +// that expect integers. Thus, it is explicitly legal to pass an "int" to +// "%c", and output will automatically look at the LSB only. It is also +// explicitly legal to pass either signed or unsigned values, and the format +// characters will automatically interpret the arguments accordingly. +// +// It is still not legal to mix-and-match integer-like values with pointer +// values. For instance, you cannot pass a pointer to %x, nor can you pass an +// integer to %p. +// +// The one exception is "0" zero being accepted by "%p". This works-around +// the problem of C++ defining NULL as an integer-like value. +// +// All format characters take an optional width parameter. This must be a +// positive integer. For %d, %o, %x, %X and %p, if the width starts with +// a leading '0', padding is done with '0' instead of ' ' characters. +// +// There are a few features of snprintf()-style format strings, that +// SafeSPrintf() does not support at this time. +// +// If an actual user showed up, there is no particularly strong reason they +// couldn't be added. But that assumes that the trade-offs between complexity +// and utility are favorable. +// +// For example, adding support for negative padding widths, and for %n are all +// likely to be viewed positively. They are all clearly useful, low-risk, easy +// to test, don't jeopardize the async-signal-safety of the code, and overall +// have little impact on other parts of SafeSPrintf() function. +// +// On the other hands, adding support for alternate forms, positional +// arguments, grouping, wide characters, localization or floating point numbers +// are all unlikely to ever be added. +// +// SafeSPrintf() and SafeSNPrintf() mimic the behavior of snprintf() and they +// return the number of bytes needed to store the untruncated output. This +// does *not* include the terminating NUL byte. +// +// They return -1, iff a fatal error happened. This typically can only happen, +// if the buffer size is a) negative, or b) zero (i.e. not even the NUL byte +// can be written). The return value can never be larger than SSIZE_MAX-1. +// This ensures that the caller can always add one to the signed return code +// in order to determine the amount of storage that needs to be allocated. +// +// While the code supports type checking and while it is generally very careful +// to avoid printing incorrect values, it tends to be conservative in printing +// as much as possible, even when given incorrect parameters. Typically, in +// case of an error, the format string will not be expanded. (i.e. something +// like SafeSPrintf(buf, "%p %d", 1, 2) results in "%p 2"). See above for +// the use of RAW_CHECK() in debug builds, though. +// +// Basic example: +// char buf[20]; +// base::strings::SafeSPrintf(buf, "The answer: %2d", 42); +// +// Example with dynamically sized buffer (async-signal-safe). This code won't +// work on Visual studio, as it requires dynamically allocating arrays on the +// stack. Consider picking a smaller value for |kMaxSize| if stack size is +// limited and known. On the other hand, if the parameters to SafeSNPrintf() +// are trusted and not controllable by the user, you can consider eliminating +// the check for |kMaxSize| altogether. The current value of SSIZE_MAX is +// essentially a no-op that just illustrates how to implement an upper bound: +// const size_t kInitialSize = 128; +// const size_t kMaxSize = std::numeric_limits::max(); +// size_t size = kInitialSize; +// for (;;) { +// char buf[size]; +// size = SafeSNPrintf(buf, size, "Error message \"%s\"\n", err) + 1; +// if (sizeof(buf) < kMaxSize && size > kMaxSize) { +// size = kMaxSize; +// continue; +// } else if (size > sizeof(buf)) +// continue; +// write(2, buf, size-1); +// break; +// } + +namespace internal { +// Helpers that use C++ overloading, templates, and specializations to deduce +// and record type information from function arguments. This allows us to +// later write a type-safe version of snprintf(). + +struct Arg { + enum Type { INT, UINT, STRING, POINTER }; + + // Any integer-like value. + Arg(signed char c) : type(INT) { + integer.i = c; + integer.width = sizeof(char); + } + Arg(unsigned char c) : type(UINT) { + integer.i = c; + integer.width = sizeof(char); + } + Arg(signed short j) : type(INT) { + integer.i = j; + integer.width = sizeof(short); + } + Arg(unsigned short j) : type(UINT) { + integer.i = j; + integer.width = sizeof(short); + } + Arg(signed int j) : type(INT) { + integer.i = j; + integer.width = sizeof(int); + } + Arg(unsigned int j) : type(UINT) { + integer.i = j; + integer.width = sizeof(int); + } + Arg(signed long j) : type(INT) { + integer.i = j; + integer.width = sizeof(long); + } + Arg(unsigned long j) : type(UINT) { + integer.i = j; + integer.width = sizeof(long); + } + Arg(signed long long j) : type(INT) { + integer.i = j; + integer.width = sizeof(long long); + } + Arg(unsigned long long j) : type(UINT) { + integer.i = j; + integer.width = sizeof(long long); + } + + // A C-style text string. + Arg(const char* s) : str(s), type(STRING) { } + Arg(char* s) : str(s), type(STRING) { } + + // Any pointer value that can be cast to a "void*". + template Arg(T* p) : ptr((void*)p), type(POINTER) { } + + union { + // An integer-like value. + struct { + int64_t i; + unsigned char width; + } integer; + + // A C-style text string. + const char* str; + + // A pointer to an arbitrary object. + const void* ptr; + }; + const enum Type type; +}; + +// This is the internal function that performs the actual formatting of +// an snprintf()-style format string. +BASE_EXPORT ssize_t SafeSNPrintf(char* buf, size_t sz, const char* fmt, + const Arg* args, size_t max_args); + +#if !defined(NDEBUG) +// In debug builds, allow unit tests to artificially lower the kSSizeMax +// constant that is used as a hard upper-bound for all buffers. In normal +// use, this constant should always be std::numeric_limits::max(). +BASE_EXPORT void SetSafeSPrintfSSizeMaxForTest(size_t max); +BASE_EXPORT size_t GetSafeSPrintfSSizeMaxForTest(); +#endif + +} // namespace internal + +template +ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt, Args... args) { + // Use Arg() object to record type information and then copy arguments to an + // array to make it easier to iterate over them. + const internal::Arg arg_array[] = { args... }; + return internal::SafeSNPrintf(buf, N, fmt, arg_array, sizeof...(args)); +} + +template +ssize_t SafeSPrintf(char (&buf)[N], const char* fmt, Args... args) { + // Use Arg() object to record type information and then copy arguments to an + // array to make it easier to iterate over them. + const internal::Arg arg_array[] = { args... }; + return internal::SafeSNPrintf(buf, N, fmt, arg_array, sizeof...(args)); +} + +// Fast-path when we don't actually need to substitute any arguments. +BASE_EXPORT ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt); +template +inline ssize_t SafeSPrintf(char (&buf)[N], const char* fmt) { + return SafeSNPrintf(buf, N, fmt); +} + +} // namespace strings +} // namespace base + +#endif // BASE_STRINGS_SAFE_SPRINTF_H_ diff --git a/security/sandbox/chromium/base/strings/safe_sprintf_unittest.cc b/security/sandbox/chromium/base/strings/safe_sprintf_unittest.cc new file mode 100644 index 0000000000..02c75f990d --- /dev/null +++ b/security/sandbox/chromium/base/strings/safe_sprintf_unittest.cc @@ -0,0 +1,759 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/strings/safe_sprintf.h" + +#include +#include + +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" + +// Death tests on Android are currently very flaky. No need to add more flaky +// tests, as they just make it hard to spot real problems. +// TODO(markus): See if the restrictions on Android can eventually be lifted. +#if defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID) +#define ALLOW_DEATH_TEST +#endif + +namespace base { +namespace strings { + +TEST(SafeSPrintfTest, Empty) { + char buf[2] = { 'X', 'X' }; + + // Negative buffer size should always result in an error. + EXPECT_EQ(-1, SafeSNPrintf(buf, static_cast(-1), "")); + EXPECT_EQ('X', buf[0]); + EXPECT_EQ('X', buf[1]); + + // Zero buffer size should always result in an error. + EXPECT_EQ(-1, SafeSNPrintf(buf, 0, "")); + EXPECT_EQ('X', buf[0]); + EXPECT_EQ('X', buf[1]); + + // A one-byte buffer should always print a single NUL byte. + EXPECT_EQ(0, SafeSNPrintf(buf, 1, "")); + EXPECT_EQ(0, buf[0]); + EXPECT_EQ('X', buf[1]); + buf[0] = 'X'; + + // A larger buffer should leave the trailing bytes unchanged. + EXPECT_EQ(0, SafeSNPrintf(buf, 2, "")); + EXPECT_EQ(0, buf[0]); + EXPECT_EQ('X', buf[1]); + buf[0] = 'X'; + + // The same test using SafeSPrintf() instead of SafeSNPrintf(). + EXPECT_EQ(0, SafeSPrintf(buf, "")); + EXPECT_EQ(0, buf[0]); + EXPECT_EQ('X', buf[1]); + buf[0] = 'X'; +} + +TEST(SafeSPrintfTest, NoArguments) { + // Output a text message that doesn't require any substitutions. This + // is roughly equivalent to calling strncpy() (but unlike strncpy(), it does + // always add a trailing NUL; it always deduplicates '%' characters). + static const char text[] = "hello world"; + char ref[20], buf[20]; + memset(ref, 'X', sizeof(char) * arraysize(buf)); + memcpy(buf, ref, sizeof(buf)); + + // A negative buffer size should always result in an error. + EXPECT_EQ(-1, SafeSNPrintf(buf, static_cast(-1), text)); + EXPECT_TRUE(!memcmp(buf, ref, sizeof(buf))); + + // Zero buffer size should always result in an error. + EXPECT_EQ(-1, SafeSNPrintf(buf, 0, text)); + EXPECT_TRUE(!memcmp(buf, ref, sizeof(buf))); + + // A one-byte buffer should always print a single NUL byte. + EXPECT_EQ(static_cast(sizeof(text))-1, SafeSNPrintf(buf, 1, text)); + EXPECT_EQ(0, buf[0]); + EXPECT_TRUE(!memcmp(buf+1, ref+1, sizeof(buf)-1)); + memcpy(buf, ref, sizeof(buf)); + + // A larger (but limited) buffer should always leave the trailing bytes + // unchanged. + EXPECT_EQ(static_cast(sizeof(text))-1, SafeSNPrintf(buf, 2, text)); + EXPECT_EQ(text[0], buf[0]); + EXPECT_EQ(0, buf[1]); + EXPECT_TRUE(!memcmp(buf+2, ref+2, sizeof(buf)-2)); + memcpy(buf, ref, sizeof(buf)); + + // A unrestricted buffer length should always leave the trailing bytes + // unchanged. + EXPECT_EQ(static_cast(sizeof(text))-1, + SafeSNPrintf(buf, sizeof(buf), text)); + EXPECT_EQ(std::string(text), std::string(buf)); + EXPECT_TRUE(!memcmp(buf + sizeof(text), ref + sizeof(text), + sizeof(buf) - sizeof(text))); + memcpy(buf, ref, sizeof(buf)); + + // The same test using SafeSPrintf() instead of SafeSNPrintf(). + EXPECT_EQ(static_cast(sizeof(text))-1, SafeSPrintf(buf, text)); + EXPECT_EQ(std::string(text), std::string(buf)); + EXPECT_TRUE(!memcmp(buf + sizeof(text), ref + sizeof(text), + sizeof(buf) - sizeof(text))); + memcpy(buf, ref, sizeof(buf)); + + // Check for deduplication of '%' percent characters. + EXPECT_EQ(1, SafeSPrintf(buf, "%%")); + EXPECT_EQ(2, SafeSPrintf(buf, "%%%%")); + EXPECT_EQ(2, SafeSPrintf(buf, "%%X")); + EXPECT_EQ(3, SafeSPrintf(buf, "%%%%X")); +#if defined(NDEBUG) + EXPECT_EQ(1, SafeSPrintf(buf, "%")); + EXPECT_EQ(2, SafeSPrintf(buf, "%%%")); + EXPECT_EQ(2, SafeSPrintf(buf, "%X")); + EXPECT_EQ(3, SafeSPrintf(buf, "%%%X")); +#elif defined(ALLOW_DEATH_TEST) + EXPECT_DEATH(SafeSPrintf(buf, "%"), "src.1. == '%'"); + EXPECT_DEATH(SafeSPrintf(buf, "%%%"), "src.1. == '%'"); + EXPECT_DEATH(SafeSPrintf(buf, "%X"), "src.1. == '%'"); + EXPECT_DEATH(SafeSPrintf(buf, "%%%X"), "src.1. == '%'"); +#endif +} + +TEST(SafeSPrintfTest, OneArgument) { + // Test basic single-argument single-character substitution. + const char text[] = "hello world"; + const char fmt[] = "hello%cworld"; + char ref[20], buf[20]; + memset(ref, 'X', sizeof(buf)); + memcpy(buf, ref, sizeof(buf)); + + // A negative buffer size should always result in an error. + EXPECT_EQ(-1, SafeSNPrintf(buf, static_cast(-1), fmt, ' ')); + EXPECT_TRUE(!memcmp(buf, ref, sizeof(buf))); + + // Zero buffer size should always result in an error. + EXPECT_EQ(-1, SafeSNPrintf(buf, 0, fmt, ' ')); + EXPECT_TRUE(!memcmp(buf, ref, sizeof(buf))); + + // A one-byte buffer should always print a single NUL byte. + EXPECT_EQ(static_cast(sizeof(text))-1, + SafeSNPrintf(buf, 1, fmt, ' ')); + EXPECT_EQ(0, buf[0]); + EXPECT_TRUE(!memcmp(buf+1, ref+1, sizeof(buf)-1)); + memcpy(buf, ref, sizeof(buf)); + + // A larger (but limited) buffer should always leave the trailing bytes + // unchanged. + EXPECT_EQ(static_cast(sizeof(text))-1, + SafeSNPrintf(buf, 2, fmt, ' ')); + EXPECT_EQ(text[0], buf[0]); + EXPECT_EQ(0, buf[1]); + EXPECT_TRUE(!memcmp(buf+2, ref+2, sizeof(buf)-2)); + memcpy(buf, ref, sizeof(buf)); + + // A unrestricted buffer length should always leave the trailing bytes + // unchanged. + EXPECT_EQ(static_cast(sizeof(text))-1, + SafeSNPrintf(buf, sizeof(buf), fmt, ' ')); + EXPECT_EQ(std::string(text), std::string(buf)); + EXPECT_TRUE(!memcmp(buf + sizeof(text), ref + sizeof(text), + sizeof(buf) - sizeof(text))); + memcpy(buf, ref, sizeof(buf)); + + // The same test using SafeSPrintf() instead of SafeSNPrintf(). + EXPECT_EQ(static_cast(sizeof(text))-1, SafeSPrintf(buf, fmt, ' ')); + EXPECT_EQ(std::string(text), std::string(buf)); + EXPECT_TRUE(!memcmp(buf + sizeof(text), ref + sizeof(text), + sizeof(buf) - sizeof(text))); + memcpy(buf, ref, sizeof(buf)); + + // Check for deduplication of '%' percent characters. + EXPECT_EQ(1, SafeSPrintf(buf, "%%", 0)); + EXPECT_EQ(2, SafeSPrintf(buf, "%%%%", 0)); + EXPECT_EQ(2, SafeSPrintf(buf, "%Y", 0)); + EXPECT_EQ(2, SafeSPrintf(buf, "%%Y", 0)); + EXPECT_EQ(3, SafeSPrintf(buf, "%%%Y", 0)); + EXPECT_EQ(3, SafeSPrintf(buf, "%%%%Y", 0)); +#if defined(NDEBUG) + EXPECT_EQ(1, SafeSPrintf(buf, "%", 0)); + EXPECT_EQ(2, SafeSPrintf(buf, "%%%", 0)); +#elif defined(ALLOW_DEATH_TEST) + EXPECT_DEATH(SafeSPrintf(buf, "%", 0), "ch"); + EXPECT_DEATH(SafeSPrintf(buf, "%%%", 0), "ch"); +#endif +} + +TEST(SafeSPrintfTest, MissingArg) { +#if defined(NDEBUG) + char buf[20]; + EXPECT_EQ(3, SafeSPrintf(buf, "%c%c", 'A')); + EXPECT_EQ("A%c", std::string(buf)); +#elif defined(ALLOW_DEATH_TEST) + char buf[20]; + EXPECT_DEATH(SafeSPrintf(buf, "%c%c", 'A'), "cur_arg < max_args"); +#endif +} + +TEST(SafeSPrintfTest, ASANFriendlyBufferTest) { + // Print into a buffer that is sized exactly to size. ASAN can verify that + // nobody attempts to write past the end of the buffer. + // There is a more complicated test in PrintLongString() that covers a lot + // more edge case, but it is also harder to debug in case of a failure. + const char kTestString[] = "This is a test"; + scoped_ptr buf(new char[sizeof(kTestString)]); + EXPECT_EQ(static_cast(sizeof(kTestString) - 1), + SafeSNPrintf(buf.get(), sizeof(kTestString), kTestString)); + EXPECT_EQ(std::string(kTestString), std::string(buf.get())); + EXPECT_EQ(static_cast(sizeof(kTestString) - 1), + SafeSNPrintf(buf.get(), sizeof(kTestString), "%s", kTestString)); + EXPECT_EQ(std::string(kTestString), std::string(buf.get())); +} + +TEST(SafeSPrintfTest, NArgs) { + // Pre-C++11 compilers have a different code path, that can only print + // up to ten distinct arguments. + // We test both SafeSPrintf() and SafeSNPrintf(). This makes sure we don't + // have typos in the copy-n-pasted code that is needed to deal with various + // numbers of arguments. + char buf[12]; + EXPECT_EQ(1, SafeSPrintf(buf, "%c", 1)); + EXPECT_EQ("\1", std::string(buf)); + EXPECT_EQ(2, SafeSPrintf(buf, "%c%c", 1, 2)); + EXPECT_EQ("\1\2", std::string(buf)); + EXPECT_EQ(3, SafeSPrintf(buf, "%c%c%c", 1, 2, 3)); + EXPECT_EQ("\1\2\3", std::string(buf)); + EXPECT_EQ(4, SafeSPrintf(buf, "%c%c%c%c", 1, 2, 3, 4)); + EXPECT_EQ("\1\2\3\4", std::string(buf)); + EXPECT_EQ(5, SafeSPrintf(buf, "%c%c%c%c%c", 1, 2, 3, 4, 5)); + EXPECT_EQ("\1\2\3\4\5", std::string(buf)); + EXPECT_EQ(6, SafeSPrintf(buf, "%c%c%c%c%c%c", 1, 2, 3, 4, 5, 6)); + EXPECT_EQ("\1\2\3\4\5\6", std::string(buf)); + EXPECT_EQ(7, SafeSPrintf(buf, "%c%c%c%c%c%c%c", 1, 2, 3, 4, 5, 6, 7)); + EXPECT_EQ("\1\2\3\4\5\6\7", std::string(buf)); + EXPECT_EQ(8, SafeSPrintf(buf, "%c%c%c%c%c%c%c%c", 1, 2, 3, 4, 5, 6, 7, 8)); + EXPECT_EQ("\1\2\3\4\5\6\7\10", std::string(buf)); + EXPECT_EQ(9, SafeSPrintf(buf, "%c%c%c%c%c%c%c%c%c", + 1, 2, 3, 4, 5, 6, 7, 8, 9)); + EXPECT_EQ("\1\2\3\4\5\6\7\10\11", std::string(buf)); + EXPECT_EQ(10, SafeSPrintf(buf, "%c%c%c%c%c%c%c%c%c%c", + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); + + // Repeat all the tests with SafeSNPrintf() instead of SafeSPrintf(). + EXPECT_EQ("\1\2\3\4\5\6\7\10\11\12", std::string(buf)); + EXPECT_EQ(1, SafeSNPrintf(buf, 11, "%c", 1)); + EXPECT_EQ("\1", std::string(buf)); + EXPECT_EQ(2, SafeSNPrintf(buf, 11, "%c%c", 1, 2)); + EXPECT_EQ("\1\2", std::string(buf)); + EXPECT_EQ(3, SafeSNPrintf(buf, 11, "%c%c%c", 1, 2, 3)); + EXPECT_EQ("\1\2\3", std::string(buf)); + EXPECT_EQ(4, SafeSNPrintf(buf, 11, "%c%c%c%c", 1, 2, 3, 4)); + EXPECT_EQ("\1\2\3\4", std::string(buf)); + EXPECT_EQ(5, SafeSNPrintf(buf, 11, "%c%c%c%c%c", 1, 2, 3, 4, 5)); + EXPECT_EQ("\1\2\3\4\5", std::string(buf)); + EXPECT_EQ(6, SafeSNPrintf(buf, 11, "%c%c%c%c%c%c", 1, 2, 3, 4, 5, 6)); + EXPECT_EQ("\1\2\3\4\5\6", std::string(buf)); + EXPECT_EQ(7, SafeSNPrintf(buf, 11, "%c%c%c%c%c%c%c", 1, 2, 3, 4, 5, 6, 7)); + EXPECT_EQ("\1\2\3\4\5\6\7", std::string(buf)); + EXPECT_EQ(8, SafeSNPrintf(buf, 11, "%c%c%c%c%c%c%c%c", + 1, 2, 3, 4, 5, 6, 7, 8)); + EXPECT_EQ("\1\2\3\4\5\6\7\10", std::string(buf)); + EXPECT_EQ(9, SafeSNPrintf(buf, 11, "%c%c%c%c%c%c%c%c%c", + 1, 2, 3, 4, 5, 6, 7, 8, 9)); + EXPECT_EQ("\1\2\3\4\5\6\7\10\11", std::string(buf)); + EXPECT_EQ(10, SafeSNPrintf(buf, 11, "%c%c%c%c%c%c%c%c%c%c", + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); + EXPECT_EQ("\1\2\3\4\5\6\7\10\11\12", std::string(buf)); + + EXPECT_EQ(11, SafeSPrintf(buf, "%c%c%c%c%c%c%c%c%c%c%c", + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)); + EXPECT_EQ("\1\2\3\4\5\6\7\10\11\12\13", std::string(buf)); + EXPECT_EQ(11, SafeSNPrintf(buf, 12, "%c%c%c%c%c%c%c%c%c%c%c", + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)); + EXPECT_EQ("\1\2\3\4\5\6\7\10\11\12\13", std::string(buf)); +} + +TEST(SafeSPrintfTest, DataTypes) { + char buf[40]; + + // Bytes + EXPECT_EQ(1, SafeSPrintf(buf, "%d", (uint8_t)1)); + EXPECT_EQ("1", std::string(buf)); + EXPECT_EQ(3, SafeSPrintf(buf, "%d", (uint8_t)-1)); + EXPECT_EQ("255", std::string(buf)); + EXPECT_EQ(1, SafeSPrintf(buf, "%d", (int8_t)1)); + EXPECT_EQ("1", std::string(buf)); + EXPECT_EQ(2, SafeSPrintf(buf, "%d", (int8_t)-1)); + EXPECT_EQ("-1", std::string(buf)); + EXPECT_EQ(4, SafeSPrintf(buf, "%d", (int8_t)-128)); + EXPECT_EQ("-128", std::string(buf)); + + // Half-words + EXPECT_EQ(1, SafeSPrintf(buf, "%d", (uint16_t)1)); + EXPECT_EQ("1", std::string(buf)); + EXPECT_EQ(5, SafeSPrintf(buf, "%d", (uint16_t)-1)); + EXPECT_EQ("65535", std::string(buf)); + EXPECT_EQ(1, SafeSPrintf(buf, "%d", (int16_t)1)); + EXPECT_EQ("1", std::string(buf)); + EXPECT_EQ(2, SafeSPrintf(buf, "%d", (int16_t)-1)); + EXPECT_EQ("-1", std::string(buf)); + EXPECT_EQ(6, SafeSPrintf(buf, "%d", (int16_t)-32768)); + EXPECT_EQ("-32768", std::string(buf)); + + // Words + EXPECT_EQ(1, SafeSPrintf(buf, "%d", (uint32_t)1)); + EXPECT_EQ("1", std::string(buf)); + EXPECT_EQ(10, SafeSPrintf(buf, "%d", (uint32_t)-1)); + EXPECT_EQ("4294967295", std::string(buf)); + EXPECT_EQ(1, SafeSPrintf(buf, "%d", (int32_t)1)); + EXPECT_EQ("1", std::string(buf)); + EXPECT_EQ(2, SafeSPrintf(buf, "%d", (int32_t)-1)); + EXPECT_EQ("-1", std::string(buf)); + // Work-around for an limitation of C90 + EXPECT_EQ(11, SafeSPrintf(buf, "%d", (int32_t)-2147483647-1)); + EXPECT_EQ("-2147483648", std::string(buf)); + + // Quads + EXPECT_EQ(1, SafeSPrintf(buf, "%d", (uint64_t)1)); + EXPECT_EQ("1", std::string(buf)); + EXPECT_EQ(20, SafeSPrintf(buf, "%d", (uint64_t)-1)); + EXPECT_EQ("18446744073709551615", std::string(buf)); + EXPECT_EQ(1, SafeSPrintf(buf, "%d", (int64_t)1)); + EXPECT_EQ("1", std::string(buf)); + EXPECT_EQ(2, SafeSPrintf(buf, "%d", (int64_t)-1)); + EXPECT_EQ("-1", std::string(buf)); + // Work-around for an limitation of C90 + EXPECT_EQ(20, SafeSPrintf(buf, "%d", (int64_t)-9223372036854775807LL-1)); + EXPECT_EQ("-9223372036854775808", std::string(buf)); + + // Strings (both const and mutable). + EXPECT_EQ(4, SafeSPrintf(buf, "test")); + EXPECT_EQ("test", std::string(buf)); + EXPECT_EQ(4, SafeSPrintf(buf, buf)); + EXPECT_EQ("test", std::string(buf)); + + // Pointer + char addr[20]; + sprintf(addr, "0x%llX", (unsigned long long)(uintptr_t)buf); + SafeSPrintf(buf, "%p", buf); + EXPECT_EQ(std::string(addr), std::string(buf)); + SafeSPrintf(buf, "%p", (const char *)buf); + EXPECT_EQ(std::string(addr), std::string(buf)); + sprintf(addr, "0x%llX", (unsigned long long)(uintptr_t)sprintf); + SafeSPrintf(buf, "%p", sprintf); + EXPECT_EQ(std::string(addr), std::string(buf)); + + // Padding for pointers is a little more complicated because of the "0x" + // prefix. Padding with '0' zeros is relatively straight-forward, but + // padding with ' ' spaces requires more effort. + sprintf(addr, "0x%017llX", (unsigned long long)(uintptr_t)buf); + SafeSPrintf(buf, "%019p", buf); + EXPECT_EQ(std::string(addr), std::string(buf)); + sprintf(addr, "0x%llX", (unsigned long long)(uintptr_t)buf); + memset(addr, ' ', + (char*)memmove(addr + sizeof(addr) - strlen(addr) - 1, + addr, strlen(addr)+1) - addr); + SafeSPrintf(buf, "%19p", buf); + EXPECT_EQ(std::string(addr), std::string(buf)); +} + +namespace { +void PrintLongString(char* buf, size_t sz) { + // Output a reasonably complex expression into a limited-size buffer. + // At least one byte is available for writing the NUL character. + CHECK_GT(sz, static_cast(0)); + + // Allocate slightly more space, so that we can verify that SafeSPrintf() + // never writes past the end of the buffer. + scoped_ptr tmp(new char[sz+2]); + memset(tmp.get(), 'X', sz+2); + + // Use SafeSPrintf() to output a complex list of arguments: + // - test padding and truncating %c single characters. + // - test truncating %s simple strings. + // - test mismatching arguments and truncating (for %d != %s). + // - test zero-padding and truncating %x hexadecimal numbers. + // - test outputting and truncating %d MININT. + // - test outputting and truncating %p arbitrary pointer values. + // - test outputting, padding and truncating NULL-pointer %s strings. + char* out = tmp.get(); + size_t out_sz = sz; + size_t len; + for (scoped_ptr perfect_buf;;) { + size_t needed = SafeSNPrintf(out, out_sz, +#if defined(NDEBUG) + "A%2cong %s: %d %010X %d %p%7s", 'l', "string", "", +#else + "A%2cong %s: %%d %010X %d %p%7s", 'l', "string", +#endif + 0xDEADBEEF, std::numeric_limits::min(), + PrintLongString, static_cast(NULL)) + 1; + + // Various sanity checks: + // The numbered of characters needed to print the full string should always + // be bigger or equal to the bytes that have actually been output. + len = strlen(tmp.get()); + CHECK_GE(needed, len+1); + + // The number of characters output should always fit into the buffer that + // was passed into SafeSPrintf(). + CHECK_LT(len, out_sz); + + // The output is always terminated with a NUL byte (actually, this test is + // always going to pass, as strlen() already verified this) + EXPECT_FALSE(tmp[len]); + + // ASAN can check that we are not overwriting buffers, iff we make sure the + // buffer is exactly the size that we are expecting to be written. After + // running SafeSNPrintf() the first time, it is possible to compute the + // correct buffer size for this test. So, allocate a second buffer and run + // the exact same SafeSNPrintf() command again. + if (!perfect_buf.get()) { + out_sz = std::min(needed, sz); + out = new char[out_sz]; + perfect_buf.reset(out); + } else { + break; + } + } + + // All trailing bytes are unchanged. + for (size_t i = len+1; i < sz+2; ++i) + EXPECT_EQ('X', tmp[i]); + + // The text that was generated by SafeSPrintf() should always match the + // equivalent text generated by sprintf(). Please note that the format + // string for sprintf() is not complicated, as it does not have the + // benefit of getting type information from the C++ compiler. + // + // N.B.: It would be so much cleaner to use snprintf(). But unfortunately, + // Visual Studio doesn't support this function, and the work-arounds + // are all really awkward. + char ref[256]; + CHECK_LE(sz, sizeof(ref)); + sprintf(ref, "A long string: %%d 00DEADBEEF %lld 0x%llX ", + static_cast(std::numeric_limits::min()), + static_cast( + reinterpret_cast(PrintLongString))); + ref[sz-1] = '\000'; + +#if defined(NDEBUG) + const size_t kSSizeMax = std::numeric_limits::max(); +#else + const size_t kSSizeMax = internal::GetSafeSPrintfSSizeMaxForTest(); +#endif + + // Compare the output from SafeSPrintf() to the one from sprintf(). + EXPECT_EQ(std::string(ref).substr(0, kSSizeMax-1), std::string(tmp.get())); + + // We allocated a slightly larger buffer, so that we could perform some + // extra sanity checks. Now that the tests have all passed, we copy the + // data to the output buffer that the caller provided. + memcpy(buf, tmp.get(), len+1); +} + +#if !defined(NDEBUG) +class ScopedSafeSPrintfSSizeMaxSetter { + public: + ScopedSafeSPrintfSSizeMaxSetter(size_t sz) { + old_ssize_max_ = internal::GetSafeSPrintfSSizeMaxForTest(); + internal::SetSafeSPrintfSSizeMaxForTest(sz); + } + + ~ScopedSafeSPrintfSSizeMaxSetter() { + internal::SetSafeSPrintfSSizeMaxForTest(old_ssize_max_); + } + + private: + size_t old_ssize_max_; + + DISALLOW_COPY_AND_ASSIGN(ScopedSafeSPrintfSSizeMaxSetter); +}; +#endif + +} // anonymous namespace + +TEST(SafeSPrintfTest, Truncation) { + // We use PrintLongString() to print a complex long string and then + // truncate to all possible lengths. This ends up exercising a lot of + // different code paths in SafeSPrintf() and IToASCII(), as truncation can + // happen in a lot of different states. + char ref[256]; + PrintLongString(ref, sizeof(ref)); + for (size_t i = strlen(ref)+1; i; --i) { + char buf[sizeof(ref)]; + PrintLongString(buf, i); + EXPECT_EQ(std::string(ref, i - 1), std::string(buf)); + } + + // When compiling in debug mode, we have the ability to fake a small + // upper limit for the maximum value that can be stored in an ssize_t. + // SafeSPrintf() uses this upper limit to determine how many bytes it will + // write to the buffer, even if the caller claimed a bigger buffer size. + // Repeat the truncation test and verify that this other code path in + // SafeSPrintf() works correctly, too. +#if !defined(NDEBUG) + for (size_t i = strlen(ref)+1; i > 1; --i) { + ScopedSafeSPrintfSSizeMaxSetter ssize_max_setter(i); + char buf[sizeof(ref)]; + PrintLongString(buf, sizeof(buf)); + EXPECT_EQ(std::string(ref, i - 1), std::string(buf)); + } + + // kSSizeMax is also used to constrain the maximum amount of padding, before + // SafeSPrintf() detects an error in the format string. + ScopedSafeSPrintfSSizeMaxSetter ssize_max_setter(100); + char buf[256]; + EXPECT_EQ(99, SafeSPrintf(buf, "%99c", ' ')); + EXPECT_EQ(std::string(99, ' '), std::string(buf)); + *buf = '\000'; +#if defined(ALLOW_DEATH_TEST) + EXPECT_DEATH(SafeSPrintf(buf, "%100c", ' '), "padding <= max_padding"); +#endif + EXPECT_EQ(0, *buf); +#endif +} + +TEST(SafeSPrintfTest, Padding) { + char buf[40], fmt[40]; + + // Chars %c + EXPECT_EQ(1, SafeSPrintf(buf, "%c", 'A')); + EXPECT_EQ("A", std::string(buf)); + EXPECT_EQ(2, SafeSPrintf(buf, "%2c", 'A')); + EXPECT_EQ(" A", std::string(buf)); + EXPECT_EQ(2, SafeSPrintf(buf, "%02c", 'A')); + EXPECT_EQ(" A", std::string(buf)); + EXPECT_EQ(4, SafeSPrintf(buf, "%-2c", 'A')); + EXPECT_EQ("%-2c", std::string(buf)); + SafeSPrintf(fmt, "%%%dc", std::numeric_limits::max() - 1); + EXPECT_EQ(std::numeric_limits::max()-1, SafeSPrintf(buf, fmt, 'A')); + SafeSPrintf(fmt, "%%%dc", + static_cast(std::numeric_limits::max())); +#if defined(NDEBUG) + EXPECT_EQ(2, SafeSPrintf(buf, fmt, 'A')); + EXPECT_EQ("%c", std::string(buf)); +#elif defined(ALLOW_DEATH_TEST) + EXPECT_DEATH(SafeSPrintf(buf, fmt, 'A'), "padding <= max_padding"); +#endif + + // Octal %o + EXPECT_EQ(1, SafeSPrintf(buf, "%o", 1)); + EXPECT_EQ("1", std::string(buf)); + EXPECT_EQ(2, SafeSPrintf(buf, "%2o", 1)); + EXPECT_EQ(" 1", std::string(buf)); + EXPECT_EQ(2, SafeSPrintf(buf, "%02o", 1)); + EXPECT_EQ("01", std::string(buf)); + EXPECT_EQ(12, SafeSPrintf(buf, "%12o", -1)); + EXPECT_EQ(" 37777777777", std::string(buf)); + EXPECT_EQ(12, SafeSPrintf(buf, "%012o", -1)); + EXPECT_EQ("037777777777", std::string(buf)); + EXPECT_EQ(23, SafeSPrintf(buf, "%23o", -1LL)); + EXPECT_EQ(" 1777777777777777777777", std::string(buf)); + EXPECT_EQ(23, SafeSPrintf(buf, "%023o", -1LL)); + EXPECT_EQ("01777777777777777777777", std::string(buf)); + EXPECT_EQ(3, SafeSPrintf(buf, "%2o", 0111)); + EXPECT_EQ("111", std::string(buf)); + EXPECT_EQ(4, SafeSPrintf(buf, "%-2o", 1)); + EXPECT_EQ("%-2o", std::string(buf)); + SafeSPrintf(fmt, "%%%do", std::numeric_limits::max()-1); + EXPECT_EQ(std::numeric_limits::max()-1, + SafeSNPrintf(buf, 4, fmt, 1)); + EXPECT_EQ(" ", std::string(buf)); + SafeSPrintf(fmt, "%%0%do", std::numeric_limits::max()-1); + EXPECT_EQ(std::numeric_limits::max()-1, + SafeSNPrintf(buf, 4, fmt, 1)); + EXPECT_EQ("000", std::string(buf)); + SafeSPrintf(fmt, "%%%do", + static_cast(std::numeric_limits::max())); +#if defined(NDEBUG) + EXPECT_EQ(2, SafeSPrintf(buf, fmt, 1)); + EXPECT_EQ("%o", std::string(buf)); +#elif defined(ALLOW_DEATH_TEST) + EXPECT_DEATH(SafeSPrintf(buf, fmt, 1), "padding <= max_padding"); +#endif + + // Decimals %d + EXPECT_EQ(1, SafeSPrintf(buf, "%d", 1)); + EXPECT_EQ("1", std::string(buf)); + EXPECT_EQ(2, SafeSPrintf(buf, "%2d", 1)); + EXPECT_EQ(" 1", std::string(buf)); + EXPECT_EQ(2, SafeSPrintf(buf, "%02d", 1)); + EXPECT_EQ("01", std::string(buf)); + EXPECT_EQ(3, SafeSPrintf(buf, "%3d", -1)); + EXPECT_EQ(" -1", std::string(buf)); + EXPECT_EQ(3, SafeSPrintf(buf, "%03d", -1)); + EXPECT_EQ("-01", std::string(buf)); + EXPECT_EQ(3, SafeSPrintf(buf, "%2d", 111)); + EXPECT_EQ("111", std::string(buf)); + EXPECT_EQ(4, SafeSPrintf(buf, "%2d", -111)); + EXPECT_EQ("-111", std::string(buf)); + EXPECT_EQ(4, SafeSPrintf(buf, "%-2d", 1)); + EXPECT_EQ("%-2d", std::string(buf)); + SafeSPrintf(fmt, "%%%dd", std::numeric_limits::max()-1); + EXPECT_EQ(std::numeric_limits::max()-1, + SafeSNPrintf(buf, 4, fmt, 1)); + EXPECT_EQ(" ", std::string(buf)); + SafeSPrintf(fmt, "%%0%dd", std::numeric_limits::max()-1); + EXPECT_EQ(std::numeric_limits::max()-1, + SafeSNPrintf(buf, 4, fmt, 1)); + EXPECT_EQ("000", std::string(buf)); + SafeSPrintf(fmt, "%%%dd", + static_cast(std::numeric_limits::max())); +#if defined(NDEBUG) + EXPECT_EQ(2, SafeSPrintf(buf, fmt, 1)); + EXPECT_EQ("%d", std::string(buf)); +#elif defined(ALLOW_DEATH_TEST) + EXPECT_DEATH(SafeSPrintf(buf, fmt, 1), "padding <= max_padding"); +#endif + + // Hex %X + EXPECT_EQ(1, SafeSPrintf(buf, "%X", 1)); + EXPECT_EQ("1", std::string(buf)); + EXPECT_EQ(2, SafeSPrintf(buf, "%2X", 1)); + EXPECT_EQ(" 1", std::string(buf)); + EXPECT_EQ(2, SafeSPrintf(buf, "%02X", 1)); + EXPECT_EQ("01", std::string(buf)); + EXPECT_EQ(9, SafeSPrintf(buf, "%9X", -1)); + EXPECT_EQ(" FFFFFFFF", std::string(buf)); + EXPECT_EQ(9, SafeSPrintf(buf, "%09X", -1)); + EXPECT_EQ("0FFFFFFFF", std::string(buf)); + EXPECT_EQ(17, SafeSPrintf(buf, "%17X", -1LL)); + EXPECT_EQ(" FFFFFFFFFFFFFFFF", std::string(buf)); + EXPECT_EQ(17, SafeSPrintf(buf, "%017X", -1LL)); + EXPECT_EQ("0FFFFFFFFFFFFFFFF", std::string(buf)); + EXPECT_EQ(3, SafeSPrintf(buf, "%2X", 0x111)); + EXPECT_EQ("111", std::string(buf)); + EXPECT_EQ(4, SafeSPrintf(buf, "%-2X", 1)); + EXPECT_EQ("%-2X", std::string(buf)); + SafeSPrintf(fmt, "%%%dX", std::numeric_limits::max()-1); + EXPECT_EQ(std::numeric_limits::max()-1, + SafeSNPrintf(buf, 4, fmt, 1)); + EXPECT_EQ(" ", std::string(buf)); + SafeSPrintf(fmt, "%%0%dX", std::numeric_limits::max()-1); + EXPECT_EQ(std::numeric_limits::max()-1, + SafeSNPrintf(buf, 4, fmt, 1)); + EXPECT_EQ("000", std::string(buf)); + SafeSPrintf(fmt, "%%%dX", + static_cast(std::numeric_limits::max())); +#if defined(NDEBUG) + EXPECT_EQ(2, SafeSPrintf(buf, fmt, 1)); + EXPECT_EQ("%X", std::string(buf)); +#elif defined(ALLOW_DEATH_TEST) + EXPECT_DEATH(SafeSPrintf(buf, fmt, 1), "padding <= max_padding"); +#endif + + // Pointer %p + EXPECT_EQ(3, SafeSPrintf(buf, "%p", (void*)1)); + EXPECT_EQ("0x1", std::string(buf)); + EXPECT_EQ(4, SafeSPrintf(buf, "%4p", (void*)1)); + EXPECT_EQ(" 0x1", std::string(buf)); + EXPECT_EQ(4, SafeSPrintf(buf, "%04p", (void*)1)); + EXPECT_EQ("0x01", std::string(buf)); + EXPECT_EQ(5, SafeSPrintf(buf, "%4p", (void*)0x111)); + EXPECT_EQ("0x111", std::string(buf)); + EXPECT_EQ(4, SafeSPrintf(buf, "%-2p", (void*)1)); + EXPECT_EQ("%-2p", std::string(buf)); + SafeSPrintf(fmt, "%%%dp", std::numeric_limits::max()-1); + EXPECT_EQ(std::numeric_limits::max()-1, + SafeSNPrintf(buf, 4, fmt, (void*)1)); + EXPECT_EQ(" ", std::string(buf)); + SafeSPrintf(fmt, "%%0%dp", std::numeric_limits::max()-1); + EXPECT_EQ(std::numeric_limits::max()-1, + SafeSNPrintf(buf, 4, fmt, (void*)1)); + EXPECT_EQ("0x0", std::string(buf)); + SafeSPrintf(fmt, "%%%dp", + static_cast(std::numeric_limits::max())); +#if defined(NDEBUG) + EXPECT_EQ(2, SafeSPrintf(buf, fmt, 1)); + EXPECT_EQ("%p", std::string(buf)); +#elif defined(ALLOW_DEATH_TEST) + EXPECT_DEATH(SafeSPrintf(buf, fmt, 1), "padding <= max_padding"); +#endif + + // String + EXPECT_EQ(1, SafeSPrintf(buf, "%s", "A")); + EXPECT_EQ("A", std::string(buf)); + EXPECT_EQ(2, SafeSPrintf(buf, "%2s", "A")); + EXPECT_EQ(" A", std::string(buf)); + EXPECT_EQ(2, SafeSPrintf(buf, "%02s", "A")); + EXPECT_EQ(" A", std::string(buf)); + EXPECT_EQ(3, SafeSPrintf(buf, "%2s", "AAA")); + EXPECT_EQ("AAA", std::string(buf)); + EXPECT_EQ(4, SafeSPrintf(buf, "%-2s", "A")); + EXPECT_EQ("%-2s", std::string(buf)); + SafeSPrintf(fmt, "%%%ds", std::numeric_limits::max()-1); + EXPECT_EQ(std::numeric_limits::max()-1, + SafeSNPrintf(buf, 4, fmt, "A")); + EXPECT_EQ(" ", std::string(buf)); + SafeSPrintf(fmt, "%%0%ds", std::numeric_limits::max()-1); + EXPECT_EQ(std::numeric_limits::max()-1, + SafeSNPrintf(buf, 4, fmt, "A")); + EXPECT_EQ(" ", std::string(buf)); + SafeSPrintf(fmt, "%%%ds", + static_cast(std::numeric_limits::max())); +#if defined(NDEBUG) + EXPECT_EQ(2, SafeSPrintf(buf, fmt, "A")); + EXPECT_EQ("%s", std::string(buf)); +#elif defined(ALLOW_DEATH_TEST) + EXPECT_DEATH(SafeSPrintf(buf, fmt, "A"), "padding <= max_padding"); +#endif +} + +TEST(SafeSPrintfTest, EmbeddedNul) { + char buf[] = { 'X', 'X', 'X', 'X' }; + EXPECT_EQ(2, SafeSPrintf(buf, "%3c", 0)); + EXPECT_EQ(' ', buf[0]); + EXPECT_EQ(' ', buf[1]); + EXPECT_EQ(0, buf[2]); + EXPECT_EQ('X', buf[3]); + + // Check handling of a NUL format character. N.B. this takes two different + // code paths depending on whether we are actually passing arguments. If + // we don't have any arguments, we are running in the fast-path code, that + // looks (almost) like a strncpy(). +#if defined(NDEBUG) + EXPECT_EQ(2, SafeSPrintf(buf, "%%%")); + EXPECT_EQ("%%", std::string(buf)); + EXPECT_EQ(2, SafeSPrintf(buf, "%%%", 0)); + EXPECT_EQ("%%", std::string(buf)); +#elif defined(ALLOW_DEATH_TEST) + EXPECT_DEATH(SafeSPrintf(buf, "%%%"), "src.1. == '%'"); + EXPECT_DEATH(SafeSPrintf(buf, "%%%", 0), "ch"); +#endif +} + +TEST(SafeSPrintfTest, EmitNULL) { + char buf[40]; +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wconversion-null" +#endif + EXPECT_EQ(1, SafeSPrintf(buf, "%d", NULL)); + EXPECT_EQ("0", std::string(buf)); + EXPECT_EQ(3, SafeSPrintf(buf, "%p", NULL)); + EXPECT_EQ("0x0", std::string(buf)); + EXPECT_EQ(6, SafeSPrintf(buf, "%s", NULL)); + EXPECT_EQ("", std::string(buf)); +#if defined(__GCC__) +#pragma GCC diagnostic pop +#endif +} + +TEST(SafeSPrintfTest, PointerSize) { + // The internal data representation is a 64bit value, independent of the + // native word size. We want to perform sign-extension for signed integers, + // but we want to avoid doing so for pointer types. This could be a + // problem on systems, where pointers are only 32bit. This tests verifies + // that there is no such problem. + char *str = reinterpret_cast(0x80000000u); + void *ptr = str; + char buf[40]; + EXPECT_EQ(10, SafeSPrintf(buf, "%p", str)); + EXPECT_EQ("0x80000000", std::string(buf)); + EXPECT_EQ(10, SafeSPrintf(buf, "%p", ptr)); + EXPECT_EQ("0x80000000", std::string(buf)); +} + +} // namespace strings +} // namespace base diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/linux_seccomp.h b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/linux_seccomp.h index c13ef20bb3..006e598940 100644 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/linux_seccomp.h +++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/linux_seccomp.h @@ -16,7 +16,7 @@ #include #include -#include +#include // Old Bionic versions do not have sys/user.h. The if can be removed once we no // longer need to support these old Bionic versions. // All x86_64 builds use a new enough bionic to have sys/user.h. diff --git a/security/sandbox/chromium/sandbox/win/src/service_resolver_64.cc b/security/sandbox/chromium/sandbox/win/src/service_resolver_64.cc index 984cb384e2..c0e684c795 100644 --- a/security/sandbox/chromium/sandbox/win/src/service_resolver_64.cc +++ b/security/sandbox/chromium/sandbox/win/src/service_resolver_64.cc @@ -17,6 +17,10 @@ const BYTE kRetNp = 0xC3; const ULONG64 kMov1 = 0x54894808244C8948; const ULONG64 kMov2 = 0x4C182444894C1024; const ULONG kMov3 = 0x20244C89; +const USHORT kTestByte = 0x04F6; +const BYTE kPtr = 0x25; +const BYTE kRet = 0xC3; +const USHORT kJne = 0x0375; // Service code for 64 bit systems. struct ServiceEntry { @@ -60,11 +64,37 @@ struct ServiceEntryW8 { BYTE nop; // = 90 }; +// Service code for 64 bit systems with int 2e fallback. +struct ServiceEntryWithInt2E { + // This struct contains roughly the following code: + // 00 4c8bd1 mov r10,rcx + // 03 b855000000 mov eax,52h + // 08 f604250803fe7f01 test byte ptr SharedUserData!308, 1 + // 10 7503 jne [over syscall] + // 12 0f05 syscall + // 14 c3 ret + // 15 cd2e int 2e + // 17 c3 ret + + ULONG mov_r10_rcx_mov_eax; // = 4C 8B D1 B8 + ULONG service_id; + USHORT test_byte; // = F6 04 + BYTE ptr; // = 25 + ULONG user_shared_data_ptr; + BYTE one; // = 01 + USHORT jne_over_syscall; // = 75 03 + USHORT syscall; // = 0F 05 + BYTE ret; // = C3 + USHORT int2e; // = CD 2E + BYTE ret2; // = C3 +}; + // We don't have an internal thunk for x64. struct ServiceFullThunk { union { ServiceEntry original; ServiceEntryW8 original_w8; + ServiceEntryWithInt2E original_int2e_fallback; }; }; @@ -78,6 +108,25 @@ bool IsService(const void* source) { kSyscall == service->syscall && kRetNp == service->ret); } +bool IsServiceW8(const void* source) { + const ServiceEntryW8* service = + reinterpret_cast(source); + + return (kMmovR10EcxMovEax == service->mov_r10_rcx_mov_eax && + kMov1 == service->mov_1 && kMov2 == service->mov_2 && + kMov3 == service->mov_3); +} + +bool IsServiceWithInt2E(const void* source) { + const ServiceEntryWithInt2E* service = + reinterpret_cast(source); + + return (kMmovR10EcxMovEax == service->mov_r10_rcx_mov_eax && + kTestByte == service->test_byte && kPtr == service->ptr && + kJne == service->jne_over_syscall && kSyscall == service->syscall && + kRet == service->ret && kRet == service->ret2); +} + }; // namespace namespace sandbox { @@ -150,15 +199,9 @@ bool ServiceResolverThunk::IsFunctionAService(void* local_thunk) const { if (sizeof(function_code) != read) return false; - if (!IsService(&function_code)) { - // See if it's the Win8 signature. - ServiceEntryW8* w8_service = &function_code.original_w8; - if (!IsService(&w8_service->mov_r10_rcx_mov_eax) || - w8_service->mov_1 != kMov1 || w8_service->mov_1 != kMov1 || - w8_service->mov_1 != kMov1) { - return false; - } - } + if (!IsService(&function_code) && !IsServiceW8(&function_code) && + !IsServiceWithInt2E(&function_code)) + return false; // Save the verified code. memcpy(local_thunk, &function_code, sizeof(function_code)); diff --git a/security/sandbox/chromium/sandbox/win/src/target_services.cc b/security/sandbox/chromium/sandbox/win/src/target_services.cc index 03813c8feb..2f8477984a 100644 --- a/security/sandbox/chromium/sandbox/win/src/target_services.cc +++ b/security/sandbox/chromium/sandbox/win/src/target_services.cc @@ -4,6 +4,8 @@ #include "sandbox/win/src/target_services.h" +#include + #include #include "base/basictypes.h" @@ -56,6 +58,13 @@ bool CloseOpenHandles() { return true; } +// Used as storage for g_target_services, because other allocation facilities +// are not available early. We can't use a regular function static because on +// VS2015, because the CRT tries to acquire a lock to guard initialization, but +// this code runs before the CRT is initialized. +char g_target_services_memory[sizeof(sandbox::TargetServicesBase)]; +sandbox::TargetServicesBase* g_target_services = nullptr; + } // namespace namespace sandbox { @@ -99,8 +108,10 @@ ProcessState* TargetServicesBase::GetState() { } TargetServicesBase* TargetServicesBase::GetInstance() { - static TargetServicesBase instance; - return &instance; + // Leak on purpose TargetServicesBase. + if (!g_target_services) + g_target_services = new (g_target_services_memory) TargetServicesBase; + return g_target_services; } // The broker services a 'test' IPC service with the IPC_PING_TAG tag. @@ -155,15 +166,18 @@ bool TargetServicesBase::TestIPCPing(int version) { return true; } -bool ProcessState::IsKernel32Loaded() { +ProcessState::ProcessState() : process_state_(0) { +} + +bool ProcessState::IsKernel32Loaded() const { return process_state_ != 0; } -bool ProcessState::InitCalled() { +bool ProcessState::InitCalled() const { return process_state_ > 1; } -bool ProcessState::RevertedToSelf() { +bool ProcessState::RevertedToSelf() const { return process_state_ > 2; } diff --git a/security/sandbox/chromium/sandbox/win/src/target_services.h b/security/sandbox/chromium/sandbox/win/src/target_services.h index 70f173b211..557eaf1dc4 100644 --- a/security/sandbox/chromium/sandbox/win/src/target_services.h +++ b/security/sandbox/chromium/sandbox/win/src/target_services.h @@ -13,23 +13,19 @@ namespace sandbox { class ProcessState { public: - ProcessState() : process_state_(0) {} - + ProcessState(); // Returns true if kernel32.dll has been loaded. - bool IsKernel32Loaded(); - + bool IsKernel32Loaded() const; // Returns true if main has been called. - bool InitCalled(); - + bool InitCalled() const; // Returns true if LowerToken has been called. - bool RevertedToSelf(); - + bool RevertedToSelf() const; // Set the current state. void SetKernel32Loaded(); void SetInitCalled(); void SetRevertedToSelf(); - public: + private: int process_state_; DISALLOW_COPY_AND_ASSIGN(ProcessState); }; diff --git a/security/sandbox/linux/LinuxCapabilities.h b/security/sandbox/linux/LinuxCapabilities.h index f8eb720fb0..9d3220841e 100644 --- a/security/sandbox/linux/LinuxCapabilities.h +++ b/security/sandbox/linux/LinuxCapabilities.h @@ -84,6 +84,15 @@ public: } } + bool AnyEffective() const { + for (size_t i = 0; i < _LINUX_CAPABILITY_U32S_3; ++i) { + if (mBits[i].effective != 0) { + return true; + } + } + return false; + } + // These three methods expose individual bits in the three // capability sets as objects that can be used as bool lvalues. // The argument is the capability number, as defined in diff --git a/security/sandbox/linux/Sandbox.cpp b/security/sandbox/linux/Sandbox.cpp index cd8b174c3a..7754e2d333 100644 --- a/security/sandbox/linux/Sandbox.cpp +++ b/security/sandbox/linux/Sandbox.cpp @@ -142,8 +142,8 @@ SigSysHandler(int nr, siginfo_t *info, void *void_context) // TODO, someday when this is enabled on MIPS: include the two extra // args in the error message. - SANDBOX_LOG_ERROR("seccomp sandbox violation: pid %d, syscall %lu," - " args %lu %lu %lu %lu %lu %lu. Killing process.", + SANDBOX_LOG_ERROR("seccomp sandbox violation: pid %d, syscall %d," + " args %d %d %d %d %d %d. Killing process.", pid, syscall_nr, args[0], args[1], args[2], args[3], args[4], args[5]); @@ -420,8 +420,8 @@ BroadcastSetThreadSandbox(const sock_fprog* aFilter) } struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - if (now.tv_sec > timeLimit.tv_nsec || - (now.tv_sec == timeLimit.tv_nsec && + if (now.tv_sec > timeLimit.tv_sec || + (now.tv_sec == timeLimit.tv_sec && now.tv_nsec > timeLimit.tv_nsec)) { SANDBOX_LOG_ERROR("Thread %d unresponsive for %d seconds." " Killing process.", @@ -513,6 +513,21 @@ SetCurrentProcessSandbox(UniquePtr aPolicy) void SandboxEarlyInit(GeckoProcessType aType, bool aIsNuwa) { + // Bug 1168555: Nuwa isn't reliably single-threaded at this point; + // it starts an IPC I/O thread and then shuts it down before calling + // the plugin-container entry point, but that thread may not have + // finished exiting. If/when any type of sandboxing is used for the + // Nuwa process (e.g., unsharing the network namespace there instead + // of for each content process, to save memory), this will need to be + // changed by moving the SandboxEarlyInit call to an earlier point. + if (aIsNuwa) { + return; + } + + const SandboxInfo info = SandboxInfo::Get(); + if (info.Test(SandboxInfo::kUnexpectedThreads)) { + return; + } MOZ_RELEASE_ASSERT(IsSingleThreaded()); // Which kinds of resource isolation (of those that need to be set @@ -527,9 +542,13 @@ SandboxEarlyInit(GeckoProcessType aType, bool aIsNuwa) return; #ifdef MOZ_GMP_SANDBOX case GeckoProcessType_GMPlugin: + if (!info.Test(SandboxInfo::kEnabledForMedia)) { + break; + } canUnshareNet = true; canUnshareIPC = true; - canChroot = true; + // Need seccomp-bpf to intercept open(). + canChroot = info.Test(SandboxInfo::kHasSeccompBPF); break; #endif // In the future, content processes will be able to use some of @@ -544,9 +563,22 @@ SandboxEarlyInit(GeckoProcessType aType, bool aIsNuwa) return; } + { + LinuxCapabilities existingCaps; + if (existingCaps.GetCurrent() && existingCaps.AnyEffective()) { + SANDBOX_LOG_ERROR("PLEASE DO NOT RUN THIS AS ROOT. Strange things may" + " happen when capabilities are dropped."); + } + } + // If capabilities can't be gained, then nothing can be done. - const SandboxInfo info = SandboxInfo::Get(); if (!info.Test(SandboxInfo::kHasUserNamespaces)) { + // Drop any existing capabilities; unsharing the user namespace + // would implicitly drop them, so if we're running in a broken + // configuration where that would matter (e.g., running as root + // from a non-root-owned mode-0700 directory) this means it will + // break the same way on all kernels and be easier to troubleshoot. + LinuxCapabilities().SetCurrent(); return; } diff --git a/security/sandbox/linux/SandboxFilter.cpp b/security/sandbox/linux/SandboxFilter.cpp index 919831065d..e014158d00 100644 --- a/security/sandbox/linux/SandboxFilter.cpp +++ b/security/sandbox/linux/SandboxFilter.cpp @@ -170,6 +170,7 @@ public: // Simple I/O case __NR_write: case __NR_read: + case __NR_writev: // see SandboxLogging.cpp return Allow(); // Memory mapping diff --git a/security/sandbox/linux/SandboxFilterUtil.cpp b/security/sandbox/linux/SandboxFilterUtil.cpp index a500317aea..93aeb9a5eb 100644 --- a/security/sandbox/linux/SandboxFilterUtil.cpp +++ b/security/sandbox/linux/SandboxFilterUtil.cpp @@ -14,10 +14,15 @@ #include "mozilla/UniquePtr.h" #include "sandbox/linux/bpf_dsl/bpf_dsl.h" +// Older kernel headers (mostly Android, but also some older desktop +// distributions) are missing some or all of these: #ifndef SYS_ACCEPT4 -// Android's kernel headers don't define these. #define SYS_ACCEPT4 18 +#endif +#ifndef SYS_RECVMMSG #define SYS_RECVMMSG 19 +#endif +#ifndef SYS_SENDMMSG #define SYS_SENDMMSG 20 #endif diff --git a/security/sandbox/linux/SandboxLogging.cpp b/security/sandbox/linux/SandboxLogging.cpp new file mode 100644 index 0000000000..952b5de4e0 --- /dev/null +++ b/security/sandbox/linux/SandboxLogging.cpp @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SandboxLogging.h" + +#ifdef ANDROID +#include +#else +#include +#include +#include +#include +#endif + +#include "base/posix/eintr_wrapper.h" + +namespace mozilla { + +#ifndef ANDROID +// Alters an iovec array to remove the first `toDrop` bytes. This +// complexity is necessary because writev can return a short write +// (e.g., if stderr is a pipe and the buffer is almost full). +static void +IOVecDrop(struct iovec* iov, int iovcnt, size_t toDrop) +{ + while (toDrop > 0 && iovcnt > 0) { + size_t toDropHere = std::min(toDrop, iov->iov_len); + iov->iov_base = static_cast(iov->iov_base) + toDropHere; + iov->iov_len -= toDropHere; + toDrop -= toDropHere; + ++iov; + --iovcnt; + } +} +#endif + +void +SandboxLogError(const char* message) +{ +#ifdef ANDROID + // This uses writev internally and appears to be async signal safe. + __android_log_write(ANDROID_LOG_ERROR, "Sandbox", message); +#else + static const char logPrefix[] = "Sandbox: ", logSuffix[] = "\n"; + struct iovec iovs[3] = { + { const_cast(logPrefix), sizeof(logPrefix) - 1 }, + { const_cast(message), strlen(message) }, + { const_cast(logSuffix), sizeof(logSuffix) - 1 }, + }; + while (iovs[2].iov_len > 0) { + ssize_t written = HANDLE_EINTR(writev(STDERR_FILENO, iovs, 3)); + if (written <= 0) { + break; + } + IOVecDrop(iovs, 3, static_cast(written)); + } +#endif +} + +} diff --git a/security/sandbox/linux/SandboxLogging.h b/security/sandbox/linux/SandboxLogging.h index 0a6794a50a..88891adfb1 100644 --- a/security/sandbox/linux/SandboxLogging.h +++ b/security/sandbox/linux/SandboxLogging.h @@ -7,16 +7,46 @@ #ifndef mozilla_SandboxLogging_h #define mozilla_SandboxLogging_h -#if defined(ANDROID) -#include +// This header defines the SANDBOX_LOG_ERROR macro used in the Linux +// sandboxing code. It uses Android logging on Android and writes to +// stderr otherwise. Android logging has severity levels; currently +// only "error" severity is exposed here, and this isn't marked when +// writing to stderr. +// +// The format strings are processed by Chromium SafeSPrintf, which +// doesn't accept size modifiers or %u because it uses C++11 variadic +// templates to obtain the actual argument types; all decimal integer +// formatting uses %d. See safe_sprintf.h for more details. + +// Build SafeSPrintf without assertions to avoid a dependency on +// Chromium logging. This doesn't affect safety; it just means that +// type mismatches (pointer vs. integer) always result in unexpanded +// %-directives instead of crashing. See also the moz.build files, +// which apply NDEBUG to the .cc file. +#ifndef NDEBUG +#define NDEBUG 1 +#include "base/strings/safe_sprintf.h" +#undef NDEBUG #else -#include +#include "base/strings/safe_sprintf.h" #endif -#if defined(ANDROID) -#define SANDBOX_LOG_ERROR(args...) __android_log_print(ANDROID_LOG_ERROR, "Sandbox", ## args) -#else -#define SANDBOX_LOG_ERROR(fmt, args...) fprintf(stderr, "Sandbox: " fmt "\n", ## args) -#endif +namespace mozilla { +// Logs the formatted string (marked with "error" severity, if supported). +void SandboxLogError(const char* aMessage); +} + +#define SANDBOX_LOG_LEN 256 + +// Formats a log message and logs it (with "error" severity, if supported). +// +// Note that SafeSPrintf doesn't accept size modifiers or %u; all +// decimal integers are %d, because it uses C++11 variadic templates +// to use the actual argument type. +#define SANDBOX_LOG_ERROR(fmt, args...) do { \ + char _sandboxLogBuf[SANDBOX_LOG_LEN]; \ + ::base::strings::SafeSPrintf(_sandboxLogBuf, fmt, ## args); \ + ::mozilla::SandboxLogError(_sandboxLogBuf); \ +} while(0) #endif // mozilla_SandboxLogging_h diff --git a/security/sandbox/linux/broker/SandboxBroker.cpp b/security/sandbox/linux/broker/SandboxBroker.cpp index a465e33ff5..3d6b6ef1ab 100644 --- a/security/sandbox/linux/broker/SandboxBroker.cpp +++ b/security/sandbox/linux/broker/SandboxBroker.cpp @@ -257,6 +257,11 @@ SandboxBroker::ThreadMain(void) snprintf(threadName, sizeof(threadName), "FS Broker %d", mChildPid); PlatformThread::SetName(threadName); + // Permissive mode can only be enabled through an environment variable, + // therefore it is sufficient to fetch the value once + // before the main thread loop starts + bool permissive = SandboxInfo::Get().Test(SandboxInfo::kPermissive); + #ifdef MOZ_WIDGET_GONK #ifdef __NR_setreuid32 static const long nr_setreuid = __NR_setreuid32; @@ -338,10 +343,15 @@ SandboxBroker::ThreadMain(void) if (perms & CRASH_INSTEAD) { // This is somewhat nonmodular, but it works. resp.mError = ENOSYS; - } else if (perms & MAY_ACCESS) { + } else if (permissive || perms & MAY_ACCESS) { + // If the operation was only allowed because of permissive mode, log it. + if (permissive && !(perms & MAY_ACCESS)) { + AuditDenial(req.mOp, req.mFlags, pathBuf); + } + switch(req.mOp) { case SANDBOX_FILE_OPEN: - if (AllowOpen(req.mFlags, perms)) { + if (permissive || AllowOpen(req.mFlags, perms)) { // Permissions for O_CREAT hardwired to 0600; if that's // ever a problem we can change the protocol (but really we // should be trying to remove uses of MAY_CREATE, not add @@ -356,7 +366,7 @@ SandboxBroker::ThreadMain(void) break; case SANDBOX_FILE_ACCESS: - if (AllowAccess(req.mFlags, perms)) { + if (permissive || AllowAccess(req.mFlags, perms)) { // This can't use access() itself because that uses the ruid // and not the euid. In theory faccessat() with AT_EACCESS // would work, but Linux doesn't actually implement the @@ -376,7 +386,7 @@ SandboxBroker::ThreadMain(void) break; case SANDBOX_FILE_STAT: - if (DoStat(pathBuf, &statBuf, req.mFlags) == 0) { + if (permissive || DoStat(pathBuf, &statBuf, req.mFlags) == 0) { resp.mError = 0; ios[1].iov_base = &statBuf; ios[1].iov_len = sizeof(statBuf); @@ -401,4 +411,21 @@ SandboxBroker::ThreadMain(void) } } +void +SandboxBroker::AuditDenial(int aOp, int aFlags, const char* aPath) +{ + MOZ_RELEASE_ASSERT(SandboxInfo::Get().Test(SandboxInfo::kPermissive)); + + struct stat statBuf; + + if (lstat(aPath, &statBuf) == 0) { + // Path exists, set errno to 0 to indicate "success". + errno = 0; + } + + SANDBOX_LOG_ERROR("SandboxBroker: denied op=%d rflags=%o path=%s for pid=%d" \ + " permissive=1 error=\"%s\"", aOp, aFlags, aPath, mChildPid, + strerror(errno)); +} + } // namespace mozilla diff --git a/security/sandbox/linux/broker/SandboxBroker.h b/security/sandbox/linux/broker/SandboxBroker.h index 0e78d43557..de02c378cf 100644 --- a/security/sandbox/linux/broker/SandboxBroker.h +++ b/security/sandbox/linux/broker/SandboxBroker.h @@ -112,6 +112,7 @@ class SandboxBroker final SandboxBroker(UniquePtr aPolicy, int aChildPid, int& aClientFd); void ThreadMain(void) override; + void AuditDenial(int aOp, int aFlags, const char* aPath); // Holding a UniquePtr should disallow copying, but to make that explicit: SandboxBroker(const SandboxBroker&) = delete; diff --git a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp index 7b51f0f871..e513e5cbf5 100644 --- a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp +++ b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp @@ -5,6 +5,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "SandboxBrokerPolicyFactory.h" +#include "SandboxInfo.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/Preferences.h" @@ -29,6 +30,12 @@ SandboxBrokerPolicyFactory::IsSystemSupported() { if (length > 0 && strcmp(hardware, "goldfish") == 0) { return true; } + + // When broker is running in permissive mode, we enable it + // automatically regardless of the device. + if (SandboxInfo::Get().Test(SandboxInfo::kPermissive)) { + return true; + } #endif return false; } diff --git a/security/sandbox/linux/common/SandboxInfo.cpp b/security/sandbox/linux/common/SandboxInfo.cpp index ddd0920c69..6b2eb36e4c 100644 --- a/security/sandbox/linux/common/SandboxInfo.cpp +++ b/security/sandbox/linux/common/SandboxInfo.cpp @@ -5,11 +5,13 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "SandboxInfo.h" +#include "SandboxLogging.h" #include "LinuxSched.h" #include #include #include +#include #include #include #include @@ -36,6 +38,30 @@ namespace mozilla { +// Bug 1229136: this is copied from ../SandboxUtil.cpp to avoid +// complicated build issues; renamespaced to avoid the possibility of +// symbol conflict. +namespace { + +static bool +IsSingleThreaded() +{ + // This detects the thread count indirectly. /proc//task has a + // subdirectory for each thread in 's thread group, and the + // link count on the "task" directory follows Unix expectations: the + // link from its parent, the "." link from itself, and the ".." link + // from each subdirectory; thus, 2+N links for N threads. + struct stat sb; + if (stat("/proc/self/task", &sb) < 0) { + MOZ_DIAGNOSTIC_ASSERT(false, "Couldn't access /proc/self/task!"); + return false; + } + MOZ_DIAGNOSTIC_ASSERT(sb.st_nlink >= 3); + return sb.st_nlink == 3; +} + +} // anonymous namespace + static bool HasSeccompBPF() { @@ -159,7 +185,7 @@ CanCreateUserNamespace() } /* static */ -const SandboxInfo SandboxInfo::sSingleton = SandboxInfo(); +SandboxInfo SandboxInfo::sSingleton = SandboxInfo(); SandboxInfo::SandboxInfo() { int flags = 0; @@ -172,10 +198,15 @@ SandboxInfo::SandboxInfo() { } } - if (HasUserNamespaceSupport()) { - flags |= kHasPrivilegedUserNamespaces; - if (CanCreateUserNamespace()) { - flags |= kHasUserNamespaces; + // Detect the threading-problem signal from the parent process. + if (getenv("MOZ_SANDBOX_UNEXPECTED_THREADS")) { + flags |= kUnexpectedThreads; + } else { + if (HasUserNamespaceSupport()) { + flags |= kHasPrivilegedUserNamespaces; + if (CanCreateUserNamespace()) { + flags |= kHasUserNamespaces; + } } } @@ -183,6 +214,9 @@ SandboxInfo::SandboxInfo() { if (!getenv("MOZ_DISABLE_CONTENT_SANDBOX")) { flags |= kEnabledForContent; } + if (getenv("MOZ_PERMISSIVE_CONTENT_SANDBOX")) { + flags |= kPermissive; + } #endif #ifdef MOZ_GMP_SANDBOX if (!getenv("MOZ_DISABLE_GMP_SANDBOX")) { @@ -196,4 +230,28 @@ SandboxInfo::SandboxInfo() { mFlags = static_cast(flags); } +/* static */ void +SandboxInfo::ThreadingCheck() +{ + // Allow MOZ_SANDBOX_UNEXPECTED_THREADS to be set manually for testing. + if (IsSingleThreaded() && + !getenv("MOZ_SANDBOX_UNEXPECTED_THREADS")) { + return; + } + SANDBOX_LOG_ERROR("unexpected multithreading found; this prevents using" + " namespace sandboxing.%s", + // getenv isn't thread-safe, but see below. + getenv("LD_PRELOAD") ? " (If you're LD_PRELOAD'ing" + " nVidia GL: that's not necessary for Gecko.)" : ""); + + // Propagate this information for use by child processes. (setenv + // isn't thread-safe, but other threads are from non-Gecko code so + // they wouldn't be using NSPR; we have to hope for the best.) + setenv("MOZ_SANDBOX_UNEXPECTED_THREADS", "1", 0); + int flags = sSingleton.mFlags; + flags |= kUnexpectedThreads; + flags &= ~(kHasUserNamespaces | kHasPrivilegedUserNamespaces); + sSingleton.mFlags = static_cast(flags); +} + } // namespace mozilla diff --git a/security/sandbox/linux/common/SandboxInfo.h b/security/sandbox/linux/common/SandboxInfo.h index 54e1cca428..d20304cbc4 100644 --- a/security/sandbox/linux/common/SandboxInfo.h +++ b/security/sandbox/linux/common/SandboxInfo.h @@ -37,6 +37,10 @@ public: kHasUserNamespaces = 1 << 5, // Could a more privileged process have user namespaces, even if we can't? kHasPrivilegedUserNamespaces = 1 << 6, + // Env var MOZ_PERMISSIVE_CONTENT_SANDBOX + kPermissive = 1 << 7, + // Something is creating threads when we need to still be single-threaded. + kUnexpectedThreads = 1 << 8, }; bool Test(Flags aFlag) const { return (mFlags & aFlag) == aFlag; } @@ -52,9 +56,18 @@ public: { return !Test(kEnabledForMedia) || Test(kHasSeccompBPF); } + + // For bug 1222500 or anything else like it: On desktop, this is + // called in the parent process at a point when it should still be + // single-threaded, to check that the SandboxEarlyInit() call in a + // child process is early enough to be single-threaded. If not, + // kUnexpectedThreads is set and affected flags (user namespaces; + // possibly others in the future) are cleared. + static void ThreadingCheck(); private: enum Flags mFlags; - static MOZ_EXPORT const SandboxInfo sSingleton; + // This should be const, but has to allow for ThreadingCheck. + static MOZ_EXPORT SandboxInfo sSingleton; SandboxInfo(); }; diff --git a/security/sandbox/linux/common/moz.build b/security/sandbox/linux/common/moz.build index 87437e7e3c..4ec5490b87 100644 --- a/security/sandbox/linux/common/moz.build +++ b/security/sandbox/linux/common/moz.build @@ -13,7 +13,8 @@ SOURCES += [ ] LOCAL_INCLUDES += [ - '/security/sandbox/chromium' + '/security/sandbox/chromium', + '/security/sandbox/linux', # SandboxLogging.h ] if CONFIG['OS_TARGET'] == 'Android': diff --git a/security/sandbox/linux/glue/SandboxCrash.cpp b/security/sandbox/linux/glue/SandboxCrash.cpp index 71dd50f369..eafe379c82 100644 --- a/security/sandbox/linux/glue/SandboxCrash.cpp +++ b/security/sandbox/linux/glue/SandboxCrash.cpp @@ -18,6 +18,9 @@ #include "mozilla/unused.h" #include "mozilla/dom/Exceptions.h" #include "nsContentUtils.h" +#ifdef MOZ_CRASHREPORTER +#include "nsExceptionHandler.h" +#endif #include "mozilla/StackWalk.h" #include "nsString.h" #include "nsThreadUtils.h" @@ -102,6 +105,9 @@ SandboxCrash(int nr, siginfo_t *info, void *void_context) pid_t pid = getpid(), tid = syscall(__NR_gettid); bool dumped = false; +#ifdef MOZ_CRASHREPORTER + dumped = CrashReporter::WriteMinidumpForSigInfo(nr, info, void_context); +#endif if (!dumped) { SANDBOX_LOG_ERROR("crash reporter is disabled (or failed);" " trying stack trace:"); diff --git a/security/sandbox/linux/glue/moz.build b/security/sandbox/linux/glue/moz.build index 1dfbb03ab0..cda502b746 100644 --- a/security/sandbox/linux/glue/moz.build +++ b/security/sandbox/linux/glue/moz.build @@ -5,10 +5,17 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. SOURCES += [ + '../../chromium/base/strings/safe_sprintf.cc', + '../SandboxLogging.cpp', 'SandboxCrash.cpp', ] +# Avoid Chromium logging dependency, because this is going into +# libxul. See also the comment in SandboxLogging.h. +SOURCES['../../chromium/base/strings/safe_sprintf.cc'].flags += ['-DNDEBUG'] + LOCAL_INCLUDES += [ + '/security/sandbox/chromium', '/security/sandbox/linux', ] diff --git a/security/sandbox/linux/gtest/TestSandboxUtil.cpp b/security/sandbox/linux/gtest/TestSandboxUtil.cpp index ea33803639..cd29813f90 100644 --- a/security/sandbox/linux/gtest/TestSandboxUtil.cpp +++ b/security/sandbox/linux/gtest/TestSandboxUtil.cpp @@ -7,6 +7,7 @@ #include "gtest/gtest.h" #include "SandboxUtil.h" +#include "SandboxInfo.h" #include @@ -32,6 +33,10 @@ static const EarlyTest gEarlyTest; TEST(SandboxUtil, IsSingleThreaded) { + // If the test system if affected by kUnexpectedThreads, (1) there's + // no point in doing this test, and (2) if that happens on Mozilla + // CI then burning the tree is an appropriate response. + ASSERT_FALSE(SandboxInfo::Get().Test(SandboxInfo::kUnexpectedThreads)); EXPECT_TRUE(gEarlyTest.mWasSingleThreaded); EXPECT_FALSE(IsSingleThreaded()); } diff --git a/security/sandbox/linux/moz.build b/security/sandbox/linux/moz.build index 8ff19c6725..712269b4b6 100644 --- a/security/sandbox/linux/moz.build +++ b/security/sandbox/linux/moz.build @@ -23,6 +23,7 @@ SOURCES += [ '../chromium/base/lazy_instance.cc', '../chromium/base/memory/ref_counted.cc', '../chromium/base/memory/singleton.cc', + '../chromium/base/strings/safe_sprintf.cc', '../chromium/base/strings/string16.cc', '../chromium/base/strings/string_piece.cc', '../chromium/base/strings/string_util.cc', @@ -61,9 +62,15 @@ SOURCES += [ 'SandboxChroot.cpp', 'SandboxFilter.cpp', 'SandboxFilterUtil.cpp', + 'SandboxLogging.cpp', 'SandboxUtil.cpp', ] +# This copy of SafeSPrintf doesn't need to avoid the Chromium logging +# dependency like the one in libxul does, but this way the behavior is +# consistent. See also the comment in SandboxLogging.h. +SOURCES['../chromium/base/strings/safe_sprintf.cc'].flags += ['-DNDEBUG'] + # gcc lto likes to put the top level asm in syscall.cc in a different partition # from the function using it which breaks the build. Work around that by # forcing there to be only one partition. diff --git a/security/sandbox/mac/Sandbox.mm b/security/sandbox/mac/Sandbox.mm index 7f3da41cb9..2c811d7918 100644 --- a/security/sandbox/mac/Sandbox.mm +++ b/security/sandbox/mac/Sandbox.mm @@ -243,7 +243,6 @@ static const char contentSandboxRules[] = " (global-name \"com.apple.pasteboard.1\")\n" " (global-name \"com.apple.window_proxies\")\n" " (global-name \"com.apple.windowserver.active\")\n" - " (global-name \"com.apple.cvmsServ\")\n" " (global-name \"com.apple.audio.coreaudiod\")\n" " (global-name \"com.apple.audio.audiohald\")\n" " (global-name \"com.apple.PowerManagement.control\")\n" @@ -262,7 +261,6 @@ static const char contentSandboxRules[] = " (global-name \"com.apple.printtool.daemon\"))\n" "\n" " (allow iokit-open\n" - " (iokit-user-client-class \"AppleGraphicsControlClient\")\n" " (iokit-user-client-class \"IOHIDParamUserClient\")\n" " (iokit-user-client-class \"IOAudioControlUserClient\")\n" " (iokit-user-client-class \"IOAudioEngineUserClient\")\n" @@ -271,8 +269,6 @@ static const char contentSandboxRules[] = " (iokit-user-client-class \"nvSharedUserClient\")\n" " (iokit-user-client-class \"nvFermiGLContext\")\n" " (iokit-user-client-class \"IGAccelGLContext\")\n" - " (iokit-user-client-class \"AGPMClient\")\n" - " (iokit-user-client-class \"IOSurfaceRootUserClient\")\n" " (iokit-user-client-class \"IGAccelSharedUserClient\")\n" " (iokit-user-client-class \"IGAccelVideoContextMain\")\n" " (iokit-user-client-class \"IGAccelVideoContextMedia\")\n" @@ -396,6 +392,26 @@ static const char contentSandboxRules[] = " (appleevent-destination \"com.apple.preview\")\n" " (appleevent-destination \"com.apple.imagecaptureextension2\"))\n" "\n" + "; bug 1153809\n" + " (allow iokit-open\n" + " (iokit-user-client-class \"NVDVDContextTesla\")\n" + " (iokit-user-client-class \"Gen6DVDContext\"))\n" + "\n" + "; accelerated graphics\n" + " (allow-shared-preferences-read \"com.apple.opengl\")\n" + " (allow-shared-preferences-read \"com.nvidia.OpenGL\")\n" + " (allow mach-lookup\n" + " (global-name \"com.apple.cvmsServ\"))\n" + " (allow iokit-open\n" + " (iokit-connection \"IOAccelerator\")\n" + " (iokit-user-client-class \"IOAccelerationUserClient\")\n" + " (iokit-user-client-class \"IOSurfaceRootUserClient\")\n" + " (iokit-user-client-class \"IOSurfaceSendRight\")\n" + " (iokit-user-client-class \"IOFramebufferSharedUserClient\")\n" + " (iokit-user-client-class \"AppleSNBFBUserClient\")\n" + " (iokit-user-client-class \"AGPMClient\")\n" + " (iokit-user-client-class \"AppleGraphicsControlClient\")\n" + " (iokit-user-client-class \"AppleGraphicsPolicyClient\"))\n" " )\n" ")\n"; diff --git a/security/sandbox/moz-chromium-commit-status.txt b/security/sandbox/moz-chromium-commit-status.txt index 2ffe8036df..139cf6acdd 100644 --- a/security/sandbox/moz-chromium-commit-status.txt +++ b/security/sandbox/moz-chromium-commit-status.txt @@ -2,3 +2,6 @@ Chromium Commit Directory / File (relative to securit ---------------------------------------- ------------------------------------------------ df7cc6c04725630dd4460f29d858a77507343b24 chromium b533d6533585377edd63ec6500469f6c4fba602a chromium/sandbox/win/src/sharedmem_ipc_server.cc +034bd64db1806d85b2ceacc736074ac07722af4a chromium/sandbox/win/src/service_resolver_64.cc +de2078cfbbb6770791d32575a1a72a288e6d66a6 chromium/sandbox/win/src/target_services.cc +de2078cfbbb6770791d32575a1a72a288e6d66a6 chromium/sandbox/win/src/target_services.h diff --git a/security/sandbox/win/src/sandboxtarget/sandboxTarget.h b/security/sandbox/win/src/sandboxtarget/sandboxTarget.h index 8b2a1a98f9..eee3f67169 100644 --- a/security/sandbox/win/src/sandboxtarget/sandboxTarget.h +++ b/security/sandbox/win/src/sandboxtarget/sandboxTarget.h @@ -45,9 +45,6 @@ public: MOZ_ASSERT(aTargetServices); MOZ_ASSERT(!mTargetServices, "Sandbox TargetServices must only be set once."); - // We use process_state_ instead of InitCalled() here due to linking issues. - MOZ_ASSERT(aTargetServices->GetState()->process_state_ > 1, - "Sandbox TargetServices must already be initialized."); mTargetServices = aTargetServices; } diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 3eb6e54d7e..a6d7d88d58 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -9401,6 +9401,13 @@ "releaseChannelCollection": "opt-out", "description": "Counts number of times a certain plugin has been activated." }, + "SCROLL_LINKED_EFFECT_FOUND": { + "alert_emails": ["kgupta@mozilla.com"], + "bug_numbers": [1229052], + "expires_in_version": "50", + "kind": "boolean", + "description": "Attempt to determine prevalence of scroll-linked effects on the web." + }, "YOUTUBE_EMBED_SEEN": { "alert_emails": ["cpeterson@mozilla.com"], "expires_in_version": "48", diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index ccef3b9bc6..0f4282348c 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -194,6 +194,10 @@ #include "AndroidBridge.h" #endif +#if defined(MOZ_SANDBOX) && defined(XP_LINUX) && !defined(ANDROID) +#include "mozilla/SandboxInfo.h" +#endif + extern uint32_t gRestartMode; extern void InstallSignalHandlers(const char *ProgramName); @@ -4270,6 +4274,10 @@ XREMain::XRE_main(int argc, char* argv[], const nsXREAppData* aAppData) { ScopedLogging log; +#if defined(MOZ_SANDBOX) && defined(XP_LINUX) && !defined(ANDROID) + SandboxInfo::ThreadingCheck(); +#endif + char aLocal; GeckoProfilerInitRAII profilerGuard(&aLocal);