From c7dc12e90f41fd892f41ce1c26354d172ccb9207 Mon Sep 17 00:00:00 2001 From: roytam1 Date: Mon, 22 Apr 2024 16:19:21 +0800 Subject: [PATCH] import changes from `dev' branch of rmottola/Arctic-Fox: - Bug 1251253 - prevent null pointer dereference of |aContext| in CacheStorageService::DoomStorageEntries. r=mayhemer (35b449c612) - Bug 1260498 - Make test_rel_preconnect work in e10s mode. r=mcmanus (e6823ce4c4) - Bug 1016628 - Add prefetch abilities to the predictor. r=mayhemer (53ab180c97) - Bug 1258482 - FileList should contain only Files, not Directories, r=smaug (ff78125454) - Bug 1258694 - Implement Directory::GetFiles(), r=smaug (305784524e) - Bug 1263992 - patch 1 - Remove DirectoryType enum, r=smaug (89e1a59041) - Bug 1263992 - patch 2 - Support the creation of directories from FileSystemTasks, r=smaug (c569092cef) - Bug 1243586 - Implement Upgrade-Insecure-Requests HTTP Request Header Field. r=rbarnes (4b8a84c656) - Bug 1262572 - http 0.9 telemetry. r=hurley (6006881336) - Bug 587177 - Update all comments before SetOriginalURI to reflect reality, r=mcmanus (b2fedb0728) - Bug 1261632 - Assert that OnStopRequest is called only once. r=michal (c35b1922b9) - Bug 1232422 - Convert 5 tests within netwerk/test to use AsyncOpen2 (r=mcmanus) (4af8d43814) - Bug 831450 - No Range Requests against weak Etag r=mayhemer (9b4a159e1e) - Bug 1214277 - Avoid bypassing opening a cache entry for possibly intercepted channels; r=mcmanus (c5b0de6990) - partial apply Bug 1234369 - Convert 25 tests within netwerk/test to use AsyncOpen2 (1b81d5a303) - Bug 299031 - heuristic cache rule for 410 should be longer r=mayhemer (848834fc31) - Bug 1121447 - trust cache less for error codes r=mayhemer (0424fec819) - Bug 1125916 - Check whether loadInfo and loadContext match. r=sicking, r=jduell (6740850922) - Bug 1258778 - Purge the skia glyph cache when receiving a low memory notice. r=erahm (633c60b0c6) - Bug 1125916 - Add SEC_FORCE_PRIVATE_BROWSING to LoadInfo. r=sicking, r=jduell (10b5a1cacb) - Bug 1105556 - test fixes. r=sicking, ckerschb (845d0dbd65) - Bug 1258481 - Use RegionBuilder for nsRegion IPC. r=jrmuizel (616c279297) - Bug 1014691 - Fix an include-what-you-use error in TestCompositor.cpp. r=kats (2797f83f1d) - Bug 1256408 - Add graphics microbenchmarking. r=mstange (49b11b051b) - Bug 1258481 - Add a RegionBuilder for accumulating rects. r=jrmuizel (acd79192db) - Track whether or not remote layers have acknowledged compositor changes. (bug 1256517 part 1, r=mattwoodrow) (e3cc77ed41) - Move compositable field out of individual compositable ops. (bug 1256517 part 2, r=mattwoodrow) (1d4a063df3) - Bug 1241058: Assure several operations properly operate on the current group target. r=jrmuizel (6119e2b4db) - Bug 1247700: Avoid crash from invalid fonts. r=bas (c4c2799b94) - Bug 1242421 - remove useless null check. r=roc@ocallahan.org (44faf6556d) - Add instrumentation for when content processes fail to acquire D3D11 devices. (bug 1247539, r=milan) (bd9265d78e) - Make access to gfxWindowsPlatform D3D11 devices thread-safe. (bug 1258174, r=bas) (032e74b163) - Disable device access on textures created against stale layers. (bug 1256517 part 3, r=nical) (ffcebbdee6) - Block compositable updates from stale layers. (bug 1256517 part 4, r=mattwoodrow,nical) (fc83339f2d) - Fix build bustage for bug 1256517 r=broken tree (3952871373) - Bug 1256678 - Replace DrawTargetCairo::FillGlyphs crashes with other crashes - r=bobowen (b7245ba436) - Bug 1120485. Log CloseHandle error reason during MessageChannel shutdown failure. r=milan (2f81d9c2aa) - Bug 1242448 - Ensure the tile pool does not hold textures during shutdown. r=edwin (d31c304258) - Bug 1258851 - Propagate the isScrollbarContainer layer flag to the compositor. r=mattwoodrow (b9906d7557) - Bug 1260391: Transfer |CompositableOperation| in |AsyncChildMessageData|, r=dvander (1def34c5f8) - Bug 1252324 - add DrawTarget API for 3D transforms for use in layers. r=jrmuizel (bc80529422) - Bug 1255342 - implement DrawTargetCairo::LockBits for Cairo Win32 surfaces. r=sotaro (6f5661691b) - Bug 1263480 - Don't let cairo go into an error state when DrawSurface is called with an empty destination rectangle. r=lsalzman (4300940101) - Bug 1251241 - return from DrawTargetCairo::FillGlyphs if |aFont| is ullptr. r=roc (bb92f95ccd) - Bug 1255320 - Create DrawTarget with DIB as similar DrawTarget r=jrmuizel (03f1da030d) - Bug 1215265 - Put shutting down gfx ipdl protocols for child processes behind a pref. r=sotaro (aa781b37f9) - Bug 1262898: Keep the GeckoChildProcessHost alive for the lifetime of the CompositorBridge and ImageBridge parent actors. r=jimm r=nical (dcca3b54e1) - Bug 1251619: Remove unused gfxPlatform::CreateDrawTargetForUpdateSurface r=mchang (b29565995e) - Bug 1255973 - Remove redundant overrides from gfxPlatform subclasses. r=jfkthame (d45f8a6640) - Bug 1259466. Rename layers.offmainthreadcomposition.enabled. r=milan We would rather people not use this pref. (f362da1bd3) - Bug 881609: Call InitLayersAccelerationPrefs only once. r=nrc (faed10a0d4) - Bug 1209780. Mark some DrawResult's as unused in layout/svg. r=seth (48192d6b34) - Bug 1251115 - Fix incorrect rendering result while mask path is not resolvable; r=mstange (a52b478fdf) - Bug 1228354 - Part1 - Support luminance mask mode. r=mstange r=bas (b03abbe8a6) - Bug 1228354 - Part2. Add test case for mask-mode. r=heycam (6bea36a70c) - Bug 1259802: Add type replacement annotations to simplify rust binding generation for nsStyleStruct.h, r=bholley (48c13e62f2) - Bug 1261754 - Part 1: Improve static assertions for style struct bits. r=dholbert (2ce6d994a5) - Bug 1261552 - Reimplement default placement-new for style structs. r=heycam (db9d7782e2) - Bug 1261552 - Introduce StaticPresData and hoist some shared functionality into it. r=heycam (adf2e16b4d) - Bug 1226627 - Truncate the result in ZoomText/UnZoomText rather than rounding it for better performance. r=roc (f1d1084ba1) - Bug 1247777 - Part1: parse and compute -webkit-text-fill-color property. r=heycam (fc4161355c) - Bug 1247777 - Part2: implement -webkit-text-fill-color rendering. r=jfkthame (0f30da9c5b) - Bug 1247777 - Part3: reftests for -webkit-text-fill-color. r=jfkthame Add this test into web-platform-tests. (02e41db8cc) - Bug 1043461 - Followup to ensure we still test custom property position when the UA style sheet doesn't have custom properties in it. r=dholbert (48df73d684) - Bug 1247777 - Part4.1: replace windows-style line endings with unix-style line endings. r=bz (be8ba60960) - Bug 1247777 - Part4.2: add compatible webkit prefixed properties in CSS properties ordering check test. r=bz (7b78825e14) - Bug 1261552 - Introduce StyleStructContext, and make all style struct constructors take it. r=heycam (65b3966841) - Bug 1258017 - Use an nsCOMPtr to hold onto the nsIStyleRule. r=dbaron (e88d7e368f) - Bug 1258017 - Use a RefPtr to hold onto the parent style context. r=dbaron (6a7289ca43) - Bug 1258017 - Redesign and simplify rule tree GC. r=dbaron (3bf60a9b04) - Bug 1253149 - Remove the #ifdef __cplusplus bits from ServoBindings.h. r=SimonSapin (bf2b18a470) - Bug 1251496 - Forward stylesheet management to RawServoStyleSet. r=heycam (0a3aa90b2d) - Bug 1260310 - Generalize nsStyleContext to support resolving styles from either nsRuleNode or ServoComputedValues. r=heycam (82b6d5d008) - Bug 1258017 - Cleanup fixes for trunk. r=me (674a65815a) - Bug 1236400 part 1: Add internal enum values to represent "display: -webkit-box" & "display: -webkit-inline-box". r=mats (509c94da15) - Bug 1236400 part 2: Extend NeedsAnonFlexOrGridItem() & related code to wrap all inline-level -webkit-box children in an anonymous flex item. r=mats (dc11b9b09f) - Bug 1236400 part 3: If webkit prefix support is enabled, skip CSS Parser code that converts "display: -webkit-box" directly to "display: flex". r=mats (e09b459124) - Bug 1236400 part 4: Add reftests to test how non-block-level content gets wrapped inside a -webkit-box. (no review) (46e4d8cb07) - Bug 1261754 - Part 2: Make quotes computed values shareable between different structs. r=dholbert (a78e43b706) - Bug 1261754 - Part 3: Move quotes from nsStyleQuotes to nsStyleList and delete nsStyleQuotes. r=dholbert (fdcd9aaa3f) - Bug 1209273 - Part 1: Support for adjust-color CSS property. r=dbaron (818a7fe0ff) - Bug 1209273 - Part 2: Force printing background if color-adjust: exact. r=dbaron (ffd52c0dbc) - Bug 1261754 - Part 4: Move image-rendering from nsStyleSVG to nsStyleVisibility. r=dholbert (ee8372fb94) - Bug 1261754 - Part 5: Move text-rendering from nsStyleSVG to nsStyleText. r=dholbert (c13a11313d) - Bug 1261754 - Part 6: Move vertical-align from nsStyleTextReset to nsStyleDisplay. r=dholbert (d374b3700b) - Bug 1261754 - Part 7: Move pointer-events from nsStyleVisibility to nsStyleUserInterface. r=dholbert (8693251243) - Bug 1261754 - Part 8: Move box-shadow from nsStyleBorder to a new nsStyleEffects struct. r=dholbert (8263476827) - Bug 1261754 - Part 9: Move clip from nsStyleDisplay to nsStyleEffects. r=dholbert (5418597309) - Bug 1261754 - Part 10: Move mix-blend-mode from nsStyleDisplay to nsStyleEffects. r=dholbert (ebae613929) - Bug 1261754 - Part 11: Move opacity from nsStyleDisplay to nsStyleEffects. r=dholbert (589292af44) - Bug 1187851 patch 6 - Make dynamic changes to filter change fixed position containing block for descendants. r=roc (003a3aa6ce) - Bug 1261754 - Part 12: Move filter from nsStyleSVGReset to nsStyleEffects. r=dholbert (78d87914f9) - Bug 1259513: Make gfxContext constructor private, use a utility function that can return nullptr. r=bas,lsalzman (43df6e429f) - Bug 1259785: Do a proper flush when taking a snapshot so our dependent targets and command lists get appropriately cleared. r=jrmuizel (9f7372cce1) - Bug 1251431 - Part 1: Allow usage of an A8 source pattern to MaskSurface for D2D 1.1 Moz2D backend. r=jwatt (632eb6d2da) - Bug 1251431 - Part 2: Do not apply the device transform when drawing to an already intermediate surface. r=jwatt (3a24f4a5c6) - Bug 1251431 - GCC compilation fixup. (2356f0a58c) - Bug 1238328: Purge stored command lists by calling EndDraw/BeginDraw on a regular basis when they're used. r=jrmuizel (33f47b281f) - Bug 1246641: Also execute an occasional EndDraw for CommandLists used by non-operator OVER drawing. r=jrmuizel (b3e03ad111) - Bug 1258168: Push ClearType compatible clipping layers when the last pushed layer was marked as opaque. r=jrmuizel (bd069ad7b6) - Bug 1264736: Crash sooner if we can't get a valid command list, at least in nightly/aurora. r=bas (fb4bb56815) - Bug 1255438 - create nsI{Mutable,}Array directly; r=keeler (1b802b23b7) - Bug 1255438 - fix OS X warning bustage and reopen this CLOSED TREE; r=me (07a05910a6) - bug 1197314: Remove PR_snprintf calls in security/manager/ssl/ r=keeler (f2271aad87) - Bug 1258298 - Switch more Scoped.h templates in PSM to UniquePtr equivalents. r=keeler (2ee1a85d8e) - Bug 1191414 - gather telemetry on usage of . r=keeler,r=vladan (150bad38a1) - Bug 1260644 - Use UniquePLArenaPool to manage PLArenaPools in PSM. r=keeler (9e8ad9c0d4) - Bug 1247250 - Enable TLS 1.3 anti-downgrade on non-secure fallback. r=keeler (7a950b427a) - Bug 1215796 - Remove the static fallback whitelist. r=keeler (fa55b5920b) - bug 1254667 - change certificate verification SHA1 policy to "allow or locally-installed roots" r=jcj (5d0bb9e8b1) - bug 1245280 - add policy mechanism to optionally enforce BRs for falling back to subject CN r=Cykesiopka,mgoodwin (ecd4f2180a) - Bug 1254653 - Add telemetry to measure how often we encounter EV certificates r=keeler (9da287b490) - Bug 1259909 - Obviate char PORT_Free() calls in PSM. r=keeler (b7ba2a47da) - Bug 1252882 - Add a Content Signature Service r=keeler,r=franziskus,r=Cykesiopka (8b806022a0) - Bug 1255784 - u2f tests should use SpecialPowers.pushPrefEnv, r=jjones (839a58476f) - Bug 1244960 - Complete FIDO u2f NSSToken (Part 1). r=keeler, r=baku (3d64aa2b7c) - Bug 1244960 - FIDO u2f NSSToken (Part 2): Use Attestation Certificates. r=keeler (aee3ffc830) - Bug 1244960 - FIDO u2f NSSToken (Part 3): Review updates. r=keeler (b2f81c2b72) - Bug 1244960 - FIDO u2f NSSToken (Part 4): Correct FacetID base algorithm. r=keeler (9e70506580) - Bug 1244960 - FIDO u2f NSSToken (Part 5): Review updates. r=keeler (62a28f2502) - Bug 1231643 - Part 1. Create skia-A8-surface for mask composition when backendtype of the source DrawTarget is CG; r=mstange (dd03d86f55) - Bug 1244598 - Move resource files of w3c-css/masking into ./support subdir. r=dbaron (4c9e789191) - Bug 1243675 - Part 1. Add mask-image property reftest. r=dbaron (18e5dfa90b) - Bug 1243675 - Part 2. Add mask-clip property reftest. r=dbaron (ddf834d408) - Bug 1243675 - Part 3. Add mask-position property reftest. r=dbaron (68cae7c7e6) - Bug 1243675 - Part 4. Add mask-repeat property reftest. r=dbaron (0a3ed45377) - Bug 1243675 - Part 5. Add mask-origin property reftest. r=dbaron (f5785145a7) - Bug 1243675 - Part 6. Add mask-size property reftest. r=dbaron (1ab2040973) - Bug 1231643 - Part 2. Enable mask-composite reftest; r=dbaron (8c3b863d97) - Bug 1263622 - Fixed nsNSSComponent.cpp compilation on mingw. r=dkeeler,ted (0e651c0211) - Bug 1266249 - Remove mHasCachedOutline. r=dbaron (c46459acf2) - Bug 1235634 - Construct nsNSSShutdownList::singleton lazily on first use r=keeler (1b53753c2e) - Bug 1262645 - Address misc issues with nsGetUserCertChoice(). r=keeler (ec675be29a) - Bug 1238001 - Allow TLS info to be updated on renegotiation, r=keeler (a2ec0c8a07) - Bug 1201437 - Add new WebProgress state flag for user-overridden cert. r=keeler (0b9edbc8d8) - Bug 1201437 - Make cert override tests check for STATE_CERT_USER_OVERRIDDEN. r=keeler (5246515084) - bug 1261936 - stop using the subject common name in certificate verification error messages r=Cykesiopka (982cf43a11) - bug 1230234 - fix a leak in client auth certificate handling r=Cykesiopka (6e83f81218) - Bug 1260643 - Convert most uses of ScopedCERTCertificate in PSM to UniqueCERTCertificate. r=keeler (806b895c41) - Bug 1207137 - Set a security state flag when weak crypto override is needed. r=keeler Bug 1254306 - Do not check the fallback limit version for the RC4 fallback. r=keeler (8b5cb7101f) - Bug 1253010 - part 3 - create all nsIDateTimeFormat instances directly; r=smontagu (c1aa5d1d62) - Bug 1260310 - Create servo style contexts from ServoStyleSet. r=heycam (05f876eb13) - Bug 759568 - Part 1. Parse background-clip:text; r=dholbert r=heycam (d013b8fd84) - Bug 1251995 part 6 - Use struct to pass params for nsTextFrame::PainText* functions. r=jfkthame (3b9c163eab) - Bug 759568 - Part 2. Render background-clip:text; r=jfkthame (e534e048bf) - Bug 759568 - Part 4. mochitest for background-clip:text; r=heycom (3e548ebf99) - Bug 759568 - Part 5. reftest for background-clip:text; r=dbaron (43d2915305) - Bug 759568 - Part 6. Remove unused nsDisplayList::mVisibleRect; r=jfkthame (960a85de40) - Bug 1264910 - Simplify pref callback register/unregister in nsLayoutUtils. r=dholbert (f50219f117) - Bug 1097499 part 1 - Control support of 'text-combine-upright: digits' via a separate pref. r=heycam (37df36e815) - Bug 1261062 - When constraining the displayport by the max texture size, maintain the relative distribution of the margins. r=dvander (9a9423bdf1) - Bug 1246290 - Add a pref to allow disabling APZ on documents which have scroll-linked effects. r=botond (781b63c578) - Bug 1263347 - When checking if displayport changes should schedule a paint, make sure to use the proper displayport. r=mstange (998f59843e) - Bug 1097499 part 15 - Add reftests for text-combine-upright. r=jfkthame (843bea00bc) - Bug 1097499 followup - Fix metadata of tests submitted to w3c. DONTBUILD (e671b5b38b) - Bug 1097499 followup 2 - Fix metadata of tests submitted to w3c. DONTBUILD (abf0895450) - Bug 1097499 part 2 - Add a macro to simplify usage of nsStyleContext::GetUniqueStyleData. r=heycam (10486f1f24) - Bug 1097499 part 3 - Add a separate anonbox for text nodes. r=heycam (7dd4347215) - Bug 1097499 part 4 - Adjust computed value of writing-mode on text frames when text-combine-upright is used. r=heycam (c193f14b27) - Bug 1097499 part 5 - Layout text combine upright. r=jfkthame (c21422930b) - Bug 1097499 part 6 - Inherit move direction from parent for horizontal-in-vertical text. r=jfkthame (cf436b8494) - Bug 1097499 part 7 - Add reverse function of GetFullWidth. r=emk (32d02e7437) - Bug 1097499 part 8 - Move CountGraphemeClusters to mozilla::unicode. r=emk (e2b8942e53) - Bug 1156588 - Add crashtest. (237adb0604) - Bug 1234622. Tweak how nsDocumentViewer::FindContainerView finds the parent presshell. r=bz (d1e76ae2e9) - Bug 1245978 part 1: Make nsDocumentViewer::CreateStyleSet directly return the thing it creates. r=heycam (ede16260a4) - Bug 1245978 part 2: Drop redundant 'virtual' keyword from NS_DECL_NSIDOCUMENTVIEWERPRINT macro (which already includes 'override' keyword). r=heycam (42b8962e4f) - Bug 1183879 - Soften "non-subdocument frame" warning to also allow dummy nsFrames, which exist while subdocument is loading. r=dholbert (6ebcb53421) - Bug 1259246. Move nsIPresShell::GetRealPrimaryFrameFor to nsLayoutUtils::GetRealPrimaryFrameFor. r=dholbert (d3efd2f03a) - Bug 645647 part 1 - Don't let empty bullet frames block suppressing white-space in intrinsic size calculations. r=dholbert (2ce0a86bfb) - Bug 645647 part 2 - Reftests. (496e491990) - Bug 645647 part 3 - Remove unused trailingTextFrame member. r=dholbert (bd26ea25e6) - Bug 645647 part 4 - Add an 'm' prefix to some members to follow our naming conventions. r=dholbert (fe3c5240c9) - Bug 1097499 part 9 - Transform full-width characters to non-full-width correspondents for combined text. r=jfkthame (5b1eafe2a7) - Bug 1097499 part 10 - Add fwid/hwid/twid/qwid font feature support to gfx. r=jfkthame (682698dd38) - Bug 1097499 part 11 - Set width variant for text-combined frame. r=jfkthame (937f61e0e9) - Bug 1097499 part 12 - Handle spacing sensibly for text-combine-upright. r=jfkthame (9ae1ab2941) - Bug 1220438 - Correct baseline offset computation of text decoration for vertical-rl. r=jfkthame (10ad32d702) - Bug 1258636 part 1 - Use structs to pass params for decoration-related functions in nsCSSRendering. r=jfkthame (deef7071f1) - Bug 1258636 part 2 - Use struct to pass params for nsTextFrame::PaintDecorationLine. r=jfkthame (df5bde2547) - Bug 1229743 part 1 - Simplify text decoration handling code with lambda function and range-based for loop. r=jfkthame (51cd3ea4ca) - Bug 1229743 part 2 - Fix up decoration rect computation for vertical-rl and sideways-lr. r=jfkthame (0113279f53) - Bug 1251995 part 7 - Use struct to pass params for nsTextFrame::Paint*Shadow functions. r=jfkthame (e81ba231aa) - Bug 759568 - Part 3. Render text-selection beneath background image; r=jfkthame (e6757762ff) - Bug 1097499 part 13 - Draw decoration line properly for text-combine-upright. r=jfkthame (8f4be7f987) - Bug 1264120. Remove usage of nsAutoPtr from gfx/src. r=jfkthame (6831454d8c) - Bug 1119619 - Allow font-selection to fall back to an alternative face within the same family if the first-found face was not Regular, to handle cases where some styled faces have a reduced character set. r=m_kato (d8851b2877) - Bug 1243226 - relax the limit on fontconfig generics. r=heycam, a=me (05df737d0e) - Bug 1245811 - part 1 (based on patch by Andrew Comminos) - Replace gfxPlatformFontList::FindFamily with FindAndAddFamilies to allow for the possibility of the implementation returning multiple font families (e.g. when fontconfig has 'prefer' aliases). r=karlt (2bef9fafb0) - Bug 1245811 - part 2 (based on patch by Andrew Comminos) - Let gfxFcPlatformFontList return multiple families for a given name once fontconfig substitutions have been applied. r=karlt (1ffb425a0e) - Bug 1265452 - Remove use of nsAutoPtr from gfx/thebes. r=jrmuizel (d02c913ad5) - Bug 1265459 - Replace uses of nsAutoPtr with UniquePtr, and let MakeTextRun and similar methods return a UniquePtr. r=jrmuizel (da32e376b7) - Bug 1097499 part 14 - Draw emphasis marks properly for text-combine-upright. r=jfkthame (c9115615c6) - Bug 1097499 part 16 - Enable text-combine-upright by default. r=jfkthame (b616987f95) - Bug 1261699 - preserve user fontconfig autohint settings in Cairo glyph rendering options. r=jfkthame (3e46dff5ff) - Bug 1216001 - Fix a typo that eliminated a possible paint optimization. r=xidorn (6a350cadb7) - Bug 1261568 - part1: take -webkit-text-fill-color into consideration while (d49cf427ab) - Bug 1261568 - part2.1: update manifest before adding test. r=jgraham Bug 1261568 - part2.2: add reftest. r=jfkthame (ef3c22cfb4) --- accessible/base/TextAttrs.cpp | 2 +- accessible/base/nsAccessiblePivot.cpp | 2 +- accessible/generic/Accessible.cpp | 3 +- b2g/app/b2g.js | 1 - b2g/components/OopCommandLine.js | 1 - config/external/nss/nss.def | 6 + dom/base/FileList.cpp | 76 +- dom/base/FileList.h | 35 +- dom/base/StructuredCloneHolder.cpp | 149 +- dom/base/test/test_postMessages.html | 26 +- dom/canvas/CanvasRenderingContext2D.cpp | 18 +- dom/canvas/DocumentRendererChild.cpp | 5 +- dom/crypto/CryptoBuffer.cpp | 37 + dom/crypto/CryptoBuffer.h | 5 + dom/events/DataTransfer.cpp | 6 +- dom/filesystem/CreateDirectoryTask.cpp | 8 +- dom/filesystem/CreateDirectoryTask.h | 2 - dom/filesystem/CreateFileTask.cpp | 6 +- dom/filesystem/DeviceStorageFileSystem.cpp | 26 +- dom/filesystem/DeviceStorageFileSystem.h | 6 +- dom/filesystem/Directory.cpp | 62 +- dom/filesystem/Directory.h | 24 +- dom/filesystem/FileSystemBase.cpp | 37 +- dom/filesystem/FileSystemBase.h | 13 +- dom/filesystem/FileSystemRequestParent.cpp | 1 + dom/filesystem/FileSystemTaskBase.h | 4 + dom/filesystem/GetDirectoryListingTask.cpp | 20 +- dom/filesystem/GetDirectoryListingTask.h | 4 - dom/filesystem/GetFileOrDirectoryTask.cpp | 26 +- dom/filesystem/GetFileOrDirectoryTask.h | 4 - dom/filesystem/GetFilesTask.cpp | 359 + dom/filesystem/GetFilesTask.h | 99 + dom/filesystem/OSFileSystem.cpp | 7 - dom/filesystem/OSFileSystem.h | 18 +- dom/filesystem/PFileSystemParams.ipdlh | 11 +- dom/filesystem/PFileSystemRequest.ipdl | 6 + dom/filesystem/RemoveTask.cpp | 4 +- dom/filesystem/RemoveTask.h | 2 - dom/filesystem/moz.build | 1 + dom/filesystem/tests/filesystem_commons.js | 74 + dom/filesystem/tests/mochitest.ini | 1 + dom/filesystem/tests/script_fileList.js | 71 +- dom/filesystem/tests/test_basic.html | 80 +- dom/filesystem/tests/test_worker_basic.html | 28 +- dom/filesystem/tests/worker_basic.js | 59 +- dom/html/HTMLInputElement.cpp | 11 +- dom/html/HTMLTableCellElement.cpp | 2 +- dom/html/HTMLTableColElement.cpp | 2 +- dom/html/HTMLTableRowElement.cpp | 2 +- dom/html/HTMLTableSectionElement.cpp | 2 +- dom/html/nsGenericHTMLElement.cpp | 37 +- dom/ipc/ContentParent.cpp | 106 +- dom/ipc/ContentParent.h | 15 + dom/ipc/PContent.ipdl | 44 + .../webrtc/MediaEngineTabVideoSource.cpp | 5 +- dom/u2f/NSSToken.cpp | 172 - dom/u2f/NSSToken.h | 57 - dom/u2f/U2F.cpp | 260 +- dom/u2f/U2F.h | 19 +- dom/u2f/moz.build | 4 +- dom/u2f/tests/frame_no_token.html | 27 + dom/u2f/tests/mochitest.ini | 6 + dom/u2f/tests/pkijs/LICENSE | 30 + dom/u2f/tests/pkijs/README | 1 + dom/u2f/tests/pkijs/asn1.js | 5466 +++++++++++++ dom/u2f/tests/pkijs/common.js | 1542 ++++ dom/u2f/tests/pkijs/x509_schema.js | 1889 +++++ dom/u2f/tests/pkijs/x509_simpl.js | 7239 +++++++++++++++++ dom/u2f/tests/test_frame.html | 70 +- dom/u2f/tests/test_frame_appid_facet.html | 11 - .../test_frame_appid_facet_insecure.html | 3 - .../test_frame_appid_facet_remoteload.html | 3 - .../test_frame_appid_facet_subdomain.html | 48 +- dom/u2f/tests/test_frame_register.html | 3 - dom/u2f/tests/test_frame_register_sign.html | 201 + dom/u2f/tests/test_no_token.html | 34 +- dom/u2f/tests/test_util_methods.html | 70 +- dom/u2f/tests/u2futil.js | 34 +- dom/webidl/Directory.webidl | 3 + dom/webidl/FileList.webidl | 4 +- dom/xul/templates/nsXULContentUtils.cpp | 7 +- embedding/browser/nsDocShellTreeOwner.cpp | 4 +- gfx/2d/2D.h | 9 + gfx/2d/DrawTargetCairo.cpp | 301 +- gfx/2d/DrawTargetCairo.h | 3 + gfx/2d/DrawTargetD2D1.cpp | 114 +- gfx/2d/DrawTargetD2D1.h | 3 + gfx/2d/DrawTargetRecording.cpp | 4 + gfx/2d/DrawTargetSkia.cpp | 96 + gfx/2d/DrawTargetSkia.h | 2 + gfx/2d/HelpersSkia.h | 8 + gfx/2d/Logging.h | 7 + gfx/2d/SFNTData.cpp | 10 +- gfx/2d/ScaledFontWin.cpp | 6 +- gfx/gl/GLLibraryEGL.cpp | 2 +- gfx/gl/SharedSurfaceANGLE.cpp | 13 +- gfx/ipc/GfxMessageUtils.h | 35 +- gfx/layers/Compositor.cpp | 12 + gfx/layers/Compositor.h | 5 + gfx/layers/CompositorTypes.h | 5 +- gfx/layers/IMFYCbCrImage.cpp | 5 +- gfx/layers/Layers.cpp | 3 + gfx/layers/apz/src/AsyncPanZoomController.cpp | 8 + gfx/layers/basic/BasicCompositor.cpp | 219 +- gfx/layers/basic/BasicCompositor.h | 2 +- gfx/layers/basic/BasicLayerManager.cpp | 230 +- gfx/layers/basic/BasicPaintedLayer.cpp | 4 +- gfx/layers/basic/X11BasicCompositor.h | 5 +- gfx/layers/client/ClientLayerManager.cpp | 15 + gfx/layers/client/ClientLayerManager.h | 8 +- gfx/layers/client/ClientPaintedLayer.cpp | 3 +- .../client/SingleTiledContentClient.cpp | 6 +- gfx/layers/client/TextureClientPool.cpp | 12 +- gfx/layers/client/TextureClientPool.h | 5 + gfx/layers/client/TiledContentClient.cpp | 12 +- gfx/layers/d3d11/CompositorD3D11.cpp | 36 +- gfx/layers/d3d11/CompositorD3D11.h | 2 +- gfx/layers/d3d11/TextureD3D11.cpp | 53 +- gfx/layers/d3d11/TextureD3D11.h | 4 +- gfx/layers/d3d9/TextureD3D9.cpp | 9 + .../ipc/CompositableTransactionParent.cpp | 75 +- gfx/layers/ipc/CompositorBridgeChild.cpp | 3 +- gfx/layers/ipc/CompositorBridgeParent.cpp | 52 +- gfx/layers/ipc/CompositorBridgeParent.h | 15 +- gfx/layers/ipc/ImageBridgeChild.cpp | 39 +- gfx/layers/ipc/ImageBridgeParent.cpp | 17 +- gfx/layers/ipc/ImageBridgeParent.h | 5 +- gfx/layers/ipc/LayerTransactionChild.cpp | 5 + gfx/layers/ipc/LayerTransactionParent.cpp | 44 +- gfx/layers/ipc/LayerTransactionParent.h | 18 + gfx/layers/ipc/LayersMessages.ipdlh | 16 +- gfx/layers/ipc/PCompositorBridge.ipdl | 4 + gfx/layers/ipc/ShadowLayers.cpp | 57 +- gfx/layers/ipc/ShadowLayers.h | 2 +- gfx/layers/opengl/CompositorOGL.cpp | 6 +- gfx/layers/opengl/CompositorOGL.h | 3 +- gfx/src/RegionBuilder.h | 32 + gfx/src/gfxCrashReporterUtils.cpp | 2 +- gfx/src/moz.build | 1 + gfx/src/nsDeviceContext.cpp | 9 +- gfx/src/nsDeviceContext.h | 2 +- gfx/src/nsFont.cpp | 26 + gfx/src/nsFont.h | 3 +- gfx/src/nsFontMetrics.cpp | 8 +- gfx/src/nsFontMetrics.h | 2 +- gfx/src/nsRegion.h | 15 + gfx/tests/gtest/TestCompositor.cpp | 84 +- gfx/tests/gtest/TestRegion.cpp | 42 + gfx/tests/gtest/gfxFontSelectionTest.cpp | 8 +- gfx/tests/gtest/gfxTextRunPerfTest.cpp | 7 +- gfx/tests/gtest/gfxWordCacheTest.cpp | 8 +- gfx/thebes/gfxAndroidPlatform.cpp | 53 - gfx/thebes/gfxAndroidPlatform.h | 19 - gfx/thebes/gfxBlur.cpp | 5 +- gfx/thebes/gfxBlur.h | 1 - gfx/thebes/gfxContext.cpp | 21 +- gfx/thebes/gfxContext.h | 28 +- gfx/thebes/gfxDWriteCommon.h | 1 - gfx/thebes/gfxDWriteFontList.cpp | 16 +- gfx/thebes/gfxDWriteFontList.h | 7 +- gfx/thebes/gfxDWriteFonts.cpp | 2 +- gfx/thebes/gfxDWriteFonts.h | 3 +- gfx/thebes/gfxDrawable.cpp | 5 +- gfx/thebes/gfxDrawable.h | 1 - gfx/thebes/gfxFT2FontList.cpp | 2 +- gfx/thebes/gfxFcPlatformFontList.cpp | 91 +- gfx/thebes/gfxFcPlatformFontList.h | 31 +- gfx/thebes/gfxFont.cpp | 52 +- gfx/thebes/gfxFont.h | 23 +- gfx/thebes/gfxFontConstants.h | 6 + gfx/thebes/gfxFontEntry.cpp | 18 +- gfx/thebes/gfxFontEntry.h | 12 +- gfx/thebes/gfxFontInfoLoader.h | 1 - gfx/thebes/gfxFontUtils.cpp | 6 +- gfx/thebes/gfxFontUtils.h | 103 +- gfx/thebes/gfxGDIFont.cpp | 4 +- gfx/thebes/gfxGDIFont.h | 4 +- gfx/thebes/gfxGDIFontList.cpp | 18 +- gfx/thebes/gfxGDIFontList.h | 7 +- gfx/thebes/gfxImageSurface.h | 1 - gfx/thebes/gfxMacFont.cpp | 2 +- gfx/thebes/gfxMacFont.h | 2 +- gfx/thebes/gfxMacPlatformFontList.h | 7 +- gfx/thebes/gfxMacPlatformFontList.mm | 25 +- gfx/thebes/gfxPattern.h | 1 - gfx/thebes/gfxPlatform.cpp | 96 +- gfx/thebes/gfxPlatform.h | 14 +- gfx/thebes/gfxPlatformFontList.cpp | 73 +- gfx/thebes/gfxPlatformFontList.h | 30 +- gfx/thebes/gfxPlatformMac.cpp | 56 - gfx/thebes/gfxPlatformMac.h | 19 - gfx/thebes/gfxPrefs.h | 7 +- gfx/thebes/gfxReusableSurfaceWrapper.h | 1 - gfx/thebes/gfxSVGGlyphs.h | 1 - gfx/thebes/gfxTeeSurface.cpp | 1 - gfx/thebes/gfxTextRun.cpp | 140 +- gfx/thebes/gfxTextRun.h | 57 +- gfx/thebes/gfxUserFontSet.cpp | 6 +- gfx/thebes/gfxUserFontSet.h | 1 - gfx/thebes/gfxUtils.cpp | 17 +- gfx/thebes/gfxWindowsPlatform.cpp | 173 +- gfx/thebes/gfxWindowsPlatform.h | 51 +- gfx/thebes/gfxXlibSurface.cpp | 1 - image/ClippedImage.cpp | 5 +- image/DynamicImage.cpp | 5 +- image/OrientedImage.cpp | 5 +- image/VectorImage.cpp | 5 +- image/imgFrame.cpp | 5 +- .../tools/genUnicodePropertyData.pl | 11 + intl/unicharutil/util/nsUnicodeProperties.cpp | 44 +- intl/unicharutil/util/nsUnicodeProperties.h | 8 + .../util/nsUnicodePropertyData.cpp | 25 + ipc/glue/GeckoChildProcessHost.cpp | 23 + ipc/glue/GeckoChildProcessHost.h | 13 + ipc/glue/MessageChannel.cpp | 8 +- layout/base/FrameLayerBuilder.cpp | 19 +- layout/base/RestyleManager.cpp | 24 +- layout/base/StaticPresData.cpp | 311 + layout/base/StaticPresData.h | 160 + layout/base/crashtests/1156588.html | 29 + layout/base/crashtests/1234622-1.html | 17 + layout/base/crashtests/crashtests.list | 6 +- layout/base/moz.build | 2 + layout/base/nsCSSFrameConstructor.cpp | 95 +- layout/base/nsCSSFrameConstructor.h | 14 +- layout/base/nsCSSRendering.cpp | 307 +- layout/base/nsCSSRendering.h | 165 +- layout/base/nsDisplayList.cpp | 97 +- layout/base/nsDisplayList.h | 13 +- layout/base/nsDocumentViewer.cpp | 66 +- layout/base/nsIDocumentViewerPrint.h | 32 +- layout/base/nsIPresShell.h | 9 - layout/base/nsLayoutUtils.cpp | 229 +- layout/base/nsLayoutUtils.h | 23 +- layout/base/nsPresContext.cpp | 264 +- layout/base/nsPresContext.h | 100 +- layout/base/nsPresShell.cpp | 28 +- layout/base/nsPresShell.h | 1 - layout/base/nsQuoteList.cpp | 13 +- layout/build/nsLayoutStatics.cpp | 3 + layout/forms/nsButtonFrameRenderer.cpp | 2 +- layout/forms/nsComboboxControlFrame.cpp | 3 +- layout/forms/nsFieldSetFrame.cpp | 2 +- layout/forms/nsGfxButtonControlFrame.cpp | 2 +- layout/generic/MathMLTextRunFactory.cpp | 4 +- layout/generic/nsBlockFrame.cpp | 28 +- layout/generic/nsBulletFrame.cpp | 36 + layout/generic/nsBulletFrame.h | 5 +- layout/generic/nsCanvasFrame.cpp | 5 +- layout/generic/nsContainerFrame.cpp | 14 +- layout/generic/nsFirstLetterFrame.cpp | 9 +- layout/generic/nsFlexContainerFrame.cpp | 2 +- layout/generic/nsFrame.cpp | 210 +- layout/generic/nsGfxScrollFrame.cpp | 4 +- layout/generic/nsHTMLReflowState.cpp | 2 + layout/generic/nsIFrame.h | 68 +- layout/generic/nsImageFrame.cpp | 30 +- layout/generic/nsLineLayout.cpp | 3 +- layout/generic/nsPlaceholderFrame.cpp | 12 +- layout/generic/nsRubyBaseContainerFrame.cpp | 38 +- layout/generic/nsSelection.cpp | 12 +- layout/generic/nsSimplePageSequenceFrame.cpp | 5 +- layout/generic/nsSubDocumentFrame.cpp | 3 +- layout/generic/nsTextFrame.cpp | 1211 +-- layout/generic/nsTextFrame.h | 163 +- layout/generic/nsTextRunTransformations.cpp | 24 +- layout/generic/nsTextRunTransformations.h | 39 +- layout/inspector/nsFontFace.cpp | 2 +- layout/inspector/tests/test_bug877690.html | 10 +- layout/ipc/RenderFrameParent.cpp | 6 +- layout/mathml/nsMathMLChar.cpp | 75 +- layout/mathml/nsMathMLChar.h | 2 +- layout/media/symbols.def.in | 1 + layout/printing/nsPrintEngine.cpp | 4 +- .../background-clip-text-1-ref.html | 19 + .../backgrounds/background-clip-text-1a.html | 24 + .../backgrounds/background-clip-text-1b.html | 24 + .../backgrounds/background-clip-text-1c.html | 26 + .../backgrounds/background-clip-text-1d.html | 23 + layout/reftests/backgrounds/reftest.list | 6 + layout/reftests/bugs/645647-1-ref.html | 5 + layout/reftests/bugs/645647-1.html | 9 + layout/reftests/bugs/645647-2-ref.html | 27 + layout/reftests/bugs/645647-2.html | 28 + layout/reftests/bugs/reftest.list | 2 + layout/reftests/font-face/reftest.list | 2 +- layout/reftests/layers/reftest.list | 2 +- .../w3c-css/submitted/filters/reftest.list | 4 +- .../submitted/masking/mask-clip-1-ref.html | 42 + .../submitted/masking/mask-clip-1.html | 57 + .../masking/mask-composite-1-ref.html | 4 +- .../submitted/masking/mask-composite-1a.html | 4 +- .../submitted/masking/mask-composite-1b.html | 4 +- .../masking/mask-composite-2-ref.html | 2 +- .../submitted/masking/mask-composite-2a.html | 4 +- .../submitted/masking/mask-composite-2b.html | 4 +- .../submitted/masking/mask-image-1-ref.html | 19 + .../submitted/masking/mask-image-1a.html | 26 + .../submitted/masking/mask-image-1b.html | 26 + .../submitted/masking/mask-image-1c.html | 26 + .../submitted/masking/mask-image-2-ref.html | 20 + .../submitted/masking/mask-image-2.html | 31 + .../submitted/masking/mask-mode-a.html | 60 + .../submitted/masking/mask-mode-b.html | 61 + .../submitted/masking/mask-mode-ref.html | 52 + .../submitted/masking/mask-origin-1-ref.html | 34 + .../submitted/masking/mask-origin-1.html | 48 + .../submitted/masking/mask-origin-2-ref.html | 35 + .../submitted/masking/mask-origin-2.html | 77 + .../masking/mask-position-1-ref.html | 33 + .../submitted/masking/mask-position-1a.html | 39 + .../submitted/masking/mask-position-1b.html | 39 + .../submitted/masking/mask-position-1c.html | 39 + .../masking/mask-position-2-ref.html | 29 + .../submitted/masking/mask-position-2a.html | 34 + .../submitted/masking/mask-position-2b.html | 34 + .../masking/mask-position-3-ref.html | 29 + .../submitted/masking/mask-position-3a.html | 34 + .../submitted/masking/mask-position-3b.html | 34 + .../masking/mask-position-4-ref.html | 29 + .../submitted/masking/mask-position-4a.html | 34 + .../submitted/masking/mask-position-4b.html | 34 + .../submitted/masking/mask-position-4c.html | 34 + .../submitted/masking/mask-position-4d.html | 32 + .../masking/mask-position-5-ref.html | 38 + .../submitted/masking/mask-position-5.html | 35 + .../masking/mask-position-6-ref.html | 30 + .../submitted/masking/mask-position-6.html | 50 + .../masking/mask-position-7-ref.html | 30 + .../submitted/masking/mask-position-7.html | 50 + .../submitted/masking/mask-repeat-1-ref.html | 45 + .../submitted/masking/mask-repeat-1.html | 47 + .../submitted/masking/mask-repeat-2-ref.html | 45 + .../submitted/masking/mask-repeat-2.html | 42 + .../submitted/masking/mask-repeat-3-ref.html | 40 + .../submitted/masking/mask-repeat-3.html | 42 + .../masking/mask-size-auto-auto.html | 34 + .../masking/mask-size-auto-length-ref.html | 27 + .../masking/mask-size-auto-length.html | 33 + .../masking/mask-size-auto-percent.html | 33 + .../submitted/masking/mask-size-auto-ref.html | 27 + .../submitted/masking/mask-size-auto.html | 34 + .../mask-size-contain-clip-border-ref.html | 27 + .../mask-size-contain-clip-border.html | 36 + .../mask-size-contain-clip-padding-ref.html | 35 + .../mask-size-contain-clip-padding.html | 34 + ...size-contain-position-fifty-fifty-ref.html | 33 + ...ask-size-contain-position-fifty-fifty.html | 35 + .../masking/mask-size-contain-ref.html | 27 + .../submitted/masking/mask-size-contain.html | 34 + .../masking/mask-size-cover-ref.html | 21 + .../submitted/masking/mask-size-cover.html | 28 + .../masking/mask-size-length-auto.html | 34 + .../masking/mask-size-length-length-ref.html | 27 + .../masking/mask-size-length-length.html | 34 + .../masking/mask-size-length-percent-ref.html | 27 + .../masking/mask-size-length-percent.html | 34 + .../submitted/masking/mask-size-length.html | 34 + .../masking/mask-size-percent-auto.html | 34 + .../masking/mask-size-percent-length.html | 34 + .../mask-size-percent-percent-ref.html | 27 + ...mask-size-percent-percent-stretch-ref.html | 29 + .../mask-size-percent-percent-stretch.html | 34 + .../masking/mask-size-percent-percent.html | 34 + .../submitted/masking/mask-size-percent.html | 34 + .../w3c-css/submitted/masking/reftest.list | 71 +- .../masking/support/50x100-opaque-blue.svg | 4 + .../masking/support/50x50-opaque-blue.svg | 4 + .../masking/support/blue-100x100.png | Bin 0 -> 40279 bytes .../masking/support/blue-100x100.svg | 4 + .../blue-100x50-transparent-100x50.png} | Bin .../blue-100x50-transparent-100x50.svg} | 6 +- .../support/blue-luminance-100x100.svg | 4 + .../masking/support/green-100x100.png | Bin 0 -> 40279 bytes .../masking/support/green-100x100.svg | 4 + .../support/green-luminance-100x100.svg | 4 + .../support/mask-half-transparent-100x100.svg | 9 + .../submitted/masking/support/red-100x100.png | Bin 0 -> 40279 bytes .../submitted/masking/support/red-100x100.svg | 4 + .../masking/support/red-luminance-100x100.svg | 4 + .../transparent-100x50-blue-100x50.png} | Bin .../transparent-100x50-blue-100x50.svg} | 6 +- .../reftests/w3c-css/submitted/reftest.list | 3 + .../submitted/writing-modes-3/reftest.list | 11 + .../support/WidthTest-Regular.otf | Bin 0 -> 3248 bytes .../submitted/writing-modes-3/support/tcy.css | 11 + .../writing-modes-3/support/width-test.css | 7 + ...t-combine-upright-compression-001-ref.html | 21 + .../text-combine-upright-compression-001.html | 24 + ...t-combine-upright-compression-002-ref.html | 21 + .../text-combine-upright-compression-002.html | 24 + ...t-combine-upright-compression-003-ref.html | 21 + .../text-combine-upright-compression-003.html | 24 + ...t-combine-upright-compression-004-ref.html | 21 + .../text-combine-upright-compression-004.html | 24 + ...t-combine-upright-compression-005-ref.html | 21 + .../text-combine-upright-compression-005.html | 24 + ...text-combine-upright-compression-005a.html | 25 + ...t-combine-upright-compression-006-ref.html | 21 + .../text-combine-upright-compression-006.html | 24 + ...text-combine-upright-compression-006a.html | 25 + ...t-combine-upright-compression-007-ref.html | 21 + .../text-combine-upright-compression-007.html | 25 + layout/reftests/webkit-box/reftest.list | 25 + .../webkit-box-anon-flex-items-1-ref.html | 101 + .../webkit-box-anon-flex-items-1.html | 83 + .../webkit-box-anon-flex-items-2-ref.html | 35 + .../webkit-box-anon-flex-items-2.html | 28 + .../webkit-box-anon-flex-items-3-ref.html | 29 + .../webkit-box-anon-flex-items-3.html | 23 + .../webkit-display-values-1-ref.html | 29 + .../webkit-box/webkit-display-values-1.html | 41 + layout/style/CounterStyleManager.cpp | 17 +- layout/style/Declaration.cpp | 16 +- layout/style/ServoBindingHelpers.h | 15 +- layout/style/ServoBindings.cpp | 63 +- layout/style/ServoBindings.h | 37 +- layout/style/ServoStyleSet.cpp | 57 +- layout/style/ServoStyleSet.h | 4 +- layout/style/ServoStyleSheet.cpp | 2 +- layout/style/ServoStyleSheet.h | 2 + layout/style/StyleAnimationValue.cpp | 27 +- layout/style/StyleContextSource.h | 138 + layout/style/StyleSetHandle.h | 6 +- layout/style/StyleSetHandleInlines.h | 25 +- layout/style/StyleStructContext.h | 121 + layout/style/generate-stylestructlist.py | 2 +- layout/style/moz.build | 2 + layout/style/nsCSSAnonBoxList.h | 8 +- layout/style/nsCSSAnonBoxes.h | 2 + layout/style/nsCSSKeywordList.h | 3 + layout/style/nsCSSParser.cpp | 94 +- layout/style/nsCSSPropList.h | 79 +- layout/style/nsCSSProps.cpp | 44 +- layout/style/nsCSSProps.h | 4 + layout/style/nsComputedDOMStyle.cpp | 75 +- layout/style/nsComputedDOMStyle.h | 2 + layout/style/nsComputedDOMStylePropertyList.h | 7 + layout/style/nsRuleNode.cpp | 839 +- layout/style/nsRuleNode.h | 79 +- layout/style/nsStyleConsts.h | 10 +- layout/style/nsStyleContext.cpp | 288 +- layout/style/nsStyleContext.h | 115 +- layout/style/nsStyleSet.cpp | 142 +- layout/style/nsStyleSet.h | 87 +- layout/style/nsStyleStruct.cpp | 581 +- layout/style/nsStyleStruct.h | 352 +- layout/style/nsStyleStructInlines.h | 6 +- layout/style/test/property_database.js | 51 +- layout/style/test/test_bug657143.html | 234 +- .../style/test/test_transitions_events.html | 11 + .../test/test_transitions_per_property.html | 2 + layout/svg/SVGTextFrame.cpp | 55 +- layout/svg/nsFilterInstance.cpp | 18 +- layout/svg/nsSVGClipPathFrame.cpp | 9 +- layout/svg/nsSVGContainerFrame.cpp | 3 +- layout/svg/nsSVGEffects.cpp | 8 +- layout/svg/nsSVGForeignObjectFrame.cpp | 2 +- layout/svg/nsSVGImageFrame.cpp | 15 +- layout/svg/nsSVGInnerSVGFrame.cpp | 2 +- layout/svg/nsSVGIntegrationUtils.cpp | 95 +- layout/svg/nsSVGMaskFrame.cpp | 18 +- layout/svg/nsSVGMaskFrame.h | 3 +- layout/svg/nsSVGPathGeometryFrame.cpp | 6 +- layout/svg/nsSVGPatternFrame.cpp | 5 +- layout/svg/nsSVGSwitchFrame.cpp | 2 +- layout/svg/nsSVGUseFrame.cpp | 2 +- layout/svg/nsSVGUtils.cpp | 31 +- layout/tables/nsTableCellFrame.cpp | 5 +- layout/tables/nsTableFrame.cpp | 2 +- layout/tables/nsTableOuterFrame.cpp | 2 +- layout/xul/nsTextBoxFrame.cpp | 53 +- layout/xul/tree/nsTreeBodyFrame.cpp | 4 +- modules/libpref/init/all.js | 19 +- netwerk/base/LoadInfo.cpp | 24 +- netwerk/base/Predictor.cpp | 559 +- netwerk/base/Predictor.h | 110 +- netwerk/base/moz.build | 1 + netwerk/base/nsBaseChannel.cpp | 2 + netwerk/base/nsICachingChannel.idl | 8 + netwerk/base/nsILoadInfo.idl | 14 + netwerk/base/nsINetworkPredictorVerifier.idl | 11 +- netwerk/base/nsIRequest.idl | 3 +- netwerk/base/nsNetUtil.cpp | 72 +- netwerk/base/nsNetUtil.h | 1 + netwerk/base/security-prefs.js | 23 +- netwerk/cache2/CacheStorageService.cpp | 20 +- netwerk/cache2/nsICacheEntry.idl | 4 + netwerk/ipc/NeckoChild.cpp | 30 + netwerk/ipc/NeckoChild.h | 4 + netwerk/ipc/NeckoParent.cpp | 1 + netwerk/ipc/PNecko.ipdl | 3 + netwerk/protocol/http/HttpBaseChannel.cpp | 46 +- netwerk/protocol/http/HttpBaseChannel.h | 7 +- netwerk/protocol/http/HttpChannelChild.cpp | 3 + netwerk/protocol/http/nsCORSListenerProxy.cpp | 4 + netwerk/protocol/http/nsHttpChannel.cpp | 81 +- netwerk/protocol/http/nsHttpHandler.cpp | 5 + netwerk/protocol/http/nsHttpResponseHead.cpp | 24 +- netwerk/protocol/http/nsIHttpChannel.idl | 2 +- .../streamconv/converters/nsIndexedToHTML.cpp | 7 +- .../test/mochitests/test_rel_preconnect.html | 32 +- netwerk/test/unit/test_auth_proxy.js | 34 +- netwerk/test/unit/test_authentication.js | 43 +- netwerk/test/unit/test_bug1064258.js | 21 +- netwerk/test/unit/test_bug263127.js | 20 +- netwerk/test/unit/test_bug412945.js | 20 +- netwerk/test/unit/test_bug482601.js | 22 +- netwerk/test/unit/test_bug490095.js | 20 +- netwerk/test/unit/test_bug561276.js | 20 +- netwerk/test/unit/test_bug596443.js | 24 +- netwerk/test/unit/test_bug618835.js | 23 +- netwerk/test/unit/test_bug856978.js | 18 +- netwerk/test/unit/test_cacheflags.js | 63 +- .../test/unit/test_content_encoding_gzip.js | 20 +- netwerk/test/unit/test_content_sniffer.js | 15 +- netwerk/test/unit/test_data_protocol.js | 16 +- netwerk/test/unit/test_duplicate_headers.js | 20 +- .../test_fallback_no-cache-entry_canceled.js | 2 +- .../test_fallback_no-cache-entry_passing.js | 3 +- ...k_redirect-to-different-origin_canceled.js | 3 +- ...ck_redirect-to-different-origin_passing.js | 3 +- netwerk/test/unit/test_gzipped_206.js | 17 +- .../test/unit/test_multipart_streamconv.js | 28 +- netwerk/test/unit/test_nojsredir.js | 20 +- netwerk/test/unit/test_plaintext_sniff.js | 21 +- netwerk/test/unit/test_predictor.js | 201 +- netwerk/test/unit/test_range_requests.js | 84 +- .../unit/test_redirect-caching_canceled.js | 19 +- netwerk/test/unit/test_redirect_history.js | 15 +- netwerk/test/unit/test_resumable_channel.js | 53 +- .../test/unit/test_synthesized_response.js | 43 +- netwerk/test/unit/test_traceable_channel.js | 17 +- netwerk/test/unit_ipc/test_predictor_wrap.js | 24 + security/apps/AppSignatureVerification.cpp | 22 +- security/apps/AppTrustDomain.cpp | 6 +- security/apps/AppTrustDomain.h | 2 +- .../certverifier/BRNameMatchingPolicy.cpp | 38 + security/certverifier/BRNameMatchingPolicy.h | 56 + security/certverifier/CertVerifier.cpp | 49 +- security/certverifier/CertVerifier.h | 11 +- .../certverifier/NSSCertDBTrustDomain.cpp | 35 +- security/certverifier/OCSPRequestor.cpp | 28 +- security/certverifier/OCSPRequestor.h | 8 +- security/certverifier/moz.build | 2 + .../en-US/chrome/pipnss/pipnss.properties | 1 - security/manager/pki/nsNSSDialogs.cpp | 43 +- security/manager/ssl/CSTrustDomain.cpp | 217 + security/manager/ssl/CSTrustDomain.h | 75 + .../manager/ssl/ContentSignatureVerifier.cpp | 372 + .../manager/ssl/ContentSignatureVerifier.h | 67 + .../manager/ssl/IntolerantFallbackList.inc | 1070 --- .../manager/ssl/SSLServerCertVerification.cpp | 114 +- security/manager/ssl/ScopedNSSTypes.h | 15 + security/manager/ssl/SharedCertVerifier.h | 5 +- .../manager/ssl/TransportSecurityInfo.cpp | 167 +- security/manager/ssl/moz.build | 9 +- .../manager/ssl/nsCertOverrideService.cpp | 5 +- security/manager/ssl/nsCertTree.cpp | 35 +- security/manager/ssl/nsCryptoHash.cpp | 12 +- .../manager/ssl/nsDataSignatureVerifier.cpp | 32 +- .../ssl/nsIContentSignatureVerifier.idl | 88 + security/manager/ssl/nsINSSU2FToken.idl | 75 + security/manager/ssl/nsKeyModule.cpp | 8 +- security/manager/ssl/nsKeyModule.h | 2 +- security/manager/ssl/nsKeygenHandler.cpp | 48 +- security/manager/ssl/nsNSSASN1Object.cpp | 11 +- security/manager/ssl/nsNSSCallbacks.cpp | 102 +- security/manager/ssl/nsNSSCertHelper.cpp | 47 +- security/manager/ssl/nsNSSCertValidity.cpp | 9 +- security/manager/ssl/nsNSSCertificate.cpp | 170 +- security/manager/ssl/nsNSSCertificate.h | 5 +- security/manager/ssl/nsNSSCertificateDB.cpp | 71 +- security/manager/ssl/nsNSSComponent.cpp | 85 +- security/manager/ssl/nsNSSComponent.h | 6 +- security/manager/ssl/nsNSSIOLayer.cpp | 285 +- security/manager/ssl/nsNSSIOLayer.h | 4 - security/manager/ssl/nsNSSModule.cpp | 10 + security/manager/ssl/nsNSSShutDown.cpp | 116 +- security/manager/ssl/nsNSSShutDown.h | 27 +- security/manager/ssl/nsNSSU2FToken.cpp | 707 ++ security/manager/ssl/nsNSSU2FToken.h | 43 + security/manager/ssl/nsNTLMAuthModule.cpp | 11 +- security/manager/ssl/nsPKCS12Blob.cpp | 39 +- security/manager/ssl/nsSDR.cpp | 2 + security/manager/ssl/nsSDR.h | 1 + .../manager/ssl/nsSecureBrowserUIImpl.cpp | 16 + security/manager/ssl/nsSecureBrowserUIImpl.h | 1 + .../manager/ssl/nsSiteSecurityService.cpp | 2 +- .../tests/compiled/TestIsCertBuiltInRoot.cpp | 4 +- .../manager/ssl/tests/gtest/OCSPCacheTest.cpp | 10 +- .../ssl/tests/gtest/TLSIntoleranceTest.cpp | 64 - security/manager/ssl/tests/unit/head_psm.js | 25 +- security/manager/ssl/tests/unit/moz.build | 1 + security/manager/ssl/tests/unit/pycert.py | 19 +- .../unit/test_baseline_requirements/ca.pem | 17 + .../ca.pem.certspec | 4 + .../unit/test_baseline_requirements/moz.build | 17 + .../test_baseline_requirements/no-san-old.pem | 17 + .../no-san-old.pem.certspec | 3 + .../no-san-recent.pem | 17 + .../no-san-recent.pem.certspec | 3 + .../san-contains-no-hostnames-old.pem | 18 + ...san-contains-no-hostnames-old.pem.certspec | 4 + .../san-contains-no-hostnames-recent.pem | 18 + ...-contains-no-hostnames-recent.pem.certspec | 4 + ...seline_requirements_subject_common_name.js | 131 + .../ssl/tests/unit/test_cert_overrides.js | 12 +- .../ssl/tests/unit/test_ocsp_caching.js | 3 +- .../tests/unit/test_ocsp_stapling_expired.js | 9 + .../unit/tlsserver/cmd/BadCertServer.cpp | 11 +- .../tlsserver/cmd/GenerateOCSPResponse.cpp | 8 +- .../unit/tlsserver/cmd/OCSPStaplingServer.cpp | 6 +- .../tests/unit/tlsserver/lib/OCSPCommon.cpp | 40 +- .../ssl/tests/unit/tlsserver/lib/OCSPCommon.h | 9 +- .../tests/unit/tlsserver/lib/TLSServer.cpp | 94 +- .../ssl/tests/unit/tlsserver/lib/TLSServer.h | 15 +- security/manager/ssl/tests/unit/xpcshell.ini | 2 + .../tools/genIntolerantFallbackList.js | 264 - security/pkix/include/pkix/pkix.h | 3 +- security/pkix/include/pkix/pkixtypes.h | 24 + security/pkix/lib/pkixnames.cpp | 23 +- security/pkix/test/gtest/pkixgtest.h | 11 + security/pkix/test/gtest/pkixnames_tests.cpp | 45 +- testing/gtest/moz.build | 2 + testing/gtest/mozilla/MozGTestBench.cpp | 39 + testing/gtest/mozilla/MozGTestBench.h | 22 + .../configs/unittests/win_unittest.py | 2 - testing/web-platform/meta/MANIFEST.json | 16 + ...kit-text-fill-color-property-001a.html.ini | 3 + ...kit-text-fill-color-property-001b.html.ini | 3 + ...kit-text-fill-color-property-001c.html.ini | 3 + ...kit-text-fill-color-property-001d.html.ini | 3 + ...bkit-text-fill-color-property-001-ref.html | 6 + .../webkit-text-fill-color-property-001a.html | 9 + .../webkit-text-fill-color-property-001b.html | 9 + .../webkit-text-fill-color-property-001c.html | 9 + .../webkit-text-fill-color-property-001d.html | 9 + toolkit/components/filepicker/nsFileView.cpp | 3 +- toolkit/components/telemetry/Histograms.json | 50 + toolkit/library/moz.build | 2 +- uriloader/base/nsIWebProgressListener.idl | 4 + widget/PuppetWidget.cpp | 6 +- widget/cocoa/nsChildView.mm | 9 +- widget/cocoa/nsCocoaUtils.mm | 2 +- widget/cocoa/nsNativeThemeCocoa.mm | 2 +- widget/gtk/nsWindow.cpp | 10 +- widget/nsBaseDragService.cpp | 4 +- widget/nsBaseFilePicker.cpp | 3 +- widget/nsFilePickerProxy.cpp | 3 +- widget/uikit/nsWindow.mm | 15 +- widget/windows/nsWindowGfx.cpp | 5 +- 652 files changed, 33082 insertions(+), 8638 deletions(-) create mode 100644 dom/filesystem/GetFilesTask.cpp create mode 100644 dom/filesystem/GetFilesTask.h create mode 100644 dom/filesystem/tests/filesystem_commons.js delete mode 100644 dom/u2f/NSSToken.cpp delete mode 100644 dom/u2f/NSSToken.h create mode 100644 dom/u2f/tests/frame_no_token.html create mode 100644 dom/u2f/tests/pkijs/LICENSE create mode 100644 dom/u2f/tests/pkijs/README create mode 100644 dom/u2f/tests/pkijs/asn1.js create mode 100644 dom/u2f/tests/pkijs/common.js create mode 100644 dom/u2f/tests/pkijs/x509_schema.js create mode 100644 dom/u2f/tests/pkijs/x509_simpl.js create mode 100644 dom/u2f/tests/test_frame_register_sign.html create mode 100644 gfx/src/RegionBuilder.h create mode 100644 layout/base/StaticPresData.cpp create mode 100644 layout/base/StaticPresData.h create mode 100644 layout/base/crashtests/1156588.html create mode 100644 layout/base/crashtests/1234622-1.html create mode 100644 layout/reftests/backgrounds/background-clip-text-1-ref.html create mode 100644 layout/reftests/backgrounds/background-clip-text-1a.html create mode 100644 layout/reftests/backgrounds/background-clip-text-1b.html create mode 100644 layout/reftests/backgrounds/background-clip-text-1c.html create mode 100644 layout/reftests/backgrounds/background-clip-text-1d.html create mode 100644 layout/reftests/bugs/645647-1-ref.html create mode 100644 layout/reftests/bugs/645647-1.html create mode 100644 layout/reftests/bugs/645647-2-ref.html create mode 100644 layout/reftests/bugs/645647-2.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-clip-1-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-clip-1.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-image-1-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-image-1a.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-image-1b.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-image-1c.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-image-2-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-image-2.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-mode-a.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-mode-b.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-mode-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-origin-1-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-origin-1.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-origin-2-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-origin-2.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-position-1-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-position-1a.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-position-1b.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-position-1c.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-position-2-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-position-2a.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-position-2b.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-position-3-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-position-3a.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-position-3b.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-position-4-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-position-4a.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-position-4b.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-position-4c.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-position-4d.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-position-5-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-position-5.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-position-6-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-position-6.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-position-7-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-position-7.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-repeat-1-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-repeat-1.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-repeat-2-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-repeat-2.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-repeat-3-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-repeat-3.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-auto-auto.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-auto-length-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-auto-length.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-auto-percent.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-auto-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-auto.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-contain-clip-border-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-contain-clip-border.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-contain-clip-padding-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-contain-clip-padding.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-contain-position-fifty-fifty-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-contain-position-fifty-fifty.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-contain-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-contain.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-cover-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-cover.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-length-auto.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-length-length-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-length-length.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-length-percent-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-length-percent.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-length.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-percent-auto.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-percent-length.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-percent-percent-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-percent-percent-stretch-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-percent-percent-stretch.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-percent-percent.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-size-percent.html create mode 100644 layout/reftests/w3c-css/submitted/masking/support/50x100-opaque-blue.svg create mode 100644 layout/reftests/w3c-css/submitted/masking/support/50x50-opaque-blue.svg create mode 100644 layout/reftests/w3c-css/submitted/masking/support/blue-100x100.png create mode 100644 layout/reftests/w3c-css/submitted/masking/support/blue-100x100.svg rename layout/reftests/w3c-css/submitted/masking/{blue-100x50-transparent-100X50.png => support/blue-100x50-transparent-100x50.png} (100%) rename layout/reftests/w3c-css/submitted/masking/{transparent-100x50-blue-100X50.svg => support/blue-100x50-transparent-100x50.svg} (64%) create mode 100644 layout/reftests/w3c-css/submitted/masking/support/blue-luminance-100x100.svg create mode 100644 layout/reftests/w3c-css/submitted/masking/support/green-100x100.png create mode 100644 layout/reftests/w3c-css/submitted/masking/support/green-100x100.svg create mode 100644 layout/reftests/w3c-css/submitted/masking/support/green-luminance-100x100.svg create mode 100644 layout/reftests/w3c-css/submitted/masking/support/mask-half-transparent-100x100.svg create mode 100644 layout/reftests/w3c-css/submitted/masking/support/red-100x100.png create mode 100644 layout/reftests/w3c-css/submitted/masking/support/red-100x100.svg create mode 100644 layout/reftests/w3c-css/submitted/masking/support/red-luminance-100x100.svg rename layout/reftests/w3c-css/submitted/masking/{transparent-100x50-blue-100X50.png => support/transparent-100x50-blue-100x50.png} (100%) rename layout/reftests/w3c-css/submitted/masking/{blue-100x50-transparent-100X50.svg => support/transparent-100x50-blue-100x50.svg} (64%) create mode 100644 layout/reftests/w3c-css/submitted/writing-modes-3/reftest.list create mode 100644 layout/reftests/w3c-css/submitted/writing-modes-3/support/WidthTest-Regular.otf create mode 100644 layout/reftests/w3c-css/submitted/writing-modes-3/support/tcy.css create mode 100644 layout/reftests/w3c-css/submitted/writing-modes-3/support/width-test.css create mode 100644 layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-001-ref.html create mode 100644 layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-001.html create mode 100644 layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-002-ref.html create mode 100644 layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-002.html create mode 100644 layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-003-ref.html create mode 100644 layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-003.html create mode 100644 layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-004-ref.html create mode 100644 layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-004.html create mode 100644 layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-005-ref.html create mode 100644 layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-005.html create mode 100644 layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-005a.html create mode 100644 layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-006-ref.html create mode 100644 layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-006.html create mode 100644 layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-006a.html create mode 100644 layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-007-ref.html create mode 100644 layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-007.html create mode 100644 layout/reftests/webkit-box/reftest.list create mode 100644 layout/reftests/webkit-box/webkit-box-anon-flex-items-1-ref.html create mode 100644 layout/reftests/webkit-box/webkit-box-anon-flex-items-1.html create mode 100644 layout/reftests/webkit-box/webkit-box-anon-flex-items-2-ref.html create mode 100644 layout/reftests/webkit-box/webkit-box-anon-flex-items-2.html create mode 100644 layout/reftests/webkit-box/webkit-box-anon-flex-items-3-ref.html create mode 100644 layout/reftests/webkit-box/webkit-box-anon-flex-items-3.html create mode 100644 layout/reftests/webkit-box/webkit-display-values-1-ref.html create mode 100644 layout/reftests/webkit-box/webkit-display-values-1.html create mode 100644 layout/style/StyleContextSource.h create mode 100644 layout/style/StyleStructContext.h create mode 100644 security/certverifier/BRNameMatchingPolicy.cpp create mode 100644 security/certverifier/BRNameMatchingPolicy.h create mode 100644 security/manager/ssl/CSTrustDomain.cpp create mode 100644 security/manager/ssl/CSTrustDomain.h create mode 100644 security/manager/ssl/ContentSignatureVerifier.cpp create mode 100644 security/manager/ssl/ContentSignatureVerifier.h delete mode 100644 security/manager/ssl/IntolerantFallbackList.inc create mode 100644 security/manager/ssl/nsIContentSignatureVerifier.idl create mode 100644 security/manager/ssl/nsINSSU2FToken.idl create mode 100644 security/manager/ssl/nsNSSU2FToken.cpp create mode 100644 security/manager/ssl/nsNSSU2FToken.h create mode 100644 security/manager/ssl/tests/unit/test_baseline_requirements/ca.pem create mode 100644 security/manager/ssl/tests/unit/test_baseline_requirements/ca.pem.certspec create mode 100644 security/manager/ssl/tests/unit/test_baseline_requirements/moz.build create mode 100644 security/manager/ssl/tests/unit/test_baseline_requirements/no-san-old.pem create mode 100644 security/manager/ssl/tests/unit/test_baseline_requirements/no-san-old.pem.certspec create mode 100644 security/manager/ssl/tests/unit/test_baseline_requirements/no-san-recent.pem create mode 100644 security/manager/ssl/tests/unit/test_baseline_requirements/no-san-recent.pem.certspec create mode 100644 security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-old.pem create mode 100644 security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-old.pem.certspec create mode 100644 security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-recent.pem create mode 100644 security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-recent.pem.certspec create mode 100644 security/manager/ssl/tests/unit/test_baseline_requirements_subject_common_name.js delete mode 100644 security/manager/tools/genIntolerantFallbackList.js create mode 100644 testing/gtest/mozilla/MozGTestBench.cpp create mode 100644 testing/gtest/mozilla/MozGTestBench.h create mode 100644 testing/web-platform/meta/compat/webkit-text-fill-color-property-001a.html.ini create mode 100644 testing/web-platform/meta/compat/webkit-text-fill-color-property-001b.html.ini create mode 100644 testing/web-platform/meta/compat/webkit-text-fill-color-property-001c.html.ini create mode 100644 testing/web-platform/meta/compat/webkit-text-fill-color-property-001d.html.ini create mode 100644 testing/web-platform/tests/compat/webkit-text-fill-color-property-001-ref.html create mode 100644 testing/web-platform/tests/compat/webkit-text-fill-color-property-001a.html create mode 100644 testing/web-platform/tests/compat/webkit-text-fill-color-property-001b.html create mode 100644 testing/web-platform/tests/compat/webkit-text-fill-color-property-001c.html create mode 100644 testing/web-platform/tests/compat/webkit-text-fill-color-property-001d.html diff --git a/accessible/base/TextAttrs.cpp b/accessible/base/TextAttrs.cpp index 6acc803029..ba83c9666a 100644 --- a/accessible/base/TextAttrs.cpp +++ b/accessible/base/TextAttrs.cpp @@ -828,7 +828,7 @@ TextAttrsMgr::TextPosValue TextAttrsMgr::TextPosTextAttr:: GetTextPosValue(nsIFrame* aFrame) const { - const nsStyleCoord& styleCoord = aFrame->StyleTextReset()->mVerticalAlign; + const nsStyleCoord& styleCoord = aFrame->StyleDisplay()->mVerticalAlign; switch (styleCoord.GetUnit()) { case eStyleUnit_Enumerated: switch (styleCoord.GetIntValue()) { diff --git a/accessible/base/nsAccessiblePivot.cpp b/accessible/base/nsAccessiblePivot.cpp index 963dfc4ec3..7ce45360d4 100644 --- a/accessible/base/nsAccessiblePivot.cpp +++ b/accessible/base/nsAccessiblePivot.cpp @@ -901,7 +901,7 @@ RuleCache::ApplyFilter(Accessible* aAccessible, uint16_t* aResult) if ((nsIAccessibleTraversalRule::PREFILTER_TRANSPARENT & mPreFilter) && !(state & states::OPAQUE1)) { nsIFrame* frame = aAccessible->GetFrame(); - if (frame->StyleDisplay()->mOpacity == 0.0f) { + if (frame->StyleEffects()->mOpacity == 0.0f) { *aResult |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE; return NS_OK; } diff --git a/accessible/generic/Accessible.cpp b/accessible/generic/Accessible.cpp index 2338221471..783900b585 100644 --- a/accessible/generic/Accessible.cpp +++ b/accessible/generic/Accessible.cpp @@ -1202,8 +1202,7 @@ Accessible::State() if (!frame) return state; - const nsStyleDisplay* display = frame->StyleDisplay(); - if (display && display->mOpacity == 1.0f && + if (frame->StyleEffects()->mOpacity == 1.0f && !(state & states::INVISIBLE)) { state |= states::OPAQUE1; } diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index 5acdfdd8db..6a36eedb70 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -296,7 +296,6 @@ pref("ui.dragThresholdY", 25); // Layers Acceleration. We can only have nice things on gonk, because // they're not maintained anywhere else. -pref("layers.offmainthreadcomposition.enabled", true); pref("layers.offmainthreadcomposition.async-animations", true); #ifndef MOZ_WIDGET_GONK pref("dom.ipc.tabs.disabled", true); diff --git a/b2g/components/OopCommandLine.js b/b2g/components/OopCommandLine.js index cfe13ed914..658bbdde52 100644 --- a/b2g/components/OopCommandLine.js +++ b/b2g/components/OopCommandLine.js @@ -26,7 +26,6 @@ oopCommandlineHandler.prototype = { // environment branch.setBoolPref("dom.ipc.tabs.disabled", false); branch.setBoolPref("layers.acceleration.disabled", false); - branch.setBoolPref("layers.offmainthreadcomposition.enabled", true); branch.setBoolPref("layers.offmainthreadcomposition.async-animations", true); branch.setBoolPref("layers.async-video.enabled", true); branch.setBoolPref("layers.async-pan-zoom.enabled", true); diff --git a/config/external/nss/nss.def b/config/external/nss/nss.def index 523beb96fb..008d967549 100644 --- a/config/external/nss/nss.def +++ b/config/external/nss/nss.def @@ -363,6 +363,7 @@ PK11_GetMechanism PK11_GetMinimumPwdLength PK11_GetModInfo PK11_GetNextSafe +PK11_GetNextSymKey PK11_GetPadMechanism PK11_GetPrivateKeyNickname PK11_GetPrivateModulusLen @@ -371,6 +372,7 @@ PK11_GetSlotInfo PK11_GetSlotName PK11_GetSlotPWValues PK11_GetSlotSeries +PK11_GetSymKeyNickname PK11_GetTokenInfo PK11_GetTokenName PK11_HashBuf @@ -396,6 +398,7 @@ PK11_KeyGen PK11_KeyGenWithTemplate PK11_ListCerts PK11_ListCertsInSlot +PK11_ListFixedKeysInSlot PK11_ListPrivateKeysInSlot PK11_ListPrivKeysInSlot PK11_LoadPrivKey @@ -425,9 +428,11 @@ PK11SDR_Decrypt PK11SDR_Encrypt PK11_SetPasswordFunc PK11_SetSlotPWValues +PK11_SetSymKeyNickname PK11_Sign PK11_SignatureLen PK11_SignWithMechanism +PK11_TokenKeyGenWithFlags PK11_UnwrapPrivKey PK11_UnwrapSymKey PK11_UpdateSlotAttribute @@ -675,6 +680,7 @@ SSL_PeerCertificateChain SSL_PeerStapledOCSPResponses SSL_ResetHandshake SSL_SetCanFalseStartCallback +SSL_SetDowngradeCheckVersion SSL_SetNextProtoNego SSL_SetPKCS11PinArg SSL_SetSockPeerID diff --git a/dom/base/FileList.cpp b/dom/base/FileList.cpp index 41dd07a71d..79aef3e065 100644 --- a/dom/base/FileList.cpp +++ b/dom/base/FileList.cpp @@ -12,7 +12,7 @@ namespace mozilla { namespace dom { -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FileList, mFilesOrDirectories, mParent) +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FileList, mFiles, mParent) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileList) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY @@ -29,20 +29,6 @@ FileList::WrapObject(JSContext* aCx, JS::Handle aGivenProto) return mozilla::dom::FileListBinding::Wrap(aCx, this, aGivenProto); } -void -FileList::Append(File* aFile) -{ - OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement(); - element->SetAsFile() = aFile; -} - -void -FileList::Append(Directory* aDirectory) -{ - OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement(); - element->SetAsDirectory() = aDirectory; -} - NS_IMETHODIMP FileList::GetLength(uint32_t* aLength) { @@ -54,75 +40,47 @@ FileList::GetLength(uint32_t* aLength) NS_IMETHODIMP FileList::Item(uint32_t aIndex, nsISupports** aValue) { - if (aIndex >= mFilesOrDirectories.Length()) { - return NS_ERROR_FAILURE; - } - - if (mFilesOrDirectories[aIndex].IsFile()) { - nsCOMPtr file = mFilesOrDirectories[aIndex].GetAsFile(); - file.forget(aValue); - return NS_OK; - } - - MOZ_ASSERT(mFilesOrDirectories[aIndex].IsDirectory()); - RefPtr directory = mFilesOrDirectories[aIndex].GetAsDirectory(); - directory.forget(aValue); + nsCOMPtr file = Item(aIndex); + file.forget(aValue); return NS_OK; } -void -FileList::Item(uint32_t aIndex, Nullable& aValue, - ErrorResult& aRv) const +File* +FileList::Item(uint32_t aIndex) const { - if (aIndex >= mFilesOrDirectories.Length()) { - aValue.SetNull(); - return; + if (aIndex >= mFiles.Length()) { + return nullptr; } - aValue.SetValue(mFilesOrDirectories[aIndex]); + return mFiles[aIndex]; } -void -FileList::IndexedGetter(uint32_t aIndex, bool& aFound, - Nullable& aFileOrDirectory, - ErrorResult& aRv) const +File* +FileList::IndexedGetter(uint32_t aIndex, bool& aFound) const { - aFound = aIndex < mFilesOrDirectories.Length(); - Item(aIndex, aFileOrDirectory, aRv); + aFound = aIndex < mFiles.Length(); + return Item(aIndex); } void -FileList::ToSequence(Sequence& aSequence, +FileList::ToSequence(Sequence>& aSequence, ErrorResult& aRv) const { MOZ_ASSERT(aSequence.IsEmpty()); - if (mFilesOrDirectories.IsEmpty()) { + if (mFiles.IsEmpty()) { return; } - if (!aSequence.SetLength(mFilesOrDirectories.Length(), + if (!aSequence.SetLength(mFiles.Length(), mozilla::fallible_t())) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return; } - for (uint32_t i = 0; i < mFilesOrDirectories.Length(); ++i) { - aSequence[i] = mFilesOrDirectories[i]; + for (uint32_t i = 0; i < mFiles.Length(); ++i) { + aSequence[i] = mFiles[i]; } } -bool -FileList::ClonableToDifferentThreadOrProcess() const -{ - for (uint32_t i = 0; i < mFilesOrDirectories.Length(); ++i) { - if (mFilesOrDirectories[i].IsDirectory() && - !mFilesOrDirectories[i].GetAsDirectory()->ClonableToDifferentThreadOrProcess()) { - return false; - } - } - - return true; -} - } // namespace dom } // namespace mozilla diff --git a/dom/base/FileList.h b/dom/base/FileList.h index b6aeeb7940..cf951ad349 100644 --- a/dom/base/FileList.h +++ b/dom/base/FileList.h @@ -40,13 +40,16 @@ public: return mParent; } - void Append(File* aFile); - void Append(Directory* aDirectory); + bool Append(File* aFile) + { + MOZ_ASSERT(aFile); + return mFiles.AppendElement(aFile, fallible); + } bool Remove(uint32_t aIndex) { - if (aIndex < mFilesOrDirectories.Length()) { - mFilesOrDirectories.RemoveElementAt(aIndex); + if (aIndex < mFiles.Length()) { + mFiles.RemoveElementAt(aIndex); return true; } @@ -55,7 +58,7 @@ public: void Clear() { - return mFilesOrDirectories.Clear(); + return mFiles.Clear(); } static FileList* FromSupports(nsISupports* aSupports) @@ -75,34 +78,22 @@ public: return static_cast(aSupports); } - const OwningFileOrDirectory& UnsafeItem(uint32_t aIndex) const - { - MOZ_ASSERT(aIndex < Length()); - return mFilesOrDirectories[aIndex]; - } + File* Item(uint32_t aIndex) const; - void Item(uint32_t aIndex, - Nullable& aFileOrDirectory, - ErrorResult& aRv) const; - - void IndexedGetter(uint32_t aIndex, bool& aFound, - Nullable& aFileOrDirectory, - ErrorResult& aRv) const; + File* IndexedGetter(uint32_t aIndex, bool& aFound) const; uint32_t Length() const { - return mFilesOrDirectories.Length(); + return mFiles.Length(); } - void ToSequence(Sequence& aSequence, + void ToSequence(Sequence>& aSequence, ErrorResult& aRv) const; - bool ClonableToDifferentThreadOrProcess() const; - private: ~FileList() {} - nsTArray mFilesOrDirectories; + FallibleTArray> mFiles; nsCOMPtr mParent; }; diff --git a/dom/base/StructuredCloneHolder.cpp b/dom/base/StructuredCloneHolder.cpp index a1d823def6..7e4070bd56 100644 --- a/dom/base/StructuredCloneHolder.cpp +++ b/dom/base/StructuredCloneHolder.cpp @@ -712,8 +712,7 @@ WriteBlob(JSStructuredCloneWriter* aWriter, } // A directory is serialized as: -// - pair of ints: SCTAG_DOM_DIRECTORY, 0 -// - pair of ints: type (eDOMRootDirectory/eDOMNotRootDirectory) - path length +// - pair of ints: SCTAG_DOM_DIRECTORY, path length // - path as string bool WriteDirectory(JSStructuredCloneWriter* aWriter, @@ -726,36 +725,25 @@ WriteDirectory(JSStructuredCloneWriter* aWriter, aDirectory->GetFullRealPath(path); size_t charSize = sizeof(nsString::char_type); - return JS_WriteUint32Pair(aWriter, SCTAG_DOM_DIRECTORY, 0) && - JS_WriteUint32Pair(aWriter, (uint32_t)aDirectory->Type(), - path.Length()) && + return JS_WriteUint32Pair(aWriter, SCTAG_DOM_DIRECTORY, path.Length()) && JS_WriteBytes(aWriter, path.get(), path.Length() * charSize); } JSObject* ReadDirectory(JSContext* aCx, JSStructuredCloneReader* aReader, - uint32_t aZero, + uint32_t aPathLength, StructuredCloneHolder* aHolder) { MOZ_ASSERT(aCx); MOZ_ASSERT(aReader); MOZ_ASSERT(aHolder); - MOZ_ASSERT(aZero == 0); - - uint32_t directoryType, lengthOfString; - if (!JS_ReadUint32Pair(aReader, &directoryType, &lengthOfString)) { - return nullptr; - } - - MOZ_ASSERT(directoryType == Directory::eDOMRootDirectory || - directoryType == Directory::eNotDOMRootDirectory); nsAutoString path; - path.SetLength(lengthOfString); + path.SetLength(aPathLength); size_t charSize = sizeof(nsString::char_type); if (!JS_ReadBytes(aReader, (void*) path.BeginWriting(), - lengthOfString * charSize)) { + aPathLength * charSize)) { return nullptr; } @@ -774,8 +762,7 @@ ReadDirectory(JSContext* aCx, JS::Rooted val(aCx); { RefPtr directory = - Directory::Create(aHolder->ParentDuringRead(), file, - (Directory::DirectoryType) directoryType); + Directory::Create(aHolder->ParentDuringRead(), file); if (!ToJSValue(aCx, directory, &val)) { return nullptr; @@ -799,59 +786,35 @@ ReadFileList(JSContext* aCx, { RefPtr fileList = new FileList(aHolder->ParentDuringRead()); - // |aCount| is the number of Files or Directory for this FileList. + uint32_t zero, index; + // |index| is the index of the first blobImpl. + if (!JS_ReadUint32Pair(aReader, &zero, &index)) { + return nullptr; + } + + MOZ_ASSERT(zero == 0); + + // |aCount| is the number of BlobImpls to use from the |index|. for (uint32_t i = 0; i < aCount; ++i) { - uint32_t tagOrDirectoryType, indexOrLengthOfString; - if (!JS_ReadUint32Pair(aReader, &tagOrDirectoryType, - &indexOrLengthOfString)) { + uint32_t pos = index + i; + MOZ_ASSERT(pos < aHolder->BlobImpls().Length()); + + RefPtr blobImpl = aHolder->BlobImpls()[pos]; + MOZ_ASSERT(blobImpl->IsFile()); + + ErrorResult rv; + blobImpl = EnsureBlobForBackgroundManager(blobImpl, nullptr, rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); return nullptr; } - MOZ_ASSERT(tagOrDirectoryType == SCTAG_DOM_BLOB || - tagOrDirectoryType == Directory::eDOMRootDirectory || - tagOrDirectoryType == Directory::eNotDOMRootDirectory); + MOZ_ASSERT(blobImpl); - if (tagOrDirectoryType == SCTAG_DOM_BLOB) { - MOZ_ASSERT(indexOrLengthOfString < aHolder->BlobImpls().Length()); - - RefPtr blobImpl = - aHolder->BlobImpls()[indexOrLengthOfString]; - MOZ_ASSERT(blobImpl->IsFile()); - - ErrorResult rv; - blobImpl = EnsureBlobForBackgroundManager(blobImpl, nullptr, rv); - if (NS_WARN_IF(rv.Failed())) { - rv.SuppressException(); - return nullptr; - } - - RefPtr file = - File::Create(aHolder->ParentDuringRead(), blobImpl); - MOZ_ASSERT(file); - - fileList->Append(file); - continue; - } - - nsAutoString path; - path.SetLength(indexOrLengthOfString); - size_t charSize = sizeof(nsString::char_type); - if (!JS_ReadBytes(aReader, (void*) path.BeginWriting(), - indexOrLengthOfString * charSize)) { + RefPtr file = File::Create(aHolder->ParentDuringRead(), blobImpl); + if (!fileList->Append(file)) { return nullptr; } - - nsCOMPtr file; - nsresult rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(path), true, - getter_AddRefs(file)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return nullptr; - } - - RefPtr directory = - Directory::Create(aHolder->ParentDuringRead(), file, - (Directory::DirectoryType) tagOrDirectoryType); - fileList->Append(directory); } if (!ToJSValue(aCx, fileList, &val)) { @@ -864,13 +827,7 @@ ReadFileList(JSContext* aCx, // The format of the FileList serialization is: // - pair of ints: SCTAG_DOM_FILELIST, Length of the FileList -// - for each element of the FileList: -// - if it's a blob: -// - pair of ints: SCTAG_DOM_BLOB, index of the BlobImpl in the array -// mBlobImplArray. -// - else: -// - pair of ints: 0/1 is root, string length -// - value string +// - pair of ints: 0, The offset of the BlobImpl array bool WriteFileList(JSStructuredCloneWriter* aWriter, FileList* aFileList, @@ -880,8 +837,13 @@ WriteFileList(JSStructuredCloneWriter* aWriter, MOZ_ASSERT(aFileList); MOZ_ASSERT(aHolder); + // A FileList is serialized writing the X number of elements and the offset + // from mBlobImplArray. The Read will take X elements from mBlobImplArray + // starting from the offset. if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_FILELIST, - aFileList->Length())) { + aFileList->Length()) || + !JS_WriteUint32Pair(aWriter, 0, + aHolder->BlobImpls().Length())) { return false; } @@ -889,39 +851,18 @@ WriteFileList(JSStructuredCloneWriter* aWriter, nsTArray> blobImpls; for (uint32_t i = 0; i < aFileList->Length(); ++i) { - const OwningFileOrDirectory& data = aFileList->UnsafeItem(i); - - if (data.IsFile()) { - RefPtr blobImpl = - EnsureBlobForBackgroundManager(data.GetAsFile()->Impl(), nullptr, rv); - if (NS_WARN_IF(rv.Failed())) { - rv.SuppressException(); - return false; - } - - if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB, - aHolder->BlobImpls().Length())) { - return false; - } - - aHolder->BlobImpls().AppendElement(blobImpl); - continue; - } - - MOZ_ASSERT(data.IsDirectory()); - - nsAutoString path; - data.GetAsDirectory()->GetFullRealPath(path); - - size_t charSize = sizeof(nsString::char_type); - if (!JS_WriteUint32Pair(aWriter, - (uint32_t)data.GetAsDirectory()->Type(), - path.Length()) || - !JS_WriteBytes(aWriter, path.get(), path.Length() * charSize)) { + RefPtr blobImpl = + EnsureBlobForBackgroundManager(aFileList->Item(i)->Impl(), nullptr, rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); return false; } + + MOZ_ASSERT(blobImpl); + blobImpls.AppendElement(blobImpl); } + aHolder->BlobImpls().AppendElements(blobImpls); return true; } @@ -1137,9 +1078,7 @@ StructuredCloneHolder::CustomWriteHandler(JSContext* aCx, // See if this is a FileList object. { FileList* fileList = nullptr; - if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, aObj, fileList)) && - (mSupportedContext == SameProcessSameThread || - fileList->ClonableToDifferentThreadOrProcess())) { + if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, aObj, fileList))) { return WriteFileList(aWriter, fileList, this); } } diff --git a/dom/base/test/test_postMessages.html b/dom/base/test/test_postMessages.html index 93e9511bf1..e6e9e7914d 100644 --- a/dom/base/test/test_postMessages.html +++ b/dom/base/test/test_postMessages.html @@ -72,7 +72,7 @@ var clonableObjects = [ new ImageData(2, 2), ]; -function create_fileList_forFile() { +function create_fileList() { var url = SimpleTest.getTestFileURL("script_postmessages_fileList.js"); var script = SpecialPowers.loadChromeScript(url); @@ -93,27 +93,6 @@ function create_fileList_forFile() { script.sendAsyncMessage("file.open"); } -function create_fileList_forDir() { - var url = SimpleTest.getTestFileURL("script_postmessages_fileList.js"); - var script = SpecialPowers.loadChromeScript(url); - - function onOpened(message) { - var fileList = document.getElementById('fileList'); - SpecialPowers.wrap(fileList).mozSetDirectory(message.dir); - - // Just a simple test - is(fileList.files.length, 1, "Filelist has 1 element"); - ok(fileList.files[0] instanceof Directory, "We have a directory."); - - clonableObjects.push(fileList.files); - script.destroy(); - next(); - } - - script.addMessageListener("dir.opened", onOpened); - script.sendAsyncMessage("dir.open"); -} - function create_directory() { if (navigator.userAgent.toLowerCase().indexOf('Android') != -1) { next(); @@ -548,8 +527,7 @@ function test_messagePort_inWorkers() { } var tests = [ - create_fileList_forFile, - create_fileList_forDir, + create_fileList, create_directory, test_windowToWindow, diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index ff1824c2fc..6db00e78d5 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -2408,7 +2408,7 @@ CanvasRenderingContext2D::ParseFilter(const nsAString& aString, return false; } - aFilterChain = sc->StyleSVGReset()->mFilters; + aFilterChain = sc->StyleEffects()->mFilters; return true; } @@ -3752,7 +3752,7 @@ struct MOZ_STACK_CLASS CanvasBidiProcessor : public nsBidiPresUtils::BidiProcess } // current text run - nsAutoPtr mTextRun; + UniquePtr mTextRun; // pointer to a screen reference context used to measure text and such RefPtr mDrawTarget; @@ -4739,7 +4739,11 @@ CanvasRenderingContext2D::DrawDirectlyToCanvas( // the matrix even though this is a temp gfxContext. AutoRestoreTransform autoRestoreTransform(mTarget); - RefPtr context = new gfxContext(tempTarget); + RefPtr context = gfxContext::ForDrawTarget(tempTarget); + if (!context) { + gfxDevCrash(LogReason::InvalidContext) << "Canvas context problem"; + return; + } context->SetMatrix(contextMatrix. Scale(1.0 / contextScale.width, 1.0 / contextScale.height). @@ -4936,19 +4940,21 @@ CanvasRenderingContext2D::DrawWindow(nsGlobalWindow& aWindow, double aX, GlobalAlpha() == 1.0f && UsedOperation() == CompositionOp::OP_OVER) { - thebes = new gfxContext(mTarget); + thebes = gfxContext::ForDrawTarget(mTarget); + MOZ_ASSERT(thebes); // alrady checked the draw target above thebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21, matrix._22, matrix._31, matrix._32)); } else { drawDT = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(ceil(sw), ceil(sh)), SurfaceFormat::B8G8R8A8); - if (!drawDT) { + if (!drawDT || !drawDT->IsValid()) { aError.Throw(NS_ERROR_FAILURE); return; } - thebes = new gfxContext(drawDT); + thebes = gfxContext::ForDrawTarget(drawDT); + MOZ_ASSERT(thebes); // alrady checked the draw target above thebes->SetMatrix(gfxMatrix::Scaling(matrix._11, matrix._22)); } diff --git a/dom/canvas/DocumentRendererChild.cpp b/dom/canvas/DocumentRendererChild.cpp index 315af1a224..aae2cc7021 100644 --- a/dom/canvas/DocumentRendererChild.cpp +++ b/dom/canvas/DocumentRendererChild.cpp @@ -80,11 +80,12 @@ DocumentRendererChild::RenderDocument(nsIDOMWindow *window, IntSize(renderSize.width, renderSize.height), 4 * renderSize.width, SurfaceFormat::B8G8R8A8); - if (!dt) { + if (!dt || !dt->IsValid()) { gfxWarning() << "DocumentRendererChild::RenderDocument failed to Factory::CreateDrawTargetForData"; return false; } - RefPtr ctx = new gfxContext(dt); + RefPtr ctx = gfxContext::ForDrawTarget(dt); + MOZ_ASSERT(ctx); // already checked the draw target above ctx->SetMatrix(mozilla::gfx::ThebesMatrix(transform)); nsCOMPtr shell = presContext->PresShell(); diff --git a/dom/crypto/CryptoBuffer.cpp b/dom/crypto/CryptoBuffer.cpp index 74c9670448..2b50074252 100644 --- a/dom/crypto/CryptoBuffer.cpp +++ b/dom/crypto/CryptoBuffer.cpp @@ -41,6 +41,13 @@ CryptoBuffer::Assign(const SECItem* aItem) return Assign(aItem->data, aItem->len); } +uint8_t* +CryptoBuffer::Assign(const InfallibleTArray& aData) +{ + return ReplaceElementsAt(0, Length(), aData.Elements(), aData.Length(), + fallible); +} + uint8_t* CryptoBuffer::Assign(const ArrayBuffer& aData) { @@ -85,6 +92,19 @@ CryptoBuffer::Assign(const OwningArrayBufferViewOrArrayBuffer& aData) return nullptr; } +uint8_t* +CryptoBuffer::AppendSECItem(const SECItem* aItem) +{ + MOZ_ASSERT(aItem); + return AppendElements(aItem->data, aItem->len, fallible); +} + +uint8_t* +CryptoBuffer::AppendSECItem(const SECItem& aItem) +{ + return AppendElements(aItem.data, aItem.len, fallible); +} + // Helpers to encode/decode JWK's special flavor of Base64 // * No whitespace // * No padding @@ -171,6 +191,23 @@ CryptoBuffer::ToUint8Array(JSContext* aCx) const return Uint8Array::Create(aCx, Length(), Elements()); } +bool +CryptoBuffer::ToNewUnsignedBuffer(uint8_t** aBuf, uint32_t* aBufLen) const +{ + MOZ_ASSERT(aBuf); + MOZ_ASSERT(aBufLen); + + uint32_t dataLen = Length(); + uint8_t* tmp = reinterpret_cast(moz_xmalloc(dataLen)); + if (NS_WARN_IF(!tmp)) { + return false; + } + + memcpy(tmp, Elements(), dataLen); + *aBuf = tmp; + *aBufLen = dataLen; + return true; +} // "BigInt" comes from the WebCrypto spec // ("unsigned long" isn't very "big", of course) diff --git a/dom/crypto/CryptoBuffer.h b/dom/crypto/CryptoBuffer.h index 44872660b8..615916e584 100644 --- a/dom/crypto/CryptoBuffer.h +++ b/dom/crypto/CryptoBuffer.h @@ -24,11 +24,15 @@ public: uint8_t* Assign(const uint8_t* aData, uint32_t aLength); uint8_t* Assign(const nsACString& aString); uint8_t* Assign(const SECItem* aItem); + uint8_t* Assign(const InfallibleTArray& aData); uint8_t* Assign(const ArrayBuffer& aData); uint8_t* Assign(const ArrayBufferView& aData); uint8_t* Assign(const ArrayBufferViewOrArrayBuffer& aData); uint8_t* Assign(const OwningArrayBufferViewOrArrayBuffer& aData); + uint8_t* AppendSECItem(const SECItem* aItem); + uint8_t* AppendSECItem(const SECItem& aItem); + template @@ -43,6 +47,7 @@ public: nsresult ToJwkBase64(nsString& aBase64); bool ToSECItem(PLArenaPool* aArena, SECItem* aItem) const; JSObject* ToUint8Array(JSContext* aCx) const; + bool ToNewUnsignedBuffer(uint8_t** aBuf, uint32_t* aBufLen) const; bool GetBigIntValue(unsigned long& aRetVal); }; diff --git a/dom/events/DataTransfer.cpp b/dom/events/DataTransfer.cpp index bab955a531..0e3c627c64 100644 --- a/dom/events/DataTransfer.cpp +++ b/dom/events/DataTransfer.cpp @@ -880,13 +880,13 @@ DataTransfer::GetFilesAndDirectories(ErrorResult& aRv) } } - Sequence filesAndDirsSeq; - mFileList->ToSequence(filesAndDirsSeq, aRv); + Sequence> filesSeq; + mFileList->ToSequence(filesSeq, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } - p->MaybeResolve(filesAndDirsSeq); + p->MaybeResolve(filesSeq); return p.forget(); } diff --git a/dom/filesystem/CreateDirectoryTask.cpp b/dom/filesystem/CreateDirectoryTask.cpp index 4b7774ee6d..d96d9d9789 100644 --- a/dom/filesystem/CreateDirectoryTask.cpp +++ b/dom/filesystem/CreateDirectoryTask.cpp @@ -119,9 +119,7 @@ CreateDirectoryTaskChild::HandlerCallback() } RefPtr dir = Directory::Create(mFileSystem->GetParentObject(), - mTargetPath, - Directory::eNotDOMRootDirectory, - mFileSystem); + mTargetPath, mFileSystem); MOZ_ASSERT(dir); mPromise->MaybeResolve(dir); @@ -131,7 +129,7 @@ CreateDirectoryTaskChild::HandlerCallback() void CreateDirectoryTaskChild::GetPermissionAccessType(nsCString& aAccess) const { - aAccess.AssignLiteral(CREATE_DIRECTORY_TASK_PERMISSION); + aAccess.AssignLiteral(DIRECTORY_CREATE_PERMISSION); } /** @@ -216,7 +214,7 @@ CreateDirectoryTaskParent::IOWork() void CreateDirectoryTaskParent::GetPermissionAccessType(nsCString& aAccess) const { - aAccess.AssignLiteral(CREATE_DIRECTORY_TASK_PERMISSION); + aAccess.AssignLiteral(DIRECTORY_CREATE_PERMISSION); } } // namespace dom diff --git a/dom/filesystem/CreateDirectoryTask.h b/dom/filesystem/CreateDirectoryTask.h index 6cb9c41ff8..6b02e87987 100644 --- a/dom/filesystem/CreateDirectoryTask.h +++ b/dom/filesystem/CreateDirectoryTask.h @@ -11,8 +11,6 @@ #include "nsAutoPtr.h" #include "mozilla/ErrorResult.h" -#define CREATE_DIRECTORY_TASK_PERMISSION "create" - namespace mozilla { namespace dom { diff --git a/dom/filesystem/CreateFileTask.cpp b/dom/filesystem/CreateFileTask.cpp index 6ee3ebc92e..a8089d20fe 100644 --- a/dom/filesystem/CreateFileTask.cpp +++ b/dom/filesystem/CreateFileTask.cpp @@ -5,8 +5,6 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "CreateFileTask.h" -#include "CreateDirectoryTask.h" -#include "RemoveTask.h" #include @@ -27,10 +25,10 @@ #define GET_PERMISSION_ACCESS_TYPE(aAccess) \ if (mReplace) { \ - aAccess.AssignLiteral(REMOVE_TASK_PERMISSION); \ + aAccess.AssignLiteral(DIRECTORY_WRITE_PERMISSION); \ return; \ } \ - aAccess.AssignLiteral(CREATE_DIRECTORY_TASK_PERMISSION); + aAccess.AssignLiteral(DIRECTORY_CREATE_PERMISSION); namespace mozilla { namespace dom { diff --git a/dom/filesystem/DeviceStorageFileSystem.cpp b/dom/filesystem/DeviceStorageFileSystem.cpp index c48ee1429f..b55d7fd456 100644 --- a/dom/filesystem/DeviceStorageFileSystem.cpp +++ b/dom/filesystem/DeviceStorageFileSystem.cpp @@ -115,10 +115,32 @@ DeviceStorageFileSystem::GetParentObject() const } void -DeviceStorageFileSystem::GetRootName(nsAString& aRetval) const +DeviceStorageFileSystem::GetDirectoryName(nsIFile* aFile, nsAString& aRetval, + ErrorResult& aRv) const { AssertIsOnOwningThread(); - aRetval = mStorageName; + MOZ_ASSERT(aFile); + + nsCOMPtr rootPath; + aRv = NS_NewLocalFile(LocalOrDeviceStorageRootPath(), false, + getter_AddRefs(rootPath)); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + bool equal = false; + aRv = aFile->Equals(rootPath, &equal); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + if (equal) { + aRetval = mStorageName; + return; + } + + FileSystemBase::GetDirectoryName(aFile, aRetval, aRv); + NS_WARN_IF(aRv.Failed()); } bool diff --git a/dom/filesystem/DeviceStorageFileSystem.h b/dom/filesystem/DeviceStorageFileSystem.h index f77e15021e..ce2c39985e 100644 --- a/dom/filesystem/DeviceStorageFileSystem.h +++ b/dom/filesystem/DeviceStorageFileSystem.h @@ -30,6 +30,9 @@ public: virtual already_AddRefed Clone() override; + virtual bool + ShouldCreateDirectory() override { return true; } + virtual void Shutdown() override; @@ -37,7 +40,8 @@ public: GetParentObject() const override; virtual void - GetRootName(nsAString& aRetval) const override; + GetDirectoryName(nsIFile* aFile, nsAString& aRetval, + ErrorResult& aRv) const override; virtual bool IsSafeFile(nsIFile* aFile) const override; diff --git a/dom/filesystem/Directory.cpp b/dom/filesystem/Directory.cpp index 591ecec0ab..8513db3920 100644 --- a/dom/filesystem/Directory.cpp +++ b/dom/filesystem/Directory.cpp @@ -11,6 +11,7 @@ #include "FileSystemPermissionRequest.h" #include "GetDirectoryListingTask.h" #include "GetFileOrDirectoryTask.h" +#include "GetFilesTask.h" #include "RemoveTask.h" #include "nsCharSeparatedTokenizer.h" @@ -136,8 +137,7 @@ Directory::GetRoot(FileSystemBase* aFileSystem, ErrorResult& aRv) } RefPtr task = - GetFileOrDirectoryTaskChild::Create(aFileSystem, path, eDOMRootDirectory, - true, aRv); + GetFileOrDirectoryTaskChild::Create(aFileSystem, path, true, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } @@ -148,7 +148,7 @@ Directory::GetRoot(FileSystemBase* aFileSystem, ErrorResult& aRv) /* static */ already_AddRefed Directory::Create(nsISupports* aParent, nsIFile* aFile, - DirectoryType aType, FileSystemBase* aFileSystem) + FileSystemBase* aFileSystem) { MOZ_ASSERT(aParent); MOZ_ASSERT(aFile); @@ -157,27 +157,17 @@ Directory::Create(nsISupports* aParent, nsIFile* aFile, bool isDir; nsresult rv = aFile->IsDirectory(&isDir); MOZ_ASSERT(NS_SUCCEEDED(rv) && isDir); - - if (aType == eNotDOMRootDirectory) { - RefPtr parent; - rv = aFile->GetParent(getter_AddRefs(parent)); - // We must have a parent if this is not the root directory. - MOZ_ASSERT(NS_SUCCEEDED(rv) && parent); - } #endif - RefPtr directory = - new Directory(aParent, aFile, aType, aFileSystem); + RefPtr directory = new Directory(aParent, aFile, aFileSystem); return directory.forget(); } Directory::Directory(nsISupports* aParent, nsIFile* aFile, - DirectoryType aType, FileSystemBase* aFileSystem) : mParent(aParent) , mFile(aFile) - , mType(aType) { MOZ_ASSERT(aFile); @@ -212,18 +202,12 @@ Directory::GetName(nsAString& aRetval, ErrorResult& aRv) { aRetval.Truncate(); - if (mType == eDOMRootDirectory) { - RefPtr fs = GetFileSystem(aRv); - if (NS_WARN_IF(aRv.Failed())) { - return; - } - - fs->GetRootName(aRetval); + RefPtr fs = GetFileSystem(aRv); + if (NS_WARN_IF(aRv.Failed())) { return; } - aRv = mFile->GetLeafName(aRetval); - NS_WARN_IF(aRv.Failed()); + fs->GetDirectoryName(mFile, aRetval, aRv); } already_AddRefed @@ -317,8 +301,7 @@ Directory::Get(const nsAString& aPath, ErrorResult& aRv) } RefPtr task = - GetFileOrDirectoryTaskChild::Create(fs, realPath, eNotDOMRootDirectory, - false, aRv); + GetFileOrDirectoryTaskChild::Create(fs, realPath, false, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } @@ -408,7 +391,7 @@ Directory::GetPath(nsAString& aRetval, ErrorResult& aRv) return; } - fs->GetDOMPath(mFile, mType, mPath, aRv); + fs->GetDOMPath(mFile, mPath, aRv); if (NS_WARN_IF(aRv.Failed())) { return; } @@ -437,7 +420,7 @@ Directory::GetFilesAndDirectories(ErrorResult& aRv) } RefPtr task = - GetDirectoryListingTaskChild::Create(fs, mFile, mType, mFilters, aRv); + GetDirectoryListingTaskChild::Create(fs, mFile, mFilters, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } @@ -446,6 +429,27 @@ Directory::GetFilesAndDirectories(ErrorResult& aRv) return task->GetPromise(); } +already_AddRefed +Directory::GetFiles(bool aRecursiveFlag, ErrorResult& aRv) +{ + ErrorResult rv; + RefPtr fs = GetFileSystem(rv); + if (NS_WARN_IF(rv.Failed())) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return nullptr; + } + + RefPtr task = + GetFilesTaskChild::Create(fs, mFile, aRecursiveFlag, rv); + if (NS_WARN_IF(rv.Failed())) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return nullptr; + } + + FileSystemPermissionRequest::RequestForTask(task); + return task->GetPromise(); +} + void Directory::SetContentFilters(const nsAString& aFilters) { @@ -456,10 +460,6 @@ FileSystemBase* Directory::GetFileSystem(ErrorResult& aRv) { if (!mFileSystem) { - // Any subdir inherits the FileSystem of the parent Directory. If we are - // here it's because we are dealing with the DOM root. - MOZ_ASSERT(mType == eDOMRootDirectory); - nsAutoString path; aRv = mFile->GetPath(path); if (NS_WARN_IF(aRv.Failed())) { diff --git a/dom/filesystem/Directory.h b/dom/filesystem/Directory.h index 88b2f56c70..e3298e7733 100644 --- a/dom/filesystem/Directory.h +++ b/dom/filesystem/Directory.h @@ -59,20 +59,9 @@ public: static already_AddRefed GetRoot(FileSystemBase* aFileSystem, ErrorResult& aRv); - enum DirectoryType { - // When a directory is selected using a HTMLInputElement, that will be the - // DOM root directory and its name will be '/'. All the sub directory will - // be called with they real name. We use this enum to mark what we must - // consider the '/' of this DOM filesystem. - eDOMRootDirectory, - - // All the sub directories of the '/' will be marked using this other value. - eNotDOMRootDirectory - }; - static already_AddRefed Create(nsISupports* aParent, nsIFile* aDirectory, - DirectoryType aType, FileSystemBase* aFileSystem = 0); + FileSystemBase* aFileSystem = 0); // ========= Begin WebIDL bindings. =========== @@ -112,6 +101,9 @@ public: already_AddRefed GetFilesAndDirectories(ErrorResult& aRv); + already_AddRefed + GetFiles(bool aRecursiveFlag, ErrorResult& aRv); + // =========== End WebIDL bindings.============ /** @@ -142,17 +134,12 @@ public: FileSystemBase* GetFileSystem(ErrorResult& aRv); - DirectoryType Type() const - { - return mType; - } - bool ClonableToDifferentThreadOrProcess() const; private: Directory(nsISupports* aParent, - nsIFile* aFile, DirectoryType aType, + nsIFile* aFile, FileSystemBase* aFileSystem = nullptr); ~Directory(); @@ -169,7 +156,6 @@ private: nsCOMPtr mParent; RefPtr mFileSystem; nsCOMPtr mFile; - DirectoryType mType; nsString mFilters; nsString mPath; diff --git a/dom/filesystem/FileSystemBase.cpp b/dom/filesystem/FileSystemBase.cpp index 64aa7bb072..3f24b44070 100644 --- a/dom/filesystem/FileSystemBase.cpp +++ b/dom/filesystem/FileSystemBase.cpp @@ -110,19 +110,26 @@ FileSystemBase::IsSafeDirectory(Directory* aDir) const return false; } +void +FileSystemBase::GetDirectoryName(nsIFile* aFile, nsAString& aRetval, + ErrorResult& aRv) const +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aFile); + + aRv = aFile->GetLeafName(aRetval); + NS_WARN_IF(aRv.Failed()); +} + void FileSystemBase::GetDOMPath(nsIFile* aFile, - Directory::DirectoryType aType, nsAString& aRetval, ErrorResult& aRv) const { AssertIsOnOwningThread(); MOZ_ASSERT(aFile); - if (aType == Directory::eDOMRootDirectory) { - aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL); - return; - } + aRetval.Truncate(); nsCOMPtr fileSystemPath; aRv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(LocalOrDeviceStorageRootPath()), @@ -131,8 +138,6 @@ FileSystemBase::GetDOMPath(nsIFile* aFile, return; } - MOZ_ASSERT(FileSystemUtils::IsDescendantPath(fileSystemPath, aFile)); - nsCOMPtr path; aRv = aFile->Clone(getter_AddRefs(path)); if (NS_WARN_IF(aRv.Failed())) { @@ -142,6 +147,14 @@ FileSystemBase::GetDOMPath(nsIFile* aFile, nsTArray parts; while (true) { + nsAutoString leafName; + aRv = path->GetLeafName(leafName); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + parts.AppendElement(leafName); + bool equal = false; aRv = fileSystemPath->Equals(path, &equal); if (NS_WARN_IF(aRv.Failed())) { @@ -152,14 +165,6 @@ FileSystemBase::GetDOMPath(nsIFile* aFile, break; } - nsAutoString leafName; - aRv = path->GetLeafName(leafName); - if (NS_WARN_IF(aRv.Failed())) { - return; - } - - parts.AppendElement(leafName); - nsCOMPtr parentPath; aRv = path->GetParent(getter_AddRefs(parentPath)); if (NS_WARN_IF(aRv.Failed())) { @@ -176,8 +181,6 @@ FileSystemBase::GetDOMPath(nsIFile* aFile, MOZ_ASSERT(!parts.IsEmpty()); - aRetval.Truncate(); - for (int32_t i = parts.Length() - 1; i >= 0; --i) { aRetval.AppendLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL); aRetval.Append(parts[i]); diff --git a/dom/filesystem/FileSystemBase.h b/dom/filesystem/FileSystemBase.h index 8330f394d6..eb6922ad04 100644 --- a/dom/filesystem/FileSystemBase.h +++ b/dom/filesystem/FileSystemBase.h @@ -37,19 +37,18 @@ public: virtual already_AddRefed Clone() = 0; + virtual bool + ShouldCreateDirectory() = 0; + virtual nsISupports* GetParentObject() const; - /* - * Get the virtual name of the root directory. This name will be exposed to - * the content page. - */ virtual void - GetRootName(nsAString& aRetval) const = 0; + GetDirectoryName(nsIFile* aFile, nsAString& aRetval, + ErrorResult& aRv) const; void - GetDOMPath(nsIFile* aFile, Directory::DirectoryType aType, - nsAString& aRetval, ErrorResult& aRv) const; + GetDOMPath(nsIFile* aFile, nsAString& aRetval, ErrorResult& aRv) const; /* * Return the local root path of the FileSystem implementation. diff --git a/dom/filesystem/FileSystemRequestParent.cpp b/dom/filesystem/FileSystemRequestParent.cpp index 12f4caee5f..b417bf6973 100644 --- a/dom/filesystem/FileSystemRequestParent.cpp +++ b/dom/filesystem/FileSystemRequestParent.cpp @@ -53,6 +53,7 @@ FileSystemRequestParent::Initialize(const FileSystemParams& aParams) FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(CreateFile) FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(GetDirectoryListing) FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(GetFileOrDirectory) + FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(GetFiles) FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(Remove) default: { diff --git a/dom/filesystem/FileSystemTaskBase.h b/dom/filesystem/FileSystemTaskBase.h index b3a6e95c48..d97001374d 100644 --- a/dom/filesystem/FileSystemTaskBase.h +++ b/dom/filesystem/FileSystemTaskBase.h @@ -20,6 +20,10 @@ class FileSystemBase; class FileSystemParams; class PBlobParent; +#define DIRECTORY_READ_PERMISSION "read" +#define DIRECTORY_WRITE_PERMISSION "write" +#define DIRECTORY_CREATE_PERMISSION "create" + /* * The base class to implement a Task class. * The file system operations can only be performed in the parent process. In diff --git a/dom/filesystem/GetDirectoryListingTask.cpp b/dom/filesystem/GetDirectoryListingTask.cpp index 793ece18a3..b1f1f78693 100644 --- a/dom/filesystem/GetDirectoryListingTask.cpp +++ b/dom/filesystem/GetDirectoryListingTask.cpp @@ -18,8 +18,6 @@ #include "nsIFile.h" #include "nsStringGlue.h" -#define GET_DIRECTORY_LISTING_PERMISSION "read" - namespace mozilla { namespace dom { @@ -30,7 +28,6 @@ namespace dom { /* static */ already_AddRefed GetDirectoryListingTaskChild::Create(FileSystemBase* aFileSystem, nsIFile* aTargetPath, - Directory::DirectoryType aType, const nsAString& aFilters, ErrorResult& aRv) { @@ -38,7 +35,7 @@ GetDirectoryListingTaskChild::Create(FileSystemBase* aFileSystem, aFileSystem->AssertIsOnOwningThread(); RefPtr task = - new GetDirectoryListingTaskChild(aFileSystem, aTargetPath, aType, aFilters); + new GetDirectoryListingTaskChild(aFileSystem, aTargetPath, aFilters); // aTargetPath can be null. In this case SetError will be called. @@ -59,12 +56,10 @@ GetDirectoryListingTaskChild::Create(FileSystemBase* aFileSystem, GetDirectoryListingTaskChild::GetDirectoryListingTaskChild(FileSystemBase* aFileSystem, nsIFile* aTargetPath, - Directory::DirectoryType aType, const nsAString& aFilters) : FileSystemTaskChildBase(aFileSystem) , mTargetPath(aTargetPath) , mFilters(aFilters) - , mType(aType) { MOZ_ASSERT(aFileSystem); aFileSystem->AssertIsOnOwningThread(); @@ -95,7 +90,6 @@ GetDirectoryListingTaskChild::GetRequestParams(const nsString& aSerializedDOMPat } return FileSystemGetDirectoryListingParams(aSerializedDOMPath, path, - mType == Directory::eDOMRootDirectory, mFilters); } @@ -181,8 +175,7 @@ GetDirectoryListingTaskChild::HandlerCallback() if (mTargetData[i].mType == Directory::FileOrDirectoryPath::eDirectoryPath) { RefPtr directory = - Directory::Create(mFileSystem->GetParentObject(), path, - Directory::eNotDOMRootDirectory, mFileSystem); + Directory::Create(mFileSystem->GetParentObject(), path, mFileSystem); MOZ_ASSERT(directory); // Propogate mFilter onto sub-Directory object: @@ -206,7 +199,7 @@ GetDirectoryListingTaskChild::HandlerCallback() void GetDirectoryListingTaskChild::GetPermissionAccessType(nsCString& aAccess) const { - aAccess.AssignLiteral(GET_DIRECTORY_LISTING_PERMISSION); + aAccess.AssignLiteral(DIRECTORY_READ_PERMISSION); } /** @@ -232,8 +225,6 @@ GetDirectoryListingTaskParent::Create(FileSystemBase* aFileSystem, return nullptr; } - task->mType = aParam.isRoot() - ? Directory::eDOMRootDirectory : Directory::eNotDOMRootDirectory; return task.forget(); } @@ -293,11 +284,10 @@ GetDirectoryListingTaskParent::IOWork() } if (!exists) { - if (mType == Directory::eNotDOMRootDirectory) { + if (!mFileSystem->ShouldCreateDirectory()) { return NS_ERROR_DOM_FILE_NOT_FOUND_ERR; } - // If the root directory doesn't exit, create it. rv = mTargetPath->Create(nsIFile::DIRECTORY_TYPE, 0777); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -394,7 +384,7 @@ GetDirectoryListingTaskParent::IOWork() void GetDirectoryListingTaskParent::GetPermissionAccessType(nsCString& aAccess) const { - aAccess.AssignLiteral(GET_DIRECTORY_LISTING_PERMISSION); + aAccess.AssignLiteral(DIRECTORY_READ_PERMISSION); } } // namespace dom diff --git a/dom/filesystem/GetDirectoryListingTask.h b/dom/filesystem/GetDirectoryListingTask.h index 50c15ec0f4..36d19e0dc1 100644 --- a/dom/filesystem/GetDirectoryListingTask.h +++ b/dom/filesystem/GetDirectoryListingTask.h @@ -23,7 +23,6 @@ public: static already_AddRefed Create(FileSystemBase* aFileSystem, nsIFile* aTargetPath, - Directory::DirectoryType aType, const nsAString& aFilters, ErrorResult& aRv); @@ -40,7 +39,6 @@ private: // If aDirectoryOnly is set, we should ensure that the target is a directory. GetDirectoryListingTaskChild(FileSystemBase* aFileSystem, nsIFile* aTargetPath, - Directory::DirectoryType aType, const nsAString& aFilters); virtual FileSystemParams @@ -57,7 +55,6 @@ private: RefPtr mPromise; nsCOMPtr mTargetPath; nsString mFilters; - Directory::DirectoryType mType; // We cannot store File or Directory objects bacause this object is created // on a different thread and File and Directory are not thread-safe. @@ -89,7 +86,6 @@ private: nsCOMPtr mTargetPath; nsString mFilters; - Directory::DirectoryType mType; // We cannot store File or Directory objects bacause this object is created // on a different thread and File and Directory are not thread-safe. diff --git a/dom/filesystem/GetFileOrDirectoryTask.cpp b/dom/filesystem/GetFileOrDirectoryTask.cpp index e45a81b580..2f16cfa799 100644 --- a/dom/filesystem/GetFileOrDirectoryTask.cpp +++ b/dom/filesystem/GetFileOrDirectoryTask.cpp @@ -17,8 +17,6 @@ #include "nsIFile.h" #include "nsStringGlue.h" -#define GET_FILE_OR_DIRECTORY_PERMISSION "read" - namespace mozilla { namespace dom { @@ -29,7 +27,6 @@ namespace dom { /* static */ already_AddRefed GetFileOrDirectoryTaskChild::Create(FileSystemBase* aFileSystem, nsIFile* aTargetPath, - Directory::DirectoryType aType, bool aDirectoryOnly, ErrorResult& aRv) { @@ -37,8 +34,7 @@ GetFileOrDirectoryTaskChild::Create(FileSystemBase* aFileSystem, MOZ_ASSERT(aFileSystem); RefPtr task = - new GetFileOrDirectoryTaskChild(aFileSystem, aTargetPath, aType, - aDirectoryOnly); + new GetFileOrDirectoryTaskChild(aFileSystem, aTargetPath, aDirectoryOnly); // aTargetPath can be null. In this case SetError will be called. @@ -59,12 +55,10 @@ GetFileOrDirectoryTaskChild::Create(FileSystemBase* aFileSystem, GetFileOrDirectoryTaskChild::GetFileOrDirectoryTaskChild(FileSystemBase* aFileSystem, nsIFile* aTargetPath, - Directory::DirectoryType aType, bool aDirectoryOnly) : FileSystemTaskChildBase(aFileSystem) , mTargetPath(aTargetPath) , mIsDirectory(aDirectoryOnly) - , mType(aType) { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); @@ -94,8 +88,7 @@ GetFileOrDirectoryTaskChild::GetRequestParams(const nsString& aSerializedDOMPath return FileSystemGetFileOrDirectoryParams(); } - return FileSystemGetFileOrDirectoryParams(aSerializedDOMPath, path, - mType == Directory::eDOMRootDirectory); + return FileSystemGetFileOrDirectoryParams(aSerializedDOMPath, path); } void @@ -153,7 +146,6 @@ GetFileOrDirectoryTaskChild::HandlerCallback() if (mIsDirectory) { RefPtr dir = Directory::Create(mFileSystem->GetParentObject(), mTargetPath, - mType, mFileSystem); MOZ_ASSERT(dir); @@ -171,7 +163,7 @@ GetFileOrDirectoryTaskChild::HandlerCallback() void GetFileOrDirectoryTaskChild::GetPermissionAccessType(nsCString& aAccess) const { - aAccess.AssignLiteral(GET_FILE_OR_DIRECTORY_PERMISSION); + aAccess.AssignLiteral(DIRECTORY_READ_PERMISSION); } /** @@ -197,8 +189,6 @@ GetFileOrDirectoryTaskParent::Create(FileSystemBase* aFileSystem, return nullptr; } - task->mType = aParam.isRoot() - ? Directory::eDOMRootDirectory : Directory::eNotDOMRootDirectory; return task.forget(); } @@ -250,11 +240,10 @@ GetFileOrDirectoryTaskParent::IOWork() } if (!exists) { - if (mType == Directory::eNotDOMRootDirectory) { + if (!mFileSystem->ShouldCreateDirectory()) { return NS_ERROR_DOM_FILE_NOT_FOUND_ERR; } - // If the root directory doesn't exit, create it. rv = mTargetPath->Create(nsIFile::DIRECTORY_TYPE, 0777); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -271,11 +260,6 @@ GetFileOrDirectoryTaskParent::IOWork() return NS_OK; } - // Check if the root is a directory. - if (mType == Directory::eDOMRootDirectory) { - return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR; - } - bool isFile; // Get isFile rv = mTargetPath->IsFile(&isFile); @@ -298,7 +282,7 @@ GetFileOrDirectoryTaskParent::IOWork() void GetFileOrDirectoryTaskParent::GetPermissionAccessType(nsCString& aAccess) const { - aAccess.AssignLiteral(GET_FILE_OR_DIRECTORY_PERMISSION); + aAccess.AssignLiteral(DIRECTORY_READ_PERMISSION); } } // namespace dom diff --git a/dom/filesystem/GetFileOrDirectoryTask.h b/dom/filesystem/GetFileOrDirectoryTask.h index a3e58e34f8..3d9b62023c 100644 --- a/dom/filesystem/GetFileOrDirectoryTask.h +++ b/dom/filesystem/GetFileOrDirectoryTask.h @@ -23,7 +23,6 @@ public: static already_AddRefed Create(FileSystemBase* aFileSystem, nsIFile* aTargetPath, - Directory::DirectoryType aType, bool aDirectoryOnly, ErrorResult& aRv); @@ -51,7 +50,6 @@ private: // If aDirectoryOnly is set, we should ensure that the target is a directory. GetFileOrDirectoryTaskChild(FileSystemBase* aFileSystem, nsIFile* aTargetPath, - Directory::DirectoryType aType, bool aDirectoryOnly); RefPtr mPromise; @@ -59,7 +57,6 @@ private: // Whether we get a directory. bool mIsDirectory; - Directory::DirectoryType mType; }; class GetFileOrDirectoryTaskParent final : public FileSystemTaskParentBase @@ -90,7 +87,6 @@ private: // Whether we get a directory. bool mIsDirectory; - Directory::DirectoryType mType; }; } // namespace dom diff --git a/dom/filesystem/GetFilesTask.cpp b/dom/filesystem/GetFilesTask.cpp new file mode 100644 index 0000000000..299e40d9bd --- /dev/null +++ b/dom/filesystem/GetFilesTask.cpp @@ -0,0 +1,359 @@ +/* -*- 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 "GetFilesTask.h" + +#include "HTMLSplitOnSpacesTokenizer.h" +#include "js/Value.h" +#include "mozilla/dom/File.h" +#include "mozilla/dom/FileSystemBase.h" +#include "mozilla/dom/FileSystemUtils.h" +#include "mozilla/dom/PFileSystemParams.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/dom/ipc/BlobChild.h" +#include "mozilla/dom/ipc/BlobParent.h" +#include "nsIFile.h" +#include "nsStringGlue.h" + +namespace mozilla { +namespace dom { + +/** + * GetFilesTaskChild + */ + +/* static */ already_AddRefed +GetFilesTaskChild::Create(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + bool aRecursiveFlag, + ErrorResult& aRv) +{ + MOZ_ASSERT(aFileSystem); + aFileSystem->AssertIsOnOwningThread(); + + nsCOMPtr globalObject = + do_QueryInterface(aFileSystem->GetParentObject()); + if (NS_WARN_IF(!globalObject)) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + RefPtr task = + new GetFilesTaskChild(aFileSystem, aTargetPath, aRecursiveFlag); + + // aTargetPath can be null. In this case SetError will be called. + + task->mPromise = Promise::Create(globalObject, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return task.forget(); +} + +GetFilesTaskChild::GetFilesTaskChild(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + bool aRecursiveFlag) + : FileSystemTaskChildBase(aFileSystem) + , mTargetPath(aTargetPath) + , mRecursiveFlag(aRecursiveFlag) +{ + MOZ_ASSERT(aFileSystem); + aFileSystem->AssertIsOnOwningThread(); +} + +GetFilesTaskChild::~GetFilesTaskChild() +{ + mFileSystem->AssertIsOnOwningThread(); +} + +already_AddRefed +GetFilesTaskChild::GetPromise() +{ + mFileSystem->AssertIsOnOwningThread(); + return RefPtr(mPromise).forget(); +} + +FileSystemParams +GetFilesTaskChild::GetRequestParams(const nsString& aSerializedDOMPath, + ErrorResult& aRv) const +{ + mFileSystem->AssertIsOnOwningThread(); + + nsAutoString path; + aRv = mTargetPath->GetPath(path); + if (NS_WARN_IF(aRv.Failed())) { + return FileSystemGetFilesParams(); + } + + return FileSystemGetFilesParams(aSerializedDOMPath, path, mRecursiveFlag); +} + +void +GetFilesTaskChild::SetSuccessRequestResult(const FileSystemResponseValue& aValue, + ErrorResult& aRv) +{ + mFileSystem->AssertIsOnOwningThread(); + MOZ_ASSERT(aValue.type() == + FileSystemResponseValue::TFileSystemFilesResponse); + + FileSystemFilesResponse r = aValue; + + if (!mTargetData.SetLength(r.data().Length(), mozilla::fallible_t())) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + + for (uint32_t i = 0; i < r.data().Length(); ++i) { + const FileSystemFileResponse& data = r.data()[i]; + mTargetData[i] = data.realPath(); + } +} + +void +GetFilesTaskChild::HandlerCallback() +{ + mFileSystem->AssertIsOnOwningThread(); + if (mFileSystem->IsShutdown()) { + mPromise = nullptr; + return; + } + + if (HasError()) { + mPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR); + mPromise = nullptr; + return; + } + + size_t count = mTargetData.Length(); + + Sequence> listing; + + if (!listing.SetLength(count, mozilla::fallible_t())) { + mPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY); + mPromise = nullptr; + return; + } + + for (unsigned i = 0; i < count; i++) { + nsCOMPtr path; + NS_ConvertUTF16toUTF8 fullPath(mTargetData[i]); + nsresult rv = NS_NewNativeLocalFile(fullPath, true, getter_AddRefs(path)); + if (NS_WARN_IF(NS_FAILED(rv))) { + mPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR); + mPromise = nullptr; + return; + } + +#ifdef DEBUG + nsCOMPtr rootPath; + rv = NS_NewLocalFile(mFileSystem->LocalOrDeviceStorageRootPath(), false, + getter_AddRefs(rootPath)); + if (NS_WARN_IF(NS_FAILED(rv))) { + mPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR); + mPromise = nullptr; + return; + } + + MOZ_ASSERT(FileSystemUtils::IsDescendantPath(rootPath, path)); +#endif + + RefPtr file = + File::CreateFromFile(mFileSystem->GetParentObject(), path); + MOZ_ASSERT(file); + + listing[i] = file; + } + + mPromise->MaybeResolve(listing); + mPromise = nullptr; +} + +void +GetFilesTaskChild::GetPermissionAccessType(nsCString& aAccess) const +{ + aAccess.AssignLiteral("read"); +} + +/** + * GetFilesTaskParent + */ + +/* static */ already_AddRefed +GetFilesTaskParent::Create(FileSystemBase* aFileSystem, + const FileSystemGetFilesParams& aParam, + FileSystemRequestParent* aParent, + ErrorResult& aRv) +{ + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aFileSystem); + + RefPtr task = + new GetFilesTaskParent(aFileSystem, aParam, aParent); + + NS_ConvertUTF16toUTF8 path(aParam.realPath()); + aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath)); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return task.forget(); +} + +GetFilesTaskParent::GetFilesTaskParent(FileSystemBase* aFileSystem, + const FileSystemGetFilesParams& aParam, + FileSystemRequestParent* aParent) + : FileSystemTaskParentBase(aFileSystem, aParam, aParent) + , mRecursiveFlag(aParam.recursiveFlag()) +{ + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aFileSystem); +} + +FileSystemResponseValue +GetFilesTaskParent::GetSuccessRequestResult(ErrorResult& aRv) const +{ + AssertIsOnBackgroundThread(); + + InfallibleTArray blobs; + + FallibleTArray inputs; + if (!inputs.SetLength(mTargetData.Length(), mozilla::fallible_t())) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + FileSystemFilesResponse response; + return response; + } + + for (unsigned i = 0; i < mTargetData.Length(); i++) { + FileSystemFileResponse fileData; + fileData.realPath() = mTargetData[i]; + inputs[i] = fileData; + } + + FileSystemFilesResponse response; + response.data().SwapElements(inputs); + return response; +} + +nsresult +GetFilesTaskParent::IOWork() +{ + MOZ_ASSERT(XRE_IsParentProcess(), + "Only call from parent process!"); + MOZ_ASSERT(!NS_IsMainThread(), "Only call on I/O thread!"); + + if (mFileSystem->IsShutdown()) { + return NS_ERROR_FAILURE; + } + + bool exists; + nsresult rv = mTargetPath->Exists(&exists); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!exists) { + return NS_OK; + } + + // Get isDirectory. + rv = ExploreDirectory(mTargetPath); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +GetFilesTaskParent::ExploreDirectory(nsIFile* aPath) +{ + MOZ_ASSERT(XRE_IsParentProcess(), + "Only call from parent process!"); + MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!"); + MOZ_ASSERT(aPath); + + bool isDir; + nsresult rv = aPath->IsDirectory(&isDir); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!isDir) { + return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR; + } + + nsCOMPtr entries; + rv = aPath->GetDirectoryEntries(getter_AddRefs(entries)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + for (;;) { + bool hasMore = false; + if (NS_WARN_IF(NS_FAILED(entries->HasMoreElements(&hasMore))) || !hasMore) { + break; + } + nsCOMPtr supp; + if (NS_WARN_IF(NS_FAILED(entries->GetNext(getter_AddRefs(supp))))) { + break; + } + + nsCOMPtr currFile = do_QueryInterface(supp); + MOZ_ASSERT(currFile); + + bool isLink, isSpecial, isFile; + if (NS_WARN_IF(NS_FAILED(currFile->IsSymlink(&isLink)) || + NS_FAILED(currFile->IsSpecial(&isSpecial))) || + isLink || isSpecial) { + continue; + } + + if (NS_WARN_IF(NS_FAILED(currFile->IsFile(&isFile)) || + NS_FAILED(currFile->IsDirectory(&isDir))) || + !(isFile || isDir)) { + continue; + } + + if (isFile) { + nsAutoString path; + if (NS_WARN_IF(NS_FAILED(currFile->GetPath(path)))) { + continue; + } + + if (!mTargetData.AppendElement(path, fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + continue; + } + + MOZ_ASSERT(isDir); + + if (!mRecursiveFlag) { + continue; + } + + // Recursive. + rv = ExploreDirectory(currFile); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + return NS_OK; +} + +void +GetFilesTaskParent::GetPermissionAccessType(nsCString& aAccess) const +{ + aAccess.AssignLiteral(DIRECTORY_READ_PERMISSION); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/filesystem/GetFilesTask.h b/dom/filesystem/GetFilesTask.h new file mode 100644 index 0000000000..a66bdb4c18 --- /dev/null +++ b/dom/filesystem/GetFilesTask.h @@ -0,0 +1,99 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_GetFilesTask_h +#define mozilla_dom_GetFilesTask_h + +#include "mozilla/dom/Directory.h" +#include "mozilla/dom/FileSystemTaskBase.h" +#include "mozilla/ErrorResult.h" +#include "nsAutoPtr.h" + +namespace mozilla { +namespace dom { + +class BlobImpl; + +class GetFilesTaskChild final : public FileSystemTaskChildBase +{ +public: + static already_AddRefed + Create(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + bool aRecursiveFlag, + ErrorResult& aRv); + + virtual + ~GetFilesTaskChild(); + + already_AddRefed + GetPromise(); + + virtual void + GetPermissionAccessType(nsCString& aAccess) const override; + +private: + // If aDirectoryOnly is set, we should ensure that the target is a directory. + GetFilesTaskChild(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + bool aRecursiveFlag); + + virtual FileSystemParams + GetRequestParams(const nsString& aSerializedDOMPath, + ErrorResult& aRv) const override; + + virtual void + SetSuccessRequestResult(const FileSystemResponseValue& aValue, + ErrorResult& aRv) override; + + virtual void + HandlerCallback() override; + + RefPtr mPromise; + nsCOMPtr mTargetPath; + bool mRecursiveFlag; + + // We store the fullpath of Files. + FallibleTArray mTargetData; +}; + +class GetFilesTaskParent final : public FileSystemTaskParentBase +{ +public: + static already_AddRefed + Create(FileSystemBase* aFileSystem, + const FileSystemGetFilesParams& aParam, + FileSystemRequestParent* aParent, + ErrorResult& aRv); + + virtual void + GetPermissionAccessType(nsCString& aAccess) const override; + +private: + GetFilesTaskParent(FileSystemBase* aFileSystem, + const FileSystemGetFilesParams& aParam, + FileSystemRequestParent* aParent); + + virtual FileSystemResponseValue + GetSuccessRequestResult(ErrorResult& aRv) const override; + + virtual nsresult + IOWork() override; + + nsresult + ExploreDirectory(nsIFile* aPath); + + nsCOMPtr mTargetPath; + bool mRecursiveFlag; + + // We store the fullpath of Files. + FallibleTArray mTargetData; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_GetFilesTask_h diff --git a/dom/filesystem/OSFileSystem.cpp b/dom/filesystem/OSFileSystem.cpp index c4a1063c8c..e083c65cec 100644 --- a/dom/filesystem/OSFileSystem.cpp +++ b/dom/filesystem/OSFileSystem.cpp @@ -62,13 +62,6 @@ OSFileSystem::GetParentObject() const return mParent; } -void -OSFileSystem::GetRootName(nsAString& aRetval) const -{ - AssertIsOnOwningThread(); - aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL); -} - bool OSFileSystem::IsSafeFile(nsIFile* aFile) const { diff --git a/dom/filesystem/OSFileSystem.h b/dom/filesystem/OSFileSystem.h index 0042f196ee..1df7e20057 100644 --- a/dom/filesystem/OSFileSystem.h +++ b/dom/filesystem/OSFileSystem.h @@ -25,12 +25,18 @@ public: virtual already_AddRefed Clone() override; + virtual bool + ShouldCreateDirectory() override + { + MOZ_CRASH("This should not be called."); + // Because OSFileSystem should not be used when the creation of directories + // is needed. For that we have OSFileSystemParent. + return false; + } + virtual nsISupports* GetParentObject() const override; - virtual void - GetRootName(nsAString& aRetval) const override; - virtual bool IsSafeFile(nsIFile* aFile) const override; @@ -67,6 +73,9 @@ public: return nullptr; } + virtual bool + ShouldCreateDirectory() override { return false; } + virtual nsISupports* GetParentObject() const override { @@ -75,7 +84,8 @@ public: } virtual void - GetRootName(nsAString& aRetval) const override + GetDirectoryName(nsIFile* aFile, nsAString& aRetval, + ErrorResult& aRv) const override { MOZ_CRASH("This should not be called on the PBackground thread."); } diff --git a/dom/filesystem/PFileSystemParams.ipdlh b/dom/filesystem/PFileSystemParams.ipdlh index 1196100524..5ddf03e12e 100644 --- a/dom/filesystem/PFileSystemParams.ipdlh +++ b/dom/filesystem/PFileSystemParams.ipdlh @@ -31,7 +31,7 @@ struct FileSystemGetDirectoryListingParams { nsString filesystem; nsString realPath; - bool isRoot; + // 'filters' could be an array rather than a semicolon separated string // (we'd then use InfallibleTArray internally), but that is // wasteful. E10s requires us to pass the filters over as a string anyway, @@ -44,11 +44,17 @@ struct FileSystemGetDirectoryListingParams nsString filters; }; +struct FileSystemGetFilesParams +{ + nsString filesystem; + nsString realPath; + bool recursiveFlag; +}; + struct FileSystemGetFileOrDirectoryParams { nsString filesystem; nsString realPath; - bool isRoot; }; struct FileSystemRemoveParams @@ -64,6 +70,7 @@ union FileSystemParams FileSystemCreateDirectoryParams; FileSystemCreateFileParams; FileSystemGetDirectoryListingParams; + FileSystemGetFilesParams; FileSystemGetFileOrDirectoryParams; FileSystemRemoveParams; }; diff --git a/dom/filesystem/PFileSystemRequest.ipdl b/dom/filesystem/PFileSystemRequest.ipdl index aa567b5f1c..680519a727 100644 --- a/dom/filesystem/PFileSystemRequest.ipdl +++ b/dom/filesystem/PFileSystemRequest.ipdl @@ -43,6 +43,11 @@ struct FileSystemDirectoryListingResponse FileSystemDirectoryListingResponseData[] data; }; +struct FileSystemFilesResponse +{ + FileSystemFileResponse[] data; +}; + struct FileSystemErrorResponse { nsresult error; @@ -59,6 +64,7 @@ union FileSystemResponseValue FileSystemDirectoryResponse; FileSystemDirectoryListingResponse; FileSystemFileResponse; + FileSystemFilesResponse; FileSystemErrorResponse; }; diff --git a/dom/filesystem/RemoveTask.cpp b/dom/filesystem/RemoveTask.cpp index ff4bd6f6cd..5f9d28b9c1 100644 --- a/dom/filesystem/RemoveTask.cpp +++ b/dom/filesystem/RemoveTask.cpp @@ -142,7 +142,7 @@ RemoveTaskChild::HandlerCallback() void RemoveTaskChild::GetPermissionAccessType(nsCString& aAccess) const { - aAccess.AssignLiteral(REMOVE_TASK_PERMISSION); + aAccess.AssignLiteral(DIRECTORY_WRITE_PERMISSION); } /** @@ -252,7 +252,7 @@ RemoveTaskParent::IOWork() void RemoveTaskParent::GetPermissionAccessType(nsCString& aAccess) const { - aAccess.AssignLiteral(REMOVE_TASK_PERMISSION); + aAccess.AssignLiteral(DIRECTORY_WRITE_PERMISSION); } } // namespace dom diff --git a/dom/filesystem/RemoveTask.h b/dom/filesystem/RemoveTask.h index 9963c9ad71..5952466fb4 100644 --- a/dom/filesystem/RemoveTask.h +++ b/dom/filesystem/RemoveTask.h @@ -11,8 +11,6 @@ #include "nsAutoPtr.h" #include "mozilla/ErrorResult.h" -#define REMOVE_TASK_PERMISSION "write" - namespace mozilla { namespace dom { diff --git a/dom/filesystem/moz.build b/dom/filesystem/moz.build index f0698c3e1d..5404ca5d98 100644 --- a/dom/filesystem/moz.build +++ b/dom/filesystem/moz.build @@ -28,6 +28,7 @@ UNIFIED_SOURCES += [ 'FileSystemUtils.cpp', 'GetDirectoryListingTask.cpp', 'GetFileOrDirectoryTask.cpp', + 'GetFilesTask.cpp', 'OSFileSystem.cpp', 'RemoveTask.cpp', ] diff --git a/dom/filesystem/tests/filesystem_commons.js b/dom/filesystem/tests/filesystem_commons.js new file mode 100644 index 0000000000..6c7c529c94 --- /dev/null +++ b/dom/filesystem/tests/filesystem_commons.js @@ -0,0 +1,74 @@ +function test_basic(aDirectory, aNext) { + ok(aDirectory, "Directory exists."); + ok(aDirectory instanceof Directory, "We have a directory."); + is(aDirectory.path, '/' + aDirectory.name, "directory.path must be '/'+name"); + aNext(); +} + +function test_getFilesAndDirectories(aDirectory, aRecursive, aNext) { + function checkSubDir(dir) { + return dir.getFilesAndDirectories().then( + function(data) { + for (var i = 0; i < data.length; ++i) { + ok (data[i] instanceof File || data[i] instanceof Directory, "Just Files or Directories"); + if (data[i] instanceof Directory) { + isnot(data[i].name, '/', "Subdirectory should be called with the leafname"); + isnot(data[i].path, '/', "Subdirectory path should be called with the leafname"); + isnot(data[i].path, dir.path, "Subdirectory path should contain the parent path."); + is(data[i].path, dir.path + '/' + data[i].name, "Subdirectory path should be called parentdir.path + '/' + leafname"); + } + } + } + ); + } + + aDirectory.getFilesAndDirectories().then( + function(data) { + ok(data.length, "We should have some data."); + var promises = []; + for (var i = 0; i < data.length; ++i) { + ok (data[i] instanceof File || data[i] instanceof Directory, "Just Files or Directories: " + data[i].name); + if (data[i] instanceof Directory) { + isnot(data[i].name, '/', "Subdirectory should be called with the leafname"); + is(data[i].path, aDirectory.path + '/' + data[i].name, "Subdirectory path should be called parentdir.path + '/' + leafname"); + if (aRecursive) { + promises.push(checkSubDir(data[i])); + } + } + } + + return Promise.all(promises); + }, + function() { + ok(false, "Something when wrong"); + } + ).then(aNext); +} + +function test_getFiles(aDirectory, aRecursive, aNext) { + aDirectory.getFiles(aRecursive).then( + function(data) { + for (var i = 0; i < data.length; ++i) { + ok (data[i] instanceof File, "File: " + data[i].name); + } + }, + function() { + ok(false, "Something when wrong"); + } + ).then(aNext); +} + +function test_getFiles_recursiveComparison(aDirectory, aNext) { + aDirectory.getFiles(true).then(function(data) { + is(data.length, 2, "Only 2 files for this test."); + ok(data[0].name == 'foo.txt' || data[0].name == 'bar.txt', "First filename matches"); + ok(data[1].name == 'foo.txt' || data[1].name == 'bar.txt', "Second filename matches"); + }).then(function() { + return aDirectory.getFiles(false); + }).then(function(data) { + is(data.length, 1, "Only 1 file for this test."); + ok(data[0].name == 'foo.txt' || data[0].name == 'bar.txt', "First filename matches"); + }).catch(function() { + ok(false, "Something when wrong"); + }).then(aNext); +} diff --git a/dom/filesystem/tests/mochitest.ini b/dom/filesystem/tests/mochitest.ini index b9f531c641..1fffb87b03 100644 --- a/dom/filesystem/tests/mochitest.ini +++ b/dom/filesystem/tests/mochitest.ini @@ -1,5 +1,6 @@ [DEFAULT] support-files = + filesystem_commons.js script_fileList.js worker_basic.js diff --git a/dom/filesystem/tests/script_fileList.js b/dom/filesystem/tests/script_fileList.js index f7fbca2e9d..23dfde2c43 100644 --- a/dom/filesystem/tests/script_fileList.js +++ b/dom/filesystem/tests/script_fileList.js @@ -1,22 +1,67 @@ var { classes: Cc, interfaces: Ci, utils: Cu } = Components; Cu.importGlobalProperties(["File"]); -addMessageListener("dir.open", function (e) { - var testFile = Cc["@mozilla.org/file/directory_service;1"] - .getService(Ci.nsIDirectoryService) - .QueryInterface(Ci.nsIProperties) - .get(e.path == 'root' ? 'ProfD' : e.path, Ci.nsIFile); +function createProfDFile() { + return Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIDirectoryService) + .QueryInterface(Ci.nsIProperties) + .get('ProfD', Ci.nsIFile); +} + +function createRootFile() { + var testFile = createProfDFile(); // Let's go back to the root of the FileSystem - if (e.path == 'root') { - while (true) { - var parent = testFile.parent; - if (!parent) { - break; - } - - testFile = parent; + while (true) { + var parent = testFile.parent; + if (!parent) { + break; } + + testFile = parent; + } + + return testFile; +} + +function createTestFile() { + var tmpFile = Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIDirectoryService) + .QueryInterface(Ci.nsIProperties) + .get('TmpD', Ci.nsIFile) + tmpFile.append('dir-test'); + tmpFile.createUnique(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o700); + + var file1 = tmpFile.clone(); + file1.append('foo.txt'); + file1.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0o600); + + var dir = tmpFile.clone(); + dir.append('subdir'); + dir.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o700); + + var file2 = dir.clone(); + file2.append('bar.txt'); + file2.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0o600); + + return tmpFile; +} + +addMessageListener("dir.open", function (e) { + var testFile; + + switch (e.path) { + case 'ProfD': + testFile = createProfDFile(); + break; + + case 'root': + testFile = createRootFile(); + break; + + case 'test': + testFile = createTestFile(); + break; } sendAsyncMessage("dir.opened", { diff --git a/dom/filesystem/tests/test_basic.html b/dom/filesystem/tests/test_basic.html index 1a929f56a8..27f8c9f44c 100644 --- a/dom/filesystem/tests/test_basic.html +++ b/dom/filesystem/tests/test_basic.html @@ -3,6 +3,7 @@ Test for Directory API + @@ -20,77 +21,34 @@ function create_fileList(aPath) { var fileList = document.getElementById('fileList'); SpecialPowers.wrap(fileList).mozSetDirectory(message.dir); - // Just a simple test - is(fileList.files.length, 1, "Filelist has 1 element"); - ok(fileList.files[0] instanceof Directory, "We have a directory."); + fileList.getFilesAndDirectories().then(function(array) { + is(array.length, 1, "We want just 1 directory."); + ok(array[0] instanceof Directory, "We want just 1 directory."); - directory = fileList.files[0]; - - script.destroy(); - next(); + directory = array[0]; + script.destroy(); + next(); + }); } script.addMessageListener("dir.opened", onOpened); script.sendAsyncMessage("dir.open", { path: aPath }); } -function test_basic() { - ok(directory, "Directory exists."); - ok(directory instanceof Directory, "We have a directory."); - is(directory.name, '/', "directory.name must be '/'"); - is(directory.path, '/', "directory.path must be '/'"); - next(); -} - -function checkSubDir(dir) { - return dir.getFilesAndDirectories().then( - function(data) { - for (var i = 0; i < data.length; ++i) { - ok (data[i] instanceof File || data[i] instanceof Directory, "Just Files or Directories"); - if (data[i] instanceof Directory) { - isnot(data[i].name, '/', "Subdirectory should be called with the leafname: " + data[i].name); - isnot(data[i].path, '/', "Subdirectory path should be called with the leafname:" + data[i].path); - isnot(data[i].path, dir.path, "Subdirectory path should contain the parent path."); - is(data[i].path,dir.path + '/' + data[i].name, "Subdirectory path should be called parentdir.path + '/' + leafname"); - } - } - } - ); -} - -function getFilesAndDirectories(aRecursive) { - directory.getFilesAndDirectories().then( - function(data) { - ok(data.length, "We should have some data."); - var promises = []; - for (var i = 0; i < data.length; ++i) { - ok (data[i] instanceof File || data[i] instanceof Directory, "Just Files or Directories"); - if (data[i] instanceof Directory) { - isnot(data[i].name, '/', "Subdirectory should be called with the leafname"); - is(data[i].path, '/' + data[i].name, "Subdirectory path should be called '/' + leafname"); - - if (aRecursive) { - promises.push(checkSubDir(data[i])); - } - } - } - - return Promise.all(promises); - }, - function() { - ok(false, "Something when wrong"); - } - ).then(next); -} - var tests = [ function() { create_fileList('ProfD') }, - test_basic, - function() { getFilesAndDirectories(true) }, + function() { test_basic(directory, next); }, + function() { test_getFilesAndDirectories(directory, true, next); }, + function() { test_getFiles(directory, false, next); }, + function() { test_getFiles(directory, true, next); }, - function() { create_fileList('root') }, - test_basic, - function() { getFilesAndDirectories(false) }, + function() { create_fileList('test') }, + function() { test_getFiles_recursiveComparison(directory, next); }, + + function() { create_fileList('root'); }, + function() { test_basic(directory, next); }, + function() { test_getFilesAndDirectories(directory, false, next); }, + function() { test_getFiles(directory, false, next); }, ]; function next() { diff --git a/dom/filesystem/tests/test_worker_basic.html b/dom/filesystem/tests/test_worker_basic.html index 0be7696242..8e81251712 100644 --- a/dom/filesystem/tests/test_worker_basic.html +++ b/dom/filesystem/tests/test_worker_basic.html @@ -17,11 +17,6 @@ function create_fileList() { function onOpened(message) { var fileList = document.getElementById('fileList'); SpecialPowers.wrap(fileList).mozSetDirectory(message.dir); - - // Just a simple test - is(fileList.files.length, 1, "Filelist has 1 element"); - ok(fileList.files[0] instanceof Directory, "We have a directory."); - script.destroy(); next(); } @@ -32,20 +27,21 @@ function create_fileList() { function test_worker() { var fileList = document.getElementById('fileList'); + fileList.getFilesAndDirectories().then(function(array) { + var worker = new Worker('worker_basic.js'); + worker.onmessage = function(e) { + if (e.data.type == 'finish') { + next(); + return; + } - var worker = new Worker('worker_basic.js'); - worker.onmessage = function(e) { - if (e.data.type == 'finish') { - next(); - return; + if (e.data.type == 'test') { + ok(e.data.test, e.data.message); + } } - if (e.data.type == 'test') { - ok(e.data.test, e.data.message); - } - } - - worker.postMessage(fileList.files); + worker.postMessage(array[0]); + }); } var tests = [ diff --git a/dom/filesystem/tests/worker_basic.js b/dom/filesystem/tests/worker_basic.js index 4da78eef0e..01df3fbd16 100644 --- a/dom/filesystem/tests/worker_basic.js +++ b/dom/filesystem/tests/worker_basic.js @@ -1,3 +1,5 @@ +importScripts('filesystem_commons.js'); + function finish() { postMessage({ type: 'finish' }); } @@ -14,45 +16,26 @@ function isnot(a, b, msg) { ok(a != b, msg); } -function checkSubDir(dir) { - return dir.getFilesAndDirectories().then( - function(data) { - for (var i = 0; i < data.length; ++i) { - ok (data[i] instanceof File || data[i] instanceof Directory, "Just Files or Directories"); - if (data[i] instanceof Directory) { - isnot(data[i].name, '/', "Subdirectory should be called with the leafname"); - isnot(data[i].path, '/', "Subdirectory path should be called with the leafname"); - isnot(data[i].path, dir.path, "Subdirectory path should contain the parent path."); - is(data[i].path,dir.path + '/' + data[i].name, "Subdirectory path should be called parentdir.path + '/' + leafname"); - } - } - } - ); +var tests = [ + function() { test_basic(directory, next); }, + function() { test_getFilesAndDirectories(directory, true, next); }, + function() { test_getFiles(directory, false, next); }, + function() { test_getFiles(directory, true, next); }, +]; + +function next() { + if (!tests.length) { + finish(); + return; + } + + var test = tests.shift(); + test(); } +var directory; + onmessage = function(e) { - var fileList = e.data; - ok(fileList instanceof FileList, "This is a fileList."); - is(fileList.length, 1, "We want just 1 element."); - ok(fileList[0] instanceof Directory, "This is a directory."); - - fileList[0].getFilesAndDirectories().then( - function(data) { - ok(data.length, "We should have some data."); - var promises = []; - for (var i = 0; i < data.length; ++i) { - ok (data[i] instanceof File || data[i] instanceof Directory, "Just Files or Directories"); - if (data[i] instanceof Directory) { - isnot(data[i].name, '/', "Subdirectory should be called with the leafname"); - is(data[i].path, '/' + data[i].name, "Subdirectory path should be called '/' + leafname"); - promises.push(checkSubDir(data[i])); - } - } - - return Promise.all(promises); - }, - function() { - ok(false, "Something when wrong"); - } - ).then(finish); + directory = e.data; + next(); } diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index e959d563c7..18792ad5a8 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -272,8 +272,7 @@ class HTMLInputElementState final : public nsISupports continue; } - RefPtr directory = Directory::Create(aWindow, file, - Directory::eDOMRootDirectory); + RefPtr directory = Directory::Create(aWindow, file); MOZ_ASSERT(directory); OwningFileOrDirectory* element = aResult.AppendElement(); @@ -2323,8 +2322,7 @@ HTMLInputElement::MozSetDirectory(const nsAString& aDirectoryPath, return; } - RefPtr directory = Directory::Create(window, file, - Directory::eDOMRootDirectory); + RefPtr directory = Directory::Create(window, file); MOZ_ASSERT(directory); nsTArray array; @@ -2574,7 +2572,7 @@ HTMLInputElement::SetFiles(nsIDOMFileList* aFiles, aFiles->GetLength(&listLength); for (uint32_t i = 0; i < listLength; i++) { OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement(); - *element = files->UnsafeItem(i); + element->SetAsFile() = files->Item(i); } } @@ -2690,9 +2688,6 @@ HTMLInputElement::UpdateFileList() for (uint32_t i = 0; i < array.Length(); ++i) { if (array[i].IsFile()) { mFileList->Append(array[i].GetAsFile()); - } else { - MOZ_ASSERT(array[i].IsDirectory()); - mFileList->Append(array[i].GetAsDirectory()); } } } diff --git a/dom/html/HTMLTableCellElement.cpp b/dom/html/HTMLTableCellElement.cpp index 10ceabd392..32b486a0ef 100644 --- a/dom/html/HTMLTableCellElement.cpp +++ b/dom/html/HTMLTableCellElement.cpp @@ -493,7 +493,7 @@ HTMLTableCellElement::MapAttributesIntoRule(const nsMappedAttributes* aAttribute } } } - if (aData->mSIDs & NS_STYLE_INHERIT_BIT(TextReset)) { + if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Display)) { nsCSSValue* verticalAlign = aData->ValueForVerticalAlign(); if (verticalAlign->GetUnit() == eCSSUnit_Null) { // valign: enum diff --git a/dom/html/HTMLTableColElement.cpp b/dom/html/HTMLTableColElement.cpp index 9557178e24..e2dfb13bff 100644 --- a/dom/html/HTMLTableColElement.cpp +++ b/dom/html/HTMLTableColElement.cpp @@ -111,7 +111,7 @@ HTMLTableColElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes textAlign->SetIntValue(value->GetEnumValue(), eCSSUnit_Enumerated); } } - if (aData->mSIDs & NS_STYLE_INHERIT_BIT(TextReset)) { + if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Display)) { nsCSSValue* verticalAlign = aData->ValueForVerticalAlign(); if (verticalAlign->GetUnit() == eCSSUnit_Null) { // valign: enum diff --git a/dom/html/HTMLTableRowElement.cpp b/dom/html/HTMLTableRowElement.cpp index bf7ec24ca8..2dec9c8838 100644 --- a/dom/html/HTMLTableRowElement.cpp +++ b/dom/html/HTMLTableRowElement.cpp @@ -286,7 +286,7 @@ HTMLTableRowElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes textAlign->SetIntValue(value->GetEnumValue(), eCSSUnit_Enumerated); } } - if (aData->mSIDs & NS_STYLE_INHERIT_BIT(TextReset)) { + if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Display)) { nsCSSValue* verticalAlign = aData->ValueForVerticalAlign(); if (verticalAlign->GetUnit() == eCSSUnit_Null) { // valign: enum diff --git a/dom/html/HTMLTableSectionElement.cpp b/dom/html/HTMLTableSectionElement.cpp index dc61ada9ea..c7b0665ddc 100644 --- a/dom/html/HTMLTableSectionElement.cpp +++ b/dom/html/HTMLTableSectionElement.cpp @@ -187,7 +187,7 @@ HTMLTableSectionElement::MapAttributesIntoRule(const nsMappedAttributes* aAttrib textAlign->SetIntValue(value->GetEnumValue(), eCSSUnit_Enumerated); } } - if (aData->mSIDs & NS_STYLE_INHERIT_BIT(TextReset)) { + if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Display)) { nsCSSValue* verticalAlign = aData->ValueForVerticalAlign(); if (verticalAlign->GetUnit() == eCSSUnit_Null) { // valign: enum diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp index 6a4133aa63..bf80b69f03 100644 --- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -1437,32 +1437,27 @@ void nsGenericHTMLElement::MapImageAlignAttributeInto(const nsMappedAttributes* aAttributes, nsRuleData* aRuleData) { - if (aRuleData->mSIDs & (NS_STYLE_INHERIT_BIT(Display) | - NS_STYLE_INHERIT_BIT(TextReset))) { + if (aRuleData->mSIDs & NS_STYLE_INHERIT_BIT(Display)) { const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::align); if (value && value->Type() == nsAttrValue::eEnum) { int32_t align = value->GetEnumValue(); - if (aRuleData->mSIDs & NS_STYLE_INHERIT_BIT(Display)) { - nsCSSValue* cssFloat = aRuleData->ValueForFloat(); - if (cssFloat->GetUnit() == eCSSUnit_Null) { - if (align == NS_STYLE_TEXT_ALIGN_LEFT) { - cssFloat->SetIntValue(NS_STYLE_FLOAT_LEFT, eCSSUnit_Enumerated); - } else if (align == NS_STYLE_TEXT_ALIGN_RIGHT) { - cssFloat->SetIntValue(NS_STYLE_FLOAT_RIGHT, eCSSUnit_Enumerated); - } + nsCSSValue* cssFloat = aRuleData->ValueForFloat(); + if (cssFloat->GetUnit() == eCSSUnit_Null) { + if (align == NS_STYLE_TEXT_ALIGN_LEFT) { + cssFloat->SetIntValue(NS_STYLE_FLOAT_LEFT, eCSSUnit_Enumerated); + } else if (align == NS_STYLE_TEXT_ALIGN_RIGHT) { + cssFloat->SetIntValue(NS_STYLE_FLOAT_RIGHT, eCSSUnit_Enumerated); } } - if (aRuleData->mSIDs & NS_STYLE_INHERIT_BIT(TextReset)) { - nsCSSValue* verticalAlign = aRuleData->ValueForVerticalAlign(); - if (verticalAlign->GetUnit() == eCSSUnit_Null) { - switch (align) { - case NS_STYLE_TEXT_ALIGN_LEFT: - case NS_STYLE_TEXT_ALIGN_RIGHT: - break; - default: - verticalAlign->SetIntValue(align, eCSSUnit_Enumerated); - break; - } + nsCSSValue* verticalAlign = aRuleData->ValueForVerticalAlign(); + if (verticalAlign->GetUnit() == eCSSUnit_Null) { + switch (align) { + case NS_STYLE_TEXT_ALIGN_LEFT: + case NS_STYLE_TEXT_ALIGN_RIGHT: + break; + default: + verticalAlign->SetIntValue(align, eCSSUnit_Enumerated); + break; } } } diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 6704b1bf8e..66cb19431f 100755 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -135,6 +135,7 @@ #include "nsIMemoryReporter.h" #include "nsIMozBrowserFrame.h" #include "nsIMutable.h" +#include "nsINSSU2FToken.h" #include "nsIObserverService.h" #include "nsIPresShell.h" #include "nsIRemoteWindowContext.h" @@ -1985,14 +1986,6 @@ ContentParent::RecvDeallocateLayerTreeId(const uint64_t& aId) namespace { -void -DelayedDeleteSubprocess(GeckoChildProcessHost* aSubprocess) -{ - XRE_GetIOMessageLoop() - ->PostTask(FROM_HERE, - new DeleteTask(aSubprocess)); -} - // This runnable only exists to delegate ownership of the // ContentParent to this runnable, until it's deleted by the event // system. @@ -2124,10 +2117,10 @@ ContentParent::ActorDestroy(ActorDestroyReason why) } mIdleListeners.Clear(); - MessageLoop::current()-> - PostTask(FROM_HERE, - NewRunnableFunction(DelayedDeleteSubprocess, mSubprocess)); - mSubprocess = nullptr; + if (mSubprocess) { + mSubprocess->DissociateActor(); + mSubprocess = nullptr; + } // IPDL rules require actors to live on past ActorDestroy, but it // may be that the kungFuDeathGrip above is the last reference to @@ -3335,7 +3328,7 @@ PCompositorBridgeParent* ContentParent::AllocPCompositorBridgeParent(mozilla::ipc::Transport* aTransport, base::ProcessId aOtherProcess) { - return CompositorBridgeParent::Create(aTransport, aOtherProcess); + return CompositorBridgeParent::Create(aTransport, aOtherProcess, mSubprocess); } gfx::PVRManagerParent* @@ -3349,7 +3342,7 @@ PImageBridgeParent* ContentParent::AllocPImageBridgeParent(mozilla::ipc::Transport* aTransport, base::ProcessId aOtherProcess) { - return ImageBridgeParent::Create(aTransport, aOtherProcess); + return ImageBridgeParent::Create(aTransport, aOtherProcess, mSubprocess); } PBackgroundParent* @@ -4229,6 +4222,91 @@ ContentParent::RecvSetURITitle(const URIParams& uri, return true; } +bool +ContentParent::RecvNSSU2FTokenIsCompatibleVersion(const nsString& aVersion, + bool* aIsCompatible) +{ + MOZ_ASSERT(aIsCompatible); + + nsCOMPtr nssToken(do_GetService(NS_NSSU2FTOKEN_CONTRACTID)); + if (NS_WARN_IF(!nssToken)) { + return false; + } + + nsresult rv = nssToken->IsCompatibleVersion(aVersion, aIsCompatible); + return NS_SUCCEEDED(rv); +} + +bool +ContentParent::RecvNSSU2FTokenIsRegistered(nsTArray&& aKeyHandle, + bool* aIsValidKeyHandle) +{ + MOZ_ASSERT(aIsValidKeyHandle); + + nsCOMPtr nssToken(do_GetService(NS_NSSU2FTOKEN_CONTRACTID)); + if (NS_WARN_IF(!nssToken)) { + return false; + } + + nsresult rv = nssToken->IsRegistered(aKeyHandle.Elements(), aKeyHandle.Length(), + aIsValidKeyHandle); + return NS_SUCCEEDED(rv); +} + +bool +ContentParent::RecvNSSU2FTokenRegister(nsTArray&& aApplication, + nsTArray&& aChallenge, + nsTArray* aRegistration) +{ + MOZ_ASSERT(aRegistration); + + nsCOMPtr nssToken(do_GetService(NS_NSSU2FTOKEN_CONTRACTID)); + if (NS_WARN_IF(!nssToken)) { + return false; + } + uint8_t* buffer; + uint32_t bufferlen; + nsresult rv = nssToken->Register(aApplication.Elements(), aApplication.Length(), + aChallenge.Elements(), aChallenge.Length(), + &buffer, &bufferlen); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + MOZ_ASSERT(buffer); + aRegistration->ReplaceElementsAt(0, aRegistration->Length(), buffer, bufferlen); + free(buffer); + return NS_SUCCEEDED(rv); +} + +bool +ContentParent::RecvNSSU2FTokenSign(nsTArray&& aApplication, + nsTArray&& aChallenge, + nsTArray&& aKeyHandle, + nsTArray* aSignature) +{ + MOZ_ASSERT(aSignature); + + nsCOMPtr nssToken(do_GetService(NS_NSSU2FTOKEN_CONTRACTID)); + if (NS_WARN_IF(!nssToken)) { + return false; + } + uint8_t* buffer; + uint32_t bufferlen; + nsresult rv = nssToken->Sign(aApplication.Elements(), aApplication.Length(), + aChallenge.Elements(), aChallenge.Length(), + aKeyHandle.Elements(), aKeyHandle.Length(), + &buffer, &bufferlen); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + MOZ_ASSERT(buffer); + aSignature->ReplaceElementsAt(0, aSignature->Length(), buffer, bufferlen); + free(buffer); + return NS_SUCCEEDED(rv); +} + bool ContentParent::RecvGetSystemMemory(const uint64_t& aGetterId) { diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index c2914a2873..171ac7ba00 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -740,6 +740,21 @@ private: virtual bool DeallocPCrashReporterParent(PCrashReporterParent* crashreporter) override; + virtual bool RecvNSSU2FTokenIsCompatibleVersion(const nsString& aVersion, + bool* aIsCompatible) override; + + virtual bool RecvNSSU2FTokenIsRegistered(nsTArray&& aKeyHandle, + bool* aIsValidKeyHandle) override; + + virtual bool RecvNSSU2FTokenRegister(nsTArray&& aApplication, + nsTArray&& aChallenge, + nsTArray* aRegistration) override; + + virtual bool RecvNSSU2FTokenSign(nsTArray&& aApplication, + nsTArray&& aChallenge, + nsTArray&& aKeyHandle, + nsTArray* aSignature) override; + virtual bool RecvIsSecureURI(const uint32_t& aType, const URIParams& aURI, const uint32_t& aFlags, bool* aIsSecureURI) override; diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index f7c163c498..5adf0903b5 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -757,6 +757,50 @@ parent: sync PCrashReporter(NativeThreadId tid, uint32_t processType); + /** + * Is this token compatible with the provided version? + * + * |version| The offered version to test + * Returns |True| if the offered version is compatible + */ + sync NSSU2FTokenIsCompatibleVersion(nsString version) + returns (bool result); + + /** + * Return whether the provided KeyHandle belongs to this Token + * + * |keyHandle| Key Handle to evaluate. + * Returns |True| if the Key Handle is ours. + */ + sync NSSU2FTokenIsRegistered(uint8_t[] keyHandle) + returns (bool isValidKeyHandle); + + /** + * Generates a public/private keypair for the provided application + * and challenge, returning the pubkey, challenge response, and + * key handle in the registration data. + * + * |application| The FIDO Application data to associate with the key. + * |challenge| The Challenge to satisfy in the response. + * |registration| An array containing the pubkey, challenge response, + * and key handle. + */ + sync NSSU2FTokenRegister(uint8_t[] application, uint8_t[] challenge) + returns (uint8_t[] registration); + + /** + * Creates a signature over the "param" arguments using the private key + * provided in the key handle argument. + * + * |application| The FIDO Application data to associate with the key. + * |challenge| The Challenge to satisfy in the response. + * |keyHandle| The Key Handle opaque object to use. + * |signature| The resulting signature. + */ + sync NSSU2FTokenSign(uint8_t[] application, uint8_t[] challenge, + uint8_t[] keyHandle) + returns (uint8_t[] signature); + async GetSystemMemory(uint64_t getterId); sync IsSecureURI(uint32_t type, URIParams uri, uint32_t flags) diff --git a/dom/media/webrtc/MediaEngineTabVideoSource.cpp b/dom/media/webrtc/MediaEngineTabVideoSource.cpp index d31e9f8cd8..0264dd54a6 100644 --- a/dom/media/webrtc/MediaEngineTabVideoSource.cpp +++ b/dom/media/webrtc/MediaEngineTabVideoSource.cpp @@ -285,10 +285,11 @@ MediaEngineTabVideoSource::Draw() { size, stride, SurfaceFormat::B8G8R8X8); - if (!dt) { + if (!dt || !dt->IsValid()) { return; } - RefPtr context = new gfxContext(dt); + RefPtr context = gfxContext::ForDrawTarget(dt); + MOZ_ASSERT(context); // already checked the draw target above context->SetMatrix(context->CurrentMatrix().Scale((((float) size.width)/mViewportWidth), (((float) size.height)/mViewportHeight))); diff --git a/dom/u2f/NSSToken.cpp b/dom/u2f/NSSToken.cpp deleted file mode 100644 index 5412f4edce..0000000000 --- a/dom/u2f/NSSToken.cpp +++ /dev/null @@ -1,172 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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 "NSSToken.h" - -#include "nsNSSComponent.h" -#include "pk11pub.h" - -namespace mozilla { -namespace dom { - -const nsString NSSToken::mVersion = NS_LITERAL_STRING("U2F_V2"); - -const uint32_t kParamLen = 32; -const uint32_t kPublicKeyLen = 65; -const uint32_t kSignedDataLen = (2 * kParamLen) + 1 + 4; - -NSSToken::NSSToken() - : mInitialized(false) - , mMutex("NSSToken::mMutex") -{} - -NSSToken::~NSSToken() -{ - nsNSSShutDownPreventionLock locker; - - if (isAlreadyShutDown()) { - return; - } - - destructorSafeDestroyNSSReference(); - shutdown(calledFromObject); -} - -void -NSSToken::virtualDestroyNSSReference() -{ - destructorSafeDestroyNSSReference(); -} - -void -NSSToken::destructorSafeDestroyNSSReference() -{ - mSlot = nullptr; -} - -nsresult -NSSToken::Init() -{ - MOZ_ASSERT(!mInitialized); - if (mInitialized) { - return NS_OK; - } - - nsNSSShutDownPreventionLock locker; - if (isAlreadyShutDown()) { - return NS_ERROR_NOT_AVAILABLE; - } - - MutexAutoLock lock(mMutex); - - if (!EnsureNSSInitializedChromeOrContent()) { - return NS_ERROR_FAILURE; - } - - mSlot = PK11_GetInternalSlot(); - if (!mSlot.get()) { - return NS_ERROR_FAILURE; - } - - mInitialized = true; - return NS_OK; -} - -bool -NSSToken::IsCompatibleVersion(const nsString& aVersionParam) const -{ - MOZ_ASSERT(mInitialized); - return mVersion == aVersionParam; -} - -/* - * IsRegistered determines if the provided key handle is usable by this token. - */ -bool -NSSToken::IsRegistered(const CryptoBuffer& aKeyHandle) const -{ - MOZ_ASSERT(mInitialized); - return false; -} - -/* - * A U2F Register operation causes a new key pair to be generated by the token. - * The token then returns the public key of the key pair, and a handle to the - * private key. The input parameters are used only for attestation, which this - * token does not provide. (We'll see how that works!) - * - * The format of the return registration data is as follows: - * - * Bytes Value - * 1 0x05 - * 65 public key - * 1 key handle length - * * key handle - * * attestation certificate (omitted for now) - * * attestation signature (omitted for now) - * - */ -nsresult -NSSToken::Register(const CryptoBuffer& /* aChallengeParam */, - const CryptoBuffer& /* aApplicationParam */, - CryptoBuffer& aRegistrationData) -{ - MOZ_ASSERT(mInitialized); - nsNSSShutDownPreventionLock locker; - if (isAlreadyShutDown()) { - return NS_ERROR_NOT_AVAILABLE; - } - - MutexAutoLock lock(mMutex); - - if (!mInitialized) { - return NS_ERROR_NOT_INITIALIZED; - } - - return NS_OK; -} - -/* - * A U2F Sign operation creates a signature over the "param" arguments (plus - * some other stuff) using the private key indicated in the key handle argument. - * - * The format of the signed data is as follows: - * - * 32 Application parameter - * 1 User presence (0x01) - * 4 Counter - * 32 Challenge parameter - * - * The format of the signature data is as follows: - * - * 1 User presence - * 4 Counter - * * Signature - * - */ -nsresult -NSSToken::Sign(const CryptoBuffer& aApplicationParam, - const CryptoBuffer& aChallengeParam, - const CryptoBuffer& aKeyHandle, - CryptoBuffer& aSignatureData) -{ - MOZ_ASSERT(mInitialized); - nsNSSShutDownPreventionLock locker; - if (isAlreadyShutDown()) { - return NS_ERROR_NOT_AVAILABLE; - } - - MutexAutoLock lock(mMutex); - - if (!mInitialized) { - return NS_ERROR_NOT_INITIALIZED; - } - - return NS_OK; -} - -} // namespace dom -} // namespace mozilla diff --git a/dom/u2f/NSSToken.h b/dom/u2f/NSSToken.h deleted file mode 100644 index 8520e3a197..0000000000 --- a/dom/u2f/NSSToken.h +++ /dev/null @@ -1,57 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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_dom_NSSToken_h -#define mozilla_dom_NSSToken_h - -#include "mozilla/dom/CryptoBuffer.h" -#include "mozilla/Mutex.h" -#include "nsNSSShutDown.h" -#include "ScopedNSSTypes.h" - -namespace mozilla { -namespace dom { - -// NSSToken will support FIDO U2F operations using NSS for the crypto layer. -// This is a stub. It will be implemented in bug 1244960. -class NSSToken final : public nsNSSShutDownObject -{ -public: - NSSToken(); - - ~NSSToken(); - - nsresult Init(); - - bool IsCompatibleVersion(const nsString& aVersionParam) const; - - bool IsRegistered(const CryptoBuffer& aKeyHandle) const; - - nsresult Register(const CryptoBuffer& aApplicationParam, - const CryptoBuffer& aChallengeParam, - CryptoBuffer& aRegistrationData); - - nsresult Sign(const CryptoBuffer& aApplicationParam, - const CryptoBuffer& aChallengeParam, - const CryptoBuffer& aKeyHandle, - CryptoBuffer& aSignatureData); - - // For nsNSSShutDownObject - virtual void virtualDestroyNSSReference() override; - void destructorSafeDestroyNSSReference(); - -private: - bool mInitialized; - ScopedPK11SlotInfo mSlot; - mozilla::Mutex mMutex; - - static const nsString mVersion; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_NSSToken_h diff --git a/dom/u2f/U2F.cpp b/dom/u2f/U2F.cpp index 5a32de5d5f..94158c2351 100644 --- a/dom/u2f/U2F.cpp +++ b/dom/u2f/U2F.cpp @@ -4,16 +4,21 @@ * 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 "hasht.h" +#include "mozilla/dom/ContentChild.h" #include "mozilla/dom/CryptoBuffer.h" #include "mozilla/dom/U2F.h" #include "mozilla/dom/U2FBinding.h" #include "mozilla/Preferences.h" #include "nsContentUtils.h" #include "nsIEffectiveTLDService.h" -#include "nsURLParsers.h" #include "nsNetCID.h" +#include "nsNSSComponent.h" +#include "nsURLParsers.h" #include "pk11pub.h" +using mozilla::dom::ContentChild; + namespace mozilla { namespace dom { @@ -29,14 +34,14 @@ enum class ErrorCode { TIMEOUT = 5 }; -#define PREF_U2F_SOFTTOKEN_ENABLED "security.webauth.u2f.softtoken" -#define PREF_U2F_USBTOKEN_ENABLED "security.webauth.u2f.usbtoken" +#define PREF_U2F_SOFTTOKEN_ENABLED "security.webauth.u2f_enable_softtoken" +#define PREF_U2F_USBTOKEN_ENABLED "security.webauth.u2f_enable_usbtoken" -const nsString -U2F::FinishEnrollment = NS_LITERAL_STRING("navigator.id.finishEnrollment"); +const nsString U2F::FinishEnrollment = + NS_LITERAL_STRING("navigator.id.finishEnrollment"); -const nsString -U2F::GetAssertion = NS_LITERAL_STRING("navigator.id.getAssertion"); +const nsString U2F::GetAssertion = + NS_LITERAL_STRING("navigator.id.getAssertion"); NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(U2F) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY @@ -48,6 +53,8 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(U2F) NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(U2F, mParent) +static mozilla::LazyLogModule gU2FLog("fido_u2f"); + U2F::U2F() {} @@ -88,12 +95,17 @@ U2F::Init(nsPIDOMWindow* aParent, ErrorResult& aRv) } if (!EnsureNSSInitializedChromeOrContent()) { + MOZ_LOG(gU2FLog, LogLevel::Debug, ("Failed to get NSS context for U2F")); + aRv.Throw(NS_ERROR_FAILURE); return; } - aRv = mSoftToken.Init(); - if (NS_WARN_IF(aRv.Failed())) { - return; + if (XRE_IsParentProcess()) { + mNSSToken = do_GetService(NS_NSSU2FTOKEN_CONTRACTID); + if (NS_WARN_IF(!mNSSToken)) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } } aRv = mUSBToken.Init(); @@ -102,6 +114,111 @@ U2F::Init(nsPIDOMWindow* aParent, ErrorResult& aRv) } } +nsresult +U2F::NSSTokenIsCompatible(const nsString& aVersionString, bool* aIsCompatible) +{ + MOZ_ASSERT(aIsCompatible); + + if (XRE_IsParentProcess()) { + MOZ_ASSERT(mNSSToken); + return mNSSToken->IsCompatibleVersion(aVersionString, aIsCompatible); + } + + ContentChild* cc = ContentChild::GetSingleton(); + MOZ_ASSERT(cc); + if (!cc->SendNSSU2FTokenIsCompatibleVersion(aVersionString, aIsCompatible)) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +nsresult +U2F::NSSTokenIsRegistered(CryptoBuffer& aKeyHandle, bool* aIsRegistered) +{ + MOZ_ASSERT(aIsRegistered); + + if (XRE_IsParentProcess()) { + MOZ_ASSERT(mNSSToken); + return mNSSToken->IsRegistered(aKeyHandle.Elements(), aKeyHandle.Length(), + aIsRegistered); + } + + ContentChild* cc = ContentChild::GetSingleton(); + MOZ_ASSERT(cc); + if (!cc->SendNSSU2FTokenIsRegistered(aKeyHandle, aIsRegistered)) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +nsresult +U2F::NSSTokenRegister(CryptoBuffer& aApplication, CryptoBuffer& aChallenge, + CryptoBuffer& aRegistrationData) +{ + if (XRE_IsParentProcess()) { + MOZ_ASSERT(mNSSToken); + uint8_t* buffer; + uint32_t bufferlen; + nsresult rv; + rv = mNSSToken->Register(aApplication.Elements(), aApplication.Length(), + aChallenge.Elements(), aChallenge.Length(), + &buffer, &bufferlen); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(buffer); + aRegistrationData.Assign(buffer, bufferlen); + free(buffer); + return NS_OK; + } + + nsTArray registrationBuffer; + ContentChild* cc = ContentChild::GetSingleton(); + MOZ_ASSERT(cc); + if (!cc->SendNSSU2FTokenRegister(aApplication, aChallenge, + ®istrationBuffer)) { + return NS_ERROR_FAILURE; + } + + aRegistrationData.Assign(registrationBuffer); + return NS_OK; +} + +nsresult +U2F::NSSTokenSign(CryptoBuffer& aKeyHandle, CryptoBuffer& aApplication, + CryptoBuffer& aChallenge, CryptoBuffer& aSignatureData) +{ + if (XRE_IsParentProcess()) { + MOZ_ASSERT(mNSSToken); + uint8_t* buffer; + uint32_t bufferlen; + nsresult rv = mNSSToken->Sign(aApplication.Elements(), aApplication.Length(), + aChallenge.Elements(), aChallenge.Length(), + aKeyHandle.Elements(), aKeyHandle.Length(), + &buffer, &bufferlen); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(buffer); + aSignatureData.Assign(buffer, bufferlen); + free(buffer); + return NS_OK; + } + + nsTArray signatureBuffer; + ContentChild* cc = ContentChild::GetSingleton(); + MOZ_ASSERT(cc); + if (!cc->SendNSSU2FTokenSign(aApplication, aChallenge, aKeyHandle, + &signatureBuffer)) { + return NS_ERROR_FAILURE; + } + + aSignatureData.Assign(signatureBuffer); + return NS_OK; +} + nsresult U2F::AssembleClientData(const nsAString& aTyp, const nsAString& aChallenge, @@ -189,24 +306,6 @@ U2F::ValidAppID(/* in/out */ nsString& aAppId) const return true; } - nsAutoCString appIdTld; - nsAutoCString facetTld; - - rv = tldService->GetBaseDomainFromHost(appIdAuth, 0, appIdTld); - if (NS_WARN_IF(NS_FAILED(rv))) { - return false; - } - rv = tldService->GetBaseDomainFromHost(facetAuth, 0, facetTld); - if (NS_WARN_IF(NS_FAILED(rv))) { - return false; - } - - // If this AppID's registered domain matches the Facet's, accept - if (!facetTld.IsEmpty() && !appIdTld.IsEmpty() && - (facetTld == appIdTld)) { - return true; - } - // TODO(Bug 1244959) Implement the remaining algorithm. return false; } @@ -258,7 +357,7 @@ U2F::Register(const nsAString& aAppId, for (size_t i = 0; i < aRegisteredKeys.Length(); ++i) { RegisteredKey request(aRegisteredKeys[i]); - // Check for equired attributes + // Check for required attributes if (!(request.mKeyHandle.WasPassed() && request.mVersion.WasPassed())) { continue; @@ -282,6 +381,9 @@ U2F::Register(const nsAString& aAppId, // We ignore mTransports, as it is intended to be used for sorting the // available devices by preference, but is not an exclusion factor. + bool isCompatible = false; + bool isRegistered = false; + // Determine if the provided keyHandle is registered at any device. If so, // then we'll return DEVICE_INELIGIBLE to signify we're already registered. if (usbTokenEnabled && @@ -291,13 +393,26 @@ U2F::Register(const nsAString& aAppId, ErrorCode::DEVICE_INELIGIBLE); return; } + if (softTokenEnabled) { + rv = NSSTokenIsCompatible(request.mVersion.Value(), &isCompatible); + if (NS_FAILED(rv)) { + SendError(aCallback, + ErrorCode::OTHER_ERROR); + return; + } - if (softTokenEnabled && - mSoftToken.IsCompatibleVersion(request.mVersion.Value()) && - mSoftToken.IsRegistered(keyHandle)) { - SendError(aCallback, - ErrorCode::DEVICE_INELIGIBLE); - return; + rv = NSSTokenIsRegistered(keyHandle, &isRegistered); + if (NS_FAILED(rv)) { + SendError(aCallback, + ErrorCode::OTHER_ERROR); + return; + } + + if (isCompatible && isRegistered) { + SendError(aCallback, + ErrorCode::DEVICE_INELIGIBLE); + return; + } } } @@ -353,28 +468,31 @@ U2F::Register(const nsAString& aAppId, // Get the registration data from the token CryptoBuffer registrationData; bool registerSuccess = false; - - if (usbTokenEnabled && - mUSBToken.IsCompatibleVersion(request.mVersion.Value())) { - rv = mUSBToken.Register(opt_aTimeoutSeconds, challengeParam, - appParam, registrationData); - if (NS_WARN_IF(NS_FAILED(rv))) { - SendError(aCallback, - ErrorCode::OTHER_ERROR); - return; - } - registerSuccess = true; + bool isCompatible = false; + if (usbTokenEnabled) { + // TODO: Implement in Bug 1245527 + SendError(aCallback, + ErrorCode::OTHER_ERROR); + return; } - if (!registerSuccess && softTokenEnabled && - mSoftToken.IsCompatibleVersion(request.mVersion.Value())) { - rv = mSoftToken.Register(challengeParam, appParam, registrationData); - if (NS_WARN_IF(NS_FAILED(rv))) { + if (!registerSuccess && softTokenEnabled) { + rv = NSSTokenIsCompatible(request.mVersion.Value(), &isCompatible); + if (NS_FAILED(rv)) { SendError(aCallback, ErrorCode::OTHER_ERROR); return; } - registerSuccess = true; + + if (isCompatible) { + rv = NSSTokenRegister(appParam, challengeParam, registrationData); + if (NS_FAILED(rv)) { + SendError(aCallback, + ErrorCode::OTHER_ERROR); + return; + } + registerSuccess = true; + } } if (!registerSuccess) { @@ -521,25 +639,39 @@ U2F::Sign(const nsAString& aAppId, if (usbTokenEnabled && mUSBToken.IsCompatibleVersion(request.mVersion.Value())) { - rv = mUSBToken.Sign(opt_aTimeoutSeconds, appParam, challengeParam, - keyHandle, signatureData); - if (NS_WARN_IF(NS_FAILED(rv))) { - SendError(aCallback, - ErrorCode::OTHER_ERROR); - return; - } - signSuccess = true; + // TODO: Implement in Bug 1245527 + SendError(aCallback, + ErrorCode::OTHER_ERROR); + return; } - if (!signSuccess && softTokenEnabled && - mSoftToken.IsCompatibleVersion(request.mVersion.Value())) { - rv = mSoftToken.Sign(appParam, challengeParam, keyHandle, signatureData); - if (NS_WARN_IF(NS_FAILED(rv))) { + if (!signSuccess && softTokenEnabled) { + bool isCompatible = false; + bool isRegistered = false; + + rv = NSSTokenIsCompatible(request.mVersion.Value(), &isCompatible); + if (NS_FAILED(rv)) { SendError(aCallback, ErrorCode::OTHER_ERROR); return; } - signSuccess = true; + + rv = NSSTokenIsRegistered(keyHandle, &isRegistered); + if (NS_FAILED(rv)) { + SendError(aCallback, + ErrorCode::OTHER_ERROR); + return; + } + + if (isCompatible && isRegistered) { + rv = NSSTokenSign(keyHandle, appParam, challengeParam, signatureData); + if (NS_FAILED(rv)) { + SendError(aCallback, + ErrorCode::OTHER_ERROR); + return; + } + signSuccess = true; + } } if (!signSuccess) { diff --git a/dom/u2f/U2F.h b/dom/u2f/U2F.h index 6f0f74a787..87a6622321 100644 --- a/dom/u2f/U2F.h +++ b/dom/u2f/U2F.h @@ -13,10 +13,11 @@ #include "mozilla/dom/Nullable.h" #include "mozilla/ErrorResult.h" #include "nsCycleCollectionParticipant.h" +#include "nsINSSU2FToken.h" +#include "nsNSSShutDown.h" #include "nsPIDOMWindow.h" #include "nsWrapperCache.h" -#include "NSSToken.h" #include "USBToken.h" namespace mozilla { @@ -78,8 +79,8 @@ public: private: nsCOMPtr mParent; nsString mOrigin; - NSSToken mSoftToken; USBToken mUSBToken; + nsCOMPtr mNSSToken; static const nsString FinishEnrollment; static const nsString GetAssertion; @@ -98,6 +99,20 @@ private: // for a description of the algorithm. bool ValidAppID(/* in/out */ nsString& aAppId) const; + + nsresult + NSSTokenIsCompatible(const nsString& versionString, bool* isCompatible); + + nsresult + NSSTokenIsRegistered(CryptoBuffer& keyHandle, bool* isRegistered); + + nsresult + NSSTokenRegister(CryptoBuffer& application, CryptoBuffer& challenge, + CryptoBuffer& registrationData); + + nsresult + NSSTokenSign(CryptoBuffer& keyHandle, CryptoBuffer& application, + CryptoBuffer& challenge, CryptoBuffer& signatureData); }; } // namespace dom diff --git a/dom/u2f/moz.build b/dom/u2f/moz.build index eba8eee4d0..f01595c891 100644 --- a/dom/u2f/moz.build +++ b/dom/u2f/moz.build @@ -5,17 +5,17 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. EXPORTS.mozilla.dom += [ - 'NSSToken.h', 'U2F.h', 'USBToken.h', ] UNIFIED_SOURCES += [ - 'NSSToken.cpp', 'U2F.cpp', 'USBToken.cpp', ] +include('/ipc/chromium/chromium-config.mozbuild') + FINAL_LIBRARY = 'xul' LOCAL_INCLUDES += [ diff --git a/dom/u2f/tests/frame_no_token.html b/dom/u2f/tests/frame_no_token.html new file mode 100644 index 0000000000..2c07ede204 --- /dev/null +++ b/dom/u2f/tests/frame_no_token.html @@ -0,0 +1,27 @@ + + + + Test for FIDO Universal Second Factor No Token + + + + + + + + + diff --git a/dom/u2f/tests/mochitest.ini b/dom/u2f/tests/mochitest.ini index 7af5dc1342..a39e92eb75 100644 --- a/dom/u2f/tests/mochitest.ini +++ b/dom/u2f/tests/mochitest.ini @@ -1,8 +1,10 @@ [DEFAULT] support-files = + frame_no_token.html u2futil.js test_frame_appid_facet.html test_frame_register.html + test_frame_register_sign.html test_frame_appid_facet_remoteload.html test_frame_appid_facet_insecure.html test_frame_appid_facet_subdomain.html @@ -13,6 +15,10 @@ support-files = facet/facetList-no_overlap^headers^ facet/facetList-invalid_format facet/facetList-invalid_format^headers^ + pkijs/common.js + pkijs/asn1.js + pkijs/x509_schema.js + pkijs/x509_simpl.js [test_util_methods.html] [test_no_token.html] diff --git a/dom/u2f/tests/pkijs/LICENSE b/dom/u2f/tests/pkijs/LICENSE new file mode 100644 index 0000000000..4f71696a7d --- /dev/null +++ b/dom/u2f/tests/pkijs/LICENSE @@ -0,0 +1,30 @@ +Copyright (c) 2014, GMO GlobalSign +Copyright (c) 2015, Peculiar Ventures +All rights reserved. + +Author 2014-2015, Yury Strozhevsky + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/dom/u2f/tests/pkijs/README b/dom/u2f/tests/pkijs/README new file mode 100644 index 0000000000..9213c9d438 --- /dev/null +++ b/dom/u2f/tests/pkijs/README @@ -0,0 +1 @@ +PKIjs and ASN1js are from https://pkijs.org/ and https://asn1js.org/. \ No newline at end of file diff --git a/dom/u2f/tests/pkijs/asn1.js b/dom/u2f/tests/pkijs/asn1.js new file mode 100644 index 0000000000..ddee052407 --- /dev/null +++ b/dom/u2f/tests/pkijs/asn1.js @@ -0,0 +1,5466 @@ +/* + * Copyright (c) 2014, GMO GlobalSign + * Copyright (c) 2015, Peculiar Ventures + * All rights reserved. + * + * Author 2014-2015, Yury Strozhevsky . + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + */ +( +function(in_window) +{ + //************************************************************************************** + // #region Declaration of global variables + //************************************************************************************** + // #region "org" namespace + if(typeof in_window.org === "undefined") + in_window.org = {}; + else + { + if(typeof in_window.org !== "object") + throw new Error("Name org already exists and it's not an object"); + } + // #endregion + + // #region "org.pkijs" namespace + if(typeof in_window.org.pkijs === "undefined") + in_window.org.pkijs = {}; + else + { + if(typeof in_window.org.pkijs !== "object") + throw new Error("Name org.pkijs already exists and it's not an object" + " but " + (typeof in_window.org.pkijs)); + } + // #endregion + + // #region "org.pkijs.asn1" namespace + if(typeof in_window.org.pkijs.asn1 === "undefined") + in_window.org.pkijs.asn1 = {}; + else + { + if(typeof in_window.org.pkijs.asn1 !== "object") + throw new Error("Name org.pkijs.asn1 already exists and it's not an object" + " but " + (typeof in_window.org.pkijs.asn1)); + } + // #endregion + + // #region "local" namespace + var local = {}; + // #endregion + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Aux-functions + //************************************************************************************** + function util_frombase(input_buffer, input_base) + { + /// Convert number from 2^base to 2^10 + /// Array of bytes representing the number to convert + /// The base of initial number + + var result = 0; + + for(var i = (input_buffer.length - 1); i >= 0; i-- ) + result += input_buffer[(input_buffer.length - 1) - i] * Math.pow(2, input_base * i); + + return result; + } + //************************************************************************************** + function util_tobase(value, base, reserved) + { + /// Convert number from 2^10 to 2^base + /// The number to convert + /// The base for 2^base + /// Pre-defined number of bytes in output array (-1 = limited by function itself) + + reserved = reserved || (-1); + + var result = 0; + var biggest = Math.pow(2, base); + + for(var i = 1; i < 8; i++) + { + if(value < biggest) + { + var ret_buf; + + if( reserved < 0 ) + { + ret_buf = new ArrayBuffer(i); + result = i; + } + else + { + if(reserved < i) + return (new ArrayBuffer(0)); + + ret_buf = new ArrayBuffer(reserved); + + result = reserved; + } + + var ret_view = new Uint8Array(ret_buf); + + for(var j = ( i - 1 ); j >= 0; j-- ) + { + var basis = Math.pow(2, j * base); + + ret_view[ result - j - 1 ] = Math.floor( value / basis ); + value -= ( ret_view[ result - j - 1 ] ) * basis; + } + + return ret_buf; + } + + biggest *= Math.pow(2, base); + } + } + //************************************************************************************** + function util_encode_tc(value) + { + /// Encode integer value to "two complement" format + /// Value to encode + + var mod_value = (value < 0) ? (value * (-1)) : value; + var big_int = 128; + + for(var i = 1; i < 8; i++) + { + if( mod_value <= big_int ) + { + if( value < 0 ) + { + var small_int = big_int - mod_value; + + var ret_buf = util_tobase( small_int, 8, i ); + var ret_view = new Uint8Array(ret_buf); + + ret_view[ 0 ] |= 0x80; + + return ret_buf; + } + else + { + var ret_buf = util_tobase( mod_value, 8, i ); + var ret_view = new Uint8Array(ret_buf); + + if( ret_view[ 0 ] & 0x80 ) + { + var temp_buf = util_copybuf(ret_buf); + var temp_view = new Uint8Array(temp_buf); + + ret_buf = new ArrayBuffer( ret_buf.byteLength + 1 ); + ret_view = new Uint8Array(ret_buf); + + for(var k = 0; k < temp_buf.byteLength; k++) + ret_view[k + 1] = temp_view[k]; + + ret_view[0] = 0x00; + } + + return ret_buf; + } + } + + big_int *= Math.pow(2, 8); + } + + return (new ArrayBuffer(0)); + } + //************************************************************************************** + function util_decode_tc() + { + /// Decoding of "two complement" values + /// The function must be called in scope of instance of "hex_block" class ("value_hex" and "warnings" properties must be present) + + var buf = new Uint8Array(this.value_hex); + + if(this.value_hex.byteLength >= 2) + { + var condition_1 = (buf[0] == 0xFF) && (buf[1] & 0x80); + var condition_2 = (buf[0] == 0x00) && ((buf[1] & 0x80) == 0x00); + + if(condition_1 || condition_2) + this.warnings.push("Needlessly long format"); + } + + // #region Create big part of the integer + var big_int_buffer = new ArrayBuffer(this.value_hex.byteLength); + var big_int_view = new Uint8Array(big_int_buffer); + for(var i = 0; i < this.value_hex.byteLength; i++) + big_int_view[i] = 0; + + big_int_view[0] = (buf[0] & 0x80); // mask only the biggest bit + + var big_int = util_frombase(big_int_view, 8); + // #endregion + + // #region Create small part of the integer + var small_int_buffer = new ArrayBuffer(this.value_hex.byteLength); + var small_int_view = new Uint8Array(small_int_buffer); + for(var j = 0; j < this.value_hex.byteLength; j++) + small_int_view[j] = buf[j]; + + small_int_view[0] &= 0x7F; // mask biggest bit + + var small_int = util_frombase(small_int_view, 8); + // #endregion + + return (small_int - big_int); + } + //************************************************************************************** + function util_copybuf(input_buffer) + { + /// Creating a copy of input ArrayBuffer + /// ArrayBuffer for coping + + if(check_buffer_params(input_buffer, 0, input_buffer.byteLength) === false) + return (new ArrayBuffer(0)); + + var input_view = new Uint8Array(input_buffer); + + var ret_buf = new ArrayBuffer(input_buffer.byteLength); + var ret_view = new Uint8Array(ret_buf); + + for(var i = 0; i < input_buffer.byteLength; i++) + ret_view[i] = input_view[i]; + + return ret_buf; + } + //************************************************************************************** + function util_copybuf_offset(input_buffer, input_offset, input_length) + { + /// Creating a copy of input ArrayBuffer + /// ArrayBuffer for coping + + if(check_buffer_params(input_buffer, input_offset, input_length) === false) + return (new ArrayBuffer(0)); + + var input_view = new Uint8Array(input_buffer, input_offset, input_length); + + var ret_buf = new ArrayBuffer(input_length); + var ret_view = new Uint8Array(ret_buf); + + for(var i = 0; i < input_length; i++) + ret_view[i] = input_view[i]; + + return ret_buf; + } + //************************************************************************************** + function util_concatbuf(input_buf1, input_buf2) + { + /// Concatenate two ArrayBuffers + /// First ArrayBuffer (first part of concatenated array) + /// Second ArrayBuffer (second part of concatenated array) + + var input_view1 = new Uint8Array(input_buf1); + var input_view2 = new Uint8Array(input_buf2); + + var ret_buf = new ArrayBuffer(input_buf1.byteLength + input_buf2.byteLength); + var ret_view = new Uint8Array(ret_buf); + + for(var i = 0; i < input_buf1.byteLength; i++) + ret_view[i] = input_view1[i]; + + for(var j = 0; j < input_buf2.byteLength; j++) + ret_view[input_buf1.byteLength + j] = input_view2[j]; + + return ret_buf; + } + //************************************************************************************** + function check_buffer_params(input_buffer, input_offset, input_length) + { + if((input_buffer instanceof ArrayBuffer) === false) + { + this.error = "Wrong parameter: input_buffer must be \"ArrayBuffer\""; + return false; + } + + if(input_buffer.byteLength === 0) + { + this.error = "Wrong parameter: input_buffer has zero length"; + return false; + } + + if(input_offset < 0) + { + this.error = "Wrong parameter: input_offset less than zero"; + return false; + } + + if(input_length < 0) + { + this.error = "Wrong parameter: input_length less than zero"; + return false; + } + + if((input_buffer.byteLength - input_offset - input_length) < 0) + { + this.error = "End of input reached before message was fully decoded (inconsistent offset and length values)"; + return false; + } + + return true; + } + //************************************************************************************** + function to_hex_codes(input_buffer, input_offset, input_lenght) + { + if(check_buffer_params(input_buffer, input_offset, input_lenght) === false) + return ""; + + var result = ""; + + var int_buffer = new Uint8Array(input_buffer, input_offset, input_lenght); + + for(var i = 0; i < int_buffer.length; i++) + { + var str = int_buffer[i].toString(16).toUpperCase(); + result = result + ((str.length === 1) ? " 0" : " ") + str; + } + + return result; + } + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Declaration of base block class + //************************************************************************************** + local.base_block = + function() + { + /// General class of all ASN.1 blocks + + if(arguments[0] instanceof Object) + { + this.block_length = in_window.org.pkijs.getValue(arguments[0], "block_length", 0); + this.error = in_window.org.pkijs.getValue(arguments[0], "error", new String()); + this.warnings = in_window.org.pkijs.getValue(arguments[0], "warnings", new Array()); + if("value_before_decode" in arguments[0]) + this.value_before_decode = util_copybuf(arguments[0].value_before_decode); + else + this.value_before_decode = new ArrayBuffer(0); + } + else + { + this.block_length = 0; + this.error = new String(); + this.warnings = new Array(); + /// Copy of the value of incoming ArrayBuffer done before decoding + this.value_before_decode = new ArrayBuffer(0); + } + }; + //************************************************************************************** + local.base_block.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "base_block"; + }; + //************************************************************************************** + local.base_block.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + return { + block_name: local.base_block.prototype.block_name.call(this), + block_length: this.block_length, + error: this.error, + warnings: this.warnings, + value_before_decode: in_window.org.pkijs.bufferToHexCodes(this.value_before_decode, 0, this.value_before_decode.byteLength) + }; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Declaration of hex block class + //************************************************************************************** + local.hex_block = + function() + { + /// Descendant of "base_block" with internal ArrayBuffer. Need to have it in case it is not possible to store ASN.1 value in native formats + + local.base_block.call(this, arguments[0]); + + if(arguments[0] instanceof Object) + { + this.is_hex_only = in_window.org.pkijs.getValue(arguments[0], "is_hex_only", false); + if("value_hex" in arguments[0]) + this.value_hex = util_copybuf(arguments[0].value_hex); + else + this.value_hex = new ArrayBuffer(0); + } + else + { + this.is_hex_only = false; + this.value_hex = new ArrayBuffer(0); + } + }; + //************************************************************************************** + local.hex_block.prototype = new local.base_block(); + local.hex_block.constructor = local.hex_block; + //************************************************************************************** + local.hex_block.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "hex_block"; + }; + //************************************************************************************** + local.hex_block.prototype.fromBER = + function(input_buffer, input_offset, input_length) + { + /// Base function for converting block from BER encoded array of bytes + /// ASN.1 BER encoded array + /// Offset in ASN.1 BER encoded array where decoding should be started + /// Maximum length of array of bytes which can be using in this function + + // #region Basic check for parameters + if(check_buffer_params.call(this, input_buffer, input_offset, input_length) === false) + return (-1); + // #endregion + + // #region Getting Uint8Array from ArrayBuffer + var int_buffer = new Uint8Array(input_buffer, input_offset, input_length); + // #endregion + + // #region Initial checks + if(int_buffer.length == 0) + { + this.warnings.push("Zero buffer length"); + return input_offset; + } + // #endregion + + // #region Copy input buffer to internal buffer + this.value_hex = new ArrayBuffer(input_length); + var view = new Uint8Array(this.value_hex); + + for(var i = 0; i < int_buffer.length; i++) + view[i] = int_buffer[i]; + // #endregion + + this.block_length = input_length; + + return (input_offset + input_length); + }; + //************************************************************************************** + local.hex_block.prototype.toBER = + function(size_only) + { + /// Encoding of current ASN.1 block into ASN.1 encoded array (BER rules) + /// Flag that we need only a size of encoding, not a real array of bytes + + if(typeof size_only === "undefined") + size_only = false; + + if(this.is_hex_only !== true) + { + this.error = "Flag \"is_hex_only\" is not set, abort"; + return (new ArrayBuffer(0)); + } + + var ret_buf = new ArrayBuffer(this.value_hex.byteLength); + + if(size_only === true) + return ret_buf; + + var ret_view = new Uint8Array(ret_buf); + var cur_view = new Uint8Array(this.value_hex); + + for(var i = 0; i < cur_view.length; i++) + ret_view[i] = cur_view[i]; + + return ret_buf; + }; + //************************************************************************************** + local.hex_block.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = local.base_block.prototype.toJSON.call(this); + + _object.block_name = local.hex_block.prototype.block_name.call(this); + _object.is_hex_only = this.is_hex_only; + _object.value_hex = in_window.org.pkijs.bufferToHexCodes(this.value_hex, 0, this.value_hex.byteLength); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Declaration of identification block class + //************************************************************************************** + local.identification_block = + function() + { + /// Base class of ASN.1 "identification block" + + local.hex_block.call(this, arguments[0]); + + this.tag_class = (-1); + this.tag_number = (-1); + this.is_constructed = false; + + if(arguments[0] instanceof Object) + { + if("id_block" in arguments[0]) + { + // #region Properties from hex_block class + this.is_hex_only = in_window.org.pkijs.getValue(arguments[0].id_block, "is_hex_only", false); + this.value_hex = in_window.org.pkijs.getValue(arguments[0].id_block, "value_hex", new ArrayBuffer(0)); + // #endregion + + this.tag_class = in_window.org.pkijs.getValue(arguments[0].id_block, "tag_class", (-1)); + this.tag_number = in_window.org.pkijs.getValue(arguments[0].id_block, "tag_number", (-1)); + this.is_constructed = in_window.org.pkijs.getValue(arguments[0].id_block, "is_constructed", false); + } + } + }; + //************************************************************************************** + local.identification_block.prototype = new local.hex_block(); + local.identification_block.constructor = local.identification_block; + //************************************************************************************** + local.identification_block.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "identification_block"; + }; + //************************************************************************************** + local.identification_block.prototype.toBER = + function(size_only) + { + /// Encoding of current ASN.1 block into ASN.1 encoded array (BER rules) + /// Flag that we need only a size of encoding, not a real array of bytes + + if(typeof size_only === "undefined") + size_only = false; + + var first_octet = 0; + + switch(this.tag_class) + { + case 1: + first_octet |= 0x00; // UNIVERSAL + break; + case 2: + first_octet |= 0x40; // APPLICATION + break; + case 3: + first_octet |= 0x80; // CONTEXT-SPECIFIC + break; + case 4: + first_octet |= 0xC0; // PRIVATE + break; + default: + this.error = "Unknown tag class"; + return (new ArrayBuffer(0)); + } + + if(this.is_constructed) + first_octet |= 0x20; + + if((this.tag_number < 31) && (!this.is_hex_only)) + { + var ret_buf = new ArrayBuffer(1); + var ret_view = new Uint8Array(ret_buf); + + if(!size_only) + { + var number = this.tag_number; + number &= 0x1F; + first_octet |= number; + + ret_view[0] = first_octet; + } + + return ret_buf; + } + else + { + if(this.is_hex_only === false) + { + var encoded_buf = util_tobase(this.tag_number, 7); + var encoded_view = new Uint8Array(encoded_buf); + var size = encoded_buf.byteLength; + + var ret_buf = new ArrayBuffer(size + 1); + var ret_view = new Uint8Array(ret_buf); + + ret_view[0] = (first_octet | 0x1F); + + if(!size_only) + { + for(var i = 0; i < (size - 1) ; i++) + ret_view[i + 1] = encoded_view[i] | 0x80; + + ret_view[size] = encoded_view[size - 1]; + } + + return ret_buf; + } + else + { + var ret_buf = new ArrayBuffer(this.value_hex.byteLength + 1); + var ret_view = new Uint8Array(ret_buf); + + ret_view[0] = (first_octet | 0x1F); + + if(size_only === false) + { + var cur_view = new Uint8Array(this.value_hex); + + for(var i = 0; i < (cur_view.length - 1); i++) + ret_view[i + 1] = cur_view[i] | 0x80; + + ret_view[this.value_hex.byteLength] = cur_view[cur_view.length - 1]; + } + + return ret_buf; + } + } + }; + //************************************************************************************** + local.identification_block.prototype.fromBER = + function(input_buffer, input_offset, input_length) + { + /// Base function for converting block from BER encoded array of bytes + /// ASN.1 BER encoded array + /// Offset in ASN.1 BER encoded array where decoding should be started + /// Maximum length of array of bytes which can be using in this function + + // #region Basic check for parameters + if(check_buffer_params.call(this, input_buffer, input_offset, input_length) === false) + return (-1); + // #endregion + + // #region Getting Uint8Array from ArrayBuffer + var int_buffer = new Uint8Array(input_buffer, input_offset, input_length); + // #endregion + + // #region Initial checks + if(int_buffer.length == 0) + { + this.error = "Zero buffer length"; + return (-1); + } + // #endregion + + // #region Find tag class + var tag_class_mask = int_buffer[0] & 0xC0; + + switch(tag_class_mask) + { + case 0x00: + this.tag_class = (1); // UNIVERSAL + break; + case 0x40: + this.tag_class = (2); // APPLICATION + break; + case 0x80: + this.tag_class = (3); // CONTEXT-SPECIFIC + break; + case 0xC0: + this.tag_class = (4); // PRIVATE + break; + default: + this.error = "Unknown tag class"; + return ( -1 ); + } + // #endregion + + // #region Find it's constructed or not + this.is_constructed = (int_buffer[0] & 0x20) == 0x20; + // #endregion + + // #region Find tag number + this.is_hex_only = false; + + var tag_number_mask = int_buffer[0] & 0x1F; + + // #region Simple case (tag number < 31) + if(tag_number_mask != 0x1F) + { + this.tag_number = (tag_number_mask); + this.block_length = 1; + } + // #endregion + // #region Tag number bigger or equal to 31 + else + { + var count = 1; + + this.value_hex = new ArrayBuffer(255); + var tag_number_buffer_max_length = 255; + var int_tag_number_buffer = new Uint8Array(this.value_hex); + + while(int_buffer[count] & 0x80) + { + int_tag_number_buffer[count - 1] = int_buffer[count] & 0x7F; + count++; + + if(count >= int_buffer.length) + { + this.error = "End of input reached before message was fully decoded"; + return (-1); + } + + // #region In case if tag number length is greater than 255 bytes (rare but possible case) + if(count == tag_number_buffer_max_length) + { + tag_number_buffer_max_length += 255; + + var temp_buffer = new ArrayBuffer(tag_number_buffer_max_length); + var temp_buffer_view = new Uint8Array(temp_buffer); + + for(var i = 0; i < int_tag_number_buffer.length; i++) + temp_buffer_view[i] = int_tag_number_buffer[i]; + + this.value_hex = new ArrayBuffer(tag_number_buffer_max_length); + int_tag_number_buffer = new Uint8Array(this.value_hex); + } + // #endregion + } + + this.block_length = (count + 1); + int_tag_number_buffer[count - 1] = int_buffer[count] & 0x7F; // Write last byte to buffer + + // #region Cut buffer + var temp_buffer = new ArrayBuffer(count); + var temp_buffer_view = new Uint8Array(temp_buffer); + for(var i = 0; i < count; i++) + temp_buffer_view[i] = int_tag_number_buffer[i]; + + this.value_hex = new ArrayBuffer(count); + int_tag_number_buffer = new Uint8Array(this.value_hex); + int_tag_number_buffer.set(temp_buffer_view); + // #endregion + + // #region Try to convert long tag number to short form + if(this.block_length <= 9) + this.tag_number = util_frombase(int_tag_number_buffer, 7); + else + { + this.is_hex_only = true; + this.warnings.push("Tag too long, represented as hex-coded"); + } + // #endregion + } + // #endregion + // #endregion + + // #region Check if constructed encoding was using for primitive type + if(((this.tag_class == 1)) && + (this.is_constructed)) + { + switch(this.tag_number) + { + case 1: // BOOLEAN + case 2: // REAL + case 5: // NULL + case 6: // OBJECT IDENTIFIER + case 9: // REAL + case 14: // TIME + case 23: + case 24: + case 31: + case 32: + case 33: + case 34: + this.error = "Constructed encoding used for primitive type"; + return (-1); + default: + ; + } + } + // #endregion + + return ( input_offset + this.block_length ); // Return current offset in input buffer + }; + //************************************************************************************** + local.identification_block.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = local.hex_block.prototype.toJSON.call(this); + + _object.block_name = local.identification_block.prototype.block_name.call(this); + _object.tag_class = this.tag_class; + _object.tag_number = this.tag_number; + _object.is_constructed = this.is_constructed; + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Declaration of length block class + //************************************************************************************** + local.length_block = + function() + { + /// Base class of ASN.1 "length block" + + local.base_block.call(this, arguments[0]); + + this.is_indefinite_form = false; + this.long_form_used = false; + this.length = (0); + + if(arguments[0] instanceof Object) + { + if("len_block" in arguments[0]) + { + this.is_indefinite_form = in_window.org.pkijs.getValue(arguments[0].len_block, "is_indefinite_form", false); + this.long_form_used = in_window.org.pkijs.getValue(arguments[0].len_block, "long_form_used", false); + this.length = in_window.org.pkijs.getValue(arguments[0].len_block, "length", 0); + } + } + }; + //************************************************************************************** + local.length_block.prototype = new local.base_block(); + local.length_block.constructor = local.length_block; + //************************************************************************************** + local.length_block.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "length_block"; + }; + //************************************************************************************** + local.length_block.prototype.fromBER = + function(input_buffer, input_offset, input_length) + { + /// Base function for converting block from BER encoded array of bytes + /// ASN.1 BER encoded array + /// Offset in ASN.1 BER encoded array where decoding should be started + /// Maximum length of array of bytes which can be using in this function + + // #region Basic check for parameters + if(check_buffer_params.call(this, input_buffer, input_offset, input_length) === false) + return (-1); + // #endregion + + // #region Getting Uint8Array from ArrayBuffer + var int_buffer = new Uint8Array(input_buffer, input_offset, input_length); + // #endregion + + // #region Initial checks + if(int_buffer.length == 0) + { + this.error = "Zero buffer length"; + return (-1); + } + + if(int_buffer[0] == 0xFF) + { + this.error = "Length block 0xFF is reserved by standard"; + return (-1); + } + // #endregion + + // #region Check for length form type + this.is_indefinite_form = int_buffer[0] == 0x80; + // #endregion + + // #region Stop working in case of indefinite length form + if(this.is_indefinite_form == true) + { + this.block_length = 1; + return (input_offset + this.block_length); + } + // #endregion + + // #region Check is long form of length encoding using + this.long_form_used = !!(int_buffer[0] & 0x80); + // #endregion + + // #region Stop working in case of short form of length value + if(this.long_form_used == false) + { + this.length = (int_buffer[0]); + this.block_length = 1; + return (input_offset + this.block_length); + } + // #endregion + + // #region Calculate length value in case of long form + var count = int_buffer[0] & 0x7F; + + if(count > 8) // Too big length value + { + this.error = "Too big integer"; + return (-1); + } + + if((count + 1) > int_buffer.length) + { + this.error = "End of input reached before message was fully decoded"; + return (-1); + } + + var length_buffer_view = new Uint8Array(count); + + for(var i = 0; i < count; i++) + length_buffer_view[i] = int_buffer[i + 1]; + + if(length_buffer_view[count - 1] == 0x00) + this.warnings.push("Needlessly long encoded length"); + + this.length = util_frombase(length_buffer_view, 8); + + if(this.long_form_used && (this.length <= 127)) + this.warnings.push("Unneccesary usage of long length form"); + + this.block_length = count + 1; + // #endregion + + return (input_offset + this.block_length); // Return current offset in input buffer + }; + //************************************************************************************** + local.length_block.prototype.toBER = + function(size_only) + { + /// Encoding of current ASN.1 block into ASN.1 encoded array (BER rules) + /// Flag that we need only a size of encoding, not a real array of bytes + + if(typeof size_only === "undefined") + size_only = false; + + if(this.length > 127) + this.long_form_used = true; + + if(this.is_indefinite_form) + { + var ret_buf = new ArrayBuffer(1); + + if(size_only === false) + { + var ret_view = new Uint8Array(ret_buf); + ret_view[0] = 0x80; + } + + return ret_buf; + } + + if(this.long_form_used === true) + { + var encoded_buf = util_tobase(this.length, 8); + + if(encoded_buf.byteLength > 127) + { + this.error = "Too big length"; + return (new ArrayBuffer(0)); + } + + var ret_buf = new ArrayBuffer(encoded_buf.byteLength + 1); + + if(size_only === true) + return ret_buf; + + var encoded_view = new Uint8Array(encoded_buf); + var ret_view = new Uint8Array(ret_buf); + + ret_view[0] = encoded_buf.byteLength | 0x80; + + for(var i = 0; i < encoded_buf.byteLength; i++) + ret_view[i + 1] = encoded_view[i]; + + return ret_buf; + } + else + { + var ret_buf = new ArrayBuffer(1); + + if(size_only === false) + { + var ret_view = new Uint8Array(ret_buf); + + ret_view[0] = this.length; + } + + return ret_buf; + } + + return (new ArrayBuffer(0)); + }; + //************************************************************************************** + local.length_block.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = local.base_block.prototype.toJSON.call(this); + + _object.block_name = local.length_block.prototype.block_name.call(this); + _object.is_indefinite_form = this.is_indefinite_form; + _object.long_form_used = this.long_form_used; + _object.length = this.length; + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Declaration of value block class + //************************************************************************************** + local.value_block = + function() + { + /// Generic class of ASN.1 "value block" + local.base_block.call(this, arguments[0]); + }; + //************************************************************************************** + local.value_block.prototype = new local.base_block(); + local.value_block.constructor = local.value_block; + //************************************************************************************** + local.value_block.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "value_block"; + }; + //************************************************************************************** + local.value_block.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = local.base_block.prototype.toJSON.call(this); + + _object.block_name = local.value_block.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Declaration of basic ASN.1 block class + //************************************************************************************** + in_window.org.pkijs.asn1.ASN1_block = + function() + { + /// Base class of ASN.1 block (identification block + length block + value block) + + local.base_block.call(this, arguments[0]); + + if(arguments[0] instanceof Object) + { + this.name = in_window.org.pkijs.getValue(arguments[0], "name", ""); + this.optional = in_window.org.pkijs.getValue(arguments[0], "optional", false); + + if("primitive_schema" in arguments[0]) + this.primitive_schema = arguments[0].primitive_schema; + } + + this.id_block = new local.identification_block(arguments[0]); + this.len_block = new local.length_block(arguments[0]); + this.value_block = new local.value_block(arguments[0]); + }; + //************************************************************************************** + in_window.org.pkijs.asn1.ASN1_block.prototype = new local.base_block(); + in_window.org.pkijs.asn1.ASN1_block.constructor = in_window.org.pkijs.asn1.ASN1_block; + //************************************************************************************** + in_window.org.pkijs.asn1.ASN1_block.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "ASN1_block"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.ASN1_block.prototype.fromBER = + function(input_buffer, input_offset, input_length) + { + /// Base function for converting block from BER encoded array of bytes + /// ASN.1 BER encoded array + /// Offset in ASN.1 BER encoded array where decoding should be started + /// Maximum length of array of bytes which can be using in this function + + var result_offset = this.value_block.fromBER(input_buffer, input_offset, (this.len_block.is_indefinite_form == true) ? input_length : this.len_block.length); + if(result_offset == (-1)) + { + this.error = this.value_block.error; + return result_offset; + } + + if(this.id_block.error.length == 0) + this.block_length += this.id_block.block_length; + + if(this.len_block.error.length == 0) + this.block_length += this.len_block.block_length; + + if(this.value_block.error.length == 0) + this.block_length += this.value_block.block_length; + + return result_offset; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.ASN1_block.prototype.toBER = + function(size_only) + { + /// Encoding of current ASN.1 block into ASN.1 encoded array (BER rules) + /// Flag that we need only a size of encoding, not a real array of bytes + + if(typeof size_only === "undefined") + size_only = false; + + var ret_buf; + + var id_block_buf = this.id_block.toBER(size_only); + var value_block_size_buf = this.value_block.toBER(true); + + this.len_block.length = value_block_size_buf.byteLength; + var len_block_buf = this.len_block.toBER(size_only); + + ret_buf = util_concatbuf(id_block_buf, len_block_buf); + + var value_block_buf; + + if(size_only === false) + value_block_buf = this.value_block.toBER(size_only); + else + value_block_buf = new ArrayBuffer(this.len_block.length); + + ret_buf = util_concatbuf(ret_buf, value_block_buf); + + if(this.len_block.is_indefinite_form === true) + { + var indef_buf = new ArrayBuffer(2); + + if(size_only === false) + { + var indef_view = new Uint8Array(indef_buf); + + indef_view[0] = 0x00; + indef_view[1] = 0x00; + } + + ret_buf = util_concatbuf(ret_buf, indef_buf); + } + + return ret_buf; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = local.base_block.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.ASN1_block.prototype.block_name.call(this); + _object.id_block = this.id_block.toJSON(); + _object.len_block = this.len_block.toJSON(); + _object.value_block = this.value_block.toJSON(); + + if("name" in this) + _object.name = this.name; + if("optional" in this) + _object.optional = this.optional; + if("primitive_schema" in this) + _object.primitive_schema = this.primitive_schema.toJSON(); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Declaration of basic block for all PRIMITIVE types + //************************************************************************************** + local.ASN1_PRIMITIVE_value_block = + function() + { + /// Base class of ASN.1 value block for primitive values (non-constructive encoding) + + local.value_block.call(this, arguments[0]); + + if(arguments[0] instanceof Object) + { + // #region Variables from "hex_block" class + if("value_hex" in arguments[0]) + this.value_hex = util_copybuf(arguments[0].value_hex); + else + this.value_hex = new ArrayBuffer(0); + + this.is_hex_only = in_window.org.pkijs.getValue(arguments[0], "is_hex_only", true); + // #endregion + } + else + { + // #region Variables from "hex_block" class + this.value_hex = new ArrayBuffer(0); + this.is_hex_only = true; + // #endregion + } + }; + //************************************************************************************** + local.ASN1_PRIMITIVE_value_block.prototype = new local.value_block(); + local.ASN1_PRIMITIVE_value_block.constructor = local.ASN1_PRIMITIVE_value_block; + //************************************************************************************** + local.ASN1_PRIMITIVE_value_block.prototype.fromBER = + function(input_buffer, input_offset, input_length) + { + /// Base function for converting block from BER encoded array of bytes + /// ASN.1 BER encoded array + /// Offset in ASN.1 BER encoded array where decoding should be started + /// Maximum length of array of bytes which can be using in this function + + // #region Basic check for parameters + if(check_buffer_params.call(this, input_buffer, input_offset, input_length) === false) + return (-1); + // #endregion + + // #region Getting Uint8Array from ArrayBuffer + var int_buffer = new Uint8Array(input_buffer, input_offset, input_length); + // #endregion + + // #region Initial checks + if(int_buffer.length == 0) + { + this.warnings.push("Zero buffer length"); + return input_offset; + } + // #endregion + + // #region Copy input buffer into internal buffer + this.value_hex = new ArrayBuffer(int_buffer.length); + var value_hex_view = new Uint8Array(this.value_hex); + + for(var i = 0; i < int_buffer.length; i++) + value_hex_view[i] = int_buffer[i]; + // #endregion + + this.block_length = input_length; + + return (input_offset + input_length); + }; + //************************************************************************************** + local.ASN1_PRIMITIVE_value_block.prototype.toBER = + function(size_only) + { + /// Encoding of current ASN.1 block into ASN.1 encoded array (BER rules) + /// Flag that we need only a size of encoding, not a real array of bytes + + return util_copybuf(this.value_hex); + }; + //************************************************************************************** + local.ASN1_PRIMITIVE_value_block.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "ASN1_PRIMITIVE_value_block"; + }; + //************************************************************************************** + local.ASN1_PRIMITIVE_value_block.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = local.value_block.prototype.toJSON.call(this); + + _object.block_name = local.ASN1_PRIMITIVE_value_block.prototype.block_name.call(this); + _object.value_hex = in_window.org.pkijs.bufferToHexCodes(this.value_hex, 0, this.value_hex.byteLength); + _object.is_hex_only = this.is_hex_only; + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.ASN1_PRIMITIVE = + function() + { + /// Base class of ASN.1 block for primitive values (non-constructive encoding) + + in_window.org.pkijs.asn1.ASN1_block.call(this, arguments[0]); + + this.id_block.is_constructed = false; + this.value_block = new local.ASN1_PRIMITIVE_value_block(arguments[0]); + }; + //************************************************************************************** + in_window.org.pkijs.asn1.ASN1_PRIMITIVE.prototype = new in_window.org.pkijs.asn1.ASN1_block(); + in_window.org.pkijs.asn1.ASN1_PRIMITIVE.constructor = in_window.org.pkijs.asn1.ASN1_PRIMITIVE; + //************************************************************************************** + in_window.org.pkijs.asn1.ASN1_PRIMITIVE.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "PRIMITIVE"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.ASN1_PRIMITIVE.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.ASN1_PRIMITIVE.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Declaration of basic block for all CONSTRUCTED types + //************************************************************************************** + local.ASN1_CONSTRUCTED_value_block = + function() + { + /// Base class of ASN.1 value block for constructive values (constructive encoding) + + local.value_block.call(this, arguments[0]); + + if(arguments[0] instanceof Object) + { + this.value = in_window.org.pkijs.getValue(arguments[0], "value", new Array()); + this.is_indefinite_form = in_window.org.pkijs.getValue(arguments[0], "is_indefinite_form", false); + } + else + { + this.value = new Array(); + this.is_indefinite_form = false; + } + }; + //************************************************************************************** + local.ASN1_CONSTRUCTED_value_block.prototype = new local.value_block(); + local.ASN1_CONSTRUCTED_value_block.constructor = local.ASN1_CONSTRUCTED_value_block; + //************************************************************************************** + local.ASN1_CONSTRUCTED_value_block.prototype.fromBER = + function(input_buffer, input_offset, input_length) + { + /// Base function for converting block from BER encoded array of bytes + /// ASN.1 BER encoded array + /// Offset in ASN.1 BER encoded array where decoding should be started + /// Maximum length of array of bytes which can be using in this function + + // #region Store initial offset and length + var initial_offset = input_offset; + var initial_length = input_length; + // #endregion + + // #region Basic check for parameters + if(check_buffer_params.call(this, input_buffer, input_offset, input_length) === false) + return (-1); + // #endregion + + // #region Getting Uint8Array from ArrayBuffer + var int_buffer = new Uint8Array(input_buffer, input_offset, input_length); + // #endregion + + // #region Initial checks + if(int_buffer.length == 0) + { + this.warnings.push("Zero buffer length"); + return input_offset; + } + // #endregion + + // #region Aux function + function check_len(_indefinite_length, _length) + { + if(_indefinite_length == true) + return 1; + + return _length; + } + // #endregion + + var current_offset = input_offset; + + while(check_len(this.is_indefinite_form, input_length) > 0) + { + var return_object = fromBER_raw(input_buffer, current_offset, input_length); + if(return_object.offset == (-1)) + { + this.error = return_object.result.error; + this.warnings.concat(return_object.result.warnings); + return (-1); + } + + current_offset = return_object.offset; + + this.block_length += return_object.result.block_length; + input_length -= return_object.result.block_length; + + this.value.push(return_object.result); + + if((this.is_indefinite_form == true) && (return_object.result.block_name() == in_window.org.pkijs.asn1.EOC.prototype.block_name())) + break; + } + + if(this.is_indefinite_form == true) + { + if(this.value[this.value.length - 1].block_name() == in_window.org.pkijs.asn1.EOC.prototype.block_name()) + this.value.pop(); + else + this.warnings.push("No EOC block encoded"); + } + + // #region Copy "input_buffer" to "value_before_decode" + this.value_before_decode = util_copybuf_offset(input_buffer, initial_offset, initial_length); + // #endregion + + return current_offset; + }; + //************************************************************************************** + local.ASN1_CONSTRUCTED_value_block.prototype.toBER = + function(size_only) + { + /// Encoding of current ASN.1 block into ASN.1 encoded array (BER rules) + /// Flag that we need only a size of encoding, not a real array of bytes + + if(typeof size_only === "undefined") + size_only = false; + + var ret_buf = new ArrayBuffer(0); + + for(var i = 0; i < this.value.length; i++) + { + var value_buf = this.value[i].toBER(size_only); + ret_buf = util_concatbuf(ret_buf, value_buf); + } + + return ret_buf; + }; + //************************************************************************************** + local.ASN1_CONSTRUCTED_value_block.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "ASN1_CONSTRUCTED_value_block"; + }; + //************************************************************************************** + local.ASN1_CONSTRUCTED_value_block.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = local.value_block.prototype.toJSON.call(this); + + _object.block_name = local.ASN1_CONSTRUCTED_value_block.prototype.block_name.call(this); + _object.is_indefinite_form = this.is_indefinite_form; + _object.value = new Array(); + for(var i = 0; i < this.value.length; i++) + _object.value.push(this.value[i].toJSON()); + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.ASN1_CONSTRUCTED = + function() + { + /// Base class of ASN.1 block for constructive values (constructive encoding) + + in_window.org.pkijs.asn1.ASN1_block.call(this, arguments[0]); + + this.id_block.is_constructed = true; + this.value_block = new local.ASN1_CONSTRUCTED_value_block(arguments[0]); + }; + //************************************************************************************** + in_window.org.pkijs.asn1.ASN1_CONSTRUCTED.prototype = new in_window.org.pkijs.asn1.ASN1_block(); + in_window.org.pkijs.asn1.ASN1_CONSTRUCTED.constructor = in_window.org.pkijs.asn1.ASN1_CONSTRUCTED; + //************************************************************************************** + in_window.org.pkijs.asn1.ASN1_CONSTRUCTED.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "CONSTRUCTED"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.ASN1_CONSTRUCTED.prototype.fromBER = + function(input_buffer, input_offset, input_length) + { + /// Base function for converting block from BER encoded array of bytes + /// ASN.1 BER encoded array + /// Offset in ASN.1 BER encoded array where decoding should be started + /// Maximum length of array of bytes which can be using in this function + + this.value_block.is_indefinite_form = this.len_block.is_indefinite_form; + + var result_offset = this.value_block.fromBER(input_buffer, input_offset, (this.len_block.is_indefinite_form == true) ? input_length : this.len_block.length); + if(result_offset == (-1)) + { + this.error = this.value_block.error; + return result_offset; + } + + if(this.id_block.error.length == 0) + this.block_length += this.id_block.block_length; + + if(this.len_block.error.length == 0) + this.block_length += this.len_block.block_length; + + if(this.value_block.error.length == 0) + this.block_length += this.value_block.block_length; + + return result_offset; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.ASN1_CONSTRUCTED.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.ASN1_CONSTRUCTED.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Declaration of ASN.1 EOC type class + //************************************************************************************** + local.EOC_value_block = + function() + { + local.value_block.call(this, arguments[0]); + }; + //************************************************************************************** + local.EOC_value_block.prototype = new local.value_block(); + local.EOC_value_block.constructor = local.EOC_value_block; + //************************************************************************************** + local.EOC_value_block.prototype.fromBER = + function(input_buffer, input_offset, input_length) + { + /// Base function for converting block from BER encoded array of bytes + /// ASN.1 BER encoded array + /// Offset in ASN.1 BER encoded array where decoding should be started + /// Maximum length of array of bytes which can be using in this function + + // #region There is no "value block" for EOC type and we need to return the same offset + return input_offset; + // #endregion + }; + //************************************************************************************** + local.EOC_value_block.prototype.toBER = + function(size_only) + { + /// Encoding of current ASN.1 block into ASN.1 encoded array (BER rules) + /// Flag that we need only a size of encoding, not a real array of bytes + + return (new ArrayBuffer(0)); + }; + //************************************************************************************** + local.EOC_value_block.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "EOC_value_block"; + }; + //************************************************************************************** + local.EOC_value_block.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = local.value_block.prototype.toJSON.call(this); + + _object.block_name = local.EOC_value_block.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.EOC = + function() + { + in_window.org.pkijs.asn1.ASN1_block.call(this, arguments[0]); + + this.value_block = new local.EOC_value_block(); + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 0; // EOC + }; + //************************************************************************************** + in_window.org.pkijs.asn1.EOC.prototype = new in_window.org.pkijs.asn1.ASN1_block(); + in_window.org.pkijs.asn1.EOC.constructor = local.EOC_value_block; + //************************************************************************************** + in_window.org.pkijs.asn1.EOC.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "END_OF_CONTENT"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.EOC.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.EOC.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Declaration of ASN.1 BOOLEAN type class + //************************************************************************************** + local.BOOLEAN_value_block = + function() + { + local.value_block.call(this, arguments[0]); + + if(arguments[0] instanceof Object) + { + this.value = in_window.org.pkijs.getValue(arguments[0], "value", false); + + // #region Variables from hex_block class + this.is_hex_only = in_window.org.pkijs.getValue(arguments[0], "is_hex_only", false); + if("value_hex" in arguments[0]) + this.value_hex = util_copybuf(arguments[0].value_hex); + else + { + this.value_hex = new ArrayBuffer(1); + if(this.value === true) + { + var view = new Uint8Array(this.value_hex); + view[0] = 0xFF; + } + } + // #endregion + } + else + { + this.value = false; + + // #region Variables from hex_block class + this.is_hex_only = false; + this.value_hex = new ArrayBuffer(1); + // #endregion + } + }; + //************************************************************************************** + local.BOOLEAN_value_block.prototype = new local.value_block(); + local.BOOLEAN_value_block.constructor = local.BOOLEAN_value_block; + //************************************************************************************** + local.BOOLEAN_value_block.prototype.fromBER = + function(input_buffer, input_offset, input_length) + { + /// Base function for converting block from BER encoded array of bytes + /// ASN.1 BER encoded array + /// Offset in ASN.1 BER encoded array where decoding should be started + /// Maximum length of array of bytes which can be using in this function + + // #region Basic check for parameters + if(check_buffer_params.call(this, input_buffer, input_offset, input_length) === false) + return (-1); + // #endregion + + // #region Getting Uint8Array from ArrayBuffer + var int_buffer = new Uint8Array(input_buffer, input_offset, input_length); + // #endregion + + if(input_length > 1) + this.warnings.push("BOOLEAN value encoded in more then 1 octet"); + + this.value = int_buffer[0] != 0x00; + + this.is_hex_only = true; + + // #region Copy input buffer to internal array + this.value_hex = new ArrayBuffer(int_buffer.length); + var view = new Uint8Array(this.value_hex); + + for(var i = 0; i < int_buffer.length; i++) + view[i] = int_buffer[i]; + // #endregion + + this.block_length = input_length; + + return (input_offset + input_length); + }; + //************************************************************************************** + local.BOOLEAN_value_block.prototype.toBER = + function(size_only) + { + /// Encoding of current ASN.1 block into ASN.1 encoded array (BER rules) + /// Flag that we need only a size of encoding, not a real array of bytes + + if(typeof size_only === "undefined") + size_only = false; + + return this.value_hex; + }; + //************************************************************************************** + local.BOOLEAN_value_block.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "BOOLEAN_value_block"; + }; + //************************************************************************************** + local.BOOLEAN_value_block.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = local.value_block.prototype.toJSON.call(this); + + _object.block_name = local.BOOLEAN_value_block.prototype.block_name.call(this); + _object.value = this.value; + _object.is_hex_only = this.is_hex_only; + _object.value_hex = in_window.org.pkijs.bufferToHexCodes(this.value_hex, 0, this.value_hex.byteLength); + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.BOOLEAN = + function() + { + in_window.org.pkijs.asn1.ASN1_block.call(this, arguments[0]); + + this.value_block = new local.BOOLEAN_value_block(arguments[0]); + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 1; // BOOLEAN + }; + //************************************************************************************** + in_window.org.pkijs.asn1.BOOLEAN.prototype = new in_window.org.pkijs.asn1.ASN1_block(); + in_window.org.pkijs.asn1.BOOLEAN.constructor = local.BOOLEAN_value_block; + //************************************************************************************** + in_window.org.pkijs.asn1.BOOLEAN.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "BOOLEAN"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.BOOLEAN.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.BOOLEAN.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Declaration of ASN.1 SEQUENCE and SET type classes + //************************************************************************************** + in_window.org.pkijs.asn1.SEQUENCE = + function() + { + in_window.org.pkijs.asn1.ASN1_CONSTRUCTED.call(this, arguments[0]); + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 16; // SEQUENCE + }; + //************************************************************************************** + in_window.org.pkijs.asn1.SEQUENCE.prototype = new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED(); + in_window.org.pkijs.asn1.SEQUENCE.constructor = in_window.org.pkijs.asn1.SEQUENCE; + //************************************************************************************** + in_window.org.pkijs.asn1.SEQUENCE.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "SEQUENCE"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.SEQUENCE.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = in_window.org.pkijs.asn1.ASN1_CONSTRUCTED.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.SEQUENCE.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.SET = + function() + { + in_window.org.pkijs.asn1.ASN1_CONSTRUCTED.call(this, arguments[0]); + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 17; // SET + }; + //************************************************************************************** + in_window.org.pkijs.asn1.SET.prototype = new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED(); + in_window.org.pkijs.asn1.SET.constructor = in_window.org.pkijs.asn1.SET; + //************************************************************************************** + in_window.org.pkijs.asn1.SET.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "SET"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.SET.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = in_window.org.pkijs.asn1.ASN1_CONSTRUCTED.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.SET.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Declaration of ASN.1 NULL type class + //************************************************************************************** + in_window.org.pkijs.asn1.NULL = + function() + { + in_window.org.pkijs.asn1.ASN1_block.call(this, arguments[0]); + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 5; // NULL + }; + //************************************************************************************** + in_window.org.pkijs.asn1.NULL.prototype = new in_window.org.pkijs.asn1.ASN1_block(); + in_window.org.pkijs.asn1.NULL.constructor = in_window.org.pkijs.asn1.NULL; + //************************************************************************************** + in_window.org.pkijs.asn1.NULL.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "NULL"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.NULL.prototype.fromBER = + function(input_buffer, input_offset, input_length) + { + /// Base function for converting block from BER encoded array of bytes + /// ASN.1 BER encoded array + /// Offset in ASN.1 BER encoded array where decoding should be started + /// Maximum length of array of bytes which can be using in this function + + if(this.len_block.length > 0) + this.warnings.push("Non-zero length of value block for NULL type"); + + if(this.id_block.error.length === 0) + this.block_length += this.id_block.block_length; + + if(this.len_block.error.length === 0) + this.block_length += this.len_block.block_length; + + this.block_length += input_length; + + return (input_offset + input_length); + }; + //************************************************************************************** + in_window.org.pkijs.asn1.NULL.prototype.toBER = + function(size_only) + { + /// Encoding of current ASN.1 block into ASN.1 encoded array (BER rules) + /// Flag that we need only a size of encoding, not a real array of bytes + + if(typeof size_only === "undefined") + size_only = false; + + var ret_buf = new ArrayBuffer(2); + + if(size_only === true) + return ret_buf; + + var ret_view = new Uint8Array(ret_buf); + ret_view[0] = 0x05; + ret_view[1] = 0x00; + + return ret_buf; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.NULL.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.NULL.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Declaration of ASN.1 OCTETSTRING type class + //************************************************************************************** + local.OCTETSTRING_value_block = + function() + { + /// + /// + /// + /// Value for the OCTETSTRING may be as hex, as well as a constructed value. + /// Constructed values consists of other OCTETSTRINGs + + local.ASN1_CONSTRUCTED_value_block.call(this, arguments[0]); + + if(arguments[0] instanceof Object) + { + this.is_constructed = in_window.org.pkijs.getValue(arguments[0], "is_constructed", false); + + // #region Variables from hex_block type + this.is_hex_only = in_window.org.pkijs.getValue(arguments[0], "is_hex_only", false); + if("value_hex" in arguments[0]) + this.value_hex = util_copybuf(arguments[0].value_hex); + else + this.value_hex = new ArrayBuffer(0); + // #endregion + } + else + { + this.is_constructed = false; + + // #region Variables from hex_block type + this.is_hex_only = false; + this.value_hex = new ArrayBuffer(0); + // #endregion + } + }; + //************************************************************************************** + local.OCTETSTRING_value_block.prototype = new local.ASN1_CONSTRUCTED_value_block(); + local.OCTETSTRING_value_block.constructor = local.OCTETSTRING_value_block; + //************************************************************************************** + local.OCTETSTRING_value_block.prototype.fromBER = + function(input_buffer, input_offset, input_length) + { + /// Base function for converting block from BER encoded array of bytes + /// ASN.1 BER encoded array + /// Offset in ASN.1 BER encoded array where decoding should be started + /// Maximum length of array of bytes which can be using in this function + + var result_offset = 0; + + if(this.is_constructed == true) + { + this.is_hex_only = false; + + result_offset = local.ASN1_CONSTRUCTED_value_block.prototype.fromBER.call(this, input_buffer, input_offset, input_length); + if(result_offset == (-1)) + return result_offset; + + for(var i = 0; i < this.value.length; i++) + { + var current_block_name = this.value[i].block_name(); + + if(current_block_name == in_window.org.pkijs.asn1.EOC.prototype.block_name()) + { + if(this.is_indefinite_form == true) + break; + else + { + this.error = "EOC is unexpected, OCTET STRING may consists of OCTET STRINGs only"; + return (-1); + } + } + + if(current_block_name != in_window.org.pkijs.asn1.OCTETSTRING.prototype.block_name()) + { + this.error = "OCTET STRING may consists of OCTET STRINGs only"; + return (-1); + } + } + } + else + { + this.is_hex_only = true; + + result_offset = local.hex_block.prototype.fromBER.call(this, input_buffer, input_offset, input_length); + this.block_length = input_length; + } + + return result_offset; + }; + //************************************************************************************** + local.OCTETSTRING_value_block.prototype.toBER = + function(size_only) + { + /// Encoding of current ASN.1 block into ASN.1 encoded array (BER rules) + /// Flag that we need only a size of encoding, not a real array of bytes + + if(typeof size_only === "undefined") + size_only = false; + + if(this.is_constructed === true) + return local.ASN1_CONSTRUCTED_value_block.prototype.toBER.call(this, size_only); + else + { + var ret_buf = new ArrayBuffer(this.value_hex.byteLength); + + if(size_only === true) + return ret_buf; + + if(this.value_hex.byteLength == 0) + return ret_buf; + + ret_buf = util_copybuf(this.value_hex); + + return ret_buf; + } + + return (new ArrayBuffer(0)); + }; + //************************************************************************************** + local.OCTETSTRING_value_block.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "OCTETSTRING_value_block"; + }; + //************************************************************************************** + local.OCTETSTRING_value_block.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = local.ASN1_CONSTRUCTED_value_block.prototype.toJSON.call(this); + + _object.block_name = local.OCTETSTRING_value_block.prototype.block_name.call(this); + _object.is_constructed = this.is_constructed; + _object.is_hex_only = this.is_hex_only; + _object.value_hex = in_window.org.pkijs.bufferToHexCodes(this.value_hex, 0, this.value_hex.byteLength); + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.OCTETSTRING = + function() + { + in_window.org.pkijs.asn1.ASN1_block.call(this, arguments[0]); + + this.value_block = new local.OCTETSTRING_value_block(arguments[0]); + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 4; // OCTETSTRING + }; + //************************************************************************************** + in_window.org.pkijs.asn1.OCTETSTRING.prototype = new in_window.org.pkijs.asn1.ASN1_block(); + in_window.org.pkijs.asn1.OCTETSTRING.constructor = in_window.org.pkijs.asn1.OCTETSTRING; + //************************************************************************************** + in_window.org.pkijs.asn1.OCTETSTRING.prototype.fromBER = + function(input_buffer, input_offset, input_length) + { + /// Base function for converting block from BER encoded array of bytes + /// ASN.1 BER encoded array + /// Offset in ASN.1 BER encoded array where decoding should be started + /// Maximum length of array of bytes which can be using in this function + + this.value_block.is_constructed = this.id_block.is_constructed; + this.value_block.is_indefinite_form = this.len_block.is_indefinite_form; + + // #region Ability to encode empty OCTET STRING + if(input_length == 0) + { + if(this.id_block.error.length == 0) + this.block_length += this.id_block.block_length; + + if(this.len_block.error.length == 0) + this.block_length += this.len_block.block_length; + + return input_offset; + } + // #endregion + + return in_window.org.pkijs.asn1.ASN1_block.prototype.fromBER.call(this, input_buffer, input_offset, input_length); + }; + //************************************************************************************** + in_window.org.pkijs.asn1.OCTETSTRING.prototype.block_name = + function() + { + return "OCTETSTRING"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.OCTETSTRING.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.OCTETSTRING.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.OCTETSTRING.prototype.isEqual = + function(octetString) + { + /// + /// The OCTETSTRING to compare with + + // #region Check input type + if((octetString instanceof in_window.org.pkijs.asn1.OCTETSTRING) == false) + return false; + // #endregion + + // #region Compare two JSON strings + if(JSON.stringify(this) != JSON.stringify(octetString)) + return false; + // #endregion + + return true; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Declaration of ASN.1 BITSTRING type class + //************************************************************************************** + local.BITSTRING_value_block = + function() + { + local.ASN1_CONSTRUCTED_value_block.call(this, arguments[0]); + + if(arguments[0] instanceof Object) + { + this.unused_bits = in_window.org.pkijs.getValue(arguments[0], "unused_bits", 0); + this.is_constructed = in_window.org.pkijs.getValue(arguments[0], "is_constructed", false); + + // #region Variables from hex_block type + this.is_hex_only = in_window.org.pkijs.getValue(arguments[0], "is_hex_only", false); + + if("value_hex" in arguments[0]) + this.value_hex = util_copybuf(arguments[0].value_hex); + else + this.value_hex = new ArrayBuffer(0); + + this.block_length = this.value_hex.byteLength; + // #endregion + } + else + { + this.unused_bits = 0; + this.is_constructed = false; + + // #region Variables from hex_block type + this.is_hex_only = false; + this.value_hex = new ArrayBuffer(0); + // #endregion + } + }; + //************************************************************************************** + local.BITSTRING_value_block.prototype = new local.ASN1_CONSTRUCTED_value_block(); + local.BITSTRING_value_block.constructor = local.BITSTRING_value_block; + //************************************************************************************** + local.BITSTRING_value_block.prototype.fromBER = + function(input_buffer, input_offset, input_length) + { + /// Base function for converting block from BER encoded array of bytes + /// ASN.1 BER encoded array + /// Offset in ASN.1 BER encoded array where decoding should be started + /// Maximum length of array of bytes which can be using in this function + + // #region Ability to decode zero-length BITSTRING value + if(input_length == 0) + return input_offset; + // #endregion + + var result_offset = (-1); + + // #region If the BISTRING supposed to be a constructed value + if(this.is_constructed == true) + { + result_offset = local.ASN1_CONSTRUCTED_value_block.prototype.fromBER.call(this, input_buffer, input_offset, input_length); + if(result_offset == (-1)) + return result_offset; + + for(var i = 0; i < this.value.length; i++) + { + var current_block_name = this.value[i].block_name(); + + if(current_block_name == in_window.org.pkijs.asn1.EOC.prototype.block_name()) + { + if(this.is_indefinite_form == true) + break; + else + { + this.error = "EOC is unexpected, BIT STRING may consists of BIT STRINGs only"; + return (-1); + } + } + + if(current_block_name != in_window.org.pkijs.asn1.BITSTRING.prototype.block_name()) + { + this.error = "BIT STRING may consists of BIT STRINGs only"; + return (-1); + } + + if((this.unused_bits > 0) && (this.value[i].unused_bits > 0)) + { + this.error = "Usign of \"unused bits\" inside constructive BIT STRING allowed for least one only"; + return (-1); + } + else + { + this.unused_bits = this.value[i].unused_bits; + if(this.unused_bits > 7) + { + this.error = "Unused bits for BITSTRING must be in range 0-7"; + return (-1); + } + } + } + + return result_offset; + } + // #endregion + // #region If the BITSTRING supposed to be a primitive value + else + { + // #region Basic check for parameters + if(check_buffer_params.call(this, input_buffer, input_offset, input_length) === false) + return (-1); + // #endregion + + var int_buffer = new Uint8Array(input_buffer, input_offset, input_length); + + this.unused_bits = int_buffer[0]; + if(this.unused_bits > 7) + { + this.error = "Unused bits for BITSTRING must be in range 0-7"; + return (-1); + } + + // #region Copy input buffer to internal buffer + this.value_hex = new ArrayBuffer(int_buffer.length - 1); + var view = new Uint8Array(this.value_hex); + for(var i = 0; i < (input_length - 1) ; i++) + view[i] = int_buffer[i + 1]; + // #endregion + + this.block_length = int_buffer.length; + + return (input_offset + input_length); + } + // #endregion + }; + //************************************************************************************** + local.BITSTRING_value_block.prototype.toBER = + function(size_only) + { + /// Encoding of current ASN.1 block into ASN.1 encoded array (BER rules) + /// Flag that we need only a size of encoding, not a real array of bytes + + if(typeof size_only === "undefined") + size_only = false; + + if(this.is_constructed === true) + return local.ASN1_CONSTRUCTED_value_block.prototype.toBER.call(this, size_only); + else + { + if(size_only === true) + return (new ArrayBuffer(this.value_hex.byteLength + 1)); + + if(this.value_hex.byteLength == 0) + return (new ArrayBuffer(0)); + + var cur_view = new Uint8Array(this.value_hex); + + var ret_buf = new ArrayBuffer(this.value_hex.byteLength + 1); + var ret_view = new Uint8Array(ret_buf); + + ret_view[0] = this.unused_bits; + + for(var i = 0; i < this.value_hex.byteLength; i++) + ret_view[i + 1] = cur_view[i]; + + return ret_buf; + } + + return (new ArrayBuffer(0)); + }; + //************************************************************************************** + local.BITSTRING_value_block.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "BITSTRING_value_block"; + }; + //************************************************************************************** + local.BITSTRING_value_block.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = local.ASN1_CONSTRUCTED_value_block.prototype.toJSON.call(this); + + _object.block_name = local.BITSTRING_value_block.prototype.block_name.call(this); + _object.unused_bits = this.unused_bits; + _object.is_constructed = this.is_constructed; + _object.is_hex_only = this.is_hex_only; + _object.value_hex = in_window.org.pkijs.bufferToHexCodes(this.value_hex, 0, this.value_hex.byteLength); + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.BITSTRING = + function() + { + in_window.org.pkijs.asn1.ASN1_block.call(this, arguments[0]); + + this.value_block = new local.BITSTRING_value_block(arguments[0]); + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 3; // BITSTRING + }; + //************************************************************************************** + in_window.org.pkijs.asn1.BITSTRING.prototype = new in_window.org.pkijs.asn1.ASN1_block(); + in_window.org.pkijs.asn1.BITSTRING.constructor = in_window.org.pkijs.asn1.BITSTRING; + //************************************************************************************** + in_window.org.pkijs.asn1.BITSTRING.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "BITSTRING"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.BITSTRING.prototype.fromBER = + function(input_buffer, input_offset, input_length) + { + /// Base function for converting block from BER encoded array of bytes + /// ASN.1 BER encoded array + /// Offset in ASN.1 BER encoded array where decoding should be started + /// Maximum length of array of bytes which can be using in this function + + // #region Ability to encode empty BITSTRING + if(input_length == 0) + return input_offset; + // #endregion + + this.value_block.is_constructed = this.id_block.is_constructed; + this.value_block.is_indefinite_form = this.len_block.is_indefinite_form; + + return in_window.org.pkijs.asn1.ASN1_block.prototype.fromBER.call(this, input_buffer, input_offset, input_length); + }; + //************************************************************************************** + in_window.org.pkijs.asn1.BITSTRING.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.BITSTRING.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Declaration of ASN.1 INTEGER type class + //************************************************************************************** + local.INTEGER_value_block = + function() + { + local.value_block.call(this, arguments[0]); + + if(arguments[0] instanceof Object) + { + this.value_dec = in_window.org.pkijs.getValue(arguments[0], "value", 0); + + // #region Variables from hex_block type + this.is_hex_only = in_window.org.pkijs.getValue(arguments[0], "is_hex_only", false); + if("value_hex" in arguments[0]) + { + this.value_hex = util_copybuf(arguments[0].value_hex); + + if(this.value_hex.byteLength >= 4) // Dummy's protection + this.is_hex_only = true; + else + this.value_dec = util_decode_tc.call(this); + } + else + this.value_hex = util_encode_tc(this.value_dec); + // #endregion + } + else + { + this.value_dec = 0; + + // #region Variables from hex_block type + this.is_hex_only = false; + this.value_hex = new ArrayBuffer(0); + // #endregion + } + }; + //************************************************************************************** + local.INTEGER_value_block.prototype = new local.value_block(); + local.INTEGER_value_block.constructor = local.INTEGER_value_block; + //************************************************************************************** + local.INTEGER_value_block.prototype.fromBER = + function(input_buffer, input_offset, input_length) + { + /// Base function for converting block from BER encoded array of bytes + /// ASN.1 BER encoded array + /// Offset in ASN.1 BER encoded array where decoding should be started + /// Maximum length of array of bytes which can be using in this function + + var result_offset = local.hex_block.prototype.fromBER.call(this, input_buffer, input_offset, input_length); + if(result_offset == (-1)) + return result_offset; + + if(this.value_hex.byteLength > 4) // In JavaScript we can effectively work with 32-bit integers only + { + this.warnings.push("Too big INTEGER for decoding, hex only"); + this.is_hex_only = true; + } + else + this.value_dec = util_decode_tc.call(this); + + this.block_length = input_length; + + return (input_offset + input_length); + }; + //************************************************************************************** + local.INTEGER_value_block.prototype.toBER = + function(size_only) + { + /// Encoding of current ASN.1 block into ASN.1 encoded array (BER rules) + /// Flag that we need only a size of encoding, not a real array of bytes + + if(typeof size_only === "undefined") + size_only = false; + + if(this.is_hex_only === false) + { + var encoded_buf = util_encode_tc(this.value_dec); + if(encoded_buf.byteLength == 0) + { + this.error = "Error during encoding INTEGER value"; + return (new ArrayBuffer(0)); + } + + return util_copybuf(encoded_buf); + } + else + return util_copybuf(this.value_hex); + + return (new ArrayBuffer(0)); + }; + //************************************************************************************** + local.INTEGER_value_block.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "INTEGER_value_block"; + }; + //************************************************************************************** + local.INTEGER_value_block.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = local.value_block.prototype.toJSON.call(this); + + _object.block_name = local.INTEGER_value_block.prototype.block_name.call(this); + _object.value_dec = this.value_dec; + _object.is_hex_only = this.is_hex_only; + _object.value_hex = in_window.org.pkijs.bufferToHexCodes(this.value_hex, 0, this.value_hex.byteLength); + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.INTEGER = + function() + { + in_window.org.pkijs.asn1.ASN1_block.call(this, arguments[0]); + + this.value_block = new local.INTEGER_value_block(arguments[0]); + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 2; // INTEGER + }; + //************************************************************************************** + in_window.org.pkijs.asn1.INTEGER.prototype = new in_window.org.pkijs.asn1.ASN1_block(); + in_window.org.pkijs.asn1.INTEGER.constructor = in_window.org.pkijs.asn1.INTEGER; + //************************************************************************************** + in_window.org.pkijs.asn1.INTEGER.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "INTEGER"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.INTEGER.prototype.isEqual = + function() + { + /// Compare two INTEGER object, or INTEGER and ArrayBuffer objects + /// + + if(arguments[0] instanceof in_window.org.pkijs.asn1.INTEGER) + { + if(this.value_block.is_hex_only && arguments[0].value_block.is_hex_only) // Compare two ArrayBuffers + return in_window.org.pkijs.isEqual_buffer(this.value_block.value_hex, arguments[0].value_block.value_hex); + else + { + if(this.value_block.is_hex_only === arguments[0].value_block.is_hex_only) + return (this.value_block.value_dec == arguments[0].value_block.value_dec); + else + return false; + } + } + else + { + if(arguments[0] instanceof ArrayBuffer) + return in_window.org.pkijs.isEqual_buffer(this.value_block.value_hex, arguments[0]); + else + return false; + } + + return false; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.INTEGER.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.INTEGER.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Declaration of ASN.1 ENUMERATED type class + //************************************************************************************** + in_window.org.pkijs.asn1.ENUMERATED = + function() + { + in_window.org.pkijs.asn1.INTEGER.call(this, arguments[0]); + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 10; // ENUMERATED + }; + //************************************************************************************** + in_window.org.pkijs.asn1.ENUMERATED.prototype = new in_window.org.pkijs.asn1.INTEGER(); + in_window.org.pkijs.asn1.ENUMERATED.constructor = in_window.org.pkijs.asn1.ENUMERATED; + //************************************************************************************** + in_window.org.pkijs.asn1.ENUMERATED.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "ENUMERATED"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.ENUMERATED.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = in_window.org.pkijs.asn1.INTEGER.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.ENUMERATED.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Declaration of ASN.1 OBJECT IDENTIFIER type class + //************************************************************************************** + local.SID_value_block = + function() + { + local.hex_block.call(this, arguments[0]); + + if(arguments[0] instanceof Object) + { + this.value_dec = in_window.org.pkijs.getValue(arguments[0], "value_dec", -1); + this.is_first_sid = in_window.org.pkijs.getValue(arguments[0], "is_first_sid", false); + } + else + { + this.value_dec = (-1); + this.is_first_sid = false; + } + }; + //************************************************************************************** + local.SID_value_block.prototype = new local.hex_block(); + local.SID_value_block.constructor = local.SID_value_block; + //************************************************************************************** + local.SID_value_block.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "sid_block"; + }; + //************************************************************************************** + local.SID_value_block.prototype.fromBER = + function(input_buffer, input_offset, input_length) + { + /// Base function for converting block from BER encoded array of bytes + /// ASN.1 BER encoded array + /// Offset in ASN.1 BER encoded array where decoding should be started + /// Maximum length of array of bytes which can be using in this function + + if(input_length == 0) + return input_offset; + + // #region Basic check for parameters + if(check_buffer_params.call(this, input_buffer, input_offset, input_length) === false) + return (-1); + // #endregion + + var int_buffer = new Uint8Array(input_buffer, input_offset, input_length); + + this.value_hex = new ArrayBuffer(input_length); + var view = new Uint8Array(this.value_hex); + + for(var i = 0; i < input_length; i++) + { + view[i] = int_buffer[i] & 0x7F; + + this.block_length++; + + if((int_buffer[i] & 0x80) == 0x00) + break; + } + + // #region Ajust size of value_hex buffer + var temp_value_hex = new ArrayBuffer(this.block_length); + var temp_view = new Uint8Array(temp_value_hex); + + for(var i = 0; i < this.block_length; i++) + temp_view[i] = view[i]; + + this.value_hex = util_copybuf(temp_value_hex); + view = new Uint8Array(this.value_hex); + // #endregion + + if((int_buffer[this.block_length - 1] & 0x80) != 0x00) + { + this.error = "End of input reached before message was fully decoded"; + return (-1); + } + + if(view[0] == 0x00) + this.warnings.push("Needlessly long format of SID encoding"); + + if(this.block_length <= 8) + this.value_dec = util_frombase(view, 7); + else + { + this.is_hex_only = true; + this.warnings.push("Too big SID for decoding, hex only"); + } + + return (input_offset + this.block_length); + }; + //************************************************************************************** + local.SID_value_block.prototype.toBER = + function(size_only) + { + /// Encoding of current ASN.1 block into ASN.1 encoded array (BER rules) + /// Flag that we need only a size of encoding, not a real array of bytes + + if(typeof size_only === "undefined") + size_only = false; + + if(this.is_hex_only) + { + if(size_only === true) + return (new ArrayBuffer(this.value_hex.byteLength)); + + var cur_view = new Uint8Array(this.value_hex); + + var ret_buf = new ArrayBuffer(this.block_length); + var ret_view = new Uint8Array(ret_buf); + + for(var i = 0; i < (this.block_length - 1) ; i++) + ret_view[i] = cur_view[i] | 0x80; + + ret_view[this.block_length - 1] = cur_view[this.block_length - 1]; + + return ret_buf; + } + else + { + var encoded_buf = util_tobase(this.value_dec, 7); + if(encoded_buf.byteLength === 0) + { + this.error = "Error during encoding SID value"; + return (new ArrayBuffer(0)); + } + + var ret_buf = new ArrayBuffer(encoded_buf.byteLength); + + if(size_only === false) + { + var encoded_view = new Uint8Array(encoded_buf); + var ret_view = new Uint8Array(ret_buf); + + for(var i = 0; i < (encoded_buf.byteLength - 1) ; i++) + ret_view[i] = encoded_view[i] | 0x80; + + ret_view[encoded_buf.byteLength - 1] = encoded_view[encoded_buf.byteLength - 1]; + } + + return ret_buf; + } + }; + //************************************************************************************** + local.SID_value_block.prototype.toString = + function() + { + var result = ""; + + if(this.is_hex_only === true) + result = to_hex_codes(this.value_hex); + else + { + if(this.is_first_sid) + { + var sid_value = this.value_dec; + + if(this.value_dec <= 39) + result = "0."; + else + { + if(this.value_dec <= 79) + { + result = "1."; + sid_value -= 40; + } + else + { + result = "2."; + sid_value -= 80; + } + } + + result = result + sid_value.toString(); + } + else + result = this.value_dec.toString(); + } + + return result; + }; + //************************************************************************************** + local.SID_value_block.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = local.hex_block.prototype.toJSON.call(this); + + _object.block_name = local.SID_value_block.prototype.block_name.call(this); + _object.value_dec = this.value_dec; + _object.is_first_sid = this.is_first_sid; + + return _object; + }; + //************************************************************************************** + local.OID_value_block = + function() + { + local.value_block.call(this, arguments[0]); + + this.value = new Array(); + + if(arguments[0] instanceof Object) + this.fromString(in_window.org.pkijs.getValue(arguments[0], "value", "")); + }; + //************************************************************************************** + local.OID_value_block.prototype = new local.value_block(); + local.OID_value_block.constructor = local.OID_value_block; + //************************************************************************************** + local.OID_value_block.prototype.fromBER = + function(input_buffer, input_offset, input_length) + { + /// Base function for converting block from BER encoded array of bytes + /// ASN.1 BER encoded array + /// Offset in ASN.1 BER encoded array where decoding should be started + /// Maximum length of array of bytes which can be using in this function + + var result_offset = input_offset; + + while(input_length > 0) + { + var sid_block = new local.SID_value_block(); + result_offset = sid_block.fromBER(input_buffer, result_offset, input_length); + if(result_offset == (-1)) + { + this.block_length = 0; + this.error = sid_block.error; + return result_offset; + } + + if(this.value.length == 0) + sid_block.is_first_sid = true; + + this.block_length += sid_block.block_length; + input_length -= sid_block.block_length; + + this.value.push(sid_block); + } + + return result_offset; + }; + //************************************************************************************** + local.OID_value_block.prototype.toBER = + function(size_only) + { + /// Encoding of current ASN.1 block into ASN.1 encoded array (BER rules) + /// Flag that we need only a size of encoding, not a real array of bytes + + if(typeof size_only === "undefined") + size_only = false; + + var ret_buf = new ArrayBuffer(0); + + for(var i = 0; i < this.value.length; i++) + { + var value_buf = this.value[i].toBER(size_only); + if(value_buf.byteLength === 0) + { + this.error = this.value[i].error; + return (new ArrayBuffer(0)); + } + + ret_buf = util_concatbuf(ret_buf, value_buf); + } + + return ret_buf; + }; + //************************************************************************************** + local.OID_value_block.prototype.fromString = + function(str) + { + this.value = new Array(); // Clear existing SID values + + var pos1 = 0; + var pos2 = 0; + + var sid = ""; + + var flag = false; + + do + { + pos2 = str.indexOf('.', pos1); + if(pos2 === (-1)) + sid = str.substr(pos1); + else + sid = str.substr(pos1, pos2 - pos1); + + pos1 = pos2 + 1; + + if(flag) + { + var sid_block = this.value[0]; + + var plus = 0; + + switch(sid_block.value_dec) + { + case 0: + break; + case 1: + plus = 40; + break; + case 2: + plus = 80; + break; + default: + this.value = new Array(); // clear SID array + return false; // ??? + } + + var parsedSID = parseInt(sid, 10); + if(isNaN(parsedSID)) + return true; + + sid_block.value_dec = parsedSID + plus; + + flag = false; + } + else + { + var sid_block = new local.SID_value_block(); + sid_block.value_dec = parseInt(sid, 10); + if(isNaN(sid_block.value_dec)) + return true; + + if(this.value.length === 0) + { + sid_block.is_first_sid = true; + flag = true; + } + + this.value.push(sid_block); + } + + } while(pos2 !== (-1)); + + return true; + }; + //************************************************************************************** + local.OID_value_block.prototype.toString = + function() + { + var result = ""; + var is_hex_only = false; + + for(var i = 0; i < this.value.length; i++) + { + is_hex_only = this.value[i].is_hex_only; + + var sid_str = this.value[i].toString(); + + if(i !== 0) + result = result + "."; + + if(is_hex_only) + { + sid_str = "{" + sid_str + "}"; + + if(this.value[i].is_first_sid) + result = "2.{" + sid_str + " - 80}"; + else + result = result + sid_str; + } + else + result = result + sid_str; + } + + return result; + }; + //************************************************************************************** + local.OID_value_block.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "OID_value_block"; + }; + //************************************************************************************** + local.OID_value_block.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = local.value_block.prototype.toJSON.call(this); + + _object.block_name = local.OID_value_block.prototype.block_name.call(this); + _object.value = local.OID_value_block.prototype.toString.call(this); + _object.sid_array = new Array(); + for(var i = 0; i < this.value.length; i++) + _object.sid_array.push(this.value[i].toJSON()); + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.OID = + function() + { + in_window.org.pkijs.asn1.ASN1_block.call(this, arguments[0]); + + this.value_block = new local.OID_value_block(arguments[0]); + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 6; // OBJECT IDENTIFIER + }; + //************************************************************************************** + in_window.org.pkijs.asn1.OID.prototype = new in_window.org.pkijs.asn1.ASN1_block(); + in_window.org.pkijs.asn1.OID.constructor = in_window.org.pkijs.asn1.OID; + //************************************************************************************** + in_window.org.pkijs.asn1.OID.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "OID"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.OID.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.OID.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Declaration of all string's classes + //************************************************************************************** + local.UTF8STRING_value_block = + function() + { + local.hex_block.call(this, arguments[0]); + + this.is_hex_only = true; + this.value = ""; // String representation of decoded ArrayBuffer + }; + //************************************************************************************** + local.UTF8STRING_value_block.prototype = new local.hex_block(); + local.UTF8STRING_value_block.constructor = local.UTF8STRING_value_block; + //************************************************************************************** + local.UTF8STRING_value_block.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "UTF8STRING_value_block"; + }; + //************************************************************************************** + local.UTF8STRING_value_block.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = local.hex_block.prototype.toJSON.call(this); + + _object.block_name = local.UTF8STRING_value_block.prototype.block_name.call(this); + _object.value = this.value; + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.UTF8STRING = + function() + { + in_window.org.pkijs.asn1.ASN1_block.call(this, arguments[0]); + + this.value_block = new local.UTF8STRING_value_block(); + + if(arguments[0] instanceof Object) + { + if("value" in arguments[0]) + in_window.org.pkijs.asn1.UTF8STRING.prototype.fromString.call(this,arguments[0].value); + } + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 12; // UTF8STRING + }; + //************************************************************************************** + in_window.org.pkijs.asn1.UTF8STRING.prototype = new in_window.org.pkijs.asn1.ASN1_block(); + in_window.org.pkijs.asn1.UTF8STRING.constructor = in_window.org.pkijs.asn1.UTF8STRING; + //************************************************************************************** + in_window.org.pkijs.asn1.UTF8STRING.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "UTF8STRING"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.UTF8STRING.prototype.fromBER = + function(input_buffer, input_offset, input_length) + { + /// Base function for converting block from BER encoded array of bytes + /// ASN.1 BER encoded array + /// Offset in ASN.1 BER encoded array where decoding should be started + /// Maximum length of array of bytes which can be using in this function + + var result_offset = this.value_block.fromBER(input_buffer, input_offset, (this.len_block.is_indefinite_form == true) ? input_length : this.len_block.length); + if(result_offset == (-1)) + { + this.error = this.value_block.error; + return result_offset; + } + + in_window.org.pkijs.asn1.UTF8STRING.prototype.fromBuffer.call(this, this.value_block.value_hex); + + if(this.id_block.error.length == 0) + this.block_length += this.id_block.block_length; + + if(this.len_block.error.length == 0) + this.block_length += this.len_block.block_length; + + if(this.value_block.error.length == 0) + this.block_length += this.value_block.block_length; + + return result_offset; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.UTF8STRING.prototype.fromBuffer = + function(input_buffer) + { + /// Array with encoded string + this.value_block.value = String.fromCharCode.apply(null, new Uint8Array(input_buffer)); + + try + { + this.value_block.value = decodeURIComponent(escape(this.value_block.value)); + } + catch(ex) + { + this.warnings.push("Error during \"decodeURIComponent\": " + ex + ", using raw string"); + } + }; + //************************************************************************************** + in_window.org.pkijs.asn1.UTF8STRING.prototype.fromString = + function(input_string) + { + /// String with UNIVERSALSTRING value + + var str = unescape(encodeURIComponent(input_string)); + var str_len = str.length; + + this.value_block.value_hex = new ArrayBuffer(str_len); + var view = new Uint8Array(this.value_block.value_hex); + + for(var i = 0; i < str_len; i++) + view[i] = str.charCodeAt(i); + + this.value_block.value = input_string; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.UTF8STRING.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.UTF8STRING.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + local.BMPSTRING_value_block = + function() + { + local.hex_block.call(this, arguments[0]); + + this.is_hex_only = true; + this.value = ""; + }; + //************************************************************************************** + local.BMPSTRING_value_block.prototype = new local.hex_block(); + local.BMPSTRING_value_block.constructor = local.BMPSTRING_value_block; + //************************************************************************************** + local.BMPSTRING_value_block.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "BMPSTRING_value_block"; + }; + //************************************************************************************** + local.BMPSTRING_value_block.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = local.hex_block.prototype.toJSON.call(this); + + _object.block_name = local.BMPSTRING_value_block.prototype.block_name.call(this); + _object.value = this.value; + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.BMPSTRING = + function() + { + in_window.org.pkijs.asn1.ASN1_block.call(this, arguments[0]); + + this.value_block = new local.BMPSTRING_value_block(); + + if(arguments[0] instanceof Object) + { + if("value" in arguments[0]) + in_window.org.pkijs.asn1.BMPSTRING.prototype.fromString.call(this, arguments[0].value); + } + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 30; // BMPSTRING + }; + //************************************************************************************** + in_window.org.pkijs.asn1.BMPSTRING.prototype = new in_window.org.pkijs.asn1.ASN1_block(); + in_window.org.pkijs.asn1.BMPSTRING.constructor = in_window.org.pkijs.asn1.BMPSTRING; + //************************************************************************************** + in_window.org.pkijs.asn1.BMPSTRING.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "BMPSTRING"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.BMPSTRING.prototype.fromBER = + function(input_buffer, input_offset, input_length) + { + /// Base function for converting block from BER encoded array of bytes + /// ASN.1 BER encoded array + /// Offset in ASN.1 BER encoded array where decoding should be started + /// Maximum length of array of bytes which can be using in this function + + var result_offset = this.value_block.fromBER(input_buffer, input_offset, (this.len_block.is_indefinite_form == true) ? input_length : this.len_block.length); + if(result_offset == (-1)) + { + this.error = this.value_block.error; + return result_offset; + } + + in_window.org.pkijs.asn1.BMPSTRING.prototype.fromBuffer.call(this, this.value_block.value_hex); + + if(this.id_block.error.length == 0) + this.block_length += this.id_block.block_length; + + if(this.len_block.error.length == 0) + this.block_length += this.len_block.block_length; + + if(this.value_block.error.length == 0) + this.block_length += this.value_block.block_length; + + return result_offset; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.BMPSTRING.prototype.fromBuffer = + function(input_buffer) + { + /// Array with encoded string + + var copy_buffer = in_window.org.pkijs.copyBuffer(input_buffer); + + var value_view = new Uint8Array(copy_buffer); + + for(var i = 0; i < value_view.length; i = i + 2) + { + var temp = value_view[i]; + + value_view[i] = value_view[i + 1]; + value_view[i + 1] = temp; + } + + this.value_block.value = String.fromCharCode.apply(null, new Uint16Array(copy_buffer)); + }; + //************************************************************************************** + in_window.org.pkijs.asn1.BMPSTRING.prototype.fromString = + function(input_string) + { + /// String with UNIVERSALSTRING value + + var str_length = input_string.length; + + this.value_block.value_hex = new ArrayBuffer(str_length * 2); + var value_hex_view = new Uint8Array(this.value_block.value_hex); + + for(var i = 0; i < str_length; i++) + { + var code_buf = util_tobase(input_string.charCodeAt(i), 8); + var code_view = new Uint8Array(code_buf); + if(code_view.length > 2) + continue; + + var dif = 2 - code_view.length; + + for(var j = (code_view.length - 1) ; j >= 0; j--) + value_hex_view[i * 2 + j + dif] = code_view[j]; + } + + this.value_block.value = input_string; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.BMPSTRING.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.BMPSTRING.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + local.UNIVERSALSTRING_value_block = + function() + { + local.hex_block.call(this, arguments[0]); + + this.is_hex_only = true; + this.value = ""; + }; + //************************************************************************************** + local.UNIVERSALSTRING_value_block.prototype = new local.hex_block(); + local.UNIVERSALSTRING_value_block.constructor = local.UNIVERSALSTRING_value_block; + //************************************************************************************** + local.UNIVERSALSTRING_value_block.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "UNIVERSALSTRING_value_block"; + }; + //************************************************************************************** + local.UNIVERSALSTRING_value_block.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = local.hex_block.prototype.toJSON.call(this); + + _object.block_name = local.UNIVERSALSTRING_value_block.prototype.block_name.call(this); + _object.value = this.value; + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.UNIVERSALSTRING = + function() + { + in_window.org.pkijs.asn1.ASN1_block.call(this, arguments[0]); + + this.value_block = new local.UNIVERSALSTRING_value_block(); + + if(arguments[0] instanceof Object) + { + if("value" in arguments[0]) + in_window.org.pkijs.asn1.UNIVERSALSTRING.prototype.fromString.call(this, arguments[0].value); + } + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 28; // UNIVERSALSTRING + }; + //************************************************************************************** + in_window.org.pkijs.asn1.UNIVERSALSTRING.prototype = new in_window.org.pkijs.asn1.ASN1_block(); + in_window.org.pkijs.asn1.UNIVERSALSTRING.constructor = in_window.org.pkijs.asn1.UNIVERSALSTRING; + //************************************************************************************** + in_window.org.pkijs.asn1.UNIVERSALSTRING.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "UNIVERSALSTRING"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.UNIVERSALSTRING.prototype.fromBER = + function(input_buffer, input_offset, input_length) + { + /// Base function for converting block from BER encoded array of bytes + /// ASN.1 BER encoded array + /// Offset in ASN.1 BER encoded array where decoding should be started + /// Maximum length of array of bytes which can be using in this function + + var result_offset = this.value_block.fromBER(input_buffer, input_offset, (this.len_block.is_indefinite_form == true) ? input_length : this.len_block.length); + if(result_offset == (-1)) + { + this.error = this.value_block.error; + return result_offset; + } + + in_window.org.pkijs.asn1.UNIVERSALSTRING.prototype.fromBuffer.call(this, this.value_block.value_hex); + + if(this.id_block.error.length == 0) + this.block_length += this.id_block.block_length; + + if(this.len_block.error.length == 0) + this.block_length += this.len_block.block_length; + + if(this.value_block.error.length == 0) + this.block_length += this.value_block.block_length; + + return result_offset; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.UNIVERSALSTRING.prototype.fromBuffer = + function(input_buffer) + { + /// Array with encoded string + + var copy_buffer = in_window.org.pkijs.copyBuffer(input_buffer); + + var value_view = new Uint8Array(copy_buffer); + + for(var i = 0; i < value_view.length; i = i + 4) + { + value_view[i] = value_view[i + 3]; + value_view[i + 1] = value_view[i + 2]; + value_view[i + 2] = 0x00; + value_view[i + 3] = 0x00; + } + + this.value_block.value = String.fromCharCode.apply(null, new Uint32Array(copy_buffer)); + }; + //************************************************************************************** + in_window.org.pkijs.asn1.UNIVERSALSTRING.prototype.fromString = + function(input_string) + { + /// String with UNIVERSALSTRING value + + var str_length = input_string.length; + + this.value_block.value_hex = new ArrayBuffer(str_length * 4); + var value_hex_view = new Uint8Array(this.value_block.value_hex); + + for(var i = 0; i < str_length; i++) + { + var code_buf = util_tobase(input_string.charCodeAt(i), 8); + var code_view = new Uint8Array(code_buf); + if(code_view.length > 4) + continue; + + var dif = 4 - code_view.length; + + for(var j = (code_view.length - 1) ; j >= 0; j--) + value_hex_view[i*4 + j + dif] = code_view[j]; + } + + this.value_block.value = input_string; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.UNIVERSALSTRING.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.UNIVERSALSTRING.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + local.SIMPLESTRING_value_block = + function() + { + local.hex_block.call(this, arguments[0]); + + /// Native string representation + this.value = ""; + this.is_hex_only = true; + }; + //************************************************************************************** + local.SIMPLESTRING_value_block.prototype = new local.hex_block(); + local.SIMPLESTRING_value_block.constructor = local.SIMPLESTRING_value_block; + //************************************************************************************** + local.SIMPLESTRING_value_block.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "SIMPLESTRING_value_block"; + }; + //************************************************************************************** + local.SIMPLESTRING_value_block.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = local.hex_block.prototype.toJSON.call(this); + + _object.block_name = local.SIMPLESTRING_value_block.prototype.block_name.call(this); + _object.value = this.value; + + return _object; + }; + //************************************************************************************** + local.SIMPLESTRING_block = + function() + { + in_window.org.pkijs.asn1.ASN1_block.call(this, arguments[0]); + + this.value_block = new local.SIMPLESTRING_value_block(); + + if(arguments[0] instanceof Object) + { + if("value" in arguments[0]) + local.SIMPLESTRING_block.prototype.fromString.call(this, arguments[0].value); + } + }; + //************************************************************************************** + local.SIMPLESTRING_block.prototype = new in_window.org.pkijs.asn1.ASN1_block(); + local.SIMPLESTRING_block.constructor = local.SIMPLESTRING_block; + //************************************************************************************** + local.SIMPLESTRING_block.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "SIMPLESTRING"; + }; + //************************************************************************************** + local.SIMPLESTRING_block.prototype.fromBER = + function(input_buffer, input_offset, input_length) + { + /// Base function for converting block from BER encoded array of bytes + /// ASN.1 BER encoded array + /// Offset in ASN.1 BER encoded array where decoding should be started + /// Maximum length of array of bytes which can be using in this function + + var result_offset = this.value_block.fromBER(input_buffer, input_offset, (this.len_block.is_indefinite_form == true) ? input_length : this.len_block.length); + if(result_offset == (-1)) + { + this.error = this.value_block.error; + return result_offset; + } + + local.SIMPLESTRING_block.prototype.fromBuffer.call(this, this.value_block.value_hex); + + if(this.id_block.error.length == 0) + this.block_length += this.id_block.block_length; + + if(this.len_block.error.length == 0) + this.block_length += this.len_block.block_length; + + if(this.value_block.error.length == 0) + this.block_length += this.value_block.block_length; + + return result_offset; + }; + //************************************************************************************** + local.SIMPLESTRING_block.prototype.fromBuffer = + function(input_buffer) + { + /// Array with encoded string + + this.value_block.value = String.fromCharCode.apply(null, new Uint8Array(input_buffer)); + }; + //************************************************************************************** + local.SIMPLESTRING_block.prototype.fromString = + function(input_string) + { + /// String with UNIVERSALSTRING value + var str_len = input_string.length; + + this.value_block.value_hex = new ArrayBuffer(str_len); + var view = new Uint8Array(this.value_block.value_hex); + + for(var i = 0; i < str_len; i++) + view[i] = input_string.charCodeAt(i); + + this.value_block.value = input_string; + }; + //************************************************************************************** + local.SIMPLESTRING_block.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON.call(this); + + _object.block_name = local.SIMPLESTRING_block.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.NUMERICSTRING = + function() + { + local.SIMPLESTRING_block.call(this, arguments[0]); + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 18; // NUMERICSTRING + }; + //************************************************************************************** + in_window.org.pkijs.asn1.NUMERICSTRING.prototype = new local.SIMPLESTRING_block(); + in_window.org.pkijs.asn1.NUMERICSTRING.constructor = in_window.org.pkijs.asn1.NUMERICSTRING; + //************************************************************************************** + in_window.org.pkijs.asn1.NUMERICSTRING.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "NUMERICSTRING"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.NUMERICSTRING.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = local.SIMPLESTRING_block.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.NUMERICSTRING.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.PRINTABLESTRING = + function() + { + local.SIMPLESTRING_block.call(this, arguments[0]); + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 19; // PRINTABLESTRING + }; + //************************************************************************************** + in_window.org.pkijs.asn1.PRINTABLESTRING.prototype = new local.SIMPLESTRING_block(); + in_window.org.pkijs.asn1.PRINTABLESTRING.constructor = in_window.org.pkijs.asn1.PRINTABLESTRING; + //************************************************************************************** + in_window.org.pkijs.asn1.PRINTABLESTRING.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "PRINTABLESTRING"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.PRINTABLESTRING.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = local.SIMPLESTRING_block.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.PRINTABLESTRING.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.TELETEXSTRING = + function() + { + local.SIMPLESTRING_block.call(this, arguments[0]); + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 20; // TELETEXSTRING + }; + //************************************************************************************** + in_window.org.pkijs.asn1.TELETEXSTRING.prototype = new local.SIMPLESTRING_block(); + in_window.org.pkijs.asn1.TELETEXSTRING.constructor = in_window.org.pkijs.asn1.TELETEXSTRING; + //************************************************************************************** + in_window.org.pkijs.asn1.TELETEXSTRING.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "TELETEXSTRING"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.TELETEXSTRING.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = local.SIMPLESTRING_block.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.TELETEXSTRING.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.VIDEOTEXSTRING = + function() + { + local.SIMPLESTRING_block.call(this, arguments[0]); + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 21; // VIDEOTEXSTRING + }; + //************************************************************************************** + in_window.org.pkijs.asn1.VIDEOTEXSTRING.prototype = new local.SIMPLESTRING_block(); + in_window.org.pkijs.asn1.VIDEOTEXSTRING.constructor = in_window.org.pkijs.asn1.VIDEOTEXSTRING; + //************************************************************************************** + in_window.org.pkijs.asn1.VIDEOTEXSTRING.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "VIDEOTEXSTRING"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.VIDEOTEXSTRING.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = local.SIMPLESTRING_block.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.VIDEOTEXSTRING.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.IA5STRING = + function() + { + local.SIMPLESTRING_block.call(this, arguments[0]); + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 22; // IA5STRING + }; + //************************************************************************************** + in_window.org.pkijs.asn1.IA5STRING.prototype = new local.SIMPLESTRING_block(); + in_window.org.pkijs.asn1.IA5STRING.constructor = in_window.org.pkijs.asn1.IA5STRING; + //************************************************************************************** + in_window.org.pkijs.asn1.IA5STRING.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "IA5STRING"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.IA5STRING.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = local.SIMPLESTRING_block.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.IA5STRING.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.GRAPHICSTRING = + function() + { + local.SIMPLESTRING_block.call(this, arguments[0]); + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 25; // GRAPHICSTRING + }; + //************************************************************************************** + in_window.org.pkijs.asn1.GRAPHICSTRING.prototype = new local.SIMPLESTRING_block(); + in_window.org.pkijs.asn1.GRAPHICSTRING.constructor = in_window.org.pkijs.asn1.GRAPHICSTRING; + //************************************************************************************** + in_window.org.pkijs.asn1.GRAPHICSTRING.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "GRAPHICSTRING"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.GRAPHICSTRING.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = local.SIMPLESTRING_block.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.GRAPHICSTRING.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.VISIBLESTRING = + function() + { + local.SIMPLESTRING_block.call(this, arguments[0]); + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 26; // VISIBLESTRING + }; + //************************************************************************************** + in_window.org.pkijs.asn1.VISIBLESTRING.prototype = new local.SIMPLESTRING_block(); + in_window.org.pkijs.asn1.VISIBLESTRING.constructor = in_window.org.pkijs.asn1.VISIBLESTRING; + //************************************************************************************** + in_window.org.pkijs.asn1.VISIBLESTRING.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "VISIBLESTRING"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.VISIBLESTRING.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = local.SIMPLESTRING_block.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.VISIBLESTRING.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.GENERALSTRING = + function() + { + local.SIMPLESTRING_block.call(this, arguments[0]); + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 27; // GENERALSTRING + }; + //************************************************************************************** + in_window.org.pkijs.asn1.GENERALSTRING.prototype = new local.SIMPLESTRING_block(); + in_window.org.pkijs.asn1.GENERALSTRING.constructor = in_window.org.pkijs.asn1.GENERALSTRING; + //************************************************************************************** + in_window.org.pkijs.asn1.GENERALSTRING.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "GENERALSTRING"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.GENERALSTRING.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = local.SIMPLESTRING_block.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.GENERALSTRING.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.CHARACTERSTRING = + function() + { + local.SIMPLESTRING_block.call(this, arguments[0]); + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 29; // CHARACTERSTRING + }; + //************************************************************************************** + in_window.org.pkijs.asn1.CHARACTERSTRING.prototype = new local.SIMPLESTRING_block(); + in_window.org.pkijs.asn1.CHARACTERSTRING.constructor = in_window.org.pkijs.asn1.CHARACTERSTRING; + //************************************************************************************** + in_window.org.pkijs.asn1.CHARACTERSTRING.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "CHARACTERSTRING"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.CHARACTERSTRING.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = local.SIMPLESTRING_block.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.CHARACTERSTRING.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Declaration of all date and time classes + //************************************************************************************** + in_window.org.pkijs.asn1.UTCTIME = + function() + { + in_window.org.pkijs.asn1.VISIBLESTRING.call(this, arguments[0]); + + this.year = 0; + this.month = 0; + this.day = 0; + this.hour = 0; + this.minute = 0; + this.second = 0; + + // #region Create UTCTIME from ASN.1 UTC string value + if((arguments[0] instanceof Object) && ("value" in arguments[0])) + { + in_window.org.pkijs.asn1.UTCTIME.prototype.fromString.call(this, arguments[0].value); + + this.value_block.value_hex = new ArrayBuffer(arguments[0].value.length); + var view = new Uint8Array(this.value_block.value_hex); + + for(var i = 0; i < arguments[0].value.length; i++) + view[i] = arguments[0].value.charCodeAt(i); + } + // #endregion + // #region Create UTCTIME from JavaScript Date type + if((arguments[0] instanceof Object) && ("value_date" in arguments[0])) + { + in_window.org.pkijs.asn1.UTCTIME.prototype.fromDate.call(this, arguments[0].value_date); + this.value_block.value_hex = in_window.org.pkijs.asn1.UTCTIME.prototype.toBuffer.call(this); + } + // #endregion + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 23; // UTCTIME + }; + //************************************************************************************** + in_window.org.pkijs.asn1.UTCTIME.prototype = new in_window.org.pkijs.asn1.VISIBLESTRING(); + in_window.org.pkijs.asn1.UTCTIME.constructor = in_window.org.pkijs.asn1.UTCTIME; + //************************************************************************************** + in_window.org.pkijs.asn1.UTCTIME.prototype.fromBER = + function(input_buffer, input_offset, input_length) + { + /// Base function for converting block from BER encoded array of bytes + /// ASN.1 BER encoded array + /// Offset in ASN.1 BER encoded array where decoding should be started + /// Maximum length of array of bytes which can be using in this function + + var result_offset = this.value_block.fromBER(input_buffer, input_offset, (this.len_block.is_indefinite_form == true) ? input_length : this.len_block.length); + if(result_offset == (-1)) + { + this.error = this.value_block.error; + return result_offset; + } + + in_window.org.pkijs.asn1.UTCTIME.prototype.fromBuffer.call(this, this.value_block.value_hex); + + if(this.id_block.error.length == 0) + this.block_length += this.id_block.block_length; + + if(this.len_block.error.length == 0) + this.block_length += this.len_block.block_length; + + if(this.value_block.error.length == 0) + this.block_length += this.value_block.block_length; + + return result_offset; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.UTCTIME.prototype.fromBuffer = + function(input_buffer) + { + in_window.org.pkijs.asn1.UTCTIME.prototype.fromString.call(this, String.fromCharCode.apply(null, new Uint8Array(input_buffer))); + }; + //************************************************************************************** + in_window.org.pkijs.asn1.UTCTIME.prototype.toBuffer = + function() + { + var str = in_window.org.pkijs.asn1.UTCTIME.prototype.toString.call(this); + + var buffer = new ArrayBuffer(str.length); + var view = new Uint8Array(buffer); + + for(var i = 0; i < str.length; i++) + view[i] = str.charCodeAt(i); + + return buffer; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.UTCTIME.prototype.fromDate = + function(input_date) + { + /// Create "UTCTime" ASN.1 type from JavaScript "Date" type + + this.year = input_date.getUTCFullYear(); + this.month = input_date.getUTCMonth() + 1; + this.day = input_date.getUTCDate(); + this.hour = input_date.getUTCHours(); + this.minute = input_date.getUTCMinutes(); + this.second = input_date.getUTCSeconds(); + }; + //************************************************************************************** + in_window.org.pkijs.asn1.UTCTIME.prototype.toDate = + function() + { + return (new Date(Date.UTC(this.year, this.month - 1, this.day, this.hour, this.minute, this.second))); + }; + //************************************************************************************** + in_window.org.pkijs.asn1.UTCTIME.prototype.fromString = + function(input_string) + { + /// Create "UTCTime" ASN.1 type from JavaScript "String" type + + // #region Parse input string + var parser = /(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})Z/ig; + var parser_array = parser.exec(input_string); + if(parser_array === null) + { + this.error = "Wrong input string for convertion"; + return; + } + // #endregion + + // #region Store parsed values + var year = parseInt(parser_array[1], 10); + if(year >= 50) + this.year = 1900 + year; + else + this.year = 2000 + year; + + this.month = parseInt(parser_array[2], 10); + this.day = parseInt(parser_array[3], 10); + this.hour = parseInt(parser_array[4], 10); + this.minute = parseInt(parser_array[5], 10); + this.second = parseInt(parser_array[6], 10); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.asn1.UTCTIME.prototype.toString = + function() + { + var output_array = new Array(7); + + output_array[0] = in_window.org.pkijs.padNumber(((this.year < 2000) ? (this.year - 1900) : (this.year - 2000)), 2); + output_array[1] = in_window.org.pkijs.padNumber(this.month, 2); + output_array[2] = in_window.org.pkijs.padNumber(this.day, 2); + output_array[3] = in_window.org.pkijs.padNumber(this.hour, 2); + output_array[4] = in_window.org.pkijs.padNumber(this.minute, 2); + output_array[5] = in_window.org.pkijs.padNumber(this.second, 2); + output_array[6] = "Z"; + + return output_array.join(''); + }; + //************************************************************************************** + in_window.org.pkijs.asn1.UTCTIME.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "UTCTIME"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.UTCTIME.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = in_window.org.pkijs.asn1.VISIBLESTRING.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.UTCTIME.prototype.block_name.call(this); + _object.year = this.year; + _object.month = this.month; + _object.day = this.day; + _object.hour = this.hour; + _object.minute = this.minute; + _object.second = this.second; + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.GENERALIZEDTIME = + function() + { + in_window.org.pkijs.asn1.VISIBLESTRING.call(this, arguments[0]); + + this.year = 0; + this.month = 0; + this.day = 0; + this.hour = 0; + this.minute = 0; + this.second = 0; + this.millisecond = 0; + + // #region Create GeneralizedTime from ASN.1 string value + if((arguments[0] instanceof Object) && ("value" in arguments[0])) + { + in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.fromString.call(this, arguments[0].value); + + this.value_block.value_hex = new ArrayBuffer(arguments[0].value.length); + var view = new Uint8Array(this.value_block.value_hex); + + for(var i = 0; i < arguments[0].value.length; i++) + view[i] = arguments[0].value.charCodeAt(i); + } + // #endregion + // #region Create GeneralizedTime from JavaScript Date type + if((arguments[0] instanceof Object) && ("value_date" in arguments[0])) + { + in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.fromDate.call(this, arguments[0].value_date); + this.value_block.value_hex = in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.toBuffer.call(this); + } + // #endregion + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 24; // GENERALIZEDTIME + }; + //************************************************************************************** + in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype = new in_window.org.pkijs.asn1.VISIBLESTRING(); + in_window.org.pkijs.asn1.GENERALIZEDTIME.constructor = in_window.org.pkijs.asn1.GENERALIZEDTIME; + //************************************************************************************** + in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.fromBER = + function(input_buffer, input_offset, input_length) + { + /// Base function for converting block from BER encoded array of bytes + /// ASN.1 BER encoded array + /// Offset in ASN.1 BER encoded array where decoding should be started + /// Maximum length of array of bytes which can be using in this function + + var result_offset = this.value_block.fromBER(input_buffer, input_offset, (this.len_block.is_indefinite_form == true) ? input_length : this.len_block.length); + if(result_offset == (-1)) + { + this.error = this.value_block.error; + return result_offset; + } + + in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.fromBuffer.call(this, this.value_block.value_hex); + + if(this.id_block.error.length == 0) + this.block_length += this.id_block.block_length; + + if(this.len_block.error.length == 0) + this.block_length += this.len_block.block_length; + + if(this.value_block.error.length == 0) + this.block_length += this.value_block.block_length; + + return result_offset; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.fromBuffer = + function(input_buffer) + { + in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.fromString.call(this, String.fromCharCode.apply(null, new Uint8Array(input_buffer))); + }; + //************************************************************************************** + in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.toBuffer = + function() + { + var str = in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.toString.call(this); + + var buffer = new ArrayBuffer(str.length); + var view = new Uint8Array(buffer); + + for(var i = 0; i < str.length; i++) + view[i] = str.charCodeAt(i); + + return buffer; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.fromDate = + function(input_date) + { + /// Create "GeneralizedTime" ASN.1 type from JavaScript "Date" type + + this.year = input_date.getUTCFullYear(); + this.month = input_date.getUTCMonth(); + this.day = input_date.getUTCDate(); + this.hour = input_date.getUTCHours(); + this.minute = input_date.getUTCMinutes(); + this.second = input_date.getUTCSeconds(); + this.millisecond = input_date.getUTCMilliseconds(); + }; + //************************************************************************************** + in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.toDate = + function() + { + return (new Date(Date.UTC(this.year, this.month - 1, this.day, this.hour, this.minute, this.second, this.millisecond))); + }; + //************************************************************************************** + in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.fromString = + function(input_string) + { + /// Create "GeneralizedTime" ASN.1 type from JavaScript "String" type + + // #region Initial variables + var isUTC = false; + + var timeString = ""; + var dateTimeString = ""; + var fractionPart = 0; + + var parser; + + var hourDifference = 0; + var minuteDifference = 0; + // #endregion + + // #region Convert as UTC time + if(input_string[input_string.length - 1] == "Z") + { + timeString = input_string.substr(0, input_string.length - 1); + + isUTC = true; + } + // #endregion + // #region Convert as local time + else + { + var number = new Number(input_string[input_string.length - 1]); + + if(isNaN(number.valueOf())) + throw new Error("Wrong input string for convertion"); + + timeString = input_string; + } + // #endregion + + // #region Check that we do not have a "+" and "-" symbols inside UTC time + if(isUTC) + { + if(timeString.indexOf("+") != (-1)) + throw new Error("Wrong input string for convertion"); + + if(timeString.indexOf("-") != (-1)) + throw new Error("Wrong input string for convertion"); + } + // #endregion + // #region Get "UTC time difference" in case of local time + else + { + var multiplier = 1; + var differencePosition = timeString.indexOf("+"); + var differenceString = ""; + + if(differencePosition == (-1)) + { + differencePosition = timeString.indexOf("-"); + multiplier = (-1); + } + + if(differencePosition != (-1)) + { + differenceString = timeString.substr(differencePosition + 1); + timeString = timeString.substr(0, differencePosition); + + if((differenceString.length != 2) && (differenceString.length != 4)) + throw new Error("Wrong input string for convertion"); + + var number = new Number(differenceString.substr(0, 2)); + + if(isNaN(number.valueOf())) + throw new Error("Wrong input string for convertion"); + + hourDifference = multiplier * number; + + if(differenceString.length == 4) + { + number = new Number(differenceString.substr(2, 2)); + + if(isNaN(number.valueOf())) + throw new Error("Wrong input string for convertion"); + + minuteDifference = multiplier * number; + } + } + } + // #endregion + + // #region Get position of fraction point + var fractionPointPosition = timeString.indexOf("."); // Check for "full stop" symbol + if(fractionPointPosition == (-1)) + fractionPointPosition = timeString.indexOf(","); // Check for "comma" symbol + // #endregion + + // #region Get fraction part + if(fractionPointPosition != (-1)) + { + var fractionPartCheck = new Number("0" + timeString.substr(fractionPointPosition)); + + if(isNaN(fractionPartCheck.valueOf())) + throw new Error("Wrong input string for convertion"); + + fractionPart = fractionPartCheck.valueOf(); + + dateTimeString = timeString.substr(0, fractionPointPosition); + } + else + dateTimeString = timeString; + // #endregion + + // #region Parse internal date + switch(true) + { + case (dateTimeString.length == 8): // "YYYYMMDD" + parser = /(\d{4})(\d{2})(\d{2})/ig; + if(fractionPointPosition !== (-1)) + throw new Error("Wrong input string for convertion"); // Here we should not have a "fraction point" + break; + case (dateTimeString.length == 10): // "YYYYMMDDHH" + parser = /(\d{4})(\d{2})(\d{2})(\d{2})/ig; + + if(fractionPointPosition !== (-1)) + { + var fractionResult = 60 * fractionPart; + this.minute = Math.floor(fractionResult); + + fractionResult = 60 * (fractionResult - this.minute); + this.second = Math.floor(fractionResult); + + fractionResult = 1000 * (fractionResult - this.second); + this.millisecond = Math.floor(fractionResult); + } + break; + case (dateTimeString.length == 12): // "YYYYMMDDHHMM" + parser = /(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})/ig; + + if(fractionPointPosition !== (-1)) + { + var fractionResult = 60 * fractionPart; + this.second = Math.floor(fractionResult); + + fractionResult = 1000 * (fractionResult - this.second); + this.millisecond = Math.floor(fractionResult); + } + break; + case (dateTimeString.length == 14): // "YYYYMMDDHHMMSS" + parser = /(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/ig; + + if(fractionPointPosition !== (-1)) + { + var fractionResult = 1000 * fractionPart; + this.millisecond = Math.floor(fractionResult); + } + break; + default: + throw new Error("Wrong input string for convertion"); + } + // #endregion + + // #region Put parsed values at right places + var parser_array = parser.exec(dateTimeString); + if(parser_array == null) + throw new Error("Wrong input string for convertion"); + + for(var j = 1; j < parser_array.length; j++) + { + switch(j) + { + case 1: + this.year = parseInt(parser_array[j], 10); + break; + case 2: + this.month = parseInt(parser_array[j], 10) - 1; // In JavaScript we have month range as "0 - 11" + break; + case 3: + this.day = parseInt(parser_array[j], 10); + break; + case 4: + this.hour = parseInt(parser_array[j], 10) + hourDifference; + break; + case 5: + this.minute = parseInt(parser_array[j], 10) + minuteDifference; + break; + case 6: + this.second = parseInt(parser_array[j], 10); + break; + default: + throw new Error("Wrong input string for convertion"); + } + } + // #endregion + + // #region Get final date + if(isUTC == false) + { + var tempDate = new Date(this.year, this.month, this.day, this.hour, this.minute, this.second, this.millisecond); + + this.year = tempDate.getUTCFullYear(); + this.month = tempDate.getUTCMonth(); + this.day = tempDate.getUTCDay(); + this.hour = tempDate.getUTCHours(); + this.minute = tempDate.getUTCMinutes(); + this.second = tempDate.getUTCSeconds(); + this.millisecond = tempDate.getUTCMilliseconds(); + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.toString = + function() + { + var output_array = new Array(); + + output_array.push(in_window.org.pkijs.padNumber(this.year, 4)); + output_array.push(in_window.org.pkijs.padNumber(this.month, 2)); + output_array.push(in_window.org.pkijs.padNumber(this.day, 2)); + output_array.push(in_window.org.pkijs.padNumber(this.hour, 2)); + output_array.push(in_window.org.pkijs.padNumber(this.minute, 2)); + output_array.push(in_window.org.pkijs.padNumber(this.second, 2)); + if(this.millisecond != 0) + { + output_array.push("."); + output_array.push(in_window.org.pkijs.padNumber(this.millisecond, 3)); + } + output_array.push("Z"); + + return output_array.join(''); + }; + //************************************************************************************** + in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "GENERALIZEDTIME"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = in_window.org.pkijs.asn1.VISIBLESTRING.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.block_name.call(this); + _object.year = this.year; + _object.month = this.month; + _object.day = this.day; + _object.hour = this.hour; + _object.minute = this.minute; + _object.second = this.second; + _object.millisecond = this.millisecond; + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.DATE = + function() + { + in_window.org.pkijs.asn1.UTF8STRING.call(this, arguments[0]); + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 31; // DATE + }; + //************************************************************************************** + in_window.org.pkijs.asn1.DATE.prototype = new in_window.org.pkijs.asn1.UTF8STRING(); + in_window.org.pkijs.asn1.DATE.constructor = in_window.org.pkijs.asn1.DATE; + //************************************************************************************** + in_window.org.pkijs.asn1.DATE.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "DATE"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.DATE.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = in_window.org.pkijs.asn1.UTF8STRING.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.DATE.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.TIMEOFDAY = + function() + { + in_window.org.pkijs.asn1.UTF8STRING.call(this, arguments[0]); + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 32; // TIMEOFDAY + }; + //************************************************************************************** + in_window.org.pkijs.asn1.TIMEOFDAY.prototype = new in_window.org.pkijs.asn1.UTF8STRING(); + in_window.org.pkijs.asn1.TIMEOFDAY.constructor = in_window.org.pkijs.asn1.TIMEOFDAY; + //************************************************************************************** + in_window.org.pkijs.asn1.TIMEOFDAY.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "TIMEOFDAY"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.TIMEOFDAY.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = in_window.org.pkijs.asn1.UTF8STRING.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.TIMEOFDAY.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.DATETIME = + function() + { + in_window.org.pkijs.asn1.UTF8STRING.call(this, arguments[0]); + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 33; // DATETIME + }; + //************************************************************************************** + in_window.org.pkijs.asn1.DATETIME.prototype = new in_window.org.pkijs.asn1.UTF8STRING(); + in_window.org.pkijs.asn1.DATETIME.constructor = in_window.org.pkijs.asn1.DATETIME; + //************************************************************************************** + in_window.org.pkijs.asn1.DATETIME.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "DATETIME"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.DATETIME.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = in_window.org.pkijs.asn1.UTF8STRING.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.DATETIME.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.DURATION = + function() + { + in_window.org.pkijs.asn1.UTF8STRING.call(this, arguments[0]); + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 34; // DURATION + }; + //************************************************************************************** + in_window.org.pkijs.asn1.DURATION.prototype = new in_window.org.pkijs.asn1.UTF8STRING(); + in_window.org.pkijs.asn1.DURATION.constructor = in_window.org.pkijs.asn1.DURATION; + //************************************************************************************** + in_window.org.pkijs.asn1.DURATION.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "DURATION"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.DURATION.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = in_window.org.pkijs.asn1.UTF8STRING.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.DURATION.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.TIME = + function() + { + in_window.org.pkijs.asn1.UTF8STRING.call(this, arguments[0]); + + this.id_block.tag_class = 1; // UNIVERSAL + this.id_block.tag_number = 14; // TIME + }; + //************************************************************************************** + in_window.org.pkijs.asn1.TIME.prototype = new in_window.org.pkijs.asn1.UTF8STRING(); + in_window.org.pkijs.asn1.TIME.constructor = in_window.org.pkijs.asn1.TIME; + //************************************************************************************** + in_window.org.pkijs.asn1.TIME.prototype.block_name = + function() + { + /// Aux function, need to get a block name. Need to have it here for inhiritence + + return "TIME"; + }; + //************************************************************************************** + in_window.org.pkijs.asn1.TIME.prototype.toJSON = + function() + { + /// Convertion for the block to JSON object + + var _object = in_window.org.pkijs.asn1.UTF8STRING.prototype.toJSON.call(this); + + _object.block_name = in_window.org.pkijs.asn1.TIME.prototype.block_name.call(this); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Declaration of special ASN.1 schema type CHOICE + //************************************************************************************** + in_window.org.pkijs.asn1.CHOICE = + function() + { + if(arguments[0] instanceof Object) + { + this.value = in_window.org.pkijs.getValue(arguments[0], "value", new Array()); // Array of ASN.1 types for make a choice from + this.optional = in_window.org.pkijs.getValue(arguments[0], "optional", false); + } + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Declaration of special ASN.1 schema type ANY + //************************************************************************************** + in_window.org.pkijs.asn1.ANY = + function() + { + if(arguments[0] instanceof Object) + { + this.name = in_window.org.pkijs.getValue(arguments[0], "name", ""); + this.optional = in_window.org.pkijs.getValue(arguments[0], "optional", false); + } + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Declaration of special ASN.1 schema type REPEATED + //************************************************************************************** + in_window.org.pkijs.asn1.REPEATED = + function() + { + if(arguments[0] instanceof Object) + { + this.name = in_window.org.pkijs.getValue(arguments[0], "name", ""); + this.optional = in_window.org.pkijs.getValue(arguments[0], "optional", false); + this.value = in_window.org.pkijs.getValue(arguments[0], "value", new in_window.org.pkijs.asn1.ANY()); + this.local = in_window.org.pkijs.getValue(arguments[0], "local", false); // Could local or global array to store elements + } + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Major ASN.1 BER decoding function + //************************************************************************************** + function fromBER_raw(input_buffer, input_offset, input_length) + { + var incoming_offset = input_offset; // Need to store initial offset since "input_offset" is changing in the function + + // #region Local function changing a type for ASN.1 classes + function local_change_type(input_object, new_type) + { + if(input_object instanceof new_type) + return input_object; + + var new_object = new new_type(); + new_object.id_block = input_object.id_block; + new_object.len_block = input_object.len_block; + new_object.warnings = input_object.warnings; + new_object.value_before_decode = util_copybuf(input_object.value_before_decode); + + return new_object; + } + // #endregion + + // #region Create a basic ASN.1 type since we need to return errors and warnings from the function + var return_object = new in_window.org.pkijs.asn1.ASN1_block(); + // #endregion + + // #region Basic check for parameters + if(check_buffer_params(input_buffer, input_offset, input_length) === false) + { + return_object.error = "Wrong input parameters"; + return { + offset: (-1), + result: return_object + }; + } + // #endregion + + // #region Getting Uint8Array from ArrayBuffer + var int_buffer = new Uint8Array(input_buffer, input_offset, input_length); + // #endregion + + // #region Initial checks + if(int_buffer.length == 0) + { + this.error = "Zero buffer length"; + return { + offset: (-1), + result: return_object + }; + } + // #endregion + + // #region Decode indentifcation block of ASN.1 BER structure + var result_offset = return_object.id_block.fromBER(input_buffer, input_offset, input_length); + return_object.warnings.concat(return_object.id_block.warnings); + if(result_offset == (-1)) + { + return_object.error = return_object.id_block.error; + return { + offset: (-1), + result: return_object + }; + } + + input_offset = result_offset; + input_length -= return_object.id_block.block_length; + // #endregion + + // #region Decode length block of ASN.1 BER structure + result_offset = return_object.len_block.fromBER(input_buffer, input_offset, input_length); + return_object.warnings.concat(return_object.len_block.warnings); + if(result_offset == (-1)) + { + return_object.error = return_object.len_block.error; + return { + offset: (-1), + result: return_object + }; + } + + input_offset = result_offset; + input_length -= return_object.len_block.block_length; + // #endregion + + // #region Check for usign indefinite length form in encoding for primitive types + if((return_object.id_block.is_constructed == false) && + (return_object.len_block.is_indefinite_form == true)) + { + return_object.error = new String("Indefinite length form used for primitive encoding form"); + return { + offset: (-1), + result: return_object + }; + } + // #endregion + + // #region Switch ASN.1 block type + var new_asn1_type = in_window.org.pkijs.asn1.ASN1_block; + + switch(return_object.id_block.tag_class) + { + // #region UNIVERSAL + case 1: + // #region Check for reserved tag numbers + if((return_object.id_block.tag_number >= 37) && + (return_object.id_block.is_hex_only == false)) + { + return_object.error = "UNIVERSAL 37 and upper tags are reserved by ASN.1 standard"; + return { + offset: (-1), + result: return_object + }; + } + // #endregion + + switch(return_object.id_block.tag_number) + { + // #region EOC type + case 0: + // #region Check for EOC type + if((return_object.id_block.is_constructed == true) && + (return_object.len_block.length > 0)) + { + return_object.error = "Type [UNIVERSAL 0] is reserved"; + return { + offset: (-1), + result: return_object + }; + } + // #endregion + + new_asn1_type = in_window.org.pkijs.asn1.EOC; + + break; + // #endregion + // #region BOOLEAN type + case 1: + new_asn1_type = in_window.org.pkijs.asn1.BOOLEAN; + break; + // #endregion + // #region INTEGER type + case 2: + new_asn1_type = in_window.org.pkijs.asn1.INTEGER; + break; + // #endregion + // #region BITSTRING type + case 3: + new_asn1_type = in_window.org.pkijs.asn1.BITSTRING; + break; + // #endregion + // #region OCTETSTRING type + case 4: + new_asn1_type = in_window.org.pkijs.asn1.OCTETSTRING; + break; + // #endregion + // #region NULL type + case 5: + new_asn1_type = in_window.org.pkijs.asn1.NULL; + break; + // #endregion + // #region OBJECT IDENTIFIER type + case 6: + new_asn1_type = in_window.org.pkijs.asn1.OID; + break; + // #endregion + // #region ENUMERATED type + case 10: + new_asn1_type = in_window.org.pkijs.asn1.ENUMERATED; + break; + // #endregion + // #region UTF8STRING type + case 12: + new_asn1_type = in_window.org.pkijs.asn1.UTF8STRING; + break; + // #endregion + // #region TIME type + case 14: + new_asn1_type = in_window.org.pkijs.asn1.TIME; + break; + // #endregion + // #region ASN.1 reserved type + case 15: + return_object.error = "[UNIVERSAL 15] is reserved by ASN.1 standard"; + return { + offset: (-1), + result: return_object + }; + break; + // #endregion + // #region SEQUENCE type + case 16: + new_asn1_type = in_window.org.pkijs.asn1.SEQUENCE; + break; + // #endregion + // #region SET type + case 17: + new_asn1_type = in_window.org.pkijs.asn1.SET; + break; + // #endregion + // #region NUMERICSTRING type + case 18: + new_asn1_type = in_window.org.pkijs.asn1.NUMERICSTRING; + break; + // #endregion + // #region PRINTABLESTRING type + case 19: + new_asn1_type = in_window.org.pkijs.asn1.PRINTABLESTRING; + break; + // #endregion + // #region TELETEXSTRING type + case 20: + new_asn1_type = in_window.org.pkijs.asn1.TELETEXSTRING; + break; + // #endregion + // #region VIDEOTEXSTRING type + case 21: + new_asn1_type = in_window.org.pkijs.asn1.VIDEOTEXSTRING; + break; + // #endregion + // #region IA5STRING type + case 22: + new_asn1_type = in_window.org.pkijs.asn1.IA5STRING; + break; + // #endregion + // #region UTCTIME type + case 23: + new_asn1_type = in_window.org.pkijs.asn1.UTCTIME; + break; + // #endregion + // #region GENERALIZEDTIME type + case 24: + new_asn1_type = in_window.org.pkijs.asn1.GENERALIZEDTIME; + break; + // #endregion + // #region GRAPHICSTRING type + case 25: + new_asn1_type = in_window.org.pkijs.asn1.GRAPHICSTRING; + break; + // #endregion + // #region VISIBLESTRING type + case 26: + new_asn1_type = in_window.org.pkijs.asn1.VISIBLESTRING; + break; + // #endregion + // #region GENERALSTRING type + case 27: + new_asn1_type = in_window.org.pkijs.asn1.GENERALSTRING; + break; + // #endregion + // #region UNIVERSALSTRING type + case 28: + new_asn1_type = in_window.org.pkijs.asn1.UNIVERSALSTRING; + break; + // #endregion + // #region CHARACTERSTRING type + case 29: + new_asn1_type = in_window.org.pkijs.asn1.CHARACTERSTRING; + break; + // #endregion + // #region BMPSTRING type + case 30: + new_asn1_type = in_window.org.pkijs.asn1.BMPSTRING; + break; + // #endregion + // #region DATE type + case 31: + new_asn1_type = in_window.org.pkijs.asn1.DATE; + break; + // #endregion + // #region TIMEOFDAY type + case 32: + new_asn1_type = in_window.org.pkijs.asn1.TIMEOFDAY; + break; + // #endregion + // #region DATE-TIME type + case 33: + new_asn1_type = in_window.org.pkijs.asn1.DATETIME; + break; + // #endregion + // #region DURATION type + case 34: + new_asn1_type = in_window.org.pkijs.asn1.DURATION; + break; + // #endregion + // #region default + default: + { + var new_object; + + if(return_object.id_block.is_constructed == true) + new_object = new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED(); + else + new_object = new in_window.org.pkijs.asn1.ASN1_PRIMITIVE(); + + new_object.id_block = return_object.id_block; + new_object.len_block = return_object.len_block; + new_object.warnings = return_object.warnings; + + return_object = new_object; + + result_offset = return_object.fromBER(input_buffer, input_offset, input_length); + } + // #endregion + } + break; + // #endregion + // #region All other tag classes + case 2: // APPLICATION + case 3: // CONTEXT-SPECIFIC + case 4: // PRIVATE + default: + { + if(return_object.id_block.is_constructed == true) + new_asn1_type = in_window.org.pkijs.asn1.ASN1_CONSTRUCTED; + else + new_asn1_type = in_window.org.pkijs.asn1.ASN1_PRIMITIVE; + } + // #endregion + } + // #endregion + + // #region Change type and perform BER decoding + return_object = local_change_type(return_object, new_asn1_type); + result_offset = return_object.fromBER(input_buffer, input_offset, (return_object.len_block.is_indefinite_form == true) ? input_length : return_object.len_block.length); + // #endregion + + // #region Coping incoming buffer for entire ASN.1 block + return_object.value_before_decode = util_copybuf_offset(input_buffer, incoming_offset, return_object.block_length); + // #endregion + + return { + offset: result_offset, + result: return_object + }; + } + //************************************************************************************** + in_window.org.pkijs.fromBER = + function(input_buffer) + { + /// Major function for decoding ASN.1 BER array into internal library structuries + /// ASN.1 BER encoded array of bytes + + if(input_buffer.byteLength == 0) + { + var result = new in_window.org.pkijs.asn1.ASN1_block(); + result.error = "Input buffer has zero length"; + + return { + offset: (-1), + result: result + }; + } + + return fromBER_raw(input_buffer, 0, input_buffer.byteLength); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Major scheme verification function + //************************************************************************************** + in_window.org.pkijs.compareSchema = + function(root, input_asn1_data, input_asn1_schema) + { + // #region Special case for CHOICE schema element type + if(input_asn1_schema instanceof in_window.org.pkijs.asn1.CHOICE) + { + var choice_result = false; + + for(var j = 0; j < input_asn1_schema.value.length; j++) + { + var result = in_window.org.pkijs.compareSchema(root, input_asn1_data, input_asn1_schema.value[j]); + if(result.verified === true) + return { + verified: true, + result: root + }; + } + + if(choice_result === false) + { + var _result = { + verified: false, + result: { + error: "Wrong values for CHOICE type" + } + }; + + if(input_asn1_schema.hasOwnProperty('name')) + _result.name = input_asn1_schema.name; + + return _result; + } + } + // #endregion + + // #region Special case for ANY schema element type + if(input_asn1_schema instanceof in_window.org.pkijs.asn1.ANY) + { + // #region Add named component of ASN.1 schema + if(input_asn1_schema.hasOwnProperty('name')) + root[input_asn1_schema.name] = input_asn1_data; + // #endregion + + return { + verified: true, + result: root + }; + } + // #endregion + + // #region Initial check + if((root instanceof Object) === false) + return { + verified: false, + result: { error: "Wrong root object" } + }; + + if((input_asn1_data instanceof Object) === false) + return { + verified: false, + result: { error: "Wrong ASN.1 data" } + }; + + if((input_asn1_schema instanceof Object) === false) + return { + verified: false, + result: { error: "Wrong ASN.1 schema" } + }; + + if(('id_block' in input_asn1_schema) === false) + return { + verified: false, + result: { error: "Wrong ASN.1 schema" } + }; + // #endregion + + // #region Comparing id_block properties in ASN.1 data and ASN.1 schema + // #region Encode and decode ASN.1 schema id_block + /// This encoding/decoding is neccessary because could be an errors in schema definition + if(('fromBER' in input_asn1_schema.id_block) === false) + return { + verified: false, + result: { error: "Wrong ASN.1 schema" } + }; + + if(('toBER' in input_asn1_schema.id_block) === false) + return { + verified: false, + result: { error: "Wrong ASN.1 schema" } + }; + + var encoded_id = input_asn1_schema.id_block.toBER(false); + if(encoded_id.byteLength === 0) + return { + verified: false, + result: { error: "Error encoding id_block for ASN.1 schema" } + }; + + var decoded_offset = input_asn1_schema.id_block.fromBER(encoded_id, 0, encoded_id.byteLength); + if(decoded_offset === (-1)) + return { + verified: false, + result: { error: "Error decoding id_block for ASN.1 schema" } + }; + // #endregion + + // #region tag_class + if(input_asn1_schema.id_block.hasOwnProperty('tag_class') === false) + return { + verified: false, + result: { error: "Wrong ASN.1 schema" } + }; + + if(input_asn1_schema.id_block.tag_class !== input_asn1_data.id_block.tag_class) + return { + verified: false, + result: root + }; + // #endregion + // #region tag_number + if(input_asn1_schema.id_block.hasOwnProperty('tag_number') === false) + return { + verified: false, + result: { error: "Wrong ASN.1 schema" } + }; + + if(input_asn1_schema.id_block.tag_number !== input_asn1_data.id_block.tag_number) + return { + verified: false, + result: root + }; + // #endregion + // #region is_constructed + if(input_asn1_schema.id_block.hasOwnProperty('is_constructed') === false) + return { + verified: false, + result: { error: "Wrong ASN.1 schema" } + }; + + if(input_asn1_schema.id_block.is_constructed !== input_asn1_data.id_block.is_constructed) + return { + verified: false, + result: root + }; + // #endregion + // #region is_hex_only + if(('is_hex_only' in input_asn1_schema.id_block) === false) // Since 'is_hex_only' is an inhirited property + return { + verified: false, + result: { error: "Wrong ASN.1 schema" } + }; + + if(input_asn1_schema.id_block.is_hex_only !== input_asn1_data.id_block.is_hex_only) + return { + verified: false, + result: root + }; + // #endregion + // #region value_hex + if(input_asn1_schema.id_block.is_hex_only === true) + { + if(('value_hex' in input_asn1_schema.id_block) === false) // Since 'value_hex' is an inhirited property + return { + verified: false, + result: { error: "Wrong ASN.1 schema" } + }; + + var schema_view = new Uint8Array(input_asn1_schema.id_block.value_hex); + var asn1_view = new Uint8Array(input_asn1_data.id_block.value_hex); + + if(schema_view.length !== asn1_view.length) + return { + verified: false, + result: root + }; + + for(var i = 0; i < schema_view.length; i++) + { + if(schema_view[i] !== asn1_view[1]) + return { + verified: false, + result: root + }; + } + } + // #endregion + // #endregion + + // #region Add named component of ASN.1 schema + if(input_asn1_schema.hasOwnProperty('name')) + { + input_asn1_schema.name = input_asn1_schema.name.replace(/^\s+|\s+$/g, ''); + if(input_asn1_schema.name !== "") + root[input_asn1_schema.name] = input_asn1_data; + } + // #endregion + + // #region Getting next ASN.1 block for comparition + if(input_asn1_schema.id_block.is_constructed === true) + { + var admission = 0; + var result = { verified: false }; + + var max_length = input_asn1_schema.value_block.value.length; + + if(max_length > 0) + { + if(input_asn1_schema.value_block.value[0] instanceof in_window.org.pkijs.asn1.REPEATED) + max_length = input_asn1_data.value_block.value.length; + } + + // #region Special case when constructive value has no elements + if(max_length === 0) + return { + verified: true, + result: root + }; + // #endregion + + // #region Special case when "input_asn1_data" has no values and "input_asn1_schema" has all optional values + if((input_asn1_data.value_block.value.length === 0) && + (input_asn1_schema.value_block.value.length !== 0)) + { + var _optional = true; + + for(var i = 0; i < input_asn1_schema.value_block.value.length; i++) + _optional = _optional && (input_asn1_schema.value_block.value[i].optional || false); + + if(_optional === true) + { + return { + verified: true, + result: root + }; + } + else + { + // #region Delete early added name of block + if(input_asn1_schema.hasOwnProperty('name')) + { + input_asn1_schema.name = input_asn1_schema.name.replace(/^\s+|\s+$/g, ''); + if(input_asn1_schema.name !== "") + delete root[input_asn1_schema.name]; + } + // #endregion + + root.error = "Inconsistent object length"; + + return { + verified: false, + result: root + }; + } + } + // #endregion + + for(var i = 0; i < max_length; i++) + { + // #region Special case when there is an "optional" element of ASN.1 schema at the end + if((i - admission) >= input_asn1_data.value_block.value.length) + { + if(input_asn1_schema.value_block.value[i].optional === false) + { + var _result = { + verified: false, + result: root + }; + + root.error = "Inconsistent length between ASN.1 data and schema"; + + // #region Delete early added name of block + if(input_asn1_schema.hasOwnProperty('name')) + { + input_asn1_schema.name = input_asn1_schema.name.replace(/^\s+|\s+$/g, ''); + if(input_asn1_schema.name !== "") + { + delete root[input_asn1_schema.name]; + _result.name = input_asn1_schema.name; + } + } + // #endregion + + return _result; + } + } + // #endregion + else + { + // #region Special case for REPEATED type of ASN.1 schema element + if(input_asn1_schema.value_block.value[0] instanceof in_window.org.pkijs.asn1.REPEATED) + { + result = in_window.org.pkijs.compareSchema(root, input_asn1_data.value_block.value[i], input_asn1_schema.value_block.value[0].value); + if(result.verified === false) + { + if(input_asn1_schema.value_block.value[0].optional === true) + admission++; + else + { + // #region Delete early added name of block + if(input_asn1_schema.hasOwnProperty('name')) + { + input_asn1_schema.name = input_asn1_schema.name.replace(/^\s+|\s+$/g, ''); + if(input_asn1_schema.name !== "") + delete root[input_asn1_schema.name]; + } + // #endregion + + return result; + } + } + + if(("name" in input_asn1_schema.value_block.value[0]) && (input_asn1_schema.value_block.value[0].name.length > 0)) + { + var array_root = {}; + + if(("local" in input_asn1_schema.value_block.value[0]) && (input_asn1_schema.value_block.value[0].local === true)) + array_root = input_asn1_data; + else + array_root = root; + + if(typeof array_root[input_asn1_schema.value_block.value[0].name] === "undefined") + array_root[input_asn1_schema.value_block.value[0].name] = new Array(); + + array_root[input_asn1_schema.value_block.value[0].name].push(input_asn1_data.value_block.value[i]); + } + } + // #endregion + else + { + result = in_window.org.pkijs.compareSchema(root, input_asn1_data.value_block.value[i - admission], input_asn1_schema.value_block.value[i]); + if(result.verified === false) + { + if(input_asn1_schema.value_block.value[i].optional === true) + admission++; + else + { + // #region Delete early added name of block + if(input_asn1_schema.hasOwnProperty('name')) + { + input_asn1_schema.name = input_asn1_schema.name.replace(/^\s+|\s+$/g, ''); + if(input_asn1_schema.name !== "") + delete root[input_asn1_schema.name]; + } + // #endregion + + return result; + } + } + } + } + } + + if(result.verified === false) // The situation may take place if last element is "optional" and verification failed + { + var _result = { + verified: false, + result: root + }; + + // #region Delete early added name of block + if(input_asn1_schema.hasOwnProperty('name')) + { + input_asn1_schema.name = input_asn1_schema.name.replace(/^\s+|\s+$/g, ''); + if(input_asn1_schema.name !== "") + { + delete root[input_asn1_schema.name]; + _result.name = input_asn1_schema.name; + } + } + // #endregion + + return _result; + } + + return { + verified: true, + result: root + }; + } + // #endregion + // #region Ability to parse internal value for primitive-encoded value (value of OCTETSTRING, for example) + else + { + if( ("primitive_schema" in input_asn1_schema) && + ("value_hex" in input_asn1_data.value_block) ) + { + // #region Decoding of raw ASN.1 data + var asn1 = in_window.org.pkijs.fromBER(input_asn1_data.value_block.value_hex); + if(asn1.offset === (-1)) + { + var _result = { + verified: false, + result: asn1.result + }; + + // #region Delete early added name of block + if(input_asn1_schema.hasOwnProperty('name')) + { + input_asn1_schema.name = input_asn1_schema.name.replace(/^\s+|\s+$/g, ''); + if(input_asn1_schema.name !== "") + { + delete root[input_asn1_schema.name]; + _result.name = input_asn1_schema.name; + } + } + // #endregion + + return _result; + } + // #endregion + + return in_window.org.pkijs.compareSchema(root, asn1.result, input_asn1_schema.primitive_schema); + } + else + return { + verified: true, + result: root + }; + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.verifySchema = + function(input_buffer, input_schema) + { + // #region Initial check + if((input_schema instanceof Object) === false) + return { + verified: false, + result: { error: "Wrong ASN.1 schema type" } + }; + // #endregion + + // #region Decoding of raw ASN.1 data + var asn1 = in_window.org.pkijs.fromBER(input_buffer); + if(asn1.offset === (-1)) + return { + verified: false, + result: asn1.result + }; + // #endregion + + // #region Compare ASN.1 struct with input schema + return in_window.org.pkijs.compareSchema(asn1.result, asn1.result, input_schema); + // #endregion + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Major function converting JSON to ASN.1 objects + //************************************************************************************** + in_window.org.pkijs.fromJSON = + function(json) + { + /// Converting from JSON to ASN.1 objects + /// JSON string or object to convert to ASN.1 objects + }; + //************************************************************************************** + // #endregion + //************************************************************************************** +} +)(typeof exports !== "undefined" ? exports : window); \ No newline at end of file diff --git a/dom/u2f/tests/pkijs/common.js b/dom/u2f/tests/pkijs/common.js new file mode 100644 index 0000000000..93fa0ec0c5 --- /dev/null +++ b/dom/u2f/tests/pkijs/common.js @@ -0,0 +1,1542 @@ +ï»?* + * Copyright (c) 2014, GMO GlobalSign + * Copyright (c) 2015, Peculiar Ventures + * All rights reserved. + * + * Author 2014-2015, Yury Strozhevsky . + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + */ +( +function(in_window) +{ + //************************************************************************************** + // #region Declaration of global variables + //************************************************************************************** + // #region "org" namespace + if(typeof in_window.org === "undefined") + in_window.org = {}; + else + { + if(typeof in_window.org !== "object") + throw new Error("Name org already exists and it's not an object"); + } + // #endregion + + // #region "org.pkijs" namespace + if(typeof in_window.org.pkijs === "undefined") + in_window.org.pkijs = {}; + else + { + if(typeof in_window.org.pkijs !== "object") + throw new Error("Name org.pkijs already exists and it's not an object" + " but " + (typeof in_window.org.pkijs)); + } + // #endregion + + // #region "local" namespace + var local = {}; + // #endregion + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Settings for "crypto engine" + //************************************************************************************** + local.engine = { + name: "none", + crypto: null, + subtle: null + }; + + if(typeof window != "undefined") + { + if("crypto" in window) + { + var engineName = "webcrypto"; + var cryptoObject = window.crypto; + var subtleObject = null; + + // Apple Safari support + if("webkitSubtle" in window.crypto) + subtleObject = window.crypto.webkitSubtle; + + if("subtle" in window.crypto) + subtleObject = window.crypto.subtle; + + local.engine = { + name: engineName, + crypto: cryptoObject, + subtle: subtleObject + }; + } + } + //************************************************************************************** + in_window.org.pkijs.setEngine = + function(name, crypto, subtle) + { + /// Setting the global "crypto engine" parameters + /// Auxiliary name for "crypto engine" + /// Object handling all root cryptographic requests (in fact currently it must handle only "getRandomValues") + /// Object handling all main cryptographic requests + + local.engine = { + name: name, + crypto: crypto, + subtle: subtle + }; + }; + //************************************************************************************** + in_window.org.pkijs.getEngine = + function() + { + return local.engine; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Declaration of common functions + //************************************************************************************** + in_window.org.pkijs.emptyObject = + function() + { + this.toJSON = function() + { + return {}; + }; + this.toSchema = function() + { + return {}; + }; + }; + //************************************************************************************** + in_window.org.pkijs.getNames = + function(arg) + { + /// Get correct "names" array for all "schema" objects + + var names = {}; + + if(arg instanceof Object) + names = (arg.names || {}); + + return names; + }; + //************************************************************************************** + in_window.org.pkijs.inheriteObjectFields = + function(from) + { + for(var i in from.prototype) + { + if(typeof from.prototype[i] === "function") + continue; + + this[i] = from.prototype[i]; + } + }; + //************************************************************************************** + in_window.org.pkijs.getUTCDate = + function(date) + { + /// Making UTC date from local date + /// Date to convert from + + var current_date = date; + return new Date(current_date.getTime() + (current_date.getTimezoneOffset() * 60000)); + }; + //************************************************************************************** + in_window.org.pkijs.padNumber = + function(input_number, full_length) + { + var str = input_number.toString(10); + var dif = full_length - str.length; + + var padding = new Array(dif); + for(var i = 0; i < dif; i++) + padding[i] = '0'; + + var padding_string = padding.join(''); + + return padding_string.concat(str); + }; + //************************************************************************************** + in_window.org.pkijs.getValue = + function(args, item, default_value) + { + if(item in args) + return args[item]; + else + return default_value; + }; + //************************************************************************************** + in_window.org.pkijs.isEqual_view = + function(input_view1, input_view2) + { + /// Compare two Uint8Arrays + /// First Uint8Array for comparision + /// Second Uint8Array for comparision + + if(input_view1.length !== input_view2.length) + return false; + + for(var i = 0; i < input_view1.length; i++) + { + if(input_view1[i] != input_view2[i]) + return false; + } + + return true; + }; + //************************************************************************************** + in_window.org.pkijs.isEqual_buffer = + function(input_buffer1, input_buffer2) + { + /// Compare two array buffers + /// First ArrayBuffer for comparision + /// Second ArrayBuffer for comparision + + if(input_buffer1.byteLength != input_buffer2.byteLength) + return false; + + var view1 = new Uint8Array(input_buffer1); + var view2 = new Uint8Array(input_buffer2); + + return in_window.org.pkijs.isEqual_view(view1, view2); + }; + //************************************************************************************** + in_window.org.pkijs.concat_buffers = + function(input_buf1, input_buf2) + { + /// Concatenate two ArrayBuffers + /// First ArrayBuffer (first part of concatenated array) + /// Second ArrayBuffer (second part of concatenated array) + + var input_view1 = new Uint8Array(input_buf1); + var input_view2 = new Uint8Array(input_buf2); + + var ret_buf = new ArrayBuffer(input_buf1.byteLength + input_buf2.byteLength); + var ret_view = new Uint8Array(ret_buf); + + for(var i = 0; i < input_buf1.byteLength; i++) + ret_view[i] = input_view1[i]; + + for(var j = 0; j < input_buf2.byteLength; j++) + ret_view[input_buf1.byteLength + j] = input_view2[j]; + + return ret_buf; + }; + //************************************************************************************** + in_window.org.pkijs.copyBuffer = + function(input_buffer) + { + var result = new ArrayBuffer(input_buffer.byteLength); + + var resultView = new Uint8Array(result); + var inputView = new Uint8Array(input_buffer); + + for(var i = 0; i < inputView.length; i++) + resultView[i] = inputView[i]; + + return result; + }; + //************************************************************************************** + in_window.org.pkijs.getCrypto = + function() + { + var crypto_temp; + + if(local.engine.subtle !== null) + crypto_temp = local.engine.subtle; + + return crypto_temp; + }; + //************************************************************************************** + in_window.org.pkijs.stringPrep = + function(input_string) + { + /// String preparation function. In a future here will be realization of algorithm from RFC4518. + /// JavaScript string. As soon as for each ASN.1 string type we have a specific transformation function here we will work with pure JavaScript string + /// Formated string + + var result = input_string.replace(/^\s+|\s+$/g, ""); // Trim input string + result = result.replace(/\s+/g, " "); // Change all sequence of SPACE down to SPACE char + result = result.toLowerCase(); + + return result; + }; + //************************************************************************************** + in_window.org.pkijs.bufferToHexCodes = + function(input_buffer, input_offset, input_lenght) + { + var result = ""; + + var int_buffer = new Uint8Array(input_buffer, input_offset, input_lenght); + + for(var i = 0; i < int_buffer.length; i++) + { + var str = int_buffer[i].toString(16).toUpperCase(); + result = result + ((str.length === 1) ? "0" : "") + str; + } + + return result; + }; + //************************************************************************************** + in_window.org.pkijs.bufferFromHexCodes = + function(hexString) + { + /// Create an ArrayBuffer from string having hexdecimal codes + /// String to create ArrayBuffer from + + // #region Initial variables + var stringLength = hexString.length; + + var resultBuffer = new ArrayBuffer(stringLength >> 1); + var resultView = new Uint8Array(resultBuffer); + + var hex_map = {}; + + hex_map['0'] = 0x00; + hex_map['1'] = 0x01; + hex_map['2'] = 0x02; + hex_map['3'] = 0x03; + hex_map['4'] = 0x04; + hex_map['5'] = 0x05; + hex_map['6'] = 0x06; + hex_map['7'] = 0x07; + hex_map['8'] = 0x08; + hex_map['9'] = 0x09; + hex_map['A'] = 0x0A; + hex_map['a'] = 0x0A; + hex_map['B'] = 0x0B; + hex_map['b'] = 0x0B; + hex_map['C'] = 0x0C; + hex_map['c'] = 0x0C; + hex_map['D'] = 0x0D; + hex_map['d'] = 0x0D; + hex_map['E'] = 0x0E; + hex_map['e'] = 0x0E; + hex_map['F'] = 0x0F; + hex_map['f'] = 0x0F; + + var j = 0; + var temp = 0x00; + // #endregion + + // #region Convert char-by-char + for(var i = 0; i < stringLength; i++) + { + if(!(i % 2)) + temp = hex_map[hexString.charAt(i)] << 4; + else + { + temp |= hex_map[hexString.charAt(i)]; + + resultView[j] = temp; + j++; + } + } + // #endregion + + return resultBuffer; + }; + //************************************************************************************** + in_window.org.pkijs.getRandomValues = + function(view) + { + /// New array which gives a length for random value + + if(local.engine.crypto !== null) + return local.engine.crypto.getRandomValues(view); + else + throw new Error("No support for Web Cryptography API"); + }; + //************************************************************************************** + in_window.org.pkijs.getAlgorithmParameters = + function(algorithmName, operation) + { + /// Algorithm name to get common parameters for + /// Kind of operation: "sign", "encrypt", "generatekey", "importkey", "exportkey", "verify" + + var result = { + algorithm: {}, + usages: [] + }; + + switch(algorithmName.toUpperCase()) + { + case "RSASSA-PKCS1-V1_5": + switch(operation.toLowerCase()) + { + case "generatekey": + result = { + algorithm: { + name: "RSASSA-PKCS1-v1_5", + modulusLength: 2048, + publicExponent: new Uint8Array([0x01, 0x00, 0x01]), + hash: { + name: "SHA-256" + } + }, + usages: ["sign", "verify"] + }; + break; + case "verify": + case "sign": + case "importkey": + result = { + algorithm: { + name: "RSASSA-PKCS1-v1_5", + hash: { + name: "SHA-256" + } + }, + usages: ["verify"] // For importKey("pkcs8") usage must be "sign" only + }; + break; + case "exportkey": + default: + return { + algorithm: { + name: "RSASSA-PKCS1-v1_5" + }, + usages: [] + }; + } + break; + case "RSA-PSS": + switch(operation.toLowerCase()) + { + case "sign": + case "verify": + result = { + algorithm: { + name: "RSA-PSS", + hash: { + name: "SHA-1" + }, + saltLength: 20 + }, + usages: ["sign", "verify"] + }; + break; + case "generatekey": + result = { + algorithm: { + name: "RSA-PSS", + modulusLength: 2048, + publicExponent: new Uint8Array([0x01, 0x00, 0x01]), + hash: { + name: "SHA-1" + } + }, + usages: ["sign", "verify"] + }; + break; + case "importkey": + result = { + algorithm: { + name: "RSA-PSS", + hash: { + name: "SHA-1" + } + }, + usages: ["verify"] // For importKey("pkcs8") usage must be "sign" only + }; + break; + case "exportkey": + default: + return { + algorithm: { + name: "RSA-PSS" + }, + usages: [] + }; + } + break; + case "RSA-OAEP": + switch(operation.toLowerCase()) + { + case "encrypt": + case "decrypt": + result = { + algorithm: { + name: "RSA-OAEP" + }, + usages: ["encrypt", "decrypt"] + }; + break; + break; + case "generatekey": + result = { + algorithm: { + name: "RSA-OAEP", + modulusLength: 2048, + publicExponent: new Uint8Array([0x01, 0x00, 0x01]), + hash: { + name: "SHA-256" + } + }, + usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] + }; + break; + case "importkey": + result = { + algorithm: { + name: "RSA-OAEP", + hash: { + name: "SHA-256" + } + }, + usages: ["encrypt"] // encrypt for "spki" and decrypt for "pkcs8" + }; + break; + case "exportkey": + default: + return { + algorithm: { + name: "RSA-OAEP" + }, + usages: [] + }; + } + break; + case "ECDSA": + switch(operation.toLowerCase()) + { + case "generatekey": + result = { + algorithm: { + name: "ECDSA", + namedCurve: "P-256" + }, + usages: ["sign", "verify"] + }; + break; + case "importkey": + result = { + algorithm: { + name: "ECDSA", + namedCurve: "P-256" + }, + usages: ["verify"] // "sign" for "pkcs8" + }; + break; + case "verify": + case "sign": + result = { + algorithm: { + name: "ECDSA", + hash: { + name: "SHA-256" + } + }, + usages: ["sign"] + }; + break; + default: + return { + algorithm: { + name: "ECDSA" + }, + usages: [] + }; + } + break; + case "ECDH": + switch(operation.toLowerCase()) + { + case "exportkey": + case "importkey": + case "generatekey": + result = { + algorithm: { + name: "ECDH", + namedCurve: "P-256" + }, + usages: ["deriveKey", "deriveBits"] + }; + break; + case "derivekey": + case "derivebits": + result = { + algorithm: { + name: "ECDH", + namedCurve: "P-256", + public: [] // Must be a "publicKey" + }, + usages: ["encrypt", "decrypt"] + }; + break; + default: + return { + algorithm: { + name: "ECDH" + }, + usages: [] + }; + } + break; + case "AES-CTR": + switch(operation.toLowerCase()) + { + case "importkey": + case "exportkey": + case "generatekey": + result = { + algorithm: { + name: "AES-CTR", + length: 256 + }, + usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] + }; + break; + case "decrypt": + case "encrypt": + result = { + algorithm: { + name: "AES-CTR", + counter: new Uint8Array(16), + length: 10 + }, + usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] + }; + break; + default: + return { + algorithm: { + name: "AES-CTR" + }, + usages: [] + }; + } + break; + case "AES-CBC": + switch(operation.toLowerCase()) + { + case "importkey": + case "exportkey": + case "generatekey": + result = { + algorithm: { + name: "AES-CBC", + length: 256 + }, + usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] + }; + break; + case "decrypt": + case "encrypt": + result = { + algorithm: { + name: "AES-CBC", + iv: in_window.org.pkijs.getRandomValues(new Uint8Array(16)) // For "decrypt" the value should be replaced with value got on "encrypt" step + }, + usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] + }; + break; + default: + return { + algorithm: { + name: "AES-CBC" + }, + usages: [] + }; + } + break; + case "AES-GCM": + switch(operation.toLowerCase()) + { + case "importkey": + case "exportkey": + case "generatekey": + result = { + algorithm: { + name: "AES-GCM", + length: 256 + }, + usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] + }; + break; + case "decrypt": + case "encrypt": + result = { + algorithm: { + name: "AES-GCM", + iv: in_window.org.pkijs.getRandomValues(new Uint8Array(16)) // For "decrypt" the value should be replaced with value got on "encrypt" step + }, + usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] + }; + break; + default: + return { + algorithm: { + name: "AES-GCM" + }, + usages: [] + }; + } + break; + case "AES-KW": + switch(operation.toLowerCase()) + { + case "importkey": + case "exportkey": + case "generatekey": + case "wrapkey": + case "unwrapkey": + result = { + algorithm: { + name: "AES-KW", + length: 256 + }, + usages: ["wrapKey", "unwrapKey"] + }; + break; + default: + return { + algorithm: { + name: "AES-KW" + }, + usages: [] + }; + } + break; + case "HMAC": + switch(operation.toLowerCase()) + { + case "sign": + case "verify": + result = { + algorithm: { + name: "HMAC" + }, + usages: ["sign", "verify"] + }; + break; + case "importkey": + case "exportkey": + case "generatekey": + result = { + algorithm: { + name: "HMAC", + length: 32, + hash: { + name: "SHA-256" + } + }, + usages: ["sign", "verify"] + }; + break; + default: + return { + algorithm: { + name: "HMAC" + }, + usages: [] + }; + } + break; + case "HKDF": + switch(operation.toLowerCase()) + { + case "derivekey": + result = { + algorithm: { + name: "HKDF", + hash: "SHA-256", + salt: new Uint8Array(), + info: new Uint8Array() + }, + usages: ["encrypt", "decrypt"] + }; + break; + default: + return { + algorithm: { + name: "HKDF" + }, + usages: [] + }; + } + break; + case "PBKDF2": + switch(operation.toLowerCase()) + { + case "derivekey": + result = { + algorithm: { + name: "PBKDF2", + hash: { name: "SHA-256" }, + salt: new Uint8Array(), + iterations: 1000 + }, + usages: ["encrypt", "decrypt"] + }; + break; + default: + return { + algorithm: { + name: "PBKDF2" + }, + usages: [] + }; + } + break; + default: + } + + return result; + }; + //************************************************************************************** + in_window.org.pkijs.getOIDByAlgorithm = + function(algorithm) + { + /// Get OID for each specific WebCrypto algorithm + /// WebCrypto algorithm + + var result = ""; + + switch(algorithm.name.toUpperCase()) + { + case "RSASSA-PKCS1-V1_5": + switch(algorithm.hash.name.toUpperCase()) + { + case "SHA-1": + result = "1.2.840.113549.1.1.5"; + break; + case "SHA-256": + result = "1.2.840.113549.1.1.11"; + break; + case "SHA-384": + result = "1.2.840.113549.1.1.12"; + break; + case "SHA-512": + result = "1.2.840.113549.1.1.13"; + break; + default: + } + break; + case "RSA-PSS": + result = "1.2.840.113549.1.1.10"; + break; + case "RSA-OAEP": + result = "1.2.840.113549.1.1.7"; + break; + case "ECDSA": + switch(algorithm.hash.name.toUpperCase()) + { + case "SHA-1": + result = "1.2.840.10045.4.1"; + break; + case "SHA-256": + result = "1.2.840.10045.4.3.2"; + break; + case "SHA-384": + result = "1.2.840.10045.4.3.3"; + break; + case "SHA-512": + result = "1.2.840.10045.4.3.4"; + break; + default: + } + break; + case "ECDH": + switch(algorithm.kdf.toUpperCase()) // Non-standard addition - hash algorithm of KDF function + { + case "SHA-1": + result = "1.3.133.16.840.63.0.2"; // dhSinglePass-stdDH-sha1kdf-scheme + break; + case "SHA-256": + result = "1.3.132.1.11.1"; // dhSinglePass-stdDH-sha256kdf-scheme + break; + case "SHA-384": + result = "1.3.132.1.11.2"; // dhSinglePass-stdDH-sha384kdf-scheme + break; + case "SHA-512": + result = "1.3.132.1.11.3"; // dhSinglePass-stdDH-sha512kdf-scheme + break; + default: + } + break; + case "AES-CTR": + break; + case "AES-CBC": + switch(algorithm.length) + { + case 128: + result = "2.16.840.1.101.3.4.1.2"; + break; + case 192: + result = "2.16.840.1.101.3.4.1.22"; + break; + case 256: + result = "2.16.840.1.101.3.4.1.42"; + break; + default: + } + break; + case "AES-CMAC": + break; + case "AES-GCM": + switch(algorithm.length) + { + case 128: + result = "2.16.840.1.101.3.4.1.6"; + break; + case 192: + result = "2.16.840.1.101.3.4.1.26"; + break; + case 256: + result = "2.16.840.1.101.3.4.1.46"; + break; + default: + } + break; + case "AES-CFB": + switch(algorithm.length) + { + case 128: + result = "2.16.840.1.101.3.4.1.4"; + break; + case 192: + result = "2.16.840.1.101.3.4.1.24"; + break; + case 256: + result = "2.16.840.1.101.3.4.1.44"; + break; + default: + } + break; + case "AES-KW": + switch(algorithm.length) + { + case 128: + result = "2.16.840.1.101.3.4.1.5"; + break; + case 192: + result = "2.16.840.1.101.3.4.1.25"; + break; + case 256: + result = "2.16.840.1.101.3.4.1.45"; + break; + default: + } + break; + case "HMAC": + switch(algorithm.hash.name.toUpperCase()) + { + case "SHA-1": + result = "1.2.840.113549.2.7"; + break; + case "SHA-256": + result = "1.2.840.113549.2.9"; + break; + case "SHA-384": + result = "1.2.840.113549.2.10"; + break; + case "SHA-512": + result = "1.2.840.113549.2.11"; + break; + default: + } + break; + case "DH": + result = "1.2.840.113549.1.9.16.3.5"; + break; + case "SHA-1": + result = "1.3.14.3.2.26"; + break; + case "SHA-256": + result = "2.16.840.1.101.3.4.2.1"; + break; + case "SHA-384": + result = "2.16.840.1.101.3.4.2.2"; + break; + case "SHA-512": + result = "2.16.840.1.101.3.4.2.3"; + break; + case "CONCAT": + break; + case "HKDF": + break; + case "PBKDF2": + result = "1.2.840.113549.1.5.12"; + break; + // #region Special case - OIDs for ECC curves + case "P-256": + result = "1.2.840.10045.3.1.7"; + break; + case "P-384": + result = "1.3.132.0.34"; + break; + case "P-521": + result = "1.3.132.0.35"; + break; + // #endregion + + default: + } + + return result; + }; + //************************************************************************************** + in_window.org.pkijs.getAlgorithmByOID = + function(oid) + { + /// Get WebCrypto algorithm by wel-known OID + /// Wel-known OID to search for + + var result = {}; + + switch(oid) + { + case "1.2.840.113549.1.1.5": + result = { + name: "RSASSA-PKCS1-v1_5", + hash: { + name: "SHA-1" + } + }; + break; + case "1.2.840.113549.1.1.11": + result = { + name: "RSASSA-PKCS1-v1_5", + hash: { + name: "SHA-256" + } + }; + break; + case "1.2.840.113549.1.1.12": + result = { + name: "RSASSA-PKCS1-v1_5", + hash: { + name: "SHA-384" + } + }; + break; + case "1.2.840.113549.1.1.13": + result = { + name: "RSASSA-PKCS1-v1_5", + hash: { + name: "SHA-512" + } + }; + break; + case "1.2.840.113549.1.1.10": + result = { + name: "RSA-PSS" + }; + break; + case "1.2.840.113549.1.1.7": + result = { + name: "RSA-OAEP" + }; + break; + case "1.2.840.10045.4.1": + result = { + name: "ECDSA", + hash: { + name: "SHA-1" + } + }; + break; + case "1.2.840.10045.4.3.2": + result = { + name: "ECDSA", + hash: { + name: "SHA-256" + } + }; + break; + case "1.2.840.10045.4.3.3": + result = { + name: "ECDSA", + hash: { + name: "SHA-384" + } + }; + break; + case "1.2.840.10045.4.3.4": + result = { + name: "ECDSA", + hash: { + name: "SHA-512" + } + }; + break; + case "1.3.133.16.840.63.0.2": + result = { + name: "ECDH", + kdf: "SHA-1" + }; + break; + case "1.3.132.1.11.1": + result = { + name: "ECDH", + kdf: "SHA-256" + }; + break; + case "1.3.132.1.11.2": + result = { + name: "ECDH", + kdf: "SHA-384" + }; + break; + case "1.3.132.1.11.3": + result = { + name: "ECDH", + kdf: "SHA-512" + }; + break; + case "2.16.840.1.101.3.4.1.2": + result = { + name: "AES-CBC", + length: 128 + }; + break; + case "2.16.840.1.101.3.4.1.22": + result = { + name: "AES-CBC", + length: 192 + }; + break; + case "2.16.840.1.101.3.4.1.42": + result = { + name: "AES-CBC", + length: 256 + }; + break; + case "2.16.840.1.101.3.4.1.6": + result = { + name: "AES-GCM", + length: 128 + }; + break; + case "2.16.840.1.101.3.4.1.26": + result = { + name: "AES-GCM", + length: 192 + }; + break; + case "2.16.840.1.101.3.4.1.46": + result = { + name: "AES-GCM", + length: 256 + }; + break; + case "2.16.840.1.101.3.4.1.4": + result = { + name: "AES-CFB", + length: 128 + }; + break; + case "2.16.840.1.101.3.4.1.24": + result = { + name: "AES-CFB", + length: 192 + }; + break; + case "2.16.840.1.101.3.4.1.44": + result = { + name: "AES-CFB", + length: 256 + }; + break; + case "2.16.840.1.101.3.4.1.5": + result = { + name: "AES-KW", + length: 128 + }; + break; + case "2.16.840.1.101.3.4.1.25": + result = { + name: "AES-KW", + length: 192 + }; + break; + case "2.16.840.1.101.3.4.1.45": + result = { + name: "AES-KW", + length: 256 + }; + break; + case "1.2.840.113549.2.7": + result = { + name: "HMAC", + hash: { + name: "SHA-1" + } + }; + break; + case "1.2.840.113549.2.9": + result = { + name: "HMAC", + hash: { + name: "SHA-256" + } + }; + break; + case "1.2.840.113549.2.10": + result = { + name: "HMAC", + hash: { + name: "SHA-384" + } + }; + break; + case "1.2.840.113549.2.11": + result = { + name: "HMAC", + hash: { + name: "SHA-512" + } + }; + break; + case "1.2.840.113549.1.9.16.3.5": + result = { + name: "DH" + }; + break; + case "1.3.14.3.2.26": + result = { + name: "SHA-1" + }; + break; + case "2.16.840.1.101.3.4.2.1": + result = { + name: "SHA-256" + }; + break; + case "2.16.840.1.101.3.4.2.2": + result = { + name: "SHA-384" + }; + break; + case "2.16.840.1.101.3.4.2.3": + result = { + name: "SHA-512" + }; + break; + case "1.2.840.113549.1.5.12": + result = { + name: "PBKDF2" + }; + break; + // #region Special case - OIDs for ECC curves + case "1.2.840.10045.3.1.7": + result = { + name: "P-256" + }; + break; + case "1.3.132.0.34": + result = { + name: "P-384" + }; + break; + case "1.3.132.0.35": + result = { + name: "P-521" + }; + break; + // #endregion + + default: + } + + return result; + }; + //************************************************************************************** + in_window.org.pkijs.getHashAlgorithm = + function(signatureAlgorithm) + { + /// Getting hash algorithm by signature algorithm + /// Signature algorithm + + var result = ""; + + switch(signatureAlgorithm.algorithm_id) + { + case "1.2.840.10045.4.1": // ecdsa-with-SHA1 + case "1.2.840.113549.1.1.5": + result = "SHA-1"; + break; + case "1.2.840.10045.4.3.2": // ecdsa-with-SHA256 + case "1.2.840.113549.1.1.11": + result = "SHA-256"; + break; + case "1.2.840.10045.4.3.3": // ecdsa-with-SHA384 + case "1.2.840.113549.1.1.12": + result = "SHA-384"; + break; + case "1.2.840.10045.4.3.4": // ecdsa-with-SHA512 + case "1.2.840.113549.1.1.13": + result = "SHA-512"; + break; + case "1.2.840.113549.1.1.10": // RSA-PSS + { + var params; + + try + { + params = new in_window.org.pkijs.simpl.x509.RSASSA_PSS_params({ schema: signatureAlgorithm.algorithm_params }); + if("hashAlgorithm" in params) + { + var algorithm = in_window.org.pkijs.getAlgorithmByOID(params.hashAlgorithm.algorithm_id); + if(("name" in algorithm) === false) + return ""; + + result = algorithm.name; + } + else + result = "SHA-1"; + } + catch(ex) + { + } + } + break; + default: + } + + return result; + }; + //************************************************************************************** + in_window.org.pkijs.createCMSECDSASignature = + function(signatureBuffer) + { + /// Create CMS ECDSA signature from WebCrypto ECDSA signature + /// WebCrypto result of "sign" function + + // #region Initial check for correct length + if((signatureBuffer.byteLength % 2) != 0) + return new ArrayBuffer(0); + // #endregion + + // #region Initial variables + var i = 0; + var length = signatureBuffer.byteLength / 2; // There are two equal parts inside incoming ArrayBuffer + + var signatureView = new Uint8Array(signatureBuffer); + + var r_buffer = new ArrayBuffer(length); + var r_view = new Uint8Array(r_buffer); + var r_corrected_buffer; + var r_corrected_view; + + var s_buffer = new ArrayBuffer(length); + var s_view = new Uint8Array(s_buffer); + var s_corrected_buffer; + var s_corrected_view; + // #endregion + + // #region Get "r" part of ECDSA signature + for(; i < length; i++) + r_view[i] = signatureView[i]; + + if(r_view[0] & 0x80) + { + r_corrected_buffer = new ArrayBuffer(length + 1); + r_corrected_view = new Uint8Array(r_corrected_buffer); + + r_corrected_view[0] = 0x00; + + for(var j = 0; j < length; j++) + r_corrected_view[j + 1] = r_view[j]; + } + else + { + r_corrected_buffer = r_buffer; + r_corrected_view = r_view; + } + // #endregion + + // #region Get "s" part of ECDSA signature + for(; i < signatureBuffer.byteLength; i++) + s_view[i - length] = signatureView[i]; + + + if(s_view[0] & 0x80) + { + s_corrected_buffer = new ArrayBuffer(length + 1); + s_corrected_view = new Uint8Array(s_corrected_buffer); + + s_corrected_view[0] = 0x00; + + for(var j = 0; j < length; j++) + s_corrected_view[j + 1] = s_view[j]; + } + else + { + s_corrected_buffer = s_buffer; + s_corrected_view = s_view; + } + // #endregion + + // #region Create ASN.1 structure of CMS ECDSA signature + var r_integer = new in_window.org.pkijs.asn1.INTEGER(); + r_integer.value_block.is_hex_only = true; + r_integer.value_block.value_hex = in_window.org.pkijs.copyBuffer(r_corrected_buffer); + + var s_integer = new in_window.org.pkijs.asn1.INTEGER(); + s_integer.value_block.is_hex_only = true; + s_integer.value_block.value_hex = in_window.org.pkijs.copyBuffer(s_corrected_buffer); + + var asn1 = new in_window.org.pkijs.asn1.SEQUENCE({ + value: [ + r_integer, + s_integer + ] + }); + // #endregion + + return asn1.toBER(false); + }; + //************************************************************************************** + in_window.org.pkijs.createECDSASignatureFromCMS = + function(cmsSignature) + { + /// Create a single ArrayBuffer from CMS ECDSA signature + /// ASN.1 SEQUENCE contains CMS ECDSA signature + + // #region Initial variables + var length = 0; + + var r_start = 0; + var s_start = 0; + + var r_length = cmsSignature.value_block.value[0].value_block.value_hex.byteLength; + var s_length = cmsSignature.value_block.value[1].value_block.value_hex.byteLength; + // #endregion + + // #region Get length of final "ArrayBuffer" + var r_view = new Uint8Array(cmsSignature.value_block.value[0].value_block.value_hex); + if((r_view[0] === 0x00) && (r_view[1] & 0x80)) + { + length = r_length - 1; + r_start = 1; + } + else + length = r_length; + + var s_view = new Uint8Array(cmsSignature.value_block.value[1].value_block.value_hex); + if((s_view[0] === 0x00) && (s_view[1] & 0x80)) + { + length += s_length - 1; + s_start = 1; + } + else + length += s_length; + // #endregion + + // #region Copy values from CMS ECDSA signature + var result = new ArrayBuffer(length); + var result_view = new Uint8Array(result); + + for(var i = r_start; i < r_length; i++) + result_view[i - r_start] = r_view[i]; + + for(var i = s_start; i < s_length; i++) + result_view[i - s_start + r_length - r_start] = s_view[i]; + // #endregion + + return result; + }; + //************************************************************************************** + in_window.org.pkijs.getEncryptionAlgorithm = + function(algorithm) + { + /// Get encryption algorithm OID by WebCrypto algorithm's object + /// WebCrypto algorithm object + + var result = ""; + + switch(algorithm.name.toUpperCase()) + { + case "AES-CBC": + switch(algorithm.length) + { + case 128: + result = "2.16.840.1.101.3.4.1.2"; + break; + case 192: + result = "2.16.840.1.101.3.4.1.22"; + break; + case 256: + result = "2.16.840.1.101.3.4.1.42"; + break; + default: + } + break; + case "AES-GCM": + switch(algorithm.length) + { + case 128: + result = "2.16.840.1.101.3.4.1.6"; + break; + case 192: + result = "2.16.840.1.101.3.4.1.26"; + break; + case 256: + result = "2.16.840.1.101.3.4.1.46"; + break; + default: + } + break; + default: + } + + return result; + }; + //************************************************************************************** + in_window.org.pkijs.getAlgorithmByEncryptionOID = + function(oid) + { + /// Get encryption algorithm name by OID + /// OID of encryption algorithm + + var result = ""; + + switch(oid) + { + case "2.16.840.1.101.3.4.1.2": + case "2.16.840.1.101.3.4.1.22": + case "2.16.840.1.101.3.4.1.42": + result = "AES-CBC"; + break; + case "2.16.840.1.101.3.4.1.6": + case "2.16.840.1.101.3.4.1.26": + case "2.16.840.1.101.3.4.1.46": + result = "AES-GCM"; + break; + default: + } + + return result; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** +} +)(typeof exports !== "undefined" ? exports : window); \ No newline at end of file diff --git a/dom/u2f/tests/pkijs/x509_schema.js b/dom/u2f/tests/pkijs/x509_schema.js new file mode 100644 index 0000000000..c4e4b3b760 --- /dev/null +++ b/dom/u2f/tests/pkijs/x509_schema.js @@ -0,0 +1,1889 @@ +ï»?* + * Copyright (c) 2014, GMO GlobalSign + * Copyright (c) 2015, Peculiar Ventures + * All rights reserved. + * + * Author 2014-2015, Yury Strozhevsky . + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + */ +( +function(in_window) +{ + //************************************************************************************** + // #region Declaration of global variables + //************************************************************************************** + // #region "org" namespace + if(typeof in_window.org === "undefined") + in_window.org = {}; + else + { + if(typeof in_window.org !== "object") + throw new Error("Name org already exists and it's not an object"); + } + // #endregion + + // #region "org.pkijs" namespace + if(typeof in_window.org.pkijs === "undefined") + in_window.org.pkijs = {}; + else + { + if(typeof in_window.org.pkijs !== "object") + throw new Error("Name org.pkijs already exists and it's not an object" + " but " + (typeof in_window.org.pkijs)); + } + // #endregion + + // #region "org.pkijs.schema" namespace + if(typeof in_window.org.pkijs.schema === "undefined") + in_window.org.pkijs.schema = {}; + else + { + if(typeof in_window.org.pkijs.schema !== "object") + throw new Error("Name org.pkijs.schema already exists and it's not an object" + " but " + (typeof in_window.org.pkijs.schema)); + } + // #endregion + + // #region "org.pkijs.schema.x509" namespace + if(typeof in_window.org.pkijs.schema.x509 === "undefined") + in_window.org.pkijs.schema.x509 = {}; + else + { + if(typeof in_window.org.pkijs.schema.x509 !== "object") + throw new Error("Name org.pkijs.schema.x509 already exists and it's not an object" + " but " + (typeof in_window.org.pkijs.schema.x509)); + } + // #endregion + + // #region "local" namespace + var local = {}; + // #endregion + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "Time" type + //************************************************************************************** + in_window.org.pkijs.schema.TIME = + function(input_names, input_optional) + { + var names = in_window.org.pkijs.getNames(arguments[0]); + var optional = (input_optional || false); + + return (new in_window.org.pkijs.asn1.CHOICE({ + optional: optional, + value: [ + new in_window.org.pkijs.asn1.UTCTIME({ name: (names.utcTimeName || "") }), + new in_window.org.pkijs.asn1.GENERALIZEDTIME({ name: (names.generalTimeName || "") }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for X.509 v3 certificate (RFC5280) + //************************************************************************************** + local.tbsCertificate = + function() + { + //TBSCertificate ::= SEQUENCE { + // version [0] EXPLICIT Version DEFAULT v1, + // serialNumber CertificateSerialNumber, + // signature AlgorithmIdentifier, + // issuer Name, + // validity Validity, + // subject Name, + // subjectPublicKeyInfo SubjectPublicKeyInfo, + // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, + // -- If present, version MUST be v2 or v3 + // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, + // -- If present, version MUST be v2 or v3 + // extensions [3] EXPLICIT Extensions OPTIONAL + // -- If present, version MUST be v3 + //} + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || "tbsCertificate"), + value: [ + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + value: [ + new in_window.org.pkijs.asn1.INTEGER({ name: (names.tbsCertificate_version || "tbsCertificate.version") }) // EXPLICIT integer value + ] + }), + new in_window.org.pkijs.asn1.INTEGER({ name: (names.tbsCertificate_serialNumber || "tbsCertificate.serialNumber") }), + in_window.org.pkijs.schema.ALGORITHM_IDENTIFIER(names.signature || { + names: { + block_name: "tbsCertificate.signature" + } + }), + in_window.org.pkijs.schema.RDN(names.issuer || { + names: { + block_name: "tbsCertificate.issuer" + } + }), + new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.tbsCertificate_validity || "tbsCertificate.validity"), + value: [ + in_window.org.pkijs.schema.TIME(names.not_before || { + names: { + utcTimeName: "tbsCertificate.notBefore", + generalTimeName: "tbsCertificate.notBefore" + } + }), + in_window.org.pkijs.schema.TIME(names.not_after || { + names: { + utcTimeName: "tbsCertificate.notAfter", + generalTimeName: "tbsCertificate.notAfter" + } + }) + ] + }), + in_window.org.pkijs.schema.RDN(names.subject || { + names: { + block_name: "tbsCertificate.subject" + } + }), + in_window.org.pkijs.schema.PUBLIC_KEY_INFO(names.subjectPublicKeyInfo || { + names: { + block_name: "tbsCertificate.subjectPublicKeyInfo" + } + }), + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + name: (names.tbsCertificate_issuerUniqueID ||"tbsCertificate.issuerUniqueID"), + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + } + }), // IMPLICIT bistring value + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + name: (names.tbsCertificate_subjectUniqueID ||"tbsCertificate.subjectUniqueID"), + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 2 // [2] + } + }), // IMPLICIT bistring value + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 3 // [3] + }, + value: [in_window.org.pkijs.schema.EXTENSIONS(names.extensions || { + names: { + block_name: "tbsCertificate.extensions" + } + })] + }) // EXPLICIT SEQUENCE value + ] + })); + }; + //************************************************************************************** + in_window.org.pkijs.schema.CERT = + function() + { + //Certificate ::= SEQUENCE { + // tbsCertificate TBSCertificate, + // signatureAlgorithm AlgorithmIdentifier, + // signatureValue BIT STRING } + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + local.tbsCertificate(names.tbsCertificate), + in_window.org.pkijs.schema.ALGORITHM_IDENTIFIER(names.signatureAlgorithm || { + names: { + block_name: "signatureAlgorithm" + } + }), + new in_window.org.pkijs.asn1.BITSTRING({ name: (names.signatureValue || "signatureValue") }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for X.509 CRL (Certificate Revocation List)(RFC5280) + //************************************************************************************** + local.tbsCertList = + function() + { + //TBSCertList ::= SEQUENCE { + // version Version OPTIONAL, + // -- if present, MUST be v2 + // signature AlgorithmIdentifier, + // issuer Name, + // thisUpdate Time, + // nextUpdate Time OPTIONAL, + // revokedCertificates SEQUENCE OF SEQUENCE { + // userCertificate CertificateSerialNumber, + // revocationDate Time, + // crlEntryExtensions Extensions OPTIONAL + // -- if present, version MUST be v2 + // } OPTIONAL, + // crlExtensions [0] EXPLICIT Extensions OPTIONAL + // -- if present, version MUST be v2 + //} + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || "tbsCertList"), + value: [ + new in_window.org.pkijs.asn1.INTEGER({ + optional: true, + name: (names.tbsCertList_version || "tbsCertList.version"), + value: 2 + }), // EXPLICIT integer value (v2) + in_window.org.pkijs.schema.ALGORITHM_IDENTIFIER(names.signature || { + names: { + block_name: "tbsCertList.signature" + } + }), + in_window.org.pkijs.schema.RDN(names.issuer || { + names: { + block_name: "tbsCertList.issuer" + } + }), + in_window.org.pkijs.schema.TIME(names.tbsCertList_thisUpdate || { + names: { + utcTimeName: "tbsCertList.thisUpdate", + generalTimeName: "tbsCertList.thisUpdate" + } + }), + in_window.org.pkijs.schema.TIME(names.tbsCertList_thisUpdate || { + names: { + utcTimeName: "tbsCertList.nextUpdate", + generalTimeName: "tbsCertList.nextUpdate" + } + }, true), + new in_window.org.pkijs.asn1.SEQUENCE({ + optional: true, + value: [ + new in_window.org.pkijs.asn1.REPEATED({ + name: (names.tbsCertList_revokedCertificates || "tbsCertList.revokedCertificates"), + value: new in_window.org.pkijs.asn1.SEQUENCE({ + value: [ + new in_window.org.pkijs.asn1.INTEGER(), + in_window.org.pkijs.schema.TIME(), + in_window.org.pkijs.schema.EXTENSIONS({}, true) + ] + }) + }) + ] + }), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + value: [in_window.org.pkijs.schema.EXTENSIONS(names.crlExtensions || { + names: { + block_name: "tbsCertList.extensions" + } + })] + }) // EXPLICIT SEQUENCE value + ] + })); + }; + //************************************************************************************** + in_window.org.pkijs.schema.CRL = + function() + { + //CertificateList ::= SEQUENCE { + // tbsCertList TBSCertList, + // signatureAlgorithm AlgorithmIdentifier, + // signatureValue BIT STRING } + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || "CertificateList"), + value: [ + local.tbsCertList(arguments[0]), + in_window.org.pkijs.schema.ALGORITHM_IDENTIFIER(names.signatureAlgorithm || { + names: { + block_name: "signatureAlgorithm" + } + }), + new in_window.org.pkijs.asn1.BITSTRING({ name: (names.signatureValue || "signatureValue") }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for PKCS#10 certificate request + //************************************************************************************** + local.CertificationRequestInfo = + function() + { + //CertificationRequestInfo ::= SEQUENCE { + // version INTEGER { v1(0) } (v1,...), + // subject Name, + // subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }}, + // attributes [0] Attributes{{ CRIAttributes }} + //} + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.CertificationRequestInfo || "CertificationRequestInfo"), + value: [ + new in_window.org.pkijs.asn1.INTEGER({ name: (names.CertificationRequestInfo_version || "CertificationRequestInfo.version") }), + new in_window.org.pkijs.schema.RDN(names.subject || { + names: { + block_name: "CertificationRequestInfo.subject" + } + }), + new in_window.org.pkijs.schema.PUBLIC_KEY_INFO({ + names: { + block_name: "CertificationRequestInfo.subjectPublicKeyInfo" + } + }), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + value: [ + new in_window.org.pkijs.asn1.REPEATED({ + optional: true, // Because OpenSSL makes wrong "attributes" field + name: (names.CertificationRequestInfo_attributes || "CertificationRequestInfo.attributes"), + value: in_window.org.pkijs.schema.ATTRIBUTE(names.attributes || {}) + }) + ] + }) + ] + })); + }; + //************************************************************************************** + in_window.org.pkijs.schema.PKCS10 = + function() + { + //CertificationRequest ::= SEQUENCE { + // certificationRequestInfo CertificationRequestInfo, + // signatureAlgorithm AlgorithmIdentifier{{ SignatureAlgorithms }}, + // signature BIT STRING + //} + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: [ + local.CertificationRequestInfo(names.certificationRequestInfo || {}), + new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.signatureAlgorithm || "signatureAlgorithm"), + value: [ + new in_window.org.pkijs.asn1.OID(), + new in_window.org.pkijs.asn1.ANY({ optional: true }) + ] + }), + new in_window.org.pkijs.asn1.BITSTRING({ name: (names.signatureValue || "signatureValue") }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for PKCS#8 private key bag + //************************************************************************************** + in_window.org.pkijs.schema.PKCS8 = + function() + { + //PrivateKeyInfo ::= SEQUENCE { + // version Version, + // privateKeyAlgorithm AlgorithmIdentifier {{PrivateKeyAlgorithms}}, + // privateKey PrivateKey, + // attributes [0] Attributes OPTIONAL } + // + //Version ::= INTEGER {v1(0)} (v1,...) + // + //PrivateKey ::= OCTET STRING + // + //Attributes ::= SET OF Attribute + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: [ + new in_window.org.pkijs.asn1.INTEGER({ name: (names.version || "") }), + in_window.org.pkijs.schema.ALGORITHM_IDENTIFIER(names.privateKeyAlgorithm || ""), + new in_window.org.pkijs.asn1.OCTETSTRING({ name: (names.privateKey || "") }), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + value: [ + new in_window.org.pkijs.asn1.REPEATED({ + name: (names.attributes || ""), + value: in_window.org.pkijs.schema.ATTRIBUTE() + }) + ] + }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "GeneralName" type + //************************************************************************************** + local.BuiltInStandardAttributes = + function(optional_flag) + { + //BuiltInStandardAttributes ::= SEQUENCE { + // country-name CountryName OPTIONAL, + // administration-domain-name AdministrationDomainName OPTIONAL, + // network-address [0] IMPLICIT NetworkAddress OPTIONAL, + // terminal-identifier [1] IMPLICIT TerminalIdentifier OPTIONAL, + // private-domain-name [2] PrivateDomainName OPTIONAL, + // organization-name [3] IMPLICIT OrganizationName OPTIONAL, + // numeric-user-identifier [4] IMPLICIT NumericUserIdentifier OPTIONAL, + // personal-name [5] IMPLICIT PersonalName OPTIONAL, + // organizational-unit-names [6] IMPLICIT OrganizationalUnitNames OPTIONAL } + + if(typeof optional_flag === "undefined") + optional_flag = false; + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + optional: optional_flag, + value: [ + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + id_block: { + tag_class: 2, // APPLICATION-SPECIFIC + tag_number: 1 // [1] + }, + name: (names.country_name || ""), + value: [ + new in_window.org.pkijs.asn1.CHOICE({ + value: [ + new in_window.org.pkijs.asn1.NUMERICSTRING(), + new in_window.org.pkijs.asn1.PRINTABLESTRING() + ] + }) + ] + }), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + id_block: { + tag_class: 2, // APPLICATION-SPECIFIC + tag_number: 2 // [2] + }, + name: (names.administration_domain_name || ""), + value: [ + new in_window.org.pkijs.asn1.CHOICE({ + value: [ + new in_window.org.pkijs.asn1.NUMERICSTRING(), + new in_window.org.pkijs.asn1.PRINTABLESTRING() + ] + }) + ] + }), + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + name: (names.network_address || ""), + is_hex_only: true + }), + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + }, + name: (names.terminal_identifier || ""), + is_hex_only: true + }), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 2 // [2] + }, + name: (names.private_domain_name || ""), + value: [ + new in_window.org.pkijs.asn1.CHOICE({ + value: [ + new in_window.org.pkijs.asn1.NUMERICSTRING(), + new in_window.org.pkijs.asn1.PRINTABLESTRING() + ] + }) + ] + }), + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 3 // [3] + }, + name: (names.organization_name || ""), + is_hex_only: true + }), + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + optional: true, + name: (names.numeric_user_identifier || ""), + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 4 // [4] + }, + is_hex_only: true + }), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + name: (names.personal_name || ""), + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 5 // [5] + }, + value: [ + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + is_hex_only: true + }), + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + }, + is_hex_only: true + }), + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 2 // [2] + }, + is_hex_only: true + }), + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 3 // [3] + }, + is_hex_only: true + }) + ] + }), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + name: (names.organizational_unit_names || ""), + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 6 // [6] + }, + value: [ + new in_window.org.pkijs.asn1.REPEATED({ + value: new in_window.org.pkijs.asn1.PRINTABLESTRING() + }) + ] + }) + ] + })); + }; + //************************************************************************************** + local.BuiltInDomainDefinedAttributes = + function(optional_flag) + { + if(typeof optional_flag === "undefined") + optional_flag = false; + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + optional: optional_flag, + value: [ + new in_window.org.pkijs.asn1.PRINTABLESTRING(), + new in_window.org.pkijs.asn1.PRINTABLESTRING() + ] + })); + }; + //************************************************************************************** + local.ExtensionAttributes = + function(optional_flag) + { + if(typeof optional_flag === "undefined") + optional_flag = false; + + return (new in_window.org.pkijs.asn1.SET({ + optional: optional_flag, + value: [ + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + is_hex_only: true + }), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + }, + value: [new in_window.org.pkijs.asn1.ANY()] + }) + ] + })); + }; + //************************************************************************************** + in_window.org.pkijs.schema.GENERAL_NAME = + function() + { + /// By passing "names" array as an argument you can name each element of "GENERAL NAME" choice + + //GeneralName ::= CHOICE { + // otherName [0] OtherName, + // rfc822Name [1] IA5String, + // dNSName [2] IA5String, + // x400Address [3] ORAddress, + // directoryName [4] Name, + // ediPartyName [5] EDIPartyName, + // uniformResourceIdentifier [6] IA5String, + // iPAddress [7] OCTET STRING, + // registeredID [8] OBJECT IDENTIFIER } + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.CHOICE({ + value: [ + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.OID(), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + value: [new in_window.org.pkijs.asn1.ANY()] + }) + ] + }), + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + name: (names.block_name || ""), + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + } + }), + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + name: (names.block_name || ""), + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 2 // [2] + } + }), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 3 // [3] + }, + name: (names.block_name || ""), + value: [ + local.BuiltInStandardAttributes(false), + local.BuiltInDomainDefinedAttributes(true), + local.ExtensionAttributes(true) + ] + }), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 4 // [4] + }, + name: (names.block_name || ""), + value: [in_window.org.pkijs.schema.RDN(names.directoryName || {})] + }), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 5 // [5] + }, + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + value: [ + new in_window.org.pkijs.asn1.CHOICE({ + value: [ + new in_window.org.pkijs.asn1.TELETEXSTRING(), + new in_window.org.pkijs.asn1.PRINTABLESTRING(), + new in_window.org.pkijs.asn1.UNIVERSALSTRING(), + new in_window.org.pkijs.asn1.UTF8STRING(), + new in_window.org.pkijs.asn1.BMPSTRING() + ] + }) + ] + }), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + }, + value: [ + new in_window.org.pkijs.asn1.CHOICE({ + value: [ + new in_window.org.pkijs.asn1.TELETEXSTRING(), + new in_window.org.pkijs.asn1.PRINTABLESTRING(), + new in_window.org.pkijs.asn1.UNIVERSALSTRING(), + new in_window.org.pkijs.asn1.UTF8STRING(), + new in_window.org.pkijs.asn1.BMPSTRING() + ] + }) + ] + }) + ] + }), + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + name: (names.block_name || ""), + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 6 // [6] + } + }), + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + name: (names.block_name || ""), + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 7 // [7] + } + }), + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + name: (names.block_name || ""), + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 8 // [8] + } + }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "AlgorithmIdentifier" type + //************************************************************************************** + in_window.org.pkijs.schema.ALGORITHM_IDENTIFIER = + function() + { + //AlgorithmIdentifier ::= SEQUENCE { + // algorithm OBJECT IDENTIFIER, + // parameters ANY DEFINED BY algorithm OPTIONAL } + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + optional: (names.optional || false), + value: [ + new in_window.org.pkijs.asn1.OID({ name: (names.algorithmIdentifier || "") }), + new in_window.org.pkijs.asn1.ANY({ name: (names.algorithmParams || ""), optional: true }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "RSAPublicKey" type (RFC3447) + //************************************************************************************** + in_window.org.pkijs.schema.x509.RSAPublicKey = + function() + { + //RSAPublicKey ::= SEQUENCE { + // modulus INTEGER, -- n + // publicExponent INTEGER -- e + //} + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.INTEGER({ name: (names.modulus || "") }), + new in_window.org.pkijs.asn1.INTEGER({ name: (names.publicExponent || "") }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "OtherPrimeInfo" type (RFC3447) + //************************************************************************************** + in_window.org.pkijs.schema.x509.OtherPrimeInfo = + function() + { + //OtherPrimeInfo ::= SEQUENCE { + // prime INTEGER, -- ri + // exponent INTEGER, -- di + // coefficient INTEGER -- ti + //} + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.INTEGER({ name: (names.prime || "") }), + new in_window.org.pkijs.asn1.INTEGER({ name: (names.exponent || "") }), + new in_window.org.pkijs.asn1.INTEGER({ name: (names.coefficient || "") }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "RSAPrivateKey" type (RFC3447) + //************************************************************************************** + in_window.org.pkijs.schema.x509.RSAPrivateKey = + function() + { + //RSAPrivateKey ::= SEQUENCE { + // version Version, + // modulus INTEGER, -- n + // publicExponent INTEGER, -- e + // privateExponent INTEGER, -- d + // prime1 INTEGER, -- p + // prime2 INTEGER, -- q + // exponent1 INTEGER, -- d mod (p-1) + // exponent2 INTEGER, -- d mod (q-1) + // coefficient INTEGER, -- (inverse of q) mod p + // otherPrimeInfos OtherPrimeInfos OPTIONAL + //} + // + //OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.INTEGER({ name: (names.version || "") }), + new in_window.org.pkijs.asn1.INTEGER({ name: (names.modulus || "") }), + new in_window.org.pkijs.asn1.INTEGER({ name: (names.publicExponent || "") }), + new in_window.org.pkijs.asn1.INTEGER({ name: (names.privateExponent || "") }), + new in_window.org.pkijs.asn1.INTEGER({ name: (names.prime1 || "") }), + new in_window.org.pkijs.asn1.INTEGER({ name: (names.prime2 || "") }), + new in_window.org.pkijs.asn1.INTEGER({ name: (names.exponent1 || "") }), + new in_window.org.pkijs.asn1.INTEGER({ name: (names.exponent2 || "") }), + new in_window.org.pkijs.asn1.INTEGER({ name: (names.coefficient || "") }), + new in_window.org.pkijs.asn1.SEQUENCE({ + optional: true, + value: [ + new in_window.org.pkijs.asn1.REPEATED({ + name: (names.otherPrimeInfos || ""), + value: in_window.org.pkijs.schema.x509.OtherPrimeInfo(names.otherPrimeInfo || {}) + }) + ] + }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "RSASSA-PSS-params" type (RFC3447) + //************************************************************************************** + in_window.org.pkijs.schema.x509.RSASSA_PSS_params = + function() + { + //RSASSA-PSS-params ::= SEQUENCE { + // hashAlgorithm [0] HashAlgorithm DEFAULT sha1Identifier, + // maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT mgf1SHA1Identifier, + // saltLength [2] INTEGER DEFAULT 20, + // trailerField [3] INTEGER DEFAULT 1 } + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + optional: true, + value: [in_window.org.pkijs.schema.ALGORITHM_IDENTIFIER(names.hashAlgorithm || {})] + }), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + }, + optional: true, + value: [in_window.org.pkijs.schema.ALGORITHM_IDENTIFIER(names.maskGenAlgorithm || {})] + }), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 2 // [2] + }, + optional: true, + value: [new in_window.org.pkijs.asn1.INTEGER({ name: (names.saltLength || "") })] + }), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 3 // [3] + }, + optional: true, + value: [new in_window.org.pkijs.asn1.INTEGER({ name: (names.trailerField || "") })] + }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "SubjectPublicKeyInfo" type + //************************************************************************************** + in_window.org.pkijs.schema.PUBLIC_KEY_INFO = + function() + { + //SubjectPublicKeyInfo ::= SEQUENCE { + // algorithm AlgorithmIdentifier, + // subjectPublicKey BIT STRING } + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + in_window.org.pkijs.schema.ALGORITHM_IDENTIFIER(names.algorithm || {}), + new in_window.org.pkijs.asn1.BITSTRING({ name: (names.subjectPublicKey || "") }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "Attribute" type + //************************************************************************************** + in_window.org.pkijs.schema.ATTRIBUTE = + function() + { + // Attribute { ATTRIBUTE:IOSet } ::= SEQUENCE { + // type ATTRIBUTE.&id({IOSet}), + // values SET SIZE(1..MAX) OF ATTRIBUTE.&Type({IOSet}{@type}) + //} + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.OID({ name: (names.type || "") }), + new in_window.org.pkijs.asn1.SET({ + name: (names.set_name || ""), + value: [ + new in_window.org.pkijs.asn1.REPEATED({ + name: (names.values || ""), + value: new in_window.org.pkijs.asn1.ANY() + }) + ] + }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "AttributeTypeAndValue" type + //************************************************************************************** + in_window.org.pkijs.schema.ATTR_TYPE_AND_VALUE = + function() + { + //AttributeTypeAndValue ::= SEQUENCE { + // type AttributeType, + // value AttributeValue } + // + //AttributeType ::= OBJECT IDENTIFIER + // + //AttributeValue ::= ANY -- DEFINED BY AttributeType + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.OID({ name: (names.type || "") }), + new in_window.org.pkijs.asn1.ANY({ name: (names.value || "") }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "RelativeDistinguishedName" type + //************************************************************************************** + in_window.org.pkijs.schema.RDN = + function() + { + //RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + // + //RelativeDistinguishedName ::= + //SET SIZE (1..MAX) OF AttributeTypeAndValue + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.REPEATED({ + name: (names.repeated_sequence || ""), + value: new in_window.org.pkijs.asn1.SET({ + value: [ + new in_window.org.pkijs.asn1.REPEATED({ + name: (names.repeated_set || ""), + value: in_window.org.pkijs.schema.ATTR_TYPE_AND_VALUE(names.attr_type_and_value || {}) + }) + ] + }) + }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "Extension" type + //************************************************************************************** + in_window.org.pkijs.schema.EXTENSION = + function() + { + //Extension ::= SEQUENCE { + // extnID OBJECT IDENTIFIER, + // critical BOOLEAN DEFAULT FALSE, + // extnValue OCTET STRING + //} + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.OID({ name: (names.extnID || "") }), + new in_window.org.pkijs.asn1.BOOLEAN({ + name: (names.critical || ""), + optional: true + }), + new in_window.org.pkijs.asn1.OCTETSTRING({ name: (names.extnValue || "") }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "Extensions" type (sequence of many Extension) + //************************************************************************************** + in_window.org.pkijs.schema.EXTENSIONS = + function(input_names, input_optional) + { + //Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension + + var names = in_window.org.pkijs.getNames(arguments[0]); + var optional = input_optional || false; + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + optional: optional, + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.REPEATED({ + name: (names.extensions || ""), + value: in_window.org.pkijs.schema.EXTENSION(names.extension || {}) + }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "AuthorityKeyIdentifier" type of extension + //************************************************************************************** + in_window.org.pkijs.schema.x509.AuthorityKeyIdentifier = + function() + { + // AuthorityKeyIdentifier OID ::= 2.5.29.35 + // + //AuthorityKeyIdentifier ::= SEQUENCE { + // keyIdentifier [0] KeyIdentifier OPTIONAL, + // authorityCertIssuer [1] GeneralNames OPTIONAL, + // authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } + // + //KeyIdentifier ::= OCTET STRING + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + name: (names.keyIdentifier || ""), + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + } + }), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + }, + value: [ + new in_window.org.pkijs.asn1.REPEATED({ + name: (names.authorityCertIssuer || ""), + value: in_window.org.pkijs.schema.GENERAL_NAME() + }) + ] + }), + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + name: (names.authorityCertSerialNumber || ""), + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 2 // [2] + } + }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "PrivateKeyUsagePeriod" type of extension + //************************************************************************************** + in_window.org.pkijs.schema.x509.PrivateKeyUsagePeriod = + function() + { + // PrivateKeyUsagePeriod OID ::= 2.5.29.16 + // + //PrivateKeyUsagePeriod ::= SEQUENCE { + // notBefore [0] GeneralizedTime OPTIONAL, + // notAfter [1] GeneralizedTime OPTIONAL } + //-- either notBefore or notAfter MUST be present + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + name: (names.notBefore || ""), + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + } + }), + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + name: (names.notAfter || ""), + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + } + }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "IssuerAltName" and "SubjectAltName" types of extension + //************************************************************************************** + in_window.org.pkijs.schema.x509.AltName = + function() + { + // SubjectAltName OID ::= 2.5.29.17 + // IssuerAltName OID ::= 2.5.29.18 + // + // AltName ::= GeneralNames + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.REPEATED({ + name: (names.altNames || ""), + value: in_window.org.pkijs.schema.GENERAL_NAME() + }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "SubjectDirectoryAttributes" type of extension + //************************************************************************************** + in_window.org.pkijs.schema.x509.SubjectDirectoryAttributes = + function() + { + // SubjectDirectoryAttributes OID ::= 2.5.29.9 + // + //SubjectDirectoryAttributes ::= SEQUENCE SIZE (1..MAX) OF Attribute + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.REPEATED({ + name: (names.attributes || ""), + value: in_window.org.pkijs.schema.ATTRIBUTE() + }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "GeneralSubtree" type + //************************************************************************************** + in_window.org.pkijs.schema.x509.GeneralSubtree = + function() + { + //GeneralSubtree ::= SEQUENCE { + // base GeneralName, + // minimum [0] BaseDistance DEFAULT 0, + // maximum [1] BaseDistance OPTIONAL } + // + //BaseDistance ::= INTEGER (0..MAX) + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + in_window.org.pkijs.schema.GENERAL_NAME(names.base || ""), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + value: [new in_window.org.pkijs.asn1.INTEGER({ name: (names.minimum || "") })] + }), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + }, + value: [new in_window.org.pkijs.asn1.INTEGER({ name: (names.maximum || "") })] + }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "NameConstraints" type of extension + //************************************************************************************** + in_window.org.pkijs.schema.x509.NameConstraints = + function() + { + // NameConstraints OID ::= 2.5.29.30 + // + //NameConstraints ::= SEQUENCE { + // permittedSubtrees [0] GeneralSubtrees OPTIONAL, + // excludedSubtrees [1] GeneralSubtrees OPTIONAL } + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + value: [ + new in_window.org.pkijs.asn1.REPEATED({ + name: (names.permittedSubtrees || ""), + value: in_window.org.pkijs.schema.x509.GeneralSubtree() + }) + ] + }), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + }, + value: [ + new in_window.org.pkijs.asn1.REPEATED({ + name: (names.excludedSubtrees || ""), + value: in_window.org.pkijs.schema.x509.GeneralSubtree() + }) + ] + }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "BasicConstraints" type of extension + //************************************************************************************** + in_window.org.pkijs.schema.x509.BasicConstraints = + function() + { + // BasicConstraints OID ::= 2.5.29.19 + // + //BasicConstraints ::= SEQUENCE { + // cA BOOLEAN DEFAULT FALSE, + // pathLenConstraint INTEGER (0..MAX) OPTIONAL } + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.BOOLEAN({ + optional: true, + name: (names.cA || "") + }), + new in_window.org.pkijs.asn1.INTEGER({ + optional: true, + name: (names.pathLenConstraint || "") + }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "PolicyQualifierInfo" type + //************************************************************************************** + in_window.org.pkijs.schema.x509.PolicyQualifierInfo = + function() + { + //PolicyQualifierInfo ::= SEQUENCE { + // policyQualifierId PolicyQualifierId, + // qualifier ANY DEFINED BY policyQualifierId } + // + //id-qt OBJECT IDENTIFIER ::= { id-pkix 2 } + //id-qt-cps OBJECT IDENTIFIER ::= { id-qt 1 } + //id-qt-unotice OBJECT IDENTIFIER ::= { id-qt 2 } + // + //PolicyQualifierId ::= OBJECT IDENTIFIER ( id-qt-cps | id-qt-unotice ) + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.OID({ name: (names.policyQualifierId || "") }), + new in_window.org.pkijs.asn1.ANY({ name: (names.qualifier || "") }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "PolicyInformation" type + //************************************************************************************** + in_window.org.pkijs.schema.x509.PolicyInformation = + function() + { + //PolicyInformation ::= SEQUENCE { + // policyIdentifier CertPolicyId, + // policyQualifiers SEQUENCE SIZE (1..MAX) OF + // PolicyQualifierInfo OPTIONAL } + // + //CertPolicyId ::= OBJECT IDENTIFIER + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.OID({ name: (names.policyIdentifier || "") }), + new in_window.org.pkijs.asn1.SEQUENCE({ + optional: true, + value: [ + new in_window.org.pkijs.asn1.REPEATED({ + name: (names.policyQualifiers || ""), + value: in_window.org.pkijs.schema.x509.PolicyQualifierInfo() + }) + ] + }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "CertificatePolicies" type of extension + //************************************************************************************** + in_window.org.pkijs.schema.x509.CertificatePolicies = + function() + { + // CertificatePolicies OID ::= 2.5.29.32 + // + //certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.REPEATED({ + name: (names.certificatePolicies || ""), + value: in_window.org.pkijs.schema.x509.PolicyInformation() + }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "PolicyMapping" type + //************************************************************************************** + in_window.org.pkijs.schema.x509.PolicyMapping = + function() + { + //PolicyMapping ::= SEQUENCE { + // issuerDomainPolicy CertPolicyId, + // subjectDomainPolicy CertPolicyId } + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.OID({ name: (names.issuerDomainPolicy || "") }), + new in_window.org.pkijs.asn1.OID({ name: (names.subjectDomainPolicy || "") }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "PolicyMappings" type of extension + //************************************************************************************** + in_window.org.pkijs.schema.x509.PolicyMappings = + function() + { + // PolicyMappings OID ::= 2.5.29.33 + // + //PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF PolicyMapping + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.REPEATED({ + name: (names.mappings || ""), + value: in_window.org.pkijs.schema.x509.PolicyMapping() + }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "PolicyConstraints" type of extension + //************************************************************************************** + in_window.org.pkijs.schema.x509.PolicyConstraints = + function() + { + // PolicyMappings OID ::= 2.5.29.36 + // + //PolicyConstraints ::= SEQUENCE { + // requireExplicitPolicy [0] SkipCerts OPTIONAL, + // inhibitPolicyMapping [1] SkipCerts OPTIONAL } + // + //SkipCerts ::= INTEGER (0..MAX) + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + name: (names.requireExplicitPolicy || ""), + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + } + }), // IMPLICIT integer value + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + name: (names.inhibitPolicyMapping || ""), + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + } + }) // IMPLICIT integer value + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "ExtKeyUsage" type of extension + //************************************************************************************** + in_window.org.pkijs.schema.x509.ExtKeyUsage = + function() + { + // ExtKeyUsage OID ::= 2.5.29.37 + // + // ExtKeyUsage ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId + + // KeyPurposeId ::= OBJECT IDENTIFIER + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.REPEATED({ + name: (names.keyPurposes || ""), + value: new in_window.org.pkijs.asn1.OID() + }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "DistributionPoint" type + //************************************************************************************** + in_window.org.pkijs.schema.x509.DistributionPoint = + function() + { + //DistributionPoint ::= SEQUENCE { + // distributionPoint [0] DistributionPointName OPTIONAL, + // reasons [1] ReasonFlags OPTIONAL, + // cRLIssuer [2] GeneralNames OPTIONAL } + // + //DistributionPointName ::= CHOICE { + // fullName [0] GeneralNames, + // nameRelativeToCRLIssuer [1] RelativeDistinguishedName } + // + //ReasonFlags ::= BIT STRING { + // unused (0), + // keyCompromise (1), + // cACompromise (2), + // affiliationChanged (3), + // superseded (4), + // cessationOfOperation (5), + // certificateHold (6), + // privilegeWithdrawn (7), + // aACompromise (8) } + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + value: [ + new in_window.org.pkijs.asn1.CHOICE({ + value: [ + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + name: (names.distributionPoint || ""), + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + value: [ + new in_window.org.pkijs.asn1.REPEATED({ + name: (names.distributionPoint_names || ""), + value: in_window.org.pkijs.schema.GENERAL_NAME() + }) + ] + }), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + name: (names.distributionPoint || ""), + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + }, + value: in_window.org.pkijs.schema.RDN().value_block.value + }) + ] + }) + ] + }), + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + name: (names.reasons || ""), + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + } + }), // IMPLICIT bitstring value + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + name: (names.cRLIssuer || ""), + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 2 // [2] + }, + value: [ + new in_window.org.pkijs.asn1.REPEATED({ + name: (names.cRLIssuer_names || ""), + value: in_window.org.pkijs.schema.GENERAL_NAME() + }) + ] + }) // IMPLICIT bitstring value + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "CRLDistributionPoints" type of extension + //************************************************************************************** + in_window.org.pkijs.schema.x509.CRLDistributionPoints = + function() + { + // CRLDistributionPoints OID ::= 2.5.29.31 + // + //CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.REPEATED({ + name: (names.distributionPoints || ""), + value: in_window.org.pkijs.schema.x509.DistributionPoint() + }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "AccessDescription" type + //************************************************************************************** + in_window.org.pkijs.schema.x509.AccessDescription = + function() + { + //AccessDescription ::= SEQUENCE { + // accessMethod OBJECT IDENTIFIER, + // accessLocation GeneralName } + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.OID({ name: (names.accessMethod || "") }), + in_window.org.pkijs.schema.GENERAL_NAME(names.accessLocation || "") + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "AuthorityInfoAccess" and "SubjectInfoAccess" types of extension + //************************************************************************************** + in_window.org.pkijs.schema.x509.InfoAccess = + function() + { + // AuthorityInfoAccess OID ::= 1.3.6.1.5.5.7.1.1 + // SubjectInfoAccess OID ::= 1.3.6.1.5.5.7.1.11 + // + //AuthorityInfoAccessSyntax ::= + //SEQUENCE SIZE (1..MAX) OF AccessDescription + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.REPEATED({ + name: (names.accessDescriptions || ""), + value: in_window.org.pkijs.schema.x509.AccessDescription() + }) + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region ASN.1 schema definition for "IssuingDistributionPoint" type of extension + //************************************************************************************** + in_window.org.pkijs.schema.x509.IssuingDistributionPoint = + function() + { + // IssuingDistributionPoint OID ::= 2.5.29.28 + // + //IssuingDistributionPoint ::= SEQUENCE { + // distributionPoint [0] DistributionPointName OPTIONAL, + // onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE, + // onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE, + // onlySomeReasons [3] ReasonFlags OPTIONAL, + // indirectCRL [4] BOOLEAN DEFAULT FALSE, + // onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE } + // + //ReasonFlags ::= BIT STRING { + // unused (0), + // keyCompromise (1), + // cACompromise (2), + // affiliationChanged (3), + // superseded (4), + // cessationOfOperation (5), + // certificateHold (6), + // privilegeWithdrawn (7), + // aACompromise (8) } + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + value: [ + new in_window.org.pkijs.asn1.CHOICE({ + value: [ + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + name: (names.distributionPoint || ""), + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + value: [ + new in_window.org.pkijs.asn1.REPEATED({ + name: (names.distributionPoint_names || ""), + value: in_window.org.pkijs.schema.GENERAL_NAME() + }) + ] + }), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + name: (names.distributionPoint || ""), + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + }, + value: in_window.org.pkijs.schema.RDN().value_block.value + }) + ] + }) + ] + }), + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + name: (names.onlyContainsUserCerts || ""), + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + } + }), // IMPLICIT boolean value + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + name: (names.onlyContainsCACerts || ""), + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 2 // [2] + } + }), // IMPLICIT boolean value + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + name: (names.onlySomeReasons || ""), + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 3 // [3] + } + }), // IMPLICIT bitstring value + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + name: (names.indirectCRL || ""), + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 4 // [4] + } + }), // IMPLICIT boolean value + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + name: (names.onlyContainsAttributeCerts || ""), + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 5 // [5] + } + }) // IMPLICIT boolean value + ] + })); + }; + //************************************************************************************** + // #endregion + //************************************************************************************** +} +)(typeof exports !== "undefined" ? exports : window); \ No newline at end of file diff --git a/dom/u2f/tests/pkijs/x509_simpl.js b/dom/u2f/tests/pkijs/x509_simpl.js new file mode 100644 index 0000000000..c00e79d55b --- /dev/null +++ b/dom/u2f/tests/pkijs/x509_simpl.js @@ -0,0 +1,7239 @@ +/* + * Copyright (c) 2014, GMO GlobalSign + * Copyright (c) 2015, Peculiar Ventures + * All rights reserved. + * + * Author 2014-2015, Yury Strozhevsky . + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + */ +( +function(in_window) +{ + //************************************************************************************** + // #region Declaration of global variables + //************************************************************************************** + // #region "org" namespace + if(typeof in_window.org === "undefined") + in_window.org = {}; + else + { + if(typeof in_window.org !== "object") + throw new Error("Name org already exists and it's not an object"); + } + // #endregion + + // #region "org.pkijs" namespace + if(typeof in_window.org.pkijs === "undefined") + in_window.org.pkijs = {}; + else + { + if(typeof in_window.org.pkijs !== "object") + throw new Error("Name org.pkijs already exists and it's not an object" + " but " + (typeof in_window.org.pkijs)); + } + // #endregion + + // #region "org.pkijs.simpl" namespace + if(typeof in_window.org.pkijs.simpl === "undefined") + in_window.org.pkijs.simpl = {}; + else + { + if(typeof in_window.org.pkijs.simpl !== "object") + throw new Error("Name org.pkijs.simpl already exists and it's not an object" + " but " + (typeof in_window.org.pkijs.simpl)); + } + // #endregion + + // #region "org.pkijs.simpl.x509" namespace + if(typeof in_window.org.pkijs.simpl.x509 === "undefined") + in_window.org.pkijs.simpl.x509 = {}; + else + { + if(typeof in_window.org.pkijs.simpl.x509 !== "object") + throw new Error("Name org.pkijs.simpl.x509 already exists and it's not an object" + " but " + (typeof in_window.org.pkijs.simpl.x509)); + } + // #endregion + + // #region "local" namespace + var local = {}; + // #endregion + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "Time" type + //************************************************************************************** + in_window.org.pkijs.simpl.TIME = + function() + { + // #region Internal properties of the object + this.type = 0; // 0 - UTCTime; 1 - GeneralizedTime; 2 - empty value + this.value = new Date(0, 0, 0); + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.TIME.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.type = (arguments[0].type || 0); + this.value = (arguments[0].value || (new Date(0, 0, 0))); + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.TIME.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.TIME({ + names: { + utcTimeName: "utcTimeName", + generalTimeName: "generalTimeName" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for TIME"); + // #endregion + + // #region Get internal properties from parsed schema + if("utcTimeName" in asn1.result) + { + this.type = 0; + this.value = asn1.result.utcTimeName.toDate(); + } + if("generalTimeName" in asn1.result) + { + this.type = 1; + this.value = asn1.result.generalTimeName.toDate(); + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.TIME.prototype.toSchema = + function() + { + // #region Construct and return new ASN.1 schema for this object + var result = {}; + + if(this.type === 0) + result = new in_window.org.pkijs.asn1.UTCTIME({ value_date: this.value }); + if(this.type === 1) + result = new in_window.org.pkijs.asn1.GENERALIZEDTIME({ value_date: this.value }); + + return result; + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.TIME.prototype.toJSON = + function() + { + return { + type: this.type, + value: this.value + }; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "GeneralName" type + //************************************************************************************** + in_window.org.pkijs.simpl.GENERAL_NAME = + function() + { + // #region Internal properties of the object + this.NameType = 9; // Name type - from a tagged value (0 for "otherName", 1 for "rfc822Name" etc.) + this.Name = {}; + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.GENERAL_NAME.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.NameType = arguments[0].NameType || 9; + this.Name = arguments[0].Name || {}; + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.GENERAL_NAME.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.GENERAL_NAME({ + names: { + block_name: "block_name", + otherName: "otherName", + rfc822Name: "rfc822Name", + dNSName: "dNSName", + x400Address: "x400Address", + directoryName: { + names: { + block_name: "directoryName" + } + }, + ediPartyName: "ediPartyName", + uniformResourceIdentifier: "uniformResourceIdentifier", + iPAddress: "iPAddress", + registeredID: "registeredID" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for GENERAL_NAME"); + // #endregion + + // #region Get internal properties from parsed schema + this.NameType = asn1.result["block_name"].id_block.tag_number; + + switch(this.NameType) + { + case 0: // otherName + this.Name = asn1.result["block_name"]; + break; + case 1: // rfc822Name + dNSName + uniformResourceIdentifier + case 2: + case 6: + { + var value = asn1.result["block_name"]; + + value.id_block.tag_class = 1; // UNIVERSAL + value.id_block.tag_number = 22; // IA5STRING + + var value_ber = value.toBER(false); + + this.Name = in_window.org.pkijs.fromBER(value_ber).result.value_block.value; + } + break; + case 3: // x400Address + this.Name = asn1.result["block_name"]; + break; + case 4: // directoryName + this.Name = new in_window.org.pkijs.simpl.RDN({ schema: asn1.result["directoryName"] }); + break; + case 5: // ediPartyName + this.Name = asn1.result["ediPartyName"]; + break; + case 7: // iPAddress + this.Name = new in_window.org.pkijs.asn1.OCTETSTRING({ value_hex: asn1.result["block_name"].value_block.value_hex }); + break; + case 8: // registeredID + { + var value = asn1.result["block_name"]; + + value.id_block.tag_class = 1; // UNIVERSAL + value.id_block.tag_number = 6; // OID + + var value_ber = value.toBER(false); + + this.Name = in_window.org.pkijs.fromBER(value_ber).result.value_block.toString(); // Getting a string representation of the OID + } + break; + default: + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.GENERAL_NAME.prototype.toSchema = + function(schema) + { + // #region Construct and return new ASN.1 schema for this object + switch(this.NameType) + { + case 0: + case 3: + case 5: + return new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: this.NameType + }, + value: [ + this.Name + ] + }); + + break; + case 1: + case 2: + case 6: + { + var value = new in_window.org.pkijs.asn1.IA5STRING({ value: this.Name }); + + value.id_block.tag_class = 3; + value.id_block.tag_number = this.NameType; + + return value; + } + break; + case 4: + return new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 4 + }, + value: [this.Name.toSchema()] + }); + break; + case 7: + { + var value = this.Name; + + value.id_block.tag_class = 3; + value.id_block.tag_number = this.NameType; + + return value; + } + break; + case 8: + { + var value = new in_window.org.pkijs.asn1.OID({ value: this.Name }); + + value.id_block.tag_class = 3; + value.id_block.tag_number = this.NameType; + + return value; + } + break; + default: + return in_window.org.pkijs.schema.GENERAL_NAME(); + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.GENERAL_NAME.prototype.toJSON = + function() + { + var _object = { + NameType: this.NameType + }; + + if((typeof this.Name) === "string") + _object.Name = this.Name; + else + _object.Name = this.Name.toJSON(); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "GeneralNames" type + //************************************************************************************** + in_window.org.pkijs.simpl.GENERAL_NAMES = + function() + { + // #region Internal properties of the object + this.names = new Array(); // Array of "org.pkijs.simpl.GENERAL_NAME" + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.GENERAL_NAMES.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.names = arguments[0].names || new Array(); // Array of "org.pkijs.simpl.GENERAL_NAME" + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.GENERAL_NAMES.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + new in_window.org.pkijs.asn1.SEQUENCE({ + value: [ + new in_window.org.pkijs.asn1.REPEATED({ + name: "names", + value: in_window.org.pkijs.schema.GENERAL_NAME() + }) + ] + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for GENERAL_NAMES"); + // #endregion + + // #region Get internal properties from parsed schema + var n = asn1.result["names"]; + + for(var i = 0; i < n.length; i++) + this.names.push(new in_window.org.pkijs.simpl.GENERAL_NAME({ schema: n[i] })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.GENERAL_NAMES.prototype.toSchema = + function(schema) + { + // #region Construct and return new ASN.1 schema for this object + var output_array = new Array(); + + for(var i = 0; i < this.names.length; i++) + output_array.push(this.names[i].toSchema()); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: output_array + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.GENERAL_NAMES.prototype.toJSON = + function() + { + var _names = new Array(); + + for(var i = 0; i < this.names.length; i++) + _names.push(this.names[i].toJSON()); + + return { + names: _names + }; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "AlgorithmIdentifier" type + //************************************************************************************** + in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER = + function() + { + // #region Internal properties of the object + this.algorithm_id = ""; + // OPTIONAL this.algorithm_params = new in_window.org.pkijs.asn1.NULL(); + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.algorithm_id = arguments[0].algorithm_id || ""; + if("algorithm_params" in arguments[0]) + this.algorithm_params = arguments[0].algorithm_params; + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.ALGORITHM_IDENTIFIER({ + names: { + algorithmIdentifier: "algorithm", + algorithmParams: "params" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for ALGORITHM_IDENTIFIER"); + // #endregion + + // #region Get internal properties from parsed schema + this.algorithm_id = asn1.result.algorithm.value_block.toString(); + if("params" in asn1.result) + this.algorithm_params = asn1.result.params; + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER.prototype.toSchema = + function() + { + // #region Create array for output sequence + var output_array = new Array(); + + output_array.push(new in_window.org.pkijs.asn1.OID({ value: this.algorithm_id })); + if("algorithm_params" in this) + output_array.push(this.algorithm_params); + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: output_array + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER.prototype.getCommonName = + function() + { + }; + //************************************************************************************** + in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER.prototype.toJSON = + function() + { + var _object = { + algorithm_id: this.algorithm_id + }; + + if("algorithm_params" in this) + _object.algorithm_params = this.algorithm_params.toJSON(); + + return _object; + }; + //************************************************************************************** + in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER.prototype.isEqual = + function(algorithmIdentifier) + { + /// Check that two "ALGORITHM_IDENTIFIERs" are equal + /// The algorithm identifier to compare with + + // #region Check input type + if((algorithmIdentifier instanceof in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER) == false) + return false; + // #endregion + + // #region Check "algorithm_id" + if(this.algorithm_id != algorithmIdentifier.algorithm_id) + return false; + // #endregion + + // #region Check "algorithm_params" + if("algorithm_params" in this) + { + if("algorithm_params" in algorithmIdentifier) + { + return JSON.stringify(this.algorithm_params) == JSON.stringify(algorithmIdentifier.algorithm_params); + } + else + return false; + } + else + { + if("algorithm_params" in algorithmIdentifier) + return false; + } + // #endregion + + return true; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "RSAPublicKey" type (RFC3447) + //************************************************************************************** + in_window.org.pkijs.simpl.x509.RSAPublicKey = + function() + { + // #region Internal properties of the object + this.modulus = new in_window.org.pkijs.asn1.INTEGER(); + this.publicExponent = new in_window.org.pkijs.asn1.INTEGER(); + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.x509.RSAPublicKey.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.modulus = arguments[0].modulus || new in_window.org.pkijs.asn1.INTEGER(); + this.publicExponent = arguments[0].publicExponent || new in_window.org.pkijs.asn1.INTEGER(); + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.RSAPublicKey.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.x509.RSAPublicKey({ + names: { + modulus: "modulus", + publicExponent: "publicExponent" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for RSAPublicKey"); + // #endregion + + // #region Get internal properties from parsed schema + this.modulus = asn1.result["modulus"]; + this.publicExponent = asn1.result["publicExponent"]; + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.RSAPublicKey.prototype.toSchema = + function() + { + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: [ + this.modulus, + this.publicExponent + ] + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.RSAPublicKey.prototype.toJSON = + function() + { + return { + modulus: this.modulus.toJSON(), + publicExponent: this.publicExponent.toJSON() + }; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "OtherPrimeInfo" type (RFC3447) + //************************************************************************************** + in_window.org.pkijs.simpl.x509.OtherPrimeInfo = + function() + { + // #region Internal properties of the object + this.prime = new in_window.org.pkijs.asn1.INTEGER(); + this.exponent = new in_window.org.pkijs.asn1.INTEGER(); + this.coefficient = new in_window.org.pkijs.asn1.INTEGER(); + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.x509.OtherPrimeInfo.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.prime = arguments[0].prime || new in_window.org.pkijs.asn1.INTEGER(); + this.exponent = arguments[0].exponent || new in_window.org.pkijs.asn1.INTEGER(); + this.coefficient = arguments[0].coefficient || new in_window.org.pkijs.asn1.INTEGER(); + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.OtherPrimeInfo.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.x509.OtherPrimeInfo({ + names: { + prime: "prime", + exponent: "exponent", + coefficient: "coefficient" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for OtherPrimeInfo"); + // #endregion + + // #region Get internal properties from parsed schema + this.prime = asn1.result["prime"]; + this.exponent = asn1.result["exponent"]; + this.coefficient = asn1.result["coefficient"]; + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.OtherPrimeInfo.prototype.toSchema = + function() + { + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: [ + this.prime, + this.exponent, + this.coefficient + ] + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.OtherPrimeInfo.prototype.toJSON = + function() + { + return { + prime: this.prime.toJSON(), + exponent: this.exponent.toJSON(), + coefficient: this.coefficient.toJSON() + }; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "RSAPrivateKey" type (RFC3447) + //************************************************************************************** + in_window.org.pkijs.simpl.x509.RSAPrivateKey = + function() + { + // #region Internal properties of the object + this.version = 0; + this.modulus = new in_window.org.pkijs.asn1.INTEGER(); + this.publicExponent = new in_window.org.pkijs.asn1.INTEGER(); + this.privateExponent = new in_window.org.pkijs.asn1.INTEGER(); + this.prime1 = new in_window.org.pkijs.asn1.INTEGER(); + this.prime2 = new in_window.org.pkijs.asn1.INTEGER(); + this.exponent1 = new in_window.org.pkijs.asn1.INTEGER(); + this.exponent2 = new in_window.org.pkijs.asn1.INTEGER(); + this.coefficient = new in_window.org.pkijs.asn1.INTEGER(); + // OPTIONAL this.otherPrimeInfos = new Array(); // Array of "OtherPrimeInfo" + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.x509.RSAPrivateKey.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.version = arguments[0].version || 0; + this.modulus = arguments[0].modulus || new in_window.org.pkijs.asn1.INTEGER(); + this.publicExponent = arguments[0].publicExponent || new in_window.org.pkijs.asn1.INTEGER(); + this.privateExponent = arguments[0].privateExponent || new in_window.org.pkijs.asn1.INTEGER(); + this.prime1 = arguments[0].prime1 || new in_window.org.pkijs.asn1.INTEGER(); + this.prime2 = arguments[0].prime2 || new in_window.org.pkijs.asn1.INTEGER(); + this.exponent1 = arguments[0].exponent1 || new in_window.org.pkijs.asn1.INTEGER(); + this.exponent2 = arguments[0].exponent2 || new in_window.org.pkijs.asn1.INTEGER(); + this.coefficient = arguments[0].coefficient || new in_window.org.pkijs.asn1.INTEGER(); + if("otherPrimeInfos" in arguments[0]) + this.otherPrimeInfos = arguments[0].otherPrimeInfos || new Array(); + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.RSAPrivateKey.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.x509.RSAPrivateKey({ + names: { + version: "version", + modulus: "modulus", + publicExponent: "publicExponent", + privateExponent: "privateExponent", + prime1: "prime1", + prime2: "prime2", + exponent1: "exponent1", + exponent2: "exponent2", + coefficient: "coefficient", + otherPrimeInfos: "otherPrimeInfos" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for RSAPrivateKey"); + // #endregion + + // #region Get internal properties from parsed schema + this.version = asn1.result["version"].value_block.value_dec; + this.modulus = asn1.result["modulus"]; + this.publicExponent = asn1.result["publicExponent"]; + this.privateExponent = asn1.result["privateExponent"]; + this.prime1 = asn1.result["prime1"]; + this.prime2 = asn1.result["prime2"]; + this.exponent1 = asn1.result["exponent1"]; + this.exponent2 = asn1.result["exponent2"]; + this.coefficient = asn1.result["coefficient"]; + + if("otherPrimeInfos" in asn1.result) + { + var otherPrimeInfos_array = asn1.result["otherPrimeInfos"]; + + for(var i = 0; i < otherPrimeInfos_array.length; i++) + this.otherPrimeInfos.push(new in_window.org.pkijs.simpl.x509.OtherPrimeInfo({ schema: otherPrimeInfos_array[i] })); + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.RSAPrivateKey.prototype.toSchema = + function() + { + // #region Create array for output sequence + var output_array = new Array(); + + output_array.push(new in_window.org.pkijs.asn1.INTEGER({ value: this.version })); + output_array.push(this.modulus); + output_array.push(this.publicExponent); + output_array.push(this.privateExponent); + output_array.push(this.prime1); + output_array.push(this.prime2); + output_array.push(this.exponent1); + output_array.push(this.exponent2); + output_array.push(this.coefficient); + + if("otherPrimeInfos" in this) + { + var otherPrimeInfos_array = new Array(); + + for(var i = 0; i < this.otherPrimeInfos.length; i++) + otherPrimeInfos_array.push(this.otherPrimeInfos[i].toSchema()); + + output_array.push(new in_window.org.pkijs.asn1.SEQUENCE({ value: otherPrimeInfos_array })); + } + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: output_array + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.RSAPrivateKey.prototype.toJSON = + function() + { + var _object = { + version: this.version, + modulus: this.modulus.toJSON(), + publicExponent: this.publicExponent.toJSON(), + privateExponent: this.privateExponent.toJSON(), + prime1: this.prime1.toJSON(), + prime2: this.prime2.toJSON(), + exponent1: this.exponent1.toJSON(), + exponent2: this.exponent2.toJSON(), + coefficient: this.coefficient.toJSON() + }; + + if("otherPrimeInfos" in this) + { + _object.otherPrimeInfos = new Array(); + + for(var i = 0; i < this.otherPrimeInfos.length; i++) + _object.otherPrimeInfos.push(this.otherPrimeInfos[i].toJSON()); + } + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "RSASSA_PSS_params" type (RFC3447) + //************************************************************************************** + in_window.org.pkijs.simpl.x509.RSASSA_PSS_params = + function() + { + // #region Internal properties of the object + // OPTIONAL this.hashAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); + // OPTIONAL this.maskGenAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); + // OPTIONAL this.saltLength = 20; // new in_window.org.pkijs.asn1.INTEGER(); + // OPTIONAL this.trailerField = 1; // new in_window.org.pkijs.asn1.INTEGER(); + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.x509.RSASSA_PSS_params.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + if("hashAlgorithm" in arguments[0]) + this.hashAlgorithm = arguments[0].hashAlgorithm; + + if("maskGenAlgorithm" in arguments[0]) + this.maskGenAlgorithm = arguments[0].maskGenAlgorithm; + + if("saltLength" in arguments[0]) + this.saltLength = arguments[0].saltLength; + + if("trailerField" in arguments[0]) + this.trailerField = arguments[0].trailerField; + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.RSASSA_PSS_params.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.x509.RSASSA_PSS_params({ + names: { + hashAlgorithm: { + names: { + block_name: "hashAlgorithm" + } + }, + maskGenAlgorithm: { + names: { + block_name: "maskGenAlgorithm" + } + }, + saltLength: "saltLength", + trailerField: "trailerField" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for RSASSA_PSS_params"); + // #endregion + + // #region Get internal properties from parsed schema + if("hashAlgorithm" in asn1.result) + this.hashAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ schema: asn1.result["hashAlgorithm"] }); + + if("maskGenAlgorithm" in asn1.result) + this.maskGenAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ schema: asn1.result["maskGenAlgorithm"] }); + + if("saltLength" in asn1.result) + this.saltLength = asn1.result["saltLength"].value_block.value_dec; + + if("trailerField" in asn1.result) + this.trailerField = asn1.result["trailerField"].value_block.value_dec; + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.RSASSA_PSS_params.prototype.toSchema = + function() + { + // #region Create array for output sequence + var output_array = new Array(); + + if("hashAlgorithm" in this) + output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + value: [this.hashAlgorithm.toSchema()] + })); + + if("maskGenAlgorithm" in this) + output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + }, + value: [this.maskGenAlgorithm.toSchema()] + })); + + if("saltLength" in this) + output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 2 // [2] + }, + value: [new in_window.org.pkijs.asn1.INTEGER({ value: this.saltLength })] + })); + + if("trailerField" in this) + output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 3 // [3] + }, + value: [new in_window.org.pkijs.asn1.INTEGER({ value: this.trailerField })] + })); + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: output_array + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.RSASSA_PSS_params.prototype.toJSON = + function() + { + var _object = {}; + + if("hashAlgorithm" in this) + _object.hashAlgorithm = this.hashAlgorithm.toJSON(); + + if("maskGenAlgorithm" in this) + _object.maskGenAlgorithm = this.maskGenAlgorithm.toJSON(); + + if("saltLength" in this) + _object.saltLength = this.saltLength.toJSON(); + + if("trailerField" in this) + _object.trailerField = this.trailerField.toJSON(); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "SubjectPublicKeyInfo" type + //************************************************************************************** + in_window.org.pkijs.simpl.PUBLIC_KEY_INFO = + function() + { + // #region Internal properties of the object + this.algorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); + this.subjectPublicKey = new in_window.org.pkijs.asn1.BITSTRING(); + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.PUBLIC_KEY_INFO.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.algorithm = (arguments[0].algorithm || (new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER())); + this.subjectPublicKey = (arguments[0].subjectPublicKey || (new in_window.org.pkijs.asn1.BITSTRING())); + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.PUBLIC_KEY_INFO.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.PUBLIC_KEY_INFO({ + names: { + algorithm: { + names: { + block_name: "algorithm" + } + }, + subjectPublicKey: "subjectPublicKey" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for PUBLIC_KEY_INFO"); + // #endregion + + // #region Get internal properties from parsed schema + this.algorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ schema: asn1.result.algorithm }); + this.subjectPublicKey = asn1.result.subjectPublicKey; + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.PUBLIC_KEY_INFO.prototype.toSchema = + function() + { + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: [ + this.algorithm.toSchema(), + this.subjectPublicKey + ] + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.PUBLIC_KEY_INFO.prototype.importKey = + function(publicKey) + { + /// Public key to work with + + // #region Initial variables + var sequence = Promise.resolve(); + var _this = this; + // #endregion + + // #region Initial check + if(typeof publicKey === "undefined") + return new Promise(function(resolve, reject) { reject("Need to provide publicKey input parameter"); }); + // #endregion + + // #region Get a "crypto" extension + var crypto = in_window.org.pkijs.getCrypto(); + if(typeof crypto == "undefined") + return new Promise(function(resolve, reject) { reject("Unable to create WebCrypto object"); }); + // #endregion + + // #region Export public key + sequence = sequence.then( + function() + { + return crypto.exportKey("spki", publicKey); + } + ); + // #endregion + + // #region Initialize internal variables by parsing exported value + sequence = sequence.then( + function(exportedKey) + { + var asn1 = in_window.org.pkijs.fromBER(exportedKey); + try + { + in_window.org.pkijs.simpl.PUBLIC_KEY_INFO.prototype.fromSchema.call(_this, asn1.result); + } + catch(exception) + { + return new Promise(function(resolve, reject) { reject("Error during initializing object from schema"); }); + } + }, + function(error) + { + return new Promise(function(resolve, reject) { reject("Error during exporting public key: " + error); }); + } + ); + // #endregion + + return sequence; + }; + //************************************************************************************** + in_window.org.pkijs.simpl.PUBLIC_KEY_INFO.prototype.toJSON = + function() + { + return { + algorithm: this.algorithm.toJSON(), + subjectPublicKey: this.subjectPublicKey.toJSON() + }; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "AttributeTypeAndValue" type (part of RelativeDistinguishedName) + //************************************************************************************** + in_window.org.pkijs.simpl.ATTR_TYPE_AND_VALUE = + function() + { + // #region Internal properties of the object + this.type = ""; + this.value = {}; // ANY -- DEFINED BY AttributeType + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.ATTR_TYPE_AND_VALUE.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.type = (arguments[0].type || ""); + this.value = (arguments[0].value || {}); + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.ATTR_TYPE_AND_VALUE.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.ATTR_TYPE_AND_VALUE({ + names: { + type: "type", + value: "typeValue" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for ATTR_TYPE_AND_VALUE"); + // #endregion + + // #region Get internal properties from parsed schema + this.type = asn1.result.type.value_block.toString(); + this.value = asn1.result.typeValue; + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.ATTR_TYPE_AND_VALUE.prototype.toSchema = + function() + { + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: [ + new in_window.org.pkijs.asn1.OID({ value: this.type }), + this.value + ] + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.ATTR_TYPE_AND_VALUE.prototype.isEqual = + function() + { + if(arguments[0] instanceof in_window.org.pkijs.simpl.ATTR_TYPE_AND_VALUE) + { + if(this.type !== arguments[0].type) + return false; + + if(((this.value instanceof in_window.org.pkijs.asn1.UTF8STRING) && (arguments[0].value instanceof in_window.org.pkijs.asn1.UTF8STRING)) || + ((this.value instanceof in_window.org.pkijs.asn1.BMPSTRING) && (arguments[0].value instanceof in_window.org.pkijs.asn1.BMPSTRING)) || + ((this.value instanceof in_window.org.pkijs.asn1.UNIVERSALSTRING) && (arguments[0].value instanceof in_window.org.pkijs.asn1.UNIVERSALSTRING)) || + ((this.value instanceof in_window.org.pkijs.asn1.NUMERICSTRING) && (arguments[0].value instanceof in_window.org.pkijs.asn1.NUMERICSTRING)) || + ((this.value instanceof in_window.org.pkijs.asn1.PRINTABLESTRING) && (arguments[0].value instanceof in_window.org.pkijs.asn1.PRINTABLESTRING)) || + ((this.value instanceof in_window.org.pkijs.asn1.TELETEXSTRING) && (arguments[0].value instanceof in_window.org.pkijs.asn1.TELETEXSTRING)) || + ((this.value instanceof in_window.org.pkijs.asn1.VIDEOTEXSTRING) && (arguments[0].value instanceof in_window.org.pkijs.asn1.VIDEOTEXSTRING)) || + ((this.value instanceof in_window.org.pkijs.asn1.IA5STRING) && (arguments[0].value instanceof in_window.org.pkijs.asn1.IA5STRING)) || + ((this.value instanceof in_window.org.pkijs.asn1.GRAPHICSTRING) && (arguments[0].value instanceof in_window.org.pkijs.asn1.GRAPHICSTRING)) || + ((this.value instanceof in_window.org.pkijs.asn1.VISIBLESTRING) && (arguments[0].value instanceof in_window.org.pkijs.asn1.VISIBLESTRING)) || + ((this.value instanceof in_window.org.pkijs.asn1.GENERALSTRING) && (arguments[0].value instanceof in_window.org.pkijs.asn1.GENERALSTRING)) || + ((this.value instanceof in_window.org.pkijs.asn1.CHARACTERSTRING) && (arguments[0].value instanceof in_window.org.pkijs.asn1.CHARACTERSTRING))) + { + var value1 = in_window.org.pkijs.stringPrep(this.value.value_block.value); + var value2 = in_window.org.pkijs.stringPrep(arguments[0].value.value_block.value); + + if(value1.localeCompare(value2) !== 0) + return false; + } + else // Comparing as two ArrayBuffers + { + if(in_window.org.pkijs.isEqual_buffer(this.value.value_before_decode, arguments[0].value.value_before_decode) === false) + return false; + } + + return true; + } + else + return false; + }; + //************************************************************************************** + in_window.org.pkijs.simpl.ATTR_TYPE_AND_VALUE.prototype.toJSON = + function() + { + var _object = { + type: this.type + }; + + if(Object.keys(this.value).length !== 0) + _object.value = this.value.toJSON(); + else + _object.value = this.value; + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "RelativeDistinguishedName" type + //************************************************************************************** + in_window.org.pkijs.simpl.RDN = + function() + { + // #region Internal properties of the object + /// Array of "type and value" objects + this.types_and_values = new Array(); + /// Value of the RDN before decoding from schema + this.value_before_decode = new ArrayBuffer(0); + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.RDN.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.types_and_values = (arguments[0].types_and_values || (new Array())); + this.value_before_decode = arguments[0].value_before_decode || new ArrayBuffer(0); + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.RDN.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.RDN({ + names: { + block_name: "RDN", + repeated_set: "types_and_values" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for RDN"); + // #endregion + + // #region Get internal properties from parsed schema + if("types_and_values" in asn1.result) // Could be a case when there is no "types and values" + { + var types_and_values_array = asn1.result.types_and_values; + for(var i = 0; i < types_and_values_array.length; i++) + this.types_and_values.push(new in_window.org.pkijs.simpl.ATTR_TYPE_AND_VALUE({ schema: types_and_values_array[i] })); + } + + this.value_before_decode = asn1.result.RDN.value_before_decode; + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.RDN.prototype.toSchema = + function() + { + // #region Decode stored TBS value + if(this.value_before_decode.byteLength === 0) // No stored encoded array, create "from scratch" + { + // #region Create array for output set + var output_array = new Array(); + + for(var i = 0; i < this.types_and_values.length; i++) + output_array.push(this.types_and_values[i].toSchema()); + // #endregion + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: [new in_window.org.pkijs.asn1.SET({ value: output_array })] + })); + } + + var asn1 = in_window.org.pkijs.fromBER(this.value_before_decode); + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return asn1.result; + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.RDN.prototype.isEqual = + function() + { + if(arguments[0] instanceof in_window.org.pkijs.simpl.RDN) + { + if(this.types_and_values.length != arguments[0].types_and_values.length) + return false; + + for(var i = 0; i < this.types_and_values.length; i++) + { + if(this.types_and_values[i].isEqual(arguments[0].types_and_values[i]) === false) + return false; + } + + return true; + } + else + { + if(arguments[0] instanceof ArrayBuffer) + return in_window.org.pkijs.isEqual_buffer(this.value_before_decode, arguments[0]); + else + return false; + } + + return false; + }; + //************************************************************************************** + in_window.org.pkijs.simpl.RDN.prototype.toJSON = + function() + { + var _object = { + types_and_values: new Array() + }; + + for(var i = 0; i < this.types_and_values.length; i++) + _object.types_and_values.push(this.types_and_values[i].toJSON()); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "AuthorityKeyIdentifier" type of extension + //************************************************************************************** + in_window.org.pkijs.simpl.x509.AuthorityKeyIdentifier = + function() + { + // #region Internal properties of the object + // OPTIONAL this.keyIdentifier - OCTETSTRING + // OPTIONAL this.authorityCertIssuer - Array of GeneralName + // OPTIONAL this.authorityCertSerialNumber - INTEGER + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.x509.AuthorityKeyIdentifier.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + if("keyIdentifier" in arguments[0]) + this.keyIdentifier = arguments[0].keyIdentifier; + + if("authorityCertIssuer" in arguments[0]) + this.authorityCertIssuer = arguments[0].authorityCertIssuer; + + if("authorityCertSerialNumber" in arguments[0]) + this.authorityCertSerialNumber = arguments[0].authorityCertSerialNumber; + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.AuthorityKeyIdentifier.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.x509.AuthorityKeyIdentifier({ + names: { + keyIdentifier: "keyIdentifier", + authorityCertIssuer: "authorityCertIssuer", + authorityCertSerialNumber: "authorityCertSerialNumber" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for AuthorityKeyIdentifier"); + // #endregion + + // #region Get internal properties from parsed schema + if("keyIdentifier" in asn1.result) + { + asn1.result["keyIdentifier"].id_block.tag_class = 1; // UNIVERSAL + asn1.result["keyIdentifier"].id_block.tag_number = 4; // OCTETSTRING + + this.keyIdentifier = asn1.result["keyIdentifier"]; + } + + if("authorityCertIssuer" in asn1.result) + { + this.authorityCertIssuer = new Array(); + var issuer_array = asn1.result["authorityCertIssuer"]; + + for(var i = 0; i < issuer_array.length; i++) + this.authorityCertIssuer.push(new in_window.org.pkijs.simpl.GENERAL_NAME({ schema: issuer_array[i] })); + } + + if("authorityCertSerialNumber" in asn1.result) + { + asn1.result["authorityCertSerialNumber"].id_block.tag_class = 1; // UNIVERSAL + asn1.result["authorityCertSerialNumber"].id_block.tag_number = 2; // INTEGER + + this.authorityCertSerialNumber = asn1.result["authorityCertSerialNumber"]; + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.AuthorityKeyIdentifier.prototype.toSchema = + function() + { + // #region Create array for output sequence + var output_array = new Array(); + + if("keyIdentifier" in this) + { + var value = this.keyIdentifier; + + value.id_block.tag_class = 3; // CONTEXT-SPECIFIC + value.id_block.tag_number = 0; // [0] + + output_array.push(value); + } + + if("authorityCertIssuer" in this) + { + var issuer_array = new Array(); + + for(var i = 0; i < this.authorityCertIssuer.length; i++) + issuer_array.push(this.authorityCertIssuer[i].toSchema()); + + output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + }, + value: [new in_window.org.pkijs.asn1.SEQUENCE({ + value: issuer_array + })] + })); + } + + if("authorityCertSerialNumber" in this) + { + var value = this.authorityCertSerialNumber; + + value.id_block.tag_class = 3; // CONTEXT-SPECIFIC + value.id_block.tag_number = 2; // [2] + + output_array.push(value); + } + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: output_array + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.AuthorityKeyIdentifier.prototype.toJSON = + function() + { + var _object = {}; + + if("keyIdentifier" in this) + _object.keyIdentifier = this.keyIdentifier.toJSON(); + + if("authorityCertIssuer" in this) + { + _object.authorityCertIssuer = new Array(); + + for(var i = 0; i < this.authorityCertIssuer.length; i++) + _object.authorityCertIssuer.push(this.authorityCertIssuer[i].toJSON()); + } + + if("authorityCertSerialNumber" in this) + _object.authorityCertSerialNumber = this.authorityCertSerialNumber.toJSON(); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "PrivateKeyUsagePeriod" type of extension + //************************************************************************************** + in_window.org.pkijs.simpl.x509.PrivateKeyUsagePeriod = + function() + { + // #region Internal properties of the object + // OPTIONAL this.notBefore - new Date() + // OPTIONAL this.notAfter - new Date() + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.x509.PrivateKeyUsagePeriod.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + if("notBefore" in arguments[0]) + this.notBefore = arguments[0].notBefore; + + if("notAfter" in arguments[0]) + this.notAfter = arguments[0].notAfter; + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.PrivateKeyUsagePeriod.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.x509.PrivateKeyUsagePeriod({ + names: { + notBefore: "notBefore", + notAfter: "notAfter" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for PrivateKeyUsagePeriod"); + // #endregion + + // #region Get internal properties from parsed schema + if("notBefore" in asn1.result) + { + var localNotBefore = new in_window.org.pkijs.asn1.GENERALIZEDTIME(); + localNotBefore.fromBuffer(asn1.result["notBefore"].value_block.value_hex); + this.notBefore = localNotBefore.toDate(); + } + + if("notAfter" in asn1.result) + { + var localNotAfter = new in_window.org.pkijs.asn1.GENERALIZEDTIME({ value_hex: asn1.result["notAfter"].value_block.value_hex }); + localNotAfter.fromBuffer(asn1.result["notAfter"].value_block.value_hex); + this.notAfter = localNotAfter.toDate(); + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.PrivateKeyUsagePeriod.prototype.toSchema = + function() + { + // #region Create array for output sequence + var output_array = new Array(); + + if("notBefore" in this) + output_array.push(new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + value_hex: (new in_window.org.pkijs.asn1.GENERALIZEDTIME({ value_date: this.notBefore })).value_block.value_hex + })); + + if("notAfter" in this) + output_array.push(new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + }, + value_hex: (new in_window.org.pkijs.asn1.GENERALIZEDTIME({ value_date: this.notAfter })).value_block.value_hex + })); + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: output_array + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.PrivateKeyUsagePeriod.prototype.toJSON = + function() + { + var _object = {}; + + if("notBefore" in this) + _object.notBefore = this.notBefore; + + if("notAfter" in this) + _object.notAfter = this.notAfter; + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "IssuerAltName" and "SubjectAltName" types of extension + //************************************************************************************** + in_window.org.pkijs.simpl.x509.AltName = + function() + { + // #region Internal properties of the object + this.altNames = new Array(); //Array of GeneralName + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.x509.AltName.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.altNames = arguments[0].altNames || new Array(); + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.AltName.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.x509.AltName({ + names: { + altNames: "altNames" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for AltName"); + // #endregion + + // #region Get internal properties from parsed schema + if("altNames" in asn1.result) + { + var altNames_array = asn1.result["altNames"]; + + for(var i = 0; i < altNames_array.length; i++) + this.altNames.push(new in_window.org.pkijs.simpl.GENERAL_NAME({ schema: altNames_array[i] })); + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.AltName.prototype.toSchema = + function() + { + // #region Create array for output sequence + var output_array = new Array(); + + for(var i = 0; i < this.altNames.length; i++) + output_array.push(this.altNames[i].toSchema()); + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: output_array + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.AltName.prototype.toJSON = + function() + { + var _object = { + altNames: new Array() + }; + + for(var i = 0; i < this.altNames.length; i++) + _object.altNames.push(this.altNames[i].toJSON()); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "SubjectDirectoryAttributes" type of extension + //************************************************************************************** + in_window.org.pkijs.simpl.x509.SubjectDirectoryAttributes = + function() + { + // #region Internal properties of the object + this.attributes = new Array(); // Array of "simpl.ATTRIBUTE" + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.x509.SubjectDirectoryAttributes.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.attributes = arguments[0].attributes || new Array(); // Array of "simpl.ATTRIBUTE" + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.SubjectDirectoryAttributes.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.x509.SubjectDirectoryAttributes({ + names: { + attributes: "attributes" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for SubjectDirectoryAttributes"); + // #endregion + + // #region Get internal properties from parsed schema + var attrs = asn1.result["attributes"]; + + for(var i = 0; i < attrs.length; i++) + this.attributes.push(new in_window.org.pkijs.simpl.ATTRIBUTE({ schema: attrs[i] })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.SubjectDirectoryAttributes.prototype.toSchema = + function() + { + // #region Create array for output sequence + var output_array = new Array(); + + for(var i = 0; i < this.attributes.length; i++) + output_array.push(this.attributes[i].toSchema()); + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: output_array + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.SubjectDirectoryAttributes.prototype.toJSON = + function() + { + var _object = { + attributes: new Array() + }; + + for(var i = 0; i < this.attributes.length; i++) + _object.attributes.push(this.attributes[i].toJSON()); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "PolicyMapping" type + //************************************************************************************** + in_window.org.pkijs.simpl.x509.PolicyMapping = + function() + { + // #region Internal properties of the object + this.issuerDomainPolicy = ""; + this.subjectDomainPolicy = ""; + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.x509.PolicyMapping.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.issuerDomainPolicy = arguments[0].issuerDomainPolicy || ""; + this.subjectDomainPolicy = arguments[0].subjectDomainPolicy || ""; + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.PolicyMapping.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.x509.PolicyMapping({ + names: { + issuerDomainPolicy: "issuerDomainPolicy", + subjectDomainPolicy: "subjectDomainPolicy" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for PolicyMapping"); + // #endregion + + // #region Get internal properties from parsed schema + this.issuerDomainPolicy = asn1.result["issuerDomainPolicy"].value_block.toString(); + this.subjectDomainPolicy = asn1.result["subjectDomainPolicy"].value_block.toString(); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.PolicyMapping.prototype.toSchema = + function() + { + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: [ + new in_window.org.pkijs.asn1.OID({ value: this.issuerDomainPolicy }), + new in_window.org.pkijs.asn1.OID({ value: this.subjectDomainPolicy }) + ] + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.PolicyMapping.prototype.toJSON = + function() + { + return { + issuerDomainPolicy: this.issuerDomainPolicy, + subjectDomainPolicy: this.subjectDomainPolicy + }; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "PolicyMappings" type of extension + //************************************************************************************** + in_window.org.pkijs.simpl.x509.PolicyMappings = + function() + { + // #region Internal properties of the object + this.mappings = new Array(); // Array of "simpl.x509.PolicyMapping" + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.x509.PolicyMappings.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.mappings = arguments[0].mappings || new Array(); + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.PolicyMappings.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.x509.PolicyMappings({ + names: { + mappings: "mappings" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for PolicyMappings"); + // #endregion + + // #region Get internal properties from parsed schema + var maps = asn1.result["mappings"]; + + for(var i = 0; i < maps.length; i++) + this.mappings.push(new in_window.org.pkijs.simpl.x509.PolicyMapping({ schema: maps[i] })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.PolicyMappings.prototype.toSchema = + function() + { + // #region Create array for output sequence + var output_array = new Array(); + + for(var i = 0; i < this.mappings.length; i++) + output_array.push(this.mappings.toSchema()); + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: output_array + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.PolicyMappings.prototype.toJSON = + function() + { + var _object = { + mappings: new Array() + }; + + for(var i = 0; i < this.mappings.length; i++) + _object.mappings.push(this.mappings[i].toJSON()); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "GeneralSubtree" type + //************************************************************************************** + in_window.org.pkijs.simpl.x509.GeneralSubtree = + function() + { + // #region Internal properties of the object + this.base = new in_window.org.pkijs.simpl.GENERAL_NAME(); + // OPTIONAL this.minimum // in_window.org.pkijs.asn1.INTEGER + // OPTIONAL this.maximum // in_window.org.pkijs.asn1.INTEGER + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.x509.GeneralSubtree.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.base = arguments[0].base || new in_window.org.pkijs.simpl.GENERAL_NAME(); + + if("minimum" in arguments[0]) + this.minimum = arguments[0].minimum; + + if("maximum" in arguments[0]) + this.maximum = arguments[0].maximum; + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.GeneralSubtree.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.x509.GeneralSubtree({ + names: { + base: { + names: { + block_name: "base" + } + }, + minimum: "minimum", + maximum: "maximum" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for "); + // #endregion + + // #region Get internal properties from parsed schema + this.base = new in_window.org.pkijs.simpl.GENERAL_NAME({ schema: asn1.result["base"] }); + + if("minimum" in asn1.result) + { + if(asn1.result["minimum"].value_block.is_hex_only) + this.minimum = asn1.result["minimum"]; + else + this.minimum = asn1.result["minimum"].value_block.value_dec; + } + + if("maximum" in asn1.result) + { + if(asn1.result["maximum"].value_block.is_hex_only) + this.maximum = asn1.result["maximum"]; + else + this.maximum = asn1.result["maximum"].value_block.value_dec; + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.GeneralSubtree.prototype.toSchema = + function() + { + // #region Create array for output sequence + var output_array = new Array(); + + output_array.push(this.base.toSchema()); + + if("minimum" in this) + { + var value_minimum = 0; + + if(this.minimum instanceof in_window.org.pkijs.asn1.INTEGER) + value_minimum = this.minimum; + else + value_minimum = new in_window.org.pkijs.asn1.INTEGER({ value: this.minimum }); + + output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + value: [value_minimum] + })); + } + + if("maximum" in this) + { + var value_maximum = 0; + + if(this.maximum instanceof in_window.org.pkijs.asn1.INTEGER) + value_maximum = this.maximum; + else + value_maximum = new in_window.org.pkijs.asn1.INTEGER({ value: this.maximum }); + + output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + }, + value: [value_maximum] + })); + } + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: output_array + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.GeneralSubtree.prototype.toJSON = + function() + { + var _object = { + base: this.base.toJSON() + }; + + if("minimum" in this) + { + if((typeof this.minimum) === "number") + _object.minimum = this.minimum; + else + _object.minimum = this.minimum.toJSON(); + } + + if("maximum" in this) + { + if((typeof this.maximum) === "number") + _object.maximum = this.maximum; + else + _object.maximum = this.maximum.toJSON(); + } + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "NameConstraints" type of extension + //************************************************************************************** + in_window.org.pkijs.simpl.x509.NameConstraints = + function() + { + // #region Internal properties of the object + // OPTIONAL this.permittedSubtrees - Array of "simpl.x509.GeneralSubtree" + // OPTIONAL this.excludedSubtrees - Array of "simpl.x509.GeneralSubtree" + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.x509.NameConstraints.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + if("permittedSubtrees" in arguments[0]) + this.permittedSubtrees = arguments[0].permittedSubtrees; + + if("excludedSubtrees" in arguments[0]) + this.excludedSubtrees = arguments[0].excludedSubtrees; + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.NameConstraints.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.x509.NameConstraints({ + names: { + permittedSubtrees: "permittedSubtrees", + excludedSubtrees: "excludedSubtrees" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for NameConstraints"); + // #endregion + + // #region Get internal properties from parsed schema + if("permittedSubtrees" in asn1.result) + { + this.permittedSubtrees = new Array(); + var permited_array = asn1.result["permittedSubtrees"]; + + for(var i = 0; i < permited_array.length; i++) + this.permittedSubtrees.push(new in_window.org.pkijs.simpl.x509.GeneralSubtree({ schema: permited_array[i] })); + } + + if("excludedSubtrees" in asn1.result) + { + this.excludedSubtrees = new Array(); + var excluded_array = asn1.result["excludedSubtrees"]; + + for(var i = 0; i < excluded_array.length; i++) + this.excludedSubtrees.push(new in_window.org.pkijs.simpl.x509.GeneralSubtree({ schema: excluded_array[i] })); + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.NameConstraints.prototype.toSchema = + function() + { + // #region Create array for output sequence + var output_array = new Array(); + + if("permittedSubtrees" in this) + { + var permited_array = new Array(); + + for(var i = 0; i < this.permittedSubtrees.length; i++) + permited_array.push(this.permittedSubtrees[i].toSchema()); + + output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + value: [new in_window.org.pkijs.asn1.SEQUENCE({ + value: permited_array + })] + })); + } + + if("excludedSubtrees" in this) + { + var excluded_array = new Array(); + + for(var i = 0; i < this.excludedSubtrees.length; i++) + excluded_array.push(this.excludedSubtrees[i].toSchema()); + + output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + }, + value: [new in_window.org.pkijs.asn1.SEQUENCE({ + value: excluded_array + })] + })); + } + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: output_array + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.NameConstraints.prototype.toJSON = + function() + { + var _object = {}; + + if("permittedSubtrees" in this) + { + _object.permittedSubtrees = new Array(); + + for(var i = 0; i < this.permittedSubtrees.length; i++) + _object.permittedSubtrees.push(this.permittedSubtrees[i].toJSON()); + } + + if("excludedSubtrees" in this) + { + _object.excludedSubtrees = new Array(); + + for(var i = 0; i < this.excludedSubtrees.length; i++) + _object.excludedSubtrees.push(this.excludedSubtrees[i].toJSON()); + } + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "BasicConstraints" type of extension + //************************************************************************************** + in_window.org.pkijs.simpl.x509.BasicConstraints = + function() + { + // #region Internal properties of the object + // OPTIONAL this.cA - boolean value + // OPTIONAL this.pathLenConstraint - integer value + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.x509.BasicConstraints.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + if("cA" in arguments[0]) + this.cA = arguments[0].cA; + + if("pathLenConstraint" in arguments[0]) + this.pathLenConstraint = arguments[0].pathLenConstraint; + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.BasicConstraints.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.x509.BasicConstraints({ + names: { + cA: "cA", + pathLenConstraint: "pathLenConstraint" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for BasicConstraints"); + // #endregion + + // #region Get internal properties from parsed schema + if("cA" in asn1.result) + this.cA = asn1.result["cA"].value_block.value; + + if("pathLenConstraint" in asn1.result) + this.pathLenConstraint = asn1.result["pathLenConstraint"].value_block.value_dec; + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.BasicConstraints.prototype.toSchema = + function() + { + // #region Create array for output sequence + var output_array = new Array(); + + if("cA" in this) + output_array.push(new in_window.org.pkijs.asn1.BOOLEAN({ value: this.cA })); + + if("pathLenConstraint" in this) + output_array.push(new in_window.org.pkijs.asn1.INTEGER({ value: this.pathLenConstraint })); + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: output_array + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.BasicConstraints.prototype.toJSON = + function() + { + var _object = {}; + + if("cA" in this) + _object.cA = this.cA; + + if("pathLenConstraint" in this) + _object.pathLenConstraint = this.pathLenConstraint; + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "PolicyQualifierInfo" type + //************************************************************************************** + in_window.org.pkijs.simpl.x509.PolicyQualifierInfo = + function() + { + // #region Internal properties of the object + this.policyQualifierId = ""; + this.qualifier = new in_window.org.pkijs.asn1.ANY(); + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.x509.PolicyQualifierInfo.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.policyQualifierId = arguments[0].policyQualifierId || ""; + this.qualifier = arguments[0].qualifier || new in_window.org.pkijs.asn1.ANY(); + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.PolicyQualifierInfo.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.x509.PolicyQualifierInfo({ + names: { + policyQualifierId: "policyQualifierId", + qualifier: "qualifier" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for PolicyQualifierInfo"); + // #endregion + + // #region Get internal properties from parsed schema + this.policyQualifierId = asn1.result["policyQualifierId"].value_block.toString(); + this.qualifier = asn1.result["qualifier"]; + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.PolicyQualifierInfo.prototype.toSchema = + function() + { + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: [ + new in_window.org.pkijs.asn1.OID({ value: this.policyQualifierId }), + this.qualifier + ] + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.PolicyQualifierInfo.prototype.toJSON = + function() + { + return { + policyQualifierId: this.policyQualifierId, + qualifier: this.qualifier.toJSON() + }; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "PolicyInformation" type + //************************************************************************************** + in_window.org.pkijs.simpl.x509.PolicyInformation = + function() + { + // #region Internal properties of the object + this.policyIdentifier = ""; + // OPTIONAL this.policyQualifiers = new Array(); // Array of "simpl.x509.PolicyQualifierInfo" + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.x509.PolicyInformation.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.policyIdentifier = arguments[0].policyIdentifier || ""; + + if("policyQualifiers" in arguments[0]) + this.policyQualifiers = arguments[0].policyQualifiers; + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.PolicyInformation.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.x509.PolicyInformation({ + names: { + policyIdentifier: "policyIdentifier", + policyQualifiers: "policyQualifiers" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for PolicyInformation"); + // #endregion + + // #region Get internal properties from parsed schema + this.policyIdentifier = asn1.result["policyIdentifier"].value_block.toString(); + + if("policyQualifiers" in asn1.result) + { + this.policyQualifiers = new Array(); + var qualifiers = asn1.result["policyQualifiers"]; + + for(var i = 0; i < qualifiers.length; i++) + this.policyQualifiers.push(new in_window.org.pkijs.simpl.x509.PolicyQualifierInfo({ schema: qualifiers[i] })); + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.PolicyInformation.prototype.toSchema = + function() + { + // #region Create array for output sequence + var output_array = new Array(); + + output_array.push(new in_window.org.pkijs.asn1.OID({ value: this.policyIdentifier })); + + if("policyQualifiers" in this) + { + var qualifiers = new Array(); + + for(var i = 0; i < this.policyQualifiers.length; i++) + qualifiers.push(this.policyQualifiers[i].toSchema()); + + output_array.push(new in_window.org.pkijs.asn1.SEQUENCE({ + value: qualifiers + })); + } + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: output_array + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.PolicyInformation.prototype.toJSON = + function() + { + var _object = { + policyIdentifier: this.policyIdentifier + }; + + if("policyQualifiers" in this) + { + _object.policyQualifiers = new Array(); + + for(var i = 0; i < this.policyQualifiers.length; i++) + _object.policyQualifiers.push(this.policyQualifiers[i].toJSON()); + } + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "CertificatePolicies" type of extension + //************************************************************************************** + in_window.org.pkijs.simpl.x509.CertificatePolicies = + function() + { + // #region Internal properties of the object + this.certificatePolicies = new Array(); // Array of "simpl.x509.PolicyInformation" + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.x509.CertificatePolicies.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.certificatePolicies = arguments[0].certificatePolicies || new Array(); // Array of "simpl.x509.PolicyInformation" + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.CertificatePolicies.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.x509.CertificatePolicies({ + names: { + certificatePolicies: "certificatePolicies" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for CertificatePolicies"); + // #endregion + + // #region Get internal properties from parsed schema + var policies = asn1.result["certificatePolicies"]; + + for(var i = 0; i < policies.length; i++) + this.certificatePolicies.push(new in_window.org.pkijs.simpl.x509.PolicyInformation({ schema: policies[i] })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.CertificatePolicies.prototype.toSchema = + function() + { + // #region Create array for output sequence + var output_array = new Array(); + + for(var i = 0; i < this.certificatePolicies.length; i++) + output_array.push(this.certificatePolicies[i].toSchema()); + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: output_array + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.CertificatePolicies.prototype.toJSON = + function() + { + var _object = { + certificatePolicies: new Array() + }; + + for(var i = 0; i < this.certificatePolicies.length; i++) + _object.certificatePolicies.push(this.certificatePolicies[i].toJSON()); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "PolicyConstraints" type of extension + //************************************************************************************** + in_window.org.pkijs.simpl.x509.PolicyConstraints = + function() + { + // #region Internal properties of the object + // OPTIONAL this.requireExplicitPolicy = 0; + // OPTIONAL this.inhibitPolicyMapping = 0; + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.x509.PolicyConstraints.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.requireExplicitPolicy = arguments[0].requireExplicitPolicy || 0; + this.inhibitPolicyMapping = arguments[0].inhibitPolicyMapping || 0; + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.PolicyConstraints.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.x509.PolicyConstraints({ + names: { + requireExplicitPolicy: "requireExplicitPolicy", + inhibitPolicyMapping: "inhibitPolicyMapping" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for PolicyConstraints"); + // #endregion + + // #region Get internal properties from parsed schema + if("requireExplicitPolicy" in asn1.result) + { + var field1 = asn1.result["requireExplicitPolicy"]; + + field1.id_block.tag_class = 1; // UNIVERSAL + field1.id_block.tag_number = 2; // INTEGER + + var ber1 = field1.toBER(false); + var int1 = in_window.org.pkijs.fromBER(ber1); + + this.requireExplicitPolicy = int1.result.value_block.value_dec; + } + + if("inhibitPolicyMapping" in asn1.result) + { + var field2 = asn1.result["inhibitPolicyMapping"]; + + field2.id_block.tag_class = 1; // UNIVERSAL + field2.id_block.tag_number = 2; // INTEGER + + var ber2 = field2.toBER(false); + var int2 = in_window.org.pkijs.fromBER(ber2); + + this.inhibitPolicyMapping = int2.result.value_block.value_dec; + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.PolicyConstraints.prototype.toSchema = + function() + { + // #region Create correct values for output sequence + var output_array = new Array(); + + if("requireExplicitPolicy" in this) + { + var int1 = new in_window.org.pkijs.asn1.INTEGER({ value: this.requireExplicitPolicy }); + + int1.id_block.tag_class = 3; // CONTEXT-SPECIFIC + int1.id_block.tag_number = 0; // [0] + + output_array.push(int1); + } + + if("inhibitPolicyMapping" in this) + { + var int2 = new in_window.org.pkijs.asn1.INTEGER({ value: this.inhibitPolicyMapping }); + + int1.id_block.tag_class = 3; // CONTEXT-SPECIFIC + int1.id_block.tag_number = 1; // [1] + + output_array.push(int2); + } + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: output_array + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.PolicyConstraints.prototype.toJSON = + function() + { + var _object = {}; + + if("requireExplicitPolicy" in this) + _object.requireExplicitPolicy = this.requireExplicitPolicy; + + if("inhibitPolicyMapping" in this) + _object.inhibitPolicyMapping = this.inhibitPolicyMapping; + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "ExtKeyUsage" type of extension + //************************************************************************************** + in_window.org.pkijs.simpl.x509.ExtKeyUsage = + function() + { + // #region Internal properties of the object + this.keyPurposes = new Array(); // Array of strings (OIDs value for key purposes) + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.x509.ExtKeyUsage.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.keyPurposes = arguments[0].keyPurposes || new Array(); // Array of strings (OIDs value for key purposes) + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.ExtKeyUsage.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.x509.ExtKeyUsage({ + names: { + keyPurposes: "keyPurposes" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for ExtKeyUsage"); + // #endregion + + // #region Get internal properties from parsed schema + var purposes = asn1.result["keyPurposes"]; + + for(var i = 0; i < purposes.length; i++) + this.keyPurposes.push(purposes[i].value_block.toString()); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.ExtKeyUsage.prototype.toSchema = + function() + { + // #region Create array for output sequence + var output_array = new Array(); + + for(var i = 0; i < this.keyPurposes.length; i++) + output_array.push(new in_window.org.pkijs.asn1.OID({ value: this.keyPurposes[i] })); + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: output_array + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.ExtKeyUsage.prototype.toJSON = + function() + { + var _object = { + keyPurposes: new Array() + }; + + for(var i = 0; i < this.keyPurposes.length; i++) + _object.keyPurposes.push(this.keyPurposes[i]); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "DistributionPoint" type + //************************************************************************************** + in_window.org.pkijs.simpl.x509.DistributionPoint = + function() + { + // #region Internal properties of the object + // OPTIONAL this.distributionPoint // Array of "simpl.GENERAL_NAME" or a value of "simpl.RDN" type + // OPTIONAL this.reasons // BITSTRING value + // OPTIONAL this.cRLIssuer // Array of "simpl.GENERAL_NAME" + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.x509.DistributionPoint.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + if("distributionPoint" in arguments[0]) + this.distributionPoint = arguments[0].distributionPoint; + + if("reasons" in arguments[0]) + this.reasons = arguments[0].reasons; + + if("cRLIssuer" in arguments[0]) + this.cRLIssuer = arguments[0].cRLIssuer; + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.DistributionPoint.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.x509.DistributionPoint({ + names: { + distributionPoint: "distributionPoint", + distributionPoint_names: "distributionPoint_names", + reasons: "reasons", + cRLIssuer: "cRLIssuer", + cRLIssuer_names: "cRLIssuer_names" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for DistributionPoint"); + // #endregion + + // #region Get internal properties from parsed schema + if("distributionPoint" in asn1.result) + { + if(asn1.result["distributionPoint"].id_block.tag_number == 0) // GENERAL_NAMES variant + { + this.distributionPoint = new Array(); + var names = asn1.result["distributionPoint_names"]; + + for(var i = 0; i < names.length; i++) + this.distributionPoint.push(new in_window.org.pkijs.simpl.GENERAL_NAME({ schema: names[i] })); + } + + if(asn1.result["distributionPoint"].id_block.tag_number == 1) // RDN variant + { + asn1.result["distributionPoint"].id_block.tag_class = 1; // UNIVERSAL + asn1.result["distributionPoint"].id_block.tag_number = 16; // SEQUENCE + + this.distributionPoint = new in_window.org.pkijs.simpl.RDN({ schema: asn1.result["distributionPoint"] }); + } + } + + if("reasons" in asn1.result) + this.reasons = new in_window.org.pkijs.asn1.BITSTRING({ value_hex: asn1.result["reasons"].value_block.value_hex }); + + if("cRLIssuer" in asn1.result) + { + this.cRLIssuer = new Array(); + var crl_names = asn1.result["cRLIssuer_names"]; + + for(var i = 0; i < crl_names; i++) + this.cRLIssuer.push(new in_window.org.pkijs.simpl.GENERAL_NAME({ schema: crl_names[i] })); + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.DistributionPoint.prototype.toSchema = + function() + { + // #region Create array for output sequence + var output_array = new Array(); + + if("distributionPoint" in this) + { + var internalValue; + + if(this.distributionPoint instanceof Array) + { + var namesArray = new Array(); + + for(var i = 0; i < this.distributionPoint.length; i++) + namesArray.push(this.distributionPoint[i].toSchema()); + + internalValue = new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + value: namesArray + }); + } + else + { + internalValue = new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + }, + value: [this.distributionPoint.toSchema()] + }); + } + + output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + value: [internalValue] + })); + } + + if("reasons" in this) + { + output_array.push(new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + }, + value_hex: this.reasons.value_block.value_hex + })); + } + + if("cRLIssuer" in this) + { + var value = new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 2 // [2] + } + }); + + for(var i = 0; i < this.cRLIssuer.length; i++) + value.value_block.value.push(this.cRLIssuer[i].toSchema()); + + output_array.push(value); + } + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: output_array + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.DistributionPoint.prototype.toJSON = + function() + { + var _object = {}; + + if("distributionPoint" in this) + { + if(this.distributionPoint instanceof Array) + { + _object.distributionPoint = new Array(); + + for(var i = 0; i < this.distributionPoint.length; i++) + _object.distributionPoint.push(this.distributionPoint[i].toJSON()); + } + else + _object.distributionPoint = this.distributionPoint.toJSON(); + } + + if("reasons" in this) + _object.reasons = this.reasons.toJSON(); + + if("cRLIssuer" in this) + { + _object.cRLIssuer = new Array(); + + for(var i = 0; i < this.cRLIssuer.length; i++) + _object.cRLIssuer.push(this.cRLIssuer[i].toJSON()); + } + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "CRLDistributionPoints" type of extension + //************************************************************************************** + in_window.org.pkijs.simpl.x509.CRLDistributionPoints = + function() + { + // #region Internal properties of the object + this.distributionPoints = new Array(); // Array of "simpl.x509.DistributionPoint" + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.x509.CRLDistributionPoints.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.distributionPoints = arguments[0].distributionPoints || new Array(); // Array of "simpl.x509.DistributionPoint" + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.CRLDistributionPoints.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.x509.CRLDistributionPoints({ + names: { + distributionPoints: "distributionPoints" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for CRLDistributionPoints"); + // #endregion + + // #region Get internal properties from parsed schema + var points = asn1.result["distributionPoints"]; + + for(var i = 0; i < points.length; i++) + this.distributionPoints.push(new in_window.org.pkijs.simpl.x509.DistributionPoint({ schema: points[i] })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.CRLDistributionPoints.prototype.toSchema = + function() + { + // #region Create array for output sequence + var output_array = new Array(); + + for(var i = 0; i < this.distributionPoints.length; i++) + output_array.push(this.distributionPoints[i].toSchema()); + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: output_array + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.CRLDistributionPoints.prototype.toJSON = + function() + { + var _object = { + distributionPoints: new Array() + }; + + for(var i = 0; i < this.distributionPoints.length; i++) + _object.distributionPoints.push(this.distributionPoints[i].toJSON()); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "AccessDescription" type + //************************************************************************************** + in_window.org.pkijs.simpl.x509.AccessDescription = + function() + { + // #region Internal properties of the object + this.accessMethod = ""; + this.accessLocation = new in_window.org.pkijs.simpl.GENERAL_NAME(); + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.x509.AccessDescription.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.accessMethod = arguments[0].accessMethod || ""; + this.accessLocation = arguments[0].accessLocation || new in_window.org.pkijs.simpl.GENERAL_NAME(); + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.AccessDescription.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.x509.AccessDescription({ + names: { + accessMethod: "accessMethod", + accessLocation: { + names: { + block_name: "accessLocation" + } + } + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for AccessDescription"); + // #endregion + + // #region Get internal properties from parsed schema + this.accessMethod = asn1.result["accessMethod"].value_block.toString(); + this.accessLocation = new in_window.org.pkijs.simpl.GENERAL_NAME({ schema: asn1.result["accessLocation"] }); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.AccessDescription.prototype.toSchema = + function() + { + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: [ + new in_window.org.pkijs.asn1.OID({ value: this.accessMethod }), + this.accessLocation.toSchema() + ] + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.AccessDescription.prototype.toJSON = + function() + { + return { + accessMethod: this.accessMethod, + accessLocation: this.accessLocation.toJSON() + }; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "AuthorityInfoAccess" and "SubjectInfoAccess" types of extension + //************************************************************************************** + in_window.org.pkijs.simpl.x509.InfoAccess = + function() + { + // #region Internal properties of the object + this.accessDescriptions = new Array(); // Array of "simpl.x509.AccessDescription" + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.x509.InfoAccess.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.accessDescriptions = arguments[0].accessDescriptions || new Array(); // Array of "simpl.x509.DistributionPoint" + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.InfoAccess.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.x509.InfoAccess({ + names: { + accessDescriptions: "accessDescriptions" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for InfoAccess"); + // #endregion + + // #region Get internal properties from parsed schema + var descriptions = asn1.result["accessDescriptions"]; + + for(var i = 0; i < descriptions.length; i++) + this.accessDescriptions.push(new in_window.org.pkijs.simpl.x509.AccessDescription({ schema: descriptions[i] })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.InfoAccess.prototype.toSchema = + function() + { + // #region Create array for output sequence + var output_array = new Array(); + + for(var i = 0; i < this.accessDescriptions.length; i++) + output_array.push(this.accessDescriptions[i].toSchema()); + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: output_array + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.InfoAccess.prototype.toJSON = + function() + { + var _object = { + accessDescriptions: new Array() + }; + + for(var i = 0; i < this.accessDescriptions.length; i++) + _object.accessDescriptions.push(this.accessDescriptions[i].toJSON()); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "IssuingDistributionPoint" type of extension + //************************************************************************************** + in_window.org.pkijs.simpl.x509.IssuingDistributionPoint = + function() + { + // #region Internal properties of the object + // OPTIONAL this.distributionPoint // Array of "simpl.GENERAL_NAME" or a value of "simpl.RDN" type + // OPTIONAL this.onlyContainsUserCerts // BOOLEAN flag + // OPTIONAL this.onlyContainsCACerts // BOOLEAN flag + // OPTIONAL this.onlySomeReasons // BITSTRING + // OPTIONAL this.indirectCRL // BOOLEAN flag + // OPTIONAL this.onlyContainsAttributeCerts // BOOLEAN flag + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.x509.IssuingDistributionPoint.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + if("distributionPoint" in arguments[0]) + this.distributionPoint = arguments[0].distributionPoint; + + if("onlyContainsUserCerts" in arguments[0]) + this.onlyContainsUserCerts = arguments[0].onlyContainsUserCerts; + + if("onlyContainsCACerts" in arguments[0]) + this.onlyContainsCACerts = arguments[0].onlyContainsCACerts; + + if("onlySomeReasons" in arguments[0]) + this.onlySomeReasons = arguments[0].onlySomeReasons; + + if("indirectCRL" in arguments[0]) + this.indirectCRL = arguments[0].indirectCRL; + + if("onlyContainsAttributeCerts" in arguments[0]) + this.onlyContainsAttributeCerts = arguments[0].onlyContainsAttributeCerts; + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.IssuingDistributionPoint.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.x509.IssuingDistributionPoint({ + names: { + distributionPoint: "distributionPoint", + distributionPoint_names: "distributionPoint_names", + onlyContainsUserCerts: "onlyContainsUserCerts", + onlyContainsCACerts: "onlyContainsCACerts", + onlySomeReasons: "onlySomeReasons", + indirectCRL: "indirectCRL", + onlyContainsAttributeCerts: "onlyContainsAttributeCerts" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for IssuingDistributionPoint"); + // #endregion + + // #region Get internal properties from parsed schema + if("distributionPoint" in asn1.result) + { + if(asn1.result["distributionPoint"].id_block.tag_number == 0) // GENERAL_NAMES variant + { + this.distributionPoint = new Array(); + var names = asn1.result["distributionPoint_names"]; + + for(var i = 0; i < names.length; i++) + this.distributionPoint.push(new in_window.org.pkijs.simpl.GENERAL_NAME({ schema: names[i] })); + } + + if(asn1.result["distributionPoint"].id_block.tag_number == 1) // RDN variant + { + asn1.result["distributionPoint"].id_block.tag_class = 1; // UNIVERSAL + asn1.result["distributionPoint"].id_block.tag_number = 16; // SEQUENCE + + this.distributionPoint = new in_window.org.pkijs.simpl.RDN({ schema: asn1.result["distributionPoint"] }); + } + } + + if("onlyContainsUserCerts" in asn1.result) + { + var view = new Uint8Array(asn1.result["onlyContainsUserCerts"].value_block.value_hex); + this.onlyContainsUserCerts = (view[0] !== 0x00); + } + + if("onlyContainsCACerts" in asn1.result) + { + var view = new Uint8Array(asn1.result["onlyContainsCACerts"].value_block.value_hex); + this.onlyContainsCACerts = (view[0] !== 0x00); + } + + if("onlySomeReasons" in asn1.result) + { + var view = new Uint8Array(asn1.result["onlySomeReasons"].value_block.value_hex); + this.onlySomeReasons = view[0]; + } + + if("indirectCRL" in asn1.result) + { + var view = new Uint8Array(asn1.result["indirectCRL"].value_block.value_hex); + this.indirectCRL = (view[0] !== 0x00); + } + + if("onlyContainsAttributeCerts" in asn1.result) + { + var view = new Uint8Array(asn1.result["onlyContainsAttributeCerts"].value_block.value_hex); + this.onlyContainsAttributeCerts = (view[0] !== 0x00); + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.IssuingDistributionPoint.prototype.toSchema = + function() + { + // #region Create array for output sequence + var output_array = new Array(); + + if("distributionPoint" in this) + { + var value; + + if(this.distributionPoint instanceof Array) + { + value = new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + } + }); + + for(var i = 0; i < this.distributionPoint.length; i++) + value.value_block.value.push(this.distributionPoint[i].toSchema()); + } + else + { + value = this.distributionPoint.toSchema(); + + value.id_block.tag_class = 3; // CONTEXT - SPECIFIC + value.id_block.tag_number = 1; // [1] + } + + output_array.push(value); + } + + if("onlyContainsUserCerts" in this) + { + var buffer = new ArrayBuffer(1); + var view = new Uint8Array(buffer); + + view[0] = (this.onlyContainsUserCerts === false) ? 0x00 : 0xFF; + + output_array.push(new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + }, + value_hex: buffer + })); + } + + if("onlyContainsCACerts" in this) + { + var buffer = new ArrayBuffer(1); + var view = new Uint8Array(buffer); + + view[0] = (this.onlyContainsCACerts === false) ? 0x00 : 0xFF; + + output_array.push(new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 2 // [2] + }, + value_hex: buffer + })); + } + + if("onlySomeReasons" in this) + { + var buffer = new ArrayBuffer(1); + var view = new Uint8Array(buffer); + + view[0] = this.onlySomeReasons; + + output_array.push(new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 3 // [3] + }, + value_hex: buffer + })); + } + + if("indirectCRL" in this) + { + var buffer = new ArrayBuffer(1); + var view = new Uint8Array(buffer); + + view[0] = (this.indirectCRL === false) ? 0x00 : 0xFF; + + output_array.push(new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 4 // [4] + }, + value_hex: buffer + })); + } + + if("onlyContainsAttributeCerts" in this) + { + var buffer = new ArrayBuffer(1); + var view = new Uint8Array(buffer); + + view[0] = (this.onlyContainsAttributeCerts === false) ? 0x00 : 0xFF; + + output_array.push(new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 5 // [5] + }, + value_hex: buffer + })); + } + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: output_array + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.x509.IssuingDistributionPoint.prototype.toJSON = + function() + { + var _object = {}; + + if("distributionPoint" in this) + { + if(this.distributionPoint instanceof Array) + { + _object.distributionPoint = new Array(); + + for(var i = 0; i < this.distributionPoint.length; i++) + _object.distributionPoint.push(this.distributionPoint[i].toJSON()); + } + else + _object.distributionPoint = this.distributionPoint.toJSON(); + } + + if("onlyContainsUserCerts" in this) + _object.onlyContainsUserCerts = this.onlyContainsUserCerts; + + if("onlyContainsCACerts" in this) + _object.onlyContainsCACerts = this.onlyContainsCACerts; + + if("onlySomeReasons" in this) + _object.onlySomeReasons = this.onlySomeReasons.toJSON(); + + if("indirectCRL" in this) + _object.indirectCRL = this.indirectCRL; + + if("onlyContainsAttributeCerts" in this) + _object.onlyContainsAttributeCerts = this.onlyContainsAttributeCerts; + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "Extension" type + //************************************************************************************** + in_window.org.pkijs.simpl.EXTENSION = + function() + { + // #region Internal properties of the object + this.extnID = ""; + this.critical = false; + this.extnValue = new in_window.org.pkijs.asn1.OCTETSTRING(); + + // OPTIONAL this.parsedValue - Parsed "extnValue" in case of well-known "extnID" + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.EXTENSION.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.extnID = (arguments[0].extnID || ""); + this.critical = (arguments[0].critical || false); + if("extnValue" in arguments[0]) + this.extnValue = new in_window.org.pkijs.asn1.OCTETSTRING({ value_hex: arguments[0].extnValue }); + else + this.extnValue = new in_window.org.pkijs.asn1.OCTETSTRING(); + + if("parsedValue" in arguments[0]) + this.parsedValue = arguments[0].parsedValue; + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.EXTENSION.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.EXTENSION({ + names: { + extnID: "extnID", + critical: "critical", + extnValue: "extnValue" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for EXTENSION"); + // #endregion + + // #region Get internal properties from parsed schema + this.extnID = asn1.result.extnID.value_block.toString(); + if("critical" in asn1.result) + this.critical = asn1.result.critical.value_block.value; + this.extnValue = asn1.result.extnValue; + + // #region Get "parsedValue" for well-known extensions + var asn1 = in_window.org.pkijs.fromBER(this.extnValue.value_block.value_hex); + if(asn1.offset === (-1)) + return; + + switch(this.extnID) + { + case "2.5.29.9": // SubjectDirectoryAttributes + this.parsedValue = new in_window.org.pkijs.simpl.x509.SubjectDirectoryAttributes({ schema: asn1.result }); + break; + case "2.5.29.14": // SubjectKeyIdentifier + this.parsedValue = asn1.result; // Should be just a simple OCTETSTRING + break; + case "2.5.29.15": // KeyUsage + this.parsedValue = asn1.result; // Should be just a simple BITSTRING + break; + case "2.5.29.16": // PrivateKeyUsagePeriod + this.parsedValue = new in_window.org.pkijs.simpl.x509.PrivateKeyUsagePeriod({ schema: asn1.result }); + break; + case "2.5.29.17": // SubjectAltName + case "2.5.29.18": // IssuerAltName + this.parsedValue = new in_window.org.pkijs.simpl.x509.AltName({ schema: asn1.result }); + break; + case "2.5.29.19": // BasicConstraints + this.parsedValue = new in_window.org.pkijs.simpl.x509.BasicConstraints({ schema: asn1.result }); + break; + case "2.5.29.20": // CRLNumber + case "2.5.29.27": // BaseCRLNumber (delta CRL indicator) + this.parsedValue = asn1.result; // Should be just a simple INTEGER + break; + case "2.5.29.21": // CRLReason + this.parsedValue = asn1.result; // Should be just a simple ENUMERATED + break; + case "2.5.29.24": // InvalidityDate + this.parsedValue = asn1.result; // Should be just a simple GeneralizedTime + break; + case "2.5.29.28": // IssuingDistributionPoint + this.parsedValue = new in_window.org.pkijs.simpl.x509.IssuingDistributionPoint({ schema: asn1.result }); + break; + case "2.5.29.29": // CertificateIssuer + this.parsedValue = new in_window.org.pkijs.simpl.GENERAL_NAMES({ schema: asn1.result }); // Should be just a simple + break; + case "2.5.29.30": // NameConstraints + this.parsedValue = new in_window.org.pkijs.simpl.x509.NameConstraints({ schema: asn1.result }); + break; + case "2.5.29.31": // CRLDistributionPoints + case "2.5.29.46": // FreshestCRL + this.parsedValue = new in_window.org.pkijs.simpl.x509.CRLDistributionPoints({ schema: asn1.result }); + break; + case "2.5.29.32": // CertificatePolicies + this.parsedValue = new in_window.org.pkijs.simpl.x509.CertificatePolicies({ schema: asn1.result }); + break; + case "2.5.29.33": // PolicyMappings + this.parsedValue = new in_window.org.pkijs.simpl.x509.PolicyMappings({ schema: asn1.result }); + break; + case "2.5.29.35": // AuthorityKeyIdentifier + this.parsedValue = new in_window.org.pkijs.simpl.x509.AuthorityKeyIdentifier({ schema: asn1.result }); + break; + case "2.5.29.36": // PolicyConstraints + this.parsedValue = new in_window.org.pkijs.simpl.x509.PolicyConstraints({ schema: asn1.result }); + break; + case "2.5.29.37": // ExtKeyUsage + this.parsedValue = new in_window.org.pkijs.simpl.x509.ExtKeyUsage({ schema: asn1.result }); + break; + case "2.5.29.54": // InhibitAnyPolicy + this.parsedValue = asn1.result; // Should be just a simple INTEGER + break; + case "1.3.6.1.5.5.7.1.1": // AuthorityInfoAccess + case "1.3.6.1.5.5.7.1.11": // SubjectInfoAccess + this.parsedValue = new in_window.org.pkijs.simpl.x509.InfoAccess({ schema: asn1.result }); + break; + default: + } + // #endregion + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.EXTENSION.prototype.toSchema = + function() + { + // #region Create array for output sequence + var output_array = new Array(); + + output_array.push(new in_window.org.pkijs.asn1.OID({ value: this.extnID })); + + if(this.critical) + output_array.push(new in_window.org.pkijs.asn1.BOOLEAN({ value: this.critical })); + + output_array.push(this.extnValue); + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: output_array + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.EXTENSION.prototype.toJSON = + function() + { + var _object = { + extnID: this.extnID, + critical: this.critical, + extnValue: this.extnValue.toJSON() + }; + + if("parsedValue" in this) + _object.parsedValue = this.parsedValue.toJSON(); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "Extensions" type (sequence of many Extension) + //************************************************************************************** + in_window.org.pkijs.simpl.EXTENSIONS = + function() + { + // #region Internal properties of the object + this.extensions_array = new Array(); + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.EXTENSIONS.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + this.extensions_array = (arguments[0].extensions_array || (new Array())); + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.EXTENSIONS.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.EXTENSIONS({ + names: { + extensions: "extensions" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for EXTENSIONS"); + // #endregion + + // #region Get internal properties from parsed schema + for(var i = 0; i < asn1.result.extensions.length; i++) + this.extensions_array.push(new in_window.org.pkijs.simpl.EXTENSION({ schema: asn1.result.extensions[i] })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.EXTENSIONS.prototype.toSchema = + function() + { + // #region Construct and return new ASN.1 schema for this object + var extension_schemas = new Array(); + + for(var i = 0; i < this.extensions_array.length; i++) + extension_schemas.push(this.extensions_array[i].toSchema()); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: extension_schemas + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.EXTENSIONS.prototype.toJSON = + function() + { + var _object = { + extensions_array: new Array() + }; + + for(var i = 0; i < this.extensions_array.length; i++) + _object.extensions_array.push(this.extensions_array[i].toJSON()); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for X.509 v3 certificate (RFC5280) + //************************************************************************************** + in_window.org.pkijs.simpl.CERT = + function() + { + // #region Internal properties of the object + // #region Properties from certificate TBS part + this.tbs = new ArrayBuffer(0); // Encoded value of certificate TBS (need to have it for certificate validation) + + // OPTIONAL this.version = 0; + this.serialNumber = new in_window.org.pkijs.asn1.INTEGER(); // Might be a very long integer value + this.signature = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); // Signature algorithm from certificate TBS part + this.issuer = new in_window.org.pkijs.simpl.RDN(); + this.notBefore = new in_window.org.pkijs.simpl.TIME(); + this.notAfter = new in_window.org.pkijs.simpl.TIME(); + this.subject = new in_window.org.pkijs.simpl.RDN(); + this.subjectPublicKeyInfo = new in_window.org.pkijs.simpl.PUBLIC_KEY_INFO(); + // OPTIONAL this.issuerUniqueID = new ArrayBuffer(0); // IMPLICIT bistring value + // OPTIONAL this.subjectUniqueID = new ArrayBuffer(0); // IMPLICIT bistring value + // OPTIONAL this.extensions = new Array(); // Array of "simpl.EXTENSION" + // #endregion + + // #region Properties from certificate major part + this.signatureAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); // Signature algorithm from certificate major part + this.signatureValue = new in_window.org.pkijs.asn1.BITSTRING(); + // #endregion + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.CERT.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + // #region Properties from certificate TBS part + this.tbs = arguments[0].tbs || new ArrayBuffer(0); + + if("version" in arguments[0]) + this.version = arguments[0].version; + this.serialNumber = arguments[0].serialNumber || new in_window.org.pkijs.asn1.INTEGER(); // Might be a very long integer value + this.signature = arguments[0].signature || new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); // Signature algorithm from certificate TBS part + this.issuer = arguments[0].issuer || new in_window.org.pkijs.simpl.RDN(); + this.notBefore = arguments[0].not_before || new in_window.org.pkijs.simpl.TIME(); + this.notAfter = arguments[0].not_after || new in_window.org.pkijs.simpl.TIME(); + this.subject = arguments[0].subject || new in_window.org.pkijs.simpl.RDN(); + this.subjectPublicKeyInfo = arguments[0].subjectPublicKeyInfo || new in_window.org.pkijs.simpl.PUBLIC_KEY_INFO(); + if("issuerUniqueID" in arguments[0]) + this.issuerUniqueID = arguments[0].issuerUniqueID; + if("subjectUniqueID" in arguments[0]) + this.subjectUniqueID = arguments[0].subjectUniqueID; + if("extensions" in arguments[0]) + this.extensions = arguments[0].extensions; + // #endregion + + // #region Properties from certificate major part + this.signatureAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); // Signature algorithm from certificate major part + this.signatureValue = new in_window.org.pkijs.asn1.BITSTRING(); + // #endregion + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.CERT.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.CERT({ + names: { + tbsCertificate: { + names: { + extensions: { + names: { + extensions: "tbsCertificate.extensions" + } + } + } + } + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for CERT"); + // #endregion + + // #region Get internal properties from parsed schema + this.tbs = asn1.result["tbsCertificate"].value_before_decode; + + if("tbsCertificate.version" in asn1.result) + this.version = asn1.result["tbsCertificate.version"].value_block.value_dec; + this.serialNumber = asn1.result["tbsCertificate.serialNumber"]; + this.signature = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ schema: asn1.result["tbsCertificate.signature"] }); + this.issuer = new in_window.org.pkijs.simpl.RDN({ schema: asn1.result["tbsCertificate.issuer"] }); + this.notBefore = new in_window.org.pkijs.simpl.TIME({ schema: asn1.result["tbsCertificate.notBefore"] }); + this.notAfter = new in_window.org.pkijs.simpl.TIME({ schema: asn1.result["tbsCertificate.notAfter"] }); + this.subject = new in_window.org.pkijs.simpl.RDN({ schema: asn1.result["tbsCertificate.subject"] }); + this.subjectPublicKeyInfo = new in_window.org.pkijs.simpl.PUBLIC_KEY_INFO({ schema: asn1.result["tbsCertificate.subjectPublicKeyInfo"] }); + if("tbsCertificate.issuerUniqueID" in asn1.result) + this.issuerUniqueID = asn1.result["tbsCertificate.issuerUniqueID"].value_block.value_hex; + if("tbsCertificate.subjectUniqueID" in asn1.result) + this.issuerUniqueID = asn1.result["tbsCertificate.subjectUniqueID"].value_block.value_hex; + if("tbsCertificate.extensions" in asn1.result) + { + this.extensions = new Array(); + + var extensions = asn1.result["tbsCertificate.extensions"]; + + for(var i = 0; i < extensions.length; i++) + this.extensions.push(new in_window.org.pkijs.simpl.EXTENSION({ schema: extensions[i] })); + } + + this.signatureAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ schema: asn1.result["signatureAlgorithm"] }); + this.signatureValue = asn1.result["signatureValue"]; + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.CERT.prototype.encodeTBS = + function() + { + /// Create ASN.1 schema for existing values of TBS part for the certificate + + // #region Create array for output sequence + var output_array = new Array(); + + if("version" in this) + output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + value: [ + new in_window.org.pkijs.asn1.INTEGER({ value: this.version }) // EXPLICIT integer value + ] + })); + + output_array.push(this.serialNumber); + output_array.push(this.signature.toSchema()); + output_array.push(this.issuer.toSchema()); + + output_array.push(new in_window.org.pkijs.asn1.SEQUENCE({ + value: [ + this.notBefore.toSchema(), + this.notAfter.toSchema() + ] + })); + + output_array.push(this.subject.toSchema()); + output_array.push(this.subjectPublicKeyInfo.toSchema()); + + if("issuerUniqueID" in this) + output_array.push(new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + }, + value_hex: this.issuerUniqueID + })); + if("subjectUniqueID" in this) + output_array.push(new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 2 // [2] + }, + value_hex: this.subjectUniqueID + })); + + if("subjectUniqueID" in this) + output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 3 // [3] + }, + value: [this.extensions.toSchema()] + })); + + if("extensions" in this) + { + var extensions = new Array(); + + for(var i = 0; i < this.extensions.length; i++) + extensions.push(this.extensions[i].toSchema()); + + output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 3 // [3] + }, + value: [new in_window.org.pkijs.asn1.SEQUENCE({ + value: extensions + })] + })); + } + // #endregion + + // #region Create and return output sequence + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: output_array + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.CERT.prototype.toSchema = + function(encodeFlag) + { + /// If param equal to false then create TBS schema via decoding stored value. In othe case create TBS schema via assembling from TBS parts. + + if(typeof encodeFlag === "undefined") + encodeFlag = false; + + var tbs_schema = {}; + + // #region Decode stored TBS value + if(encodeFlag === false) + { + if(this.tbs.length === 0) // No stored certificate TBS part + return in_window.org.pkijs.schema.CERT().value[0]; + + var tbs_asn1 = in_window.org.pkijs.fromBER(this.tbs); + + tbs_schema = tbs_asn1.result; + } + // #endregion + // #region Create TBS schema via assembling from TBS parts + else + tbs_schema = in_window.org.pkijs.simpl.CERT.prototype.encodeTBS.call(this); + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: [ + tbs_schema, + this.signatureAlgorithm.toSchema(), + this.signatureValue + ] + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.CERT.prototype.verify = + function() + { + /// !!! Works well in Chrome dev versions only (April 2014th) !!! + /// Returns a new Promise object (in case of error), or a result of "crypto.subtle.veryfy" function + + // #region Global variables + var sequence = Promise.resolve(); + + var subjectPublicKeyInfo = {}; + + var signature = this.signatureValue; + var tbs = this.tbs; + + var _this = this; + // #endregion + + // #region Set correct "subjectPublicKeyInfo" value + if(arguments[0] instanceof Object) + { + if("issuerCertificate" in arguments[0]) // Must be of type "simpl.CERT" + subjectPublicKeyInfo = arguments[0].issuerCertificate.subjectPublicKeyInfo; + } + else + { + if(this.issuer.isEqual(this.subject)) // Self-signed certificate + subjectPublicKeyInfo = this.subjectPublicKeyInfo; + } + + if((subjectPublicKeyInfo instanceof in_window.org.pkijs.simpl.PUBLIC_KEY_INFO) === false) + return new Promise(function(resolve, reject) { reject("Please provide issuer certificate as a parameter"); }); + // #endregion + + // #region Get a "crypto" extension + var crypto = in_window.org.pkijs.getCrypto(); + if(typeof crypto == "undefined") + return new Promise(function(resolve, reject) { reject("Unable to create WebCrypto object"); }); + // #endregion + + // #region Find signer's hashing algorithm + var sha_algorithm = in_window.org.pkijs.getHashAlgorithm(this.signatureAlgorithm); + if(sha_algorithm === "") + return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + _this.signatureAlgorithm.algorithm_id); }); + // #endregion + + // #region Importing public key + sequence = sequence.then( + function() + { + // #region Get information about public key algorithm and default parameters for import + var algorithmObject = in_window.org.pkijs.getAlgorithmByOID(_this.signatureAlgorithm.algorithm_id); + if(("name" in algorithmObject) === false) + return new Promise(function(resolve, reject) { reject("Unsupported public key algorithm: " + _this.signatureAlgorithm.algorithm_id); }); + + var algorithm_name = algorithmObject.name; + + var algorithm = in_window.org.pkijs.getAlgorithmParameters(algorithm_name, "importkey"); + if("hash" in algorithm.algorithm) + algorithm.algorithm.hash.name = sha_algorithm; + // #endregion + + var publicKeyInfo_schema = subjectPublicKeyInfo.toSchema(); + var publicKeyInfo_buffer = publicKeyInfo_schema.toBER(false); + var publicKeyInfo_view = new Uint8Array(publicKeyInfo_buffer); + + return crypto.importKey("spki", publicKeyInfo_view, algorithm.algorithm, true, algorithm.usages); + } + ); + // #endregion + + // #region Verify signature for the certificate + sequence = sequence.then( + function(publicKey) + { + // #region Get default algorithm parameters for verification + var algorithm = in_window.org.pkijs.getAlgorithmParameters(publicKey.algorithm.name, "verify"); + if("hash" in algorithm.algorithm) + algorithm.algorithm.hash.name = sha_algorithm; + // #endregion + + // #region Special case for ECDSA signatures + var signature_value = signature.value_block.value_hex; + + if(publicKey.algorithm.name === "ECDSA") + { + var asn1 = in_window.org.pkijs.fromBER(signature_value); + signature_value = in_window.org.pkijs.createECDSASignatureFromCMS(asn1.result); + } + // #endregion + + // #region Special case for RSA-PSS + if(publicKey.algorithm.name === "RSA-PSS") + { + var pssParameters; + + try + { + pssParameters = new in_window.org.pkijs.simpl.x509.RSASSA_PSS_params({ schema: _this.signatureAlgorithm.algorithm_params }); + } + catch(ex) + { + return new Promise(function(resolve, reject) { reject(ex); }); + } + + if("saltLength" in pssParameters) + algorithm.algorithm.saltLength = pssParameters.saltLength; + else + algorithm.algorithm.saltLength = 20; + + var hash_algo = "SHA-1"; + + if("hashAlgorithm" in pssParameters) + { + var hashAlgorithm = in_window.org.pkijs.getAlgorithmByOID(pssParameters.hashAlgorithm.algorithm_id); + if(("name" in hashAlgorithm) === false) + return new Promise(function(resolve, reject) { reject("Unrecognized hash algorithm: " + pssParameters.hashAlgorithm.algorithm_id); }); + + hash_algo = hashAlgorithm.name; + } + + algorithm.algorithm.hash.name = hash_algo; + } + // #endregion + + return crypto.verify(algorithm.algorithm, + publicKey, + new Uint8Array(signature_value), + new Uint8Array(tbs)); + } + ); + // #endregion + + return sequence; + }; + //************************************************************************************** + in_window.org.pkijs.simpl.CERT.prototype.sign = + function(privateKey, hashAlgorithm) + { + /// Private key for "subjectPublicKeyInfo" structure + /// Hashing algorithm. Default SHA-1 + + // #region Initial variables + var _this = this; + // #endregion + + // #region Get a private key from function parameter + if(typeof privateKey === "undefined") + return new Promise(function(resolve, reject) { reject("Need to provide a private key for signing"); }); + // #endregion + + // #region Get hashing algorithm + if(typeof hashAlgorithm === "undefined") + hashAlgorithm = "SHA-1"; + else + { + // #region Simple check for supported algorithm + var oid = in_window.org.pkijs.getOIDByAlgorithm({ name: hashAlgorithm }); + if(oid === "") + return new Promise(function(resolve, reject) { reject("Unsupported hash algorithm: " + hashAlgorithm); }); + // #endregion + } + // #endregion + + // #region Get a "default parameters" for current algorithm + var defParams = in_window.org.pkijs.getAlgorithmParameters(privateKey.algorithm.name, "sign"); + defParams.algorithm.hash.name = hashAlgorithm; + // #endregion + + // #region Fill internal structures base on "privateKey" and "hashAlgorithm" + switch(privateKey.algorithm.name.toUpperCase()) + { + case "RSASSA-PKCS1-V1_5": + case "ECDSA": + _this.signature.algorithm_id = in_window.org.pkijs.getOIDByAlgorithm(defParams.algorithm); + _this.signatureAlgorithm.algorithm_id = _this.signature.algorithm_id; + break; + case "RSA-PSS": + { + // #region Set "saltLength" as a length (in octets) of hash function result + switch(hashAlgorithm.toUpperCase()) + { + case "SHA-256": + defParams.algorithm.saltLength = 32; + break; + case "SHA-384": + defParams.algorithm.saltLength = 48; + break; + case "SHA-512": + defParams.algorithm.saltLength = 64; + break; + default: + } + // #endregion + + // #region Fill "RSASSA_PSS_params" object + var paramsObject = {}; + + if(hashAlgorithm.toUpperCase() !== "SHA-1") + { + var hashAlgorithmOID = in_window.org.pkijs.getOIDByAlgorithm({ name: hashAlgorithm }); + if(hashAlgorithmOID === "") + return new Promise(function(resolve, reject) { reject("Unsupported hash algorithm: " + hashAlgorithm); }); + + paramsObject.hashAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: hashAlgorithmOID, + algorithm_params: new in_window.org.pkijs.asn1.NULL() + }); + + paramsObject.maskGenAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: "1.2.840.113549.1.1.8", // MGF1 + algorithm_params: paramsObject.hashAlgorithm.toSchema() + }) + } + + if(defParams.algorithm.saltLength !== 20) + paramsObject.saltLength = defParams.algorithm.saltLength; + + var pssParameters = new in_window.org.pkijs.simpl.x509.RSASSA_PSS_params(paramsObject); + // #endregion + + // #region Automatically set signature algorithm + _this.signature = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: "1.2.840.113549.1.1.10", + algorithm_params: pssParameters.toSchema() + }); + _this.signatureAlgorithm = _this.signature; // Must be the same + // #endregion + } + break; + default: + return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + privateKey.algorithm.name); }); + } + // #endregion + + // #region Create TBS data for signing + _this.tbs = in_window.org.pkijs.simpl.CERT.prototype.encodeTBS.call(this).toBER(false); + // #endregion + + // #region Get a "crypto" extension + var crypto = in_window.org.pkijs.getCrypto(); + if(typeof crypto == "undefined") + return new Promise(function(resolve, reject) { reject("Unable to create WebCrypto object"); }); + // #endregion + + // #region Signing TBS data on provided private key + return crypto.sign(defParams.algorithm, + privateKey, + new Uint8Array(_this.tbs)).then( + function(result) + { + // #region Special case for ECDSA algorithm + if(defParams.algorithm.name === "ECDSA") + result = in_window.org.pkijs.createCMSECDSASignature(result); + // #endregion + + _this.signatureValue = new in_window.org.pkijs.asn1.BITSTRING({ value_hex: result }); + }, + function(error) + { + return new Promise(function(resolve, reject) { reject("Signing error: " + error); }); + } + ); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.CERT.prototype.getPublicKey = + function() + { + /// Importing public key for current certificate + + // #region Initial variables + var algorithm; + // #endregion + + // #region Get a "crypto" extension + var crypto = in_window.org.pkijs.getCrypto(); + if(typeof crypto == "undefined") + return new Promise(function(resolve, reject) { reject("Unable to create WebCrypto object"); }); + // #endregion + + // #region Find correct algorithm for imported public key + if(arguments[0] instanceof Object) + { + if("algorithm" in arguments[0]) + algorithm = arguments[0].algorithm; + else + return new Promise(function(resolve, reject) { reject("Absent mandatory parameter \"algorithm\""); }); + } + else + { + // #region Find signer's hashing algorithm + var sha_algorithm = in_window.org.pkijs.getHashAlgorithm(this.signatureAlgorithm); + if(sha_algorithm === "") + return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + _this.signatureAlgorithm.algorithm_id); }); + // #endregion + + // #region Get information about public key algorithm and default parameters for import + var algorithmObject = in_window.org.pkijs.getAlgorithmByOID(this.signatureAlgorithm.algorithm_id); + if(("name" in algorithmObject) === false) + return new Promise(function(resolve, reject) { reject("Unsupported public key algorithm: " + _this.signatureAlgorithm.algorithm_id); }); + + var algorithm_name = algorithmObject.name; + + algorithm = in_window.org.pkijs.getAlgorithmParameters(algorithm_name, "importkey"); + if("hash" in algorithm.algorithm) + algorithm.algorithm.hash.name = sha_algorithm; + // #endregion + } + // #endregion + + // #region Get neccessary values from internal fields for current certificate + var publicKeyInfo_schema = this.subjectPublicKeyInfo.toSchema(); + var publicKeyInfo_buffer = publicKeyInfo_schema.toBER(false); + var publicKeyInfo_view = new Uint8Array(publicKeyInfo_buffer); + // #endregion + + return crypto.importKey("spki", publicKeyInfo_view, algorithm.algorithm, true, algorithm.usages); + }; + //************************************************************************************** + in_window.org.pkijs.simpl.CERT.prototype.getKeyHash = + function() + { + /// Get SHA-1 hash value for subject public key + + // #region Get a "crypto" extension + var crypto = in_window.org.pkijs.getCrypto(); + if(typeof crypto == "undefined") + return new Promise(function(resolve, reject) { reject("Unable to create WebCrypto object"); }); + // #endregion + + return crypto.digest({ name: "sha-1" }, new Uint8Array(this.subjectPublicKeyInfo.subjectPublicKey.value_block.value_hex)); + }; + //************************************************************************************** + in_window.org.pkijs.simpl.CERT.prototype.toJSON = + function() + { + var _object = { + tbs: in_window.org.pkijs.bufferToHexCodes(this.tbs, 0, this.tbs.byteLength), + serialNumber: this.serialNumber.toJSON(), + signature: this.signature.toJSON(), + issuer: this.issuer.toJSON(), + notBefore: this.notBefore.toJSON(), + notAfter: this.notAfter.toJSON(), + subject: this.subject.toJSON(), + subjectPublicKeyInfo: this.subjectPublicKeyInfo.toJSON(), + signatureAlgorithm: this.signatureAlgorithm.toJSON(), + signatureValue: this.signatureValue.toJSON() + }; + + if("version" in this) + _object.version = this.version; + + if("issuerUniqueID" in this) + _object.issuerUniqueID = in_window.org.pkijs.bufferToHexCodes(this.issuerUniqueID, 0, this.issuerUniqueID.byteLength); + + if("subjectUniqueID" in this) + _object.subjectUniqueID = in_window.org.pkijs.bufferToHexCodes(this.subjectUniqueID, 0, this.subjectUniqueID.byteLength); + + if("extensions" in this) + { + _object.extensions = new Array(); + + for(var i = 0; i < this.extensions.length; i++) + _object.extensions.push(this.extensions[i].toJSON()); + } + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "revoked certificate" type (to use in CRL) + //************************************************************************************** + in_window.org.pkijs.simpl.REV_CERT = + function() + { + // #region Internal properties of the object + this.userCertificate = new in_window.org.pkijs.asn1.INTEGER(); + this.revocationDate = new in_window.org.pkijs.simpl.TIME(); + // OPTIONAL this.crlEntryExtensions = new Array(); // Array of "in_window.org.pkijs.simpl.EXTENSION"); + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.REV_CERT.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.userCertificate = arguments[0].userCertificate || new in_window.org.pkijs.asn1.INTEGER(); + this.revocationDate = arguments[0].revocationDate || new in_window.org.pkijs.simpl.TIME(); + if("crlEntryExtensions" in arguments[0]) + this.crlEntryExtensions = arguments[0].crlEntryExtensions; + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.REV_CERT.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + new in_window.org.pkijs.asn1.SEQUENCE({ + value: [ + new in_window.org.pkijs.asn1.INTEGER({ name: "userCertificate" }), + in_window.org.pkijs.schema.TIME({ + names: { + utcTimeName: "revocationDate", + generalTimeName: "revocationDate" + } + }), + in_window.org.pkijs.schema.EXTENSIONS({ + names: { + block_name: "crlEntryExtensions" + } + }, true) + ] + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for REV_CERT"); + // #endregion + + // #region Get internal properties from parsed schema + this.userCertificate = asn1.result["userCertificate"]; + this.revocationDate = new in_window.org.pkijs.simpl.TIME({ schema: asn1.result["revocationDate"] }); + + if("crlEntryExtensions" in asn1.result) + { + this.crlEntryExtensions = new Array(); + var exts = asn1.result["crlEntryExtensions"].value_block.value; + + for(var i = 0; i < exts.length; i++) + this.crlEntryExtensions.push(new in_window.org.pkijs.simpl.EXTENSION({ schema: exts[i] })); + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.REV_CERT.prototype.toSchema = + function() + { + // #region Create array for output sequence + var sequence_array = new Array(); + sequence_array.push(this.userCertificate); + sequence_array.push(this.revocationDate.toSchema()); + + if("crlEntryExtensions" in this) + { + var exts = new Array(); + + for(var i = 0; i < this.crlEntryExtensions.length; i++) + exts.push(this.crlEntryExtensions[i].toSchema()); + + sequence_array.push(new in_window.org.pkijs.asn1.SEQUENCE({ value: exts })); + } + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: sequence_array + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.REV_CERT.prototype.toJSON = + function() + { + var _object = { + userCertificate: this.userCertificate.toJSON(), + revocationDate: this.revocationDate.toJSON + }; + + if("crlEntryExtensions" in this) + { + _object.crlEntryExtensions = new Array(); + + for(var i = 0; i < this.crlEntryExtensions.length; i++) + _object.crlEntryExtensions.push(this.crlEntryExtensions[i].toJSON()); + } + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for X.509 CRL (Certificate Revocation List)(RFC5280) + //************************************************************************************** + in_window.org.pkijs.simpl.CRL = + function() + { + // #region Internal properties of the object + // #region Properties from CRL TBS part + this.tbs = new ArrayBuffer(0); + + // OPTIONAL this.version = 1; + this.signature = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); + this.issuer = new in_window.org.pkijs.simpl.RDN(); + this.thisUpdate = new in_window.org.pkijs.simpl.TIME(); + // OPTIONAL this.nextUpdate = new in_window.org.pkijs.simpl.TIME(); + // OPTIONAL this.revokedCertificates = new Array(); // Array of REV_CERT objects + // OPTIONAL this.crlExtensions = new Array(); // Array of in_window.org.pkijs.simpl.EXTENSION(); + // #endregion + + // #region Properties from CRL major part + this.signatureAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); + this.signatureValue = new in_window.org.pkijs.asn1.BITSTRING(); + // #endregion + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.CRL.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + // #region Properties from CRL TBS part + this.tbs = arguments[0].tbs || new ArrayBuffer(0); + + if("version" in arguments[0]) + this.version = arguments[0].version; + this.signature = arguments[0].signature || new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); + this.issuer = arguments[0].issuer || new in_window.org.pkijs.simpl.RDN(); + this.thisUpdate = arguments[0].thisUpdate || new in_window.org.pkijs.simpl.TIME(); + if("nextUpdate" in arguments[0]) + this.nextUpdate = arguments[0].nextUpdate; + if("revokedCertificates" in arguments[0]) + this.revokedCertificates = arguments[0].revokedCertificates; + if("crlExtensions" in arguments[0]) + this.crlExtensions = arguments[0].crlExtensions; + // #endregion + + // #region Properties from CRL major part + this.signatureAlgorithm = arguments[0].signatureAlgorithm || new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); + this.signatureValue = arguments[0].signatureValue || new in_window.org.pkijs.asn1.BITSTRING(); + // #endregion + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.CRL.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.CRL() + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for CRL"); + // #endregion + + // #region Get internal properties from parsed schema + this.tbs = asn1.result["tbsCertList"].value_before_decode; + + if("tbsCertList.version" in asn1.result) + this.version = asn1.result["tbsCertList.version"].value_block.value_dec; + this.signature = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ schema: asn1.result["tbsCertList.signature"] }); + this.issuer = new in_window.org.pkijs.simpl.RDN({ schema: asn1.result["tbsCertList.issuer"] }); + this.thisUpdate = new in_window.org.pkijs.simpl.TIME({ schema: asn1.result["tbsCertList.thisUpdate"] }); + if("tbsCertList.nextUpdate" in asn1.result) + this.nextUpdate = new in_window.org.pkijs.simpl.TIME({ schema: asn1.result["tbsCertList.nextUpdate"] }); + if("tbsCertList.revokedCertificates" in asn1.result) + { + this.revokedCertificates = new Array(); + + var rev_certs = asn1.result["tbsCertList.revokedCertificates"]; + for(var i = 0; i < rev_certs.length; i++) + this.revokedCertificates.push(new in_window.org.pkijs.simpl.REV_CERT({ schema: rev_certs[i] })); + } + if("tbsCertList.extensions" in asn1.result) + { + this.crlExtensions = new Array(); + var exts = asn1.result["tbsCertList.extensions"].value_block.value; + + for(var i = 0; i < exts.length; i++) + this.crlExtensions.push(new in_window.org.pkijs.simpl.EXTENSION({ schema: exts[i] })); + } + + this.signatureAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ schema: asn1.result["signatureAlgorithm"] }); + this.signatureValue = asn1.result["signatureValue"]; + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.CRL.prototype.encodeTBS = + function() + { + // #region Create array for output sequence + var output_array = new Array(); + + if("version" in this) + output_array.push(new in_window.org.pkijs.asn1.INTEGER({ value: this.version })); + + output_array.push(this.signature.toSchema()); + output_array.push(this.issuer.toSchema()); + output_array.push(this.thisUpdate.toSchema()); + + if("nextUpdate" in this) + output_array.push(this.nextUpdate.toSchema()); + + if("revokedCertificates" in this) + { + var rev_certificates = new Array(); + + for(var i = 0; i < this.revokedCertificates.length; i++) + rev_certificates.push(this.revokedCertificates[i].toSchema()); + + output_array.push(new in_window.org.pkijs.asn1.SEQUENCE({ + value: rev_certificates + })); + } + + if("crlExtensions" in this) + { + var extensions = new Array(); + + for(var j = 0; j < this.crlExtensions.length; j++) + extensions.push(this.crlExtensions[j].toSchema()); + + output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + value: [ + new in_window.org.pkijs.asn1.SEQUENCE({ + value: extensions + }) + ] + })); + } + // #endregion + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: output_array + })); + }; + //************************************************************************************** + in_window.org.pkijs.simpl.CRL.prototype.toSchema = + function(encodeFlag) + { + /// If param equal to false then create TBS schema via decoding stored value. In othe case create TBS schema via assembling from TBS parts. + + // #region Check "encodeFlag" + if(typeof encodeFlag === "undefined") + encodeFlag = false; + // #endregion + + // #region Decode stored TBS value + var tbs_schema; + + if(encodeFlag === false) + { + if(this.tbs.length === 0) // No stored TBS part + return in_window.org.pkijs.schema.CRL(); + + tbs_schema = in_window.org.pkijs.fromBER(this.tbs).result; + } + // #endregion + // #region Create TBS schema via assembling from TBS parts + else + tbs_schema = in_window.org.pkijs.simpl.CRL.prototype.encodeTBS.call(this); + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: [ + tbs_schema, + this.signatureAlgorithm.toSchema(), + this.signatureValue + ] + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.CRL.prototype.verify = + function() + { + // #region Global variables + var sequence = Promise.resolve(); + + var signature = this.signatureValue; + var tbs = this.tbs; + + var subjectPublicKeyInfo = -1; + + var _this = this; + // #endregion + + // #region Get information about CRL issuer certificate + if(arguments[0] instanceof Object) + { + if("issuerCertificate" in arguments[0]) // "issuerCertificate" must be of type "simpl.CERT" + { + subjectPublicKeyInfo = arguments[0].issuerCertificate.subjectPublicKeyInfo; + + // The CRL issuer name and "issuerCertificate" subject name are not equal + if(this.issuer.isEqual(arguments[0].issuerCertificate.subject) == false) + return new Promise(function(resolve, reject) { resolve(false); }); + } + + // #region In case if there is only public key during verification + if("publicKeyInfo" in arguments[0]) + subjectPublicKeyInfo = arguments[0].publicKeyInfo; // Must be of type "org.pkijs.simpl.PUBLIC_KEY_INFO" + // #endregion + } + + if((subjectPublicKeyInfo instanceof in_window.org.pkijs.simpl.PUBLIC_KEY_INFO) === false) + return new Promise(function(resolve, reject) { reject("Issuer's certificate must be provided as an input parameter"); }); + // #endregion + + // #region Check the CRL for unknown critical extensions + if("crlExtensions" in this) + { + for(var i = 0; i < this.crlExtensions.length; i++) + { + if(this.crlExtensions[i].critical) + { + // We can not be sure that unknown extension has no value for CRL signature + if(("parsedValue" in this.crlExtensions[i]) == false) + return new Promise(function(resolve, reject) { resolve(false); }); + } + } + } + // #endregion + + // #region Get a "crypto" extension + var crypto = in_window.org.pkijs.getCrypto(); + if(typeof crypto == "undefined") + return new Promise(function(resolve, reject) { reject("Unable to create WebCrypto object"); }); + // #endregion + + // #region Find signer's hashing algorithm + var sha_algorithm = in_window.org.pkijs.getHashAlgorithm(this.signatureAlgorithm); + if(sha_algorithm === "") + return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + _this.signatureAlgorithm.algorithm_id); }); + // #endregion + + // #region Import public key + sequence = sequence.then( + function() + { + // #region Get information about public key algorithm and default parameters for import + var algorithmObject = in_window.org.pkijs.getAlgorithmByOID(_this.signature.algorithm_id); + if(("name" in algorithmObject) === "") + return new Promise(function(resolve, reject) { reject("Unsupported public key algorithm: " + _this.signature.algorithm_id); }); + + var algorithm_name = algorithmObject.name; + + var algorithm = in_window.org.pkijs.getAlgorithmParameters(algorithm_name, "importkey"); + if("hash" in algorithm.algorithm) + algorithm.algorithm.hash.name = sha_algorithm; + // #endregion + + var publicKeyInfo_schema = subjectPublicKeyInfo.toSchema(); + var publicKeyInfo_buffer = publicKeyInfo_schema.toBER(false); + var publicKeyInfo_view = new Uint8Array(publicKeyInfo_buffer); + + return crypto.importKey("spki", + publicKeyInfo_view, + algorithm.algorithm, + true, + algorithm.usages); + } + ); + // #endregion + + // #region Verify signature for the certificate + sequence = sequence.then( + function(publicKey) + { + // #region Get default algorithm parameters for verification + var algorithm = in_window.org.pkijs.getAlgorithmParameters(publicKey.algorithm.name, "verify"); + if("hash" in algorithm.algorithm) + algorithm.algorithm.hash.name = sha_algorithm; + // #endregion + + // #region Special case for ECDSA signatures + var signature_value = signature.value_block.value_hex; + + if(publicKey.algorithm.name === "ECDSA") + { + var asn1 = in_window.org.pkijs.fromBER(signature_value); + signature_value = in_window.org.pkijs.createECDSASignatureFromCMS(asn1.result); + } + // #endregion + + // #region Special case for RSA-PSS + if(publicKey.algorithm.name === "RSA-PSS") + { + var pssParameters; + + try + { + pssParameters = new in_window.org.pkijs.simpl.x509.RSASSA_PSS_params({ schema: _this.signatureAlgorithm.algorithm_params }); + } + catch(ex) + { + return new Promise(function(resolve, reject) { reject(ex); }); + } + + if("saltLength" in pssParameters) + algorithm.algorithm.saltLength = pssParameters.saltLength; + else + algorithm.algorithm.saltLength = 20; + + var hash_algo = "SHA-1"; + + if("hashAlgorithm" in pssParameters) + { + var hashAlgorithm = in_window.org.pkijs.getAlgorithmByOID(pssParameters.hashAlgorithm.algorithm_id); + if(("name" in hashAlgorithm) === false) + return new Promise(function(resolve, reject) { reject("Unrecognized hash algorithm: " + pssParameters.hashAlgorithm.algorithm_id); }); + + hash_algo = hashAlgorithm.name; + } + + algorithm.algorithm.hash.name = hash_algo; + } + // #endregion + + return crypto.verify(algorithm.algorithm, + publicKey, + new Uint8Array(signature_value), + new Uint8Array(tbs)); + } + ); + // #endregion + + return sequence; + }; + //************************************************************************************** + in_window.org.pkijs.simpl.CRL.prototype.sign = + function(privateKey, hashAlgorithm) + { + /// Private key for "subjectPublicKeyInfo" structure + /// Hashing algorithm. Default SHA-1 + + // #region Initial variables + var _this = this; + // #endregion + + // #region Get a private key from function parameter + if(typeof privateKey === "undefined") + return new Promise(function(resolve, reject) { reject("Need to provide a private key for signing"); }); + // #endregion + + // #region Get hashing algorithm + if(typeof hashAlgorithm === "undefined") + hashAlgorithm = "SHA-1"; + else + { + // #region Simple check for supported algorithm + var oid = in_window.org.pkijs.getOIDByAlgorithm({ name: hashAlgorithm }); + if(oid === "") + return new Promise(function(resolve, reject) { reject("Unsupported hash algorithm: " + hashAlgorithm); }); + // #endregion + } + // #endregion + + // #region Get a "default parameters" for current algorithm + var defParams = in_window.org.pkijs.getAlgorithmParameters(privateKey.algorithm.name, "sign"); + defParams.algorithm.hash.name = hashAlgorithm; + // #endregion + + // #region Fill internal structures base on "privateKey" and "hashAlgorithm" + switch(privateKey.algorithm.name.toUpperCase()) + { + case "RSASSA-PKCS1-V1_5": + case "ECDSA": + _this.signature.algorithm_id = in_window.org.pkijs.getOIDByAlgorithm(defParams.algorithm); + _this.signatureAlgorithm.algorithm_id = _this.signature.algorithm_id; + break; + case "RSA-PSS": + { + // #region Set "saltLength" as a length (in octets) of hash function result + switch(hashAlgorithm.toUpperCase()) + { + case "SHA-256": + defParams.algorithm.saltLength = 32; + break; + case "SHA-384": + defParams.algorithm.saltLength = 48; + break; + case "SHA-512": + defParams.algorithm.saltLength = 64; + break; + default: + } + // #endregion + + // #region Fill "RSASSA_PSS_params" object + var paramsObject = {}; + + if(hashAlgorithm.toUpperCase() !== "SHA-1") + { + var hashAlgorithmOID = in_window.org.pkijs.getOIDByAlgorithm({ name: hashAlgorithm }); + if(hashAlgorithmOID === "") + return new Promise(function(resolve, reject) { reject("Unsupported hash algorithm: " + hashAlgorithm); }); + + paramsObject.hashAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: hashAlgorithmOID, + algorithm_params: new in_window.org.pkijs.asn1.NULL() + }); + + paramsObject.maskGenAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: "1.2.840.113549.1.1.8", // MGF1 + algorithm_params: paramsObject.hashAlgorithm.toSchema() + }) + } + + if(defParams.algorithm.saltLength !== 20) + paramsObject.saltLength = defParams.algorithm.saltLength; + + var pssParameters = new in_window.org.pkijs.simpl.x509.RSASSA_PSS_params(paramsObject); + // #endregion + + // #region Automatically set signature algorithm + _this.signature = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: "1.2.840.113549.1.1.10", + algorithm_params: pssParameters.toSchema() + }); + _this.signatureAlgorithm = _this.signature; // Must be the same + // #endregion + } + break; + default: + return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + privateKey.algorithm.name); }); + } + // #endregion + + // #region Create TBS data for signing + _this.tbs = in_window.org.pkijs.simpl.CRL.prototype.encodeTBS.call(this).toBER(false); + // #endregion + + // #region Get a "crypto" extension + var crypto = in_window.org.pkijs.getCrypto(); + if(typeof crypto == "undefined") + return new Promise(function(resolve, reject) { reject("Unable to create WebCrypto object"); }); + // #endregion + + // #region Signing TBS data on provided private key + return crypto.sign( + defParams.algorithm, + privateKey, + new Uint8Array(_this.tbs)). + then( + function(result) + { + // #region Special case for ECDSA algorithm + if(defParams.algorithm.name === "ECDSA") + result = in_window.org.pkijs.createCMSECDSASignature(result); + // #endregion + + _this.signatureValue = new in_window.org.pkijs.asn1.BITSTRING({ value_hex: result }); + }, + function(error) + { + return new Promise(function(resolve, reject) { reject("Signing error: " + error); }); + } + ); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.CRL.prototype.isCertificateRevoked = + function() + { + // #region Get input certificate + var certificate = {}; + + if(arguments[0] instanceof Object) + { + if("certificate" in arguments[0]) + certificate = arguments[0].certificate; + } + + if((certificate instanceof in_window.org.pkijs.simpl.CERT) === false) + return false; + // #endregion + + // #region Check that issuer of the input certificate is the same with issuer of this CRL + if(this.issuer.isEqual(certificate.issuer) === false) + return false; + // #endregion + + // #region Check that there are revoked certificates in this CRL + if(("revokedCertificates" in this) === false) + return false; + // #endregion + + // #region Search for input certificate in revoked certificates array + for(var i = 0; i < this.revokedCertificates.length; i++) + { + if(this.revokedCertificates[i].userCertificate.isEqual(certificate.serialNumber)) + return true; + } + // #endregion + + return false; + }; + //************************************************************************************** + in_window.org.pkijs.simpl.CRL.prototype.toJSON = + function() + { + var _object = { + tbs: in_window.org.pkijs.bufferToHexCodes(this.tbs, 0, this.tbs.byteLength), + signature: this.signature.toJSON(), + issuer: this.issuer.toJSON(), + thisUpdate: this.thisUpdate.toJSON(), + signatureAlgorithm: this.signatureAlgorithm.toJSON(), + signatureValue: this.signatureValue.toJSON() + }; + + if("version" in this) + _object.version = this.version; + + if("nextUpdate" in this) + _object.nextUpdate = this.nextUpdate.toJSON(); + + if("revokedCertificates" in this) + { + _object.revokedCertificates = new Array(); + + for(var i = 0; i < this.revokedCertificates.length; i++) + _object.revokedCertificates.push(this.revokedCertificates[i].toJSON()); + } + + if("crlExtensions" in this) + { + _object.crlExtensions = new Array(); + + for(var i = 0; i < this.crlExtensions.length; i++) + _object.crlExtensions.push(this.crlExtensions[i].toJSON()); + } + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for "Attribute" type + //************************************************************************************** + in_window.org.pkijs.simpl.ATTRIBUTE = + function() + { + // #region Internal properties of the object + this.type = ""; + this.values = new Array(); + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.ATTRIBUTE.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.type = arguments[0].type || ""; + this.values = arguments[0].values || new Array(); + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.ATTRIBUTE.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.ATTRIBUTE({ + names: { + type: "type", + values: "values" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for ATTRIBUTE"); + // #endregion + + // #region Get internal properties from parsed schema + this.type = asn1.result["type"].value_block.toString(); + this.values = asn1.result["values"]; + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.ATTRIBUTE.prototype.toSchema = + function() + { + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: [ + new in_window.org.pkijs.asn1.OID({ value: this.type }), + new in_window.org.pkijs.asn1.SET({ + value: this.values + }) + ] + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.ATTRIBUTE.prototype.toJSON = + function() + { + var _object = { + type: this.type, + values: new Array() + }; + + for(var i = 0; i < this.values.length; i++) + _object.values.push(this.values[i].toJSON()); + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for PKCS#10 certificate request + //************************************************************************************** + in_window.org.pkijs.simpl.PKCS10 = + function() + { + // #region Internal properties of the object + this.tbs = new ArrayBuffer(0); + + this.version = 0; + this.subject = new in_window.org.pkijs.simpl.RDN(); + this.subjectPublicKeyInfo = new in_window.org.pkijs.simpl.PUBLIC_KEY_INFO(); + // OPTIONAL this.attributes = new Array(); // Array of simpl.ATTRIBUTE objects + + this.signatureAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); // Signature algorithm from certificate major part + this.signatureValue = new in_window.org.pkijs.asn1.BITSTRING(); + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.PKCS10.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.tbs = arguments[0].tbs || new ArrayBuffer(0); + + this.version = arguments[0].version || 0; + this.subject = arguments[0].subject || new in_window.org.pkijs.simpl.RDN(); + this.subjectPublicKeyInfo = arguments[0].subjectPublicKeyInfo || new in_window.org.pkijs.simpl.PUBLIC_KEY_INFO(); + + if("attributes" in arguments[0]) + this.attributes = arguments[0].attributes; + + this.signatureAlgorithm = arguments[0].signatureAlgorithm || new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); // Signature algorithm from certificate major part + this.signatureValue = arguments[0].signatureValue || new in_window.org.pkijs.asn1.BITSTRING(); + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.PKCS10.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.PKCS10() + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for PKCS10"); + // #endregion + + // #region Get internal properties from parsed schema + this.tbs = asn1.result["CertificationRequestInfo"].value_before_decode; + + this.version = asn1.result["CertificationRequestInfo.version"].value_block.value_dec; + this.subject = new in_window.org.pkijs.simpl.RDN({ schema: asn1.result["CertificationRequestInfo.subject"] }); + this.subjectPublicKeyInfo = new in_window.org.pkijs.simpl.PUBLIC_KEY_INFO({ schema: asn1.result["CertificationRequestInfo.subjectPublicKeyInfo"] }); + if("CertificationRequestInfo.attributes" in asn1.result) + { + this.attributes = new Array(); + + var attrs = asn1.result["CertificationRequestInfo.attributes"]; + for(var i = 0; i < attrs.length; i++) + this.attributes.push(new in_window.org.pkijs.simpl.ATTRIBUTE({ schema: attrs[i] })); + } + + this.signatureAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ schema: asn1.result["signatureAlgorithm"] }); + this.signatureValue = asn1.result["signatureValue"]; + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.PKCS10.prototype.encodeTBS = + function() + { + // #region Create array for output sequence + var output_array = new Array(); + + output_array.push(new in_window.org.pkijs.asn1.INTEGER({ value: this.version })); + output_array.push(this.subject.toSchema()); + output_array.push(this.subjectPublicKeyInfo.toSchema()); + + if("attributes" in this) + { + var attributes = new Array(); + + for(var i = 0; i < this.attributes.length; i++) + attributes.push(this.attributes[i].toSchema()); + + output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + value: attributes + })); + } + // #endregion + + return (new in_window.org.pkijs.asn1.SEQUENCE({ value: output_array })); + }; + //************************************************************************************** + in_window.org.pkijs.simpl.PKCS10.prototype.toSchema = + function(encodeFlag) + { + /// If param equal to false then create TBS schema via decoding stored value. In othe case create TBS schema via assembling from TBS parts. + + // #region Check "encodeFlag" + if(typeof encodeFlag === "undefined") + encodeFlag = false; + // #endregion + + // #region Decode stored TBS value + var tbs_schema; + + if(encodeFlag === false) + { + if(this.tbs.length === 0) // No stored TBS part + return in_window.org.pkijs.schema.PKCS10(); + + tbs_schema = in_window.org.pkijs.fromBER(this.tbs).result; + } + // #endregion + // #region Create TBS schema via assembling from TBS parts + else + tbs_schema = in_window.org.pkijs.simpl.PKCS10.prototype.encodeTBS.call(this); + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: [ + tbs_schema, + this.signatureAlgorithm.toSchema(), + this.signatureValue + ] + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.PKCS10.prototype.verify = + function() + { + /// !!! Works well in Chrome dev versions only (April 2014th) !!! + /// Returns a new Promise object (in case of error), or a result of "crypto.subtle.veryfy" function + + // #region Global variables + var _this = this; + var sha_algorithm = ""; + + var sequence = Promise.resolve(); + + var subjectPublicKeyInfo = this.subjectPublicKeyInfo; + var signature = this.signatureValue; + var tbs = this.tbs; + // #endregion + + // #region Get a "crypto" extension + var crypto = in_window.org.pkijs.getCrypto(); + if(typeof crypto == "undefined") + return new Promise(function(resolve, reject) { reject("Unable to create WebCrypto object"); }); + // #endregion + + // #region Find a correct hashing algorithm + sha_algorithm = in_window.org.pkijs.getHashAlgorithm(this.signatureAlgorithm); + if(sha_algorithm === "") + return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + _this.signatureAlgorithm.algorithm_id); }); + // #endregion + + // #region Importing public key + sequence = sequence.then( + function() + { + // #region Get information about public key algorithm and default parameters for import + var algorithmObject = in_window.org.pkijs.getAlgorithmByOID(_this.signatureAlgorithm.algorithm_id); + if(("name" in algorithmObject) === false) + return new Promise(function(resolve, reject) { reject("Unsupported public key algorithm: " + _this.signatureAlgorithm.algorithm_id); }); + + var algorithm_name = algorithmObject.name; + + var algorithm = in_window.org.pkijs.getAlgorithmParameters(algorithm_name, "importkey"); + if("hash" in algorithm.algorithm) + algorithm.algorithm.hash.name = sha_algorithm; + // #endregion + + var publicKeyInfo_schema = subjectPublicKeyInfo.toSchema(); + var publicKeyInfo_buffer = publicKeyInfo_schema.toBER(false); + var publicKeyInfo_view = new Uint8Array(publicKeyInfo_buffer); + + return crypto.importKey("spki", publicKeyInfo_view, algorithm.algorithm, true, algorithm.usages); + } + ); + // #endregion + + // #region Verify signature + sequence = sequence.then( + function(publicKey) + { + // #region Get default algorithm parameters for verification + var algorithm = in_window.org.pkijs.getAlgorithmParameters(publicKey.algorithm.name, "verify"); + if("hash" in algorithm.algorithm) + algorithm.algorithm.hash.name = sha_algorithm; + // #endregion + + // #region Special case for ECDSA signatures + var signature_value = signature.value_block.value_hex; + + if(publicKey.algorithm.name === "ECDSA") + { + var asn1 = in_window.org.pkijs.fromBER(signature_value); + signature_value = in_window.org.pkijs.createECDSASignatureFromCMS(asn1.result); + } + // #endregion + + // #region Special case for RSA-PSS + if(publicKey.algorithm.name === "RSA-PSS") + { + var pssParameters; + + try + { + pssParameters = new in_window.org.pkijs.simpl.x509.RSASSA_PSS_params({ schema: _this.signatureAlgorithm.algorithm_params }); + } + catch(ex) + { + return new Promise(function(resolve, reject) { reject(ex); }); + } + + if("saltLength" in pssParameters) + algorithm.algorithm.saltLength = pssParameters.saltLength; + else + algorithm.algorithm.saltLength = 20; + + var hash_algo = "SHA-1"; + + if("hashAlgorithm" in pssParameters) + { + var hashAlgorithm = in_window.org.pkijs.getAlgorithmByOID(pssParameters.hashAlgorithm.algorithm_id); + if(("name" in hashAlgorithm) === false) + return new Promise(function(resolve, reject) { reject("Unrecognized hash algorithm: " + pssParameters.hashAlgorithm.algorithm_id); }); + + hash_algo = hashAlgorithm.name; + } + + algorithm.algorithm.hash.name = hash_algo; + } + // #endregion + + return crypto.verify(algorithm.algorithm, + publicKey, + new Uint8Array(signature_value), + new Uint8Array(tbs)); + } + ); + // #endregion + + return sequence; + }; + //************************************************************************************** + in_window.org.pkijs.simpl.PKCS10.prototype.sign = + function(privateKey, hashAlgorithm) + { + /// Private key for "subjectPublicKeyInfo" structure + /// Hashing algorithm. Default SHA-1 + + // #region Initial variables + var _this = this; + // #endregion + + // #region Get a private key from function parameter + if(typeof privateKey === "undefined") + return new Promise(function(resolve, reject) { reject("Need to provide a private key for signing"); }); + // #endregion + + // #region Get hashing algorithm + if(typeof hashAlgorithm === "undefined") + hashAlgorithm = "SHA-1"; + else + { + // #region Simple check for supported algorithm + var oid = in_window.org.pkijs.getOIDByAlgorithm({ name: hashAlgorithm }); + if(oid === "") + return new Promise(function(resolve, reject) { reject("Unsupported hash algorithm: " + hashAlgorithm); }); + // #endregion + } + // #endregion + + // #region Get a "default parameters" for current algorithm + var defParams = in_window.org.pkijs.getAlgorithmParameters(privateKey.algorithm.name, "sign"); + defParams.algorithm.hash.name = hashAlgorithm; + // #endregion + + // #region Fill internal structures base on "privateKey" and "hashAlgorithm" + switch(privateKey.algorithm.name.toUpperCase()) + { + case "RSASSA-PKCS1-V1_5": + case "ECDSA": + _this.signatureAlgorithm.algorithm_id = in_window.org.pkijs.getOIDByAlgorithm(defParams.algorithm); + break; + case "RSA-PSS": + { + // #region Set "saltLength" as a length (in octets) of hash function result + switch(hashAlgorithm.toUpperCase()) + { + case "SHA-256": + defParams.algorithm.saltLength = 32; + break; + case "SHA-384": + defParams.algorithm.saltLength = 48; + break; + case "SHA-512": + defParams.algorithm.saltLength = 64; + break; + default: + } + // #endregion + + // #region Fill "RSASSA_PSS_params" object + var paramsObject = {}; + + if(hashAlgorithm.toUpperCase() !== "SHA-1") + { + var hashAlgorithmOID = in_window.org.pkijs.getOIDByAlgorithm({ name: hashAlgorithm }); + if(hashAlgorithmOID === "") + return new Promise(function(resolve, reject) { reject("Unsupported hash algorithm: " + hashAlgorithm); }); + + paramsObject.hashAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: hashAlgorithmOID, + algorithm_params: new in_window.org.pkijs.asn1.NULL() + }); + + paramsObject.maskGenAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: "1.2.840.113549.1.1.8", // MGF1 + algorithm_params: paramsObject.hashAlgorithm.toSchema() + }) + } + + if(defParams.algorithm.saltLength !== 20) + paramsObject.saltLength = defParams.algorithm.saltLength; + + var pssParameters = new in_window.org.pkijs.simpl.x509.RSASSA_PSS_params(paramsObject); + // #endregion + + // #region Automatically set signature algorithm + _this.signatureAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: "1.2.840.113549.1.1.10", + algorithm_params: pssParameters.toSchema() + }); + // #endregion + } + break; + default: + return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + privateKey.algorithm.name); }); + } + // #endregion + + // #region Create TBS data for signing + _this.tbs = in_window.org.pkijs.simpl.PKCS10.prototype.encodeTBS.call(this).toBER(false); + // #endregion + + // #region Get a "crypto" extension + var crypto = in_window.org.pkijs.getCrypto(); + if(typeof crypto == "undefined") + return new Promise(function(resolve, reject) { reject("Unable to create WebCrypto object"); }); + // #endregion + + // #region Signing TBS data on provided private key + return crypto.sign(defParams.algorithm, + privateKey, + new Uint8Array(_this.tbs)).then( + function(result) + { + // #region Special case for ECDSA algorithm + if(defParams.algorithm.name === "ECDSA") + result = in_window.org.pkijs.createCMSECDSASignature(result); + // #endregion + + _this.signatureValue = new in_window.org.pkijs.asn1.BITSTRING({ value_hex: result }); + }, + function(error) + { + return new Promise(function(resolve, reject) { reject("Signing error: " + error); }); + } + ); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.PKCS10.prototype.toJSON = + function() + { + var _object = { + tbs: in_window.org.pkijs.bufferToHexCodes(this.tbs, 0, this.tbs.byteLength), + version: this.version, + subject: this.subject.toJSON(), + subjectPublicKeyInfo: this.subjectPublicKeyInfo.toJSON(), + signatureAlgorithm: this.signatureAlgorithm.toJSON(), + signatureValue: this.signatureValue.toJSON() + }; + + if("attributes" in this) + { + _object.attributes = new Array(); + + for(var i = 0; i < this.attributes.length; i++) + _object.attributes.push(this.attributes[i].toJSON()); + } + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for PKCS#8 private key bag + //************************************************************************************** + in_window.org.pkijs.simpl.PKCS8 = + function() + { + // #region Internal properties of the object + this.version = 0; + this.privateKeyAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); + this.privateKey = new in_window.org.pkijs.asn1.OCTETSTRING(); + // OPTIONAL this.attributes // Array of "in_window.org.pkijs.simpl.ATTRIBUTE" + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.PKCS8.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + this.version = arguments[0].version || 0; + this.privateKeyAlgorithm = arguments[0].privateKeyAlgorithm || new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); + this.privateKey = arguments[0].privateKey || new in_window.org.pkijs.asn1.OCTETSTRING(); + + if("attributes" in arguments[0]) + this.attributes = arguments[0].attributes; + } + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.PKCS8.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.PKCS8({ + names: { + version: "version", + privateKeyAlgorithm: { + names: { + block_name: "privateKeyAlgorithm" + } + }, + privateKey: "privateKey", + attributes: "attributes" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for PKCS8"); + // #endregion + + // #region Get internal properties from parsed schema + this.version = asn1.result["version"].value_block.value_dec; + this.privateKeyAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ schema: asn1.result["privateKeyAlgorithm"] }); + this.privateKey = asn1.result["privateKey"]; + + if("attributes" in asn1.result) + { + this.attributes = new Array(); + var attrs = asn1.result["attributes"]; + + for(var i = 0; i < attrs.length; i++) + this.attributes.push(new in_window.org.pkijs.simpl.ATTRIBUTE({ schema: attrs[i] })); + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.PKCS8.prototype.toSchema = + function() + { + // #region Create array for output sequence + var output_array = new Array(); + + output_array.push(new in_window.org.pkijs.asn1.INTEGER({ value: this.version })); + output_array.push(this.privateKeyAlgorithm.toSchema()); + output_array.push(this.privateKey); + + if("attributes" in this) + { + var attrs = new Array(); + + for(var i = 0; i < this.attributes.length; i++) + attrs.push(this.attributes[i].toSchema()); + + output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + optional: true, + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + value: attrs + })); + } + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: output_array + })); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.PKCS8.prototype.toJSON = + function() + { + var _object = { + version: this.version, + privateKeyAlgorithm: this.privateKeyAlgorithm.toJSON(), + privateKey: this.privateKey.toJSON() + }; + + if("attributes" in this) + { + _object.attributes = new Array(); + + for(var i = 0; i < this.attributes.length; i++) + _object.attributes.push(this.attributes[i].toJSON()); + } + + return _object; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** + // #region Simplified structure for working with X.509 certificate chains + //************************************************************************************** + in_window.org.pkijs.simpl.CERT_CHAIN = + function() + { + // #region Internal properties of the object + /// Array of pre-defined trusted (by user) certificates + this.trusted_certs = new Array(); + /// Array with certificate chain. Could be only one end-user certificate in there! + this.certs = new Array(); + /// Array of all CRLs for all certificates from certificate chain + this.crls = new Array(); + // #endregion + + // #region Initialize internal properties by input values + if(arguments[0] instanceof Object) + { + this.trusted_certs = arguments[0].trusted_certs || new Array(); + this.certs = arguments[0].certs || new Array(); + this.crls = arguments[0].crls || new Array(); + } + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.CERT_CHAIN.prototype.sort = + function() + { + // #region Initial variables + /// Array of sorted certificates + var sorted_certs = new Array(); + + /// Initial array of certificates + var certs = this.certs.slice(0); // Explicity copy "this.certs" + + /// Date for checking certificate validity period + var check_date = new Date(); + + var _this = this; + // #endregion + + // #region Initial checks + if(certs.length === 0) + return new Promise(function(resolve, reject) + { + reject({ + result: false, + result_code: 2, + result_message: "Certificate's array can not be empty" + }); + }); + // #endregion + + // #region Find end-user certificate + var end_user_index = -1; + + for(var i = 0; i < certs.length; i++) + { + var isCA = false; + + if("extensions" in certs[i]) + { + var mustBeCA = false; + var keyUsagePresent = false; + var cRLSign = false; + + for(var j = 0; j < certs[i].extensions.length; j++) + { + if((certs[i].extensions[j].critical === true) && + (("parsedValue" in certs[i].extensions[j]) === false)) + { + return new Promise(function(resolve, reject) + { + reject({ + result: false, + result_code: 6, + result_message: "Unable to parse critical certificate extension: " + certs[i].extensions[j].extnID + }); + }); + } + + if(certs[i].extensions[j].extnID === "2.5.29.15") // KeyUsage + { + keyUsagePresent = true; + + var view = new Uint8Array(certs[i].extensions[j].parsedValue.value_block.value_hex); + + if((view[0] & 0x04) === 0x04) // Set flag "keyCertSign" + mustBeCA = true; + + if((view[0] & 0x02) === 0x02) // Set flag "cRLSign" + cRLSign = true; + } + + if(certs[i].extensions[j].extnID === "2.5.29.19") // BasicConstraints + { + if("cA" in certs[i].extensions[j].parsedValue) + { + if(certs[i].extensions[j].parsedValue.cA === true) + isCA = true; + } + } + } + + if((mustBeCA === true) && (isCA === false)) + return new Promise(function(resolve, reject) + { + reject({ + result: false, + result_code: 3, + result_message: "Unable to build certificate chain - using \"keyCertSign\" flag set without BasicConstaints" + }); + }); + + if((keyUsagePresent === true) && (isCA === true) && (mustBeCA === false)) + return new Promise(function(resolve, reject) + { + reject({ + result: false, + result_code: 4, + result_message: "Unable to build certificate chain - \"keyCertSign\" flag was not set" + }); + }); + + if((isCA === true) && (keyUsagePresent === true) && (cRLSign === false)) + return new Promise(function(resolve, reject) + { + reject({ + result: false, + result_code: 5, + result_message: "Unable to build certificate chain - intermediate certificate must have \"cRLSign\" key usage flag" + }); + }); + } + + if(isCA === false) + { + if(sorted_certs.length !== 0) + return new Promise(function(resolve, reject) + { + reject({ + result: false, + result_code: 7, + result_message: "Unable to build certificate chain - more than one possible end-user certificate" + }); + }); + + sorted_certs.push(certs[i]); + end_user_index = i; + } + } + + certs.splice(end_user_index, 1); + // #endregion + + // #region Check that end-user certificate was found + if(sorted_certs.length === 0) + return new Promise(function(resolve, reject) + { + reject({ + result: false, + result_code: 1, + result_message: "Can't find end-user certificate" + }); + }); + // #endregion + + // #region Return if there is only one certificate in certificate's array + if(certs.length === 0) + { + if(sorted_certs[0].issuer.isEqual(sorted_certs[0].subject) === true) + return new Promise(function(resolve, reject) { resolve(sorted_certs); }); + else + { + if(this.trusted_certs.length === 0) + { + return new Promise(function(resolve, reject) + { + reject({ + result: false, + result_code: 70, + result_message: "Can't find root certificate" + }); + }); + } + else + { + certs = _this.trusted_certs.splice(0); + } + } + + } + // #endregion + + /// Current certificate (to find issuer for) + var current_certificate = sorted_certs[0]; + + // #region Auxiliary functions working with Promises + function basic(subject_certificate, issuer_certificate) + { + /// Basic certificate checks + /// Certificate for testing (subject) + /// Certificate for issuer of subject certificate + + // #region Initial variables + var sequence = Promise.resolve(); + // #endregion + + // #region Check validity period for subject certificate + sequence = sequence.then( + function() + { + if((subject_certificate.notBefore.value > check_date) || + (subject_certificate.notAfter.value < check_date)) + { + return new Promise(function(resolve, reject) + { + reject({ + result: false, + result_code: 8, + result_message: "Certificate validity period is out of checking date" + }); + }); + } + } + ); + // #endregion + + // #region Give ability to not provide CRLs (all certificates assume to be valid) + if(_this.crls.length === 0) + return sequence.then( + function() + { + return new Promise(function(resolve, reject) { resolve(); }); + } + ); + // #endregion + + // #region Find correct CRL for "issuer_certificate" + function find_crl(index) + { + return _this.crls[index].verify({ issuerCertificate: issuer_certificate }).then( + function(result) + { + if(result === true) + return new Promise(function(resolve, reject) { resolve(_this.crls[index]); }); + else + { + index++; + + if(index < _this.crls.length) + return find_crl(index); + else + return new Promise(function(resolve, reject) + { + reject({ + result: false, + result_code: 9, + result_message: "Unable to find CRL for issuer's certificate" + }); + }); + } + }, + function(error) + { + return new Promise(function(resolve, reject) + { + reject({ + result: false, + result_code: 10, + result_message: "Unable to find CRL for issuer's certificate" + }); + }); + } + ); + } + + sequence = sequence.then( + function() + { + return find_crl(0); + } + ); + // #endregion + + // #region Check that subject certificate is not in the CRL + sequence = sequence.then( + function(crl) + { + /// CRL for issuer's certificate + + if(crl.isCertificateRevoked({ certificate: subject_certificate }) === true) + return new Promise(function(resolve, reject) + { + reject({ + result: false, + result_code: 11, + result_message: "Subject certificate was revoked" + }); + }); + else + return new Promise(function(resolve, reject) { resolve(); }); + }, + function(error) + { + /// Not for all certificates we have a CRL. So, this "stub" is for handling such situation - assiming we have a valid, non-revoked certificate + return new Promise(function(resolve, reject) { resolve(); }); + } + ); + // #endregion + + return sequence; + } + + function outer() + { + return inner(current_certificate, 0).then( + function(index) + { + sorted_certs.push(certs[index]); + current_certificate = certs[index]; + + certs.splice(index, 1); + + if(current_certificate.issuer.isEqual(current_certificate.subject) === true) + { + // #region Check that the "self-signed" certificate there is in "trusted_certs" array + var found = (_this.trusted_certs.length === 0); // If user did not set "trusted_certs" then we have an option to trust any self-signed certificate as root + + for(var i = 0; i < _this.trusted_certs.length; i++) + { + if((current_certificate.issuer.isEqual(_this.trusted_certs[i].issuer) === true) && + (current_certificate.subject.isEqual(_this.trusted_certs[i].subject) === true) && + (current_certificate.serialNumber.isEqual(_this.trusted_certs[i].serialNumber) === true)) + { + found = true; + break; + } + } + + if(found === false) + return new Promise(function(resolve, reject) + { + reject({ + result: false, + result_code: 22, + result_message: "Self-signed root certificate not in \"trusted certificates\" array" + }); + }); + // #endregion + + return (current_certificate.verify()).then( // Verifing last, self-signed certificate + function(result) + { + if(result === true) + return basic(current_certificate, current_certificate).then( + function() + { + return new Promise(function(resolve, reject) { resolve(sorted_certs); }); + }, + function(error) + { + return new Promise(function(resolve, reject) + { + reject({ + result: false, + result_code: 12, + result_message: error + }); + }); + } + ); + else + return new Promise(function(resolve, reject) + { + reject({ + result: false, + result_code: 13, + result_message: "Unable to build certificate chain - signature of root certificate is invalid" + }); + }); + }, + function(error) + { + return new Promise(function(resolve, reject) + { + reject({ + result: false, + result_code: 14, + result_message: error + }); + }); + } + ); + } + else // In case if self-signed cert for the chain in the "trusted_certs" array + { + if(certs.length > 0) + return outer(); + else + { + if(_this.trusted_certs.length !== 0) + { + certs = _this.trusted_certs.splice(0); + return outer(); + } + else + return new Promise(function(resolve, reject) + { + reject({ + result: false, + result_code: 23, + result_message: "Root certificate not found" + }); + }); + } + } + }, + function(error) + { + return new Promise(function(resolve, reject) + { + reject(error); + }); + } + ); + } + + function inner(current_certificate, index) + { + if(certs[index].subject.isEqual(current_certificate.issuer) === true) + { + return current_certificate.verify({ issuerCertificate: certs[index] }).then( + function(result) + { + if(result === true) + { + return basic(current_certificate, certs[index]).then( + function() + { + return new Promise(function(resolve, reject) { resolve(index); }); + }, + function(error) + { + return new Promise(function(resolve, reject) + { + reject({ + result: false, + result_code: 16, + result_message: error + }); + }); + } + ); + } + else + { + if(index < (certs.length - 1)) + return inner(current_certificate, index + 1); + else + return new Promise(function(resolve, reject) + { + reject({ + result: false, + result_code: 17, + result_message: "Unable to build certificate chain - incomplete certificate chain or signature of some certificate is invalid" + }); + }); + } + }, + function(error) + { + return new Promise(function(resolve, reject) + { + reject({ + result: false, + result_code: 18, + result_message: "Unable to build certificate chain - error during certificate signature verification" + }); + }); + } + ); + } + else + { + if(index < (certs.length - 1)) + return inner(current_certificate, index + 1); + else + return new Promise(function(resolve, reject) + { + reject({ + result: false, + result_code: 19, + result_message: "Unable to build certificate chain - incomplete certificate chain" + }); + }); + } + } + // #endregion + + // #region Find certificates for all issuers + return outer(); + // #endregion + }; + //************************************************************************************** + in_window.org.pkijs.simpl.CERT_CHAIN.prototype.verify = + function() + { + // #region Initial checks + if(this.certs.length === 0) + return new Promise(function(resolve, reject) { reject("Empty certificate array"); }); + // #endregion + + // #region Initial variables + var sequence = Promise.resolve(); + + var _this = this; + // #endregion + + // #region Get input variables + var initial_policy_set = new Array(); + initial_policy_set.push("2.5.29.32.0"); // "anyPolicy" + + var initial_explicit_policy = false; + var initial_policy_mapping_inhibit = false; + var initial_inhibit_policy = false; + + var initial_permitted_subtrees_set = new Array(); // Array of "simpl.x509.GeneralSubtree" + var initial_excluded_subtrees_set = new Array(); // Array of "simpl.x509.GeneralSubtree" + var initial_required_name_forms = new Array(); // Array of "simpl.x509.GeneralSubtree" + + var verification_time = new Date(); + + if(arguments[0] instanceof Object) + { + if("initial_policy_set" in arguments[0]) + initial_policy_set = arguments[0].initial_policy_set; + + if("initial_explicit_policy" in arguments[0]) + initial_explicit_policy = arguments[0].initial_explicit_policy; + + if("initial_policy_mapping_inhibit" in arguments[0]) + initial_policy_mapping_inhibit = arguments[0].initial_policy_mapping_inhibit; + + if("initial_inhibit_policy" in arguments[0]) + initial_inhibit_policy = arguments[0].initial_inhibit_policy; + + if("initial_permitted_subtrees_set" in arguments[0]) + initial_permitted_subtrees_set = arguments[0].initial_permitted_subtrees_set; + + if("initial_excluded_subtrees_set" in arguments[0]) + initial_excluded_subtrees_set = arguments[0].initial_excluded_subtrees_set; + + if("initial_required_name_forms" in arguments[0]) + initial_required_name_forms = arguments[0].initial_required_name_forms; + } + + var explicit_policy_indicator = initial_explicit_policy; + var policy_mapping_inhibit_indicator = initial_policy_mapping_inhibit; + var inhibit_any_policy_indicator = initial_inhibit_policy; + + var pending_constraints = new Array(3); + pending_constraints[0] = false; // For "explicit_policy_pending" + pending_constraints[1] = false; // For "policy_mapping_inhibit_pending" + pending_constraints[2] = false; // For "inhibit_any_policy_pending" + + var explicit_policy_pending = 0; + var policy_mapping_inhibit_pending = 0; + var inhibit_any_policy_pending = 0; + + var permitted_subtrees = initial_permitted_subtrees_set; + var excluded_subtrees = initial_excluded_subtrees_set; + var required_name_forms = initial_required_name_forms; + + var path_depth = 1; + // #endregion + + // #region Sorting certificates in the chain array + sequence = (in_window.org.pkijs.simpl.CERT_CHAIN.prototype.sort.call(this)).then( + function(sorted_certs) + { + _this.certs = sorted_certs; + } + ); + // #endregion + + // #region Work with policies + sequence = sequence.then( + function() + { + // #region Support variables + var all_policies = new Array(); // Array of all policies (string values) + all_policies.push("2.5.29.32.0"); // Put "anyPolicy" at first place + + var policies_and_certs = new Array(); // In fact "array of array" where rows are for each specific policy, column for each certificate and value is "true/false" + + var any_policy_array = new Array(_this.certs.length - 1); // Minus "trusted anchor" + for(var ii = 0; ii < (_this.certs.length - 1) ; ii++) + any_policy_array[ii] = true; + + policies_and_certs.push(any_policy_array); + + var policy_mappings = new Array(_this.certs.length - 1); // Array of "PolicyMappings" for each certificate + var cert_policies = new Array(_this.certs.length - 1); // Array of "CertificatePolicies" for each certificate + // #endregion + + for(var i = (_this.certs.length - 2) ; i >= 0 ; i--, path_depth++) + { + if("extensions" in _this.certs[i]) + { + for(var j = 0; j < _this.certs[i].extensions.length; j++) + { + // #region CertificatePolicies + if(_this.certs[i].extensions[j].extnID === "2.5.29.32") + { + cert_policies[i] = _this.certs[i].extensions[j].parsedValue; + + for(var k = 0; k < _this.certs[i].extensions[j].parsedValue.certificatePolicies.length; k++) + { + var policy_index = (-1); + + // #region Try to find extension in "all_policies" array + for(var s = 0; s < all_policies.length; s++) + { + if(_this.certs[i].extensions[j].parsedValue.certificatePolicies[k].policyIdentifier === all_policies[s]) + { + policy_index = s; + break; + } + } + // #endregion + + if(policy_index === (-1)) + { + all_policies.push(_this.certs[i].extensions[j].parsedValue.certificatePolicies[k].policyIdentifier); + + var cert_array = new Array(_this.certs.length - 1); + cert_array[i] = true; + + policies_and_certs.push(cert_array); + } + else(policies_and_certs[policy_index])[i] = true; + } + } + // #endregion + + // #region PolicyMappings + if(_this.certs[i].extensions[j].extnID === "2.5.29.33") + policy_mappings[i] = _this.certs[i].extensions[j].parsedValue; + // #endregion + + // #region PolicyConstraints + if(_this.certs[i].extensions[j].extnID === "2.5.29.36") + { + if(explicit_policy_indicator == false) + { + // #region requireExplicitPolicy + if(_this.certs[i].extensions[j].parsedValue.requireExplicitPolicy === 0) + explicit_policy_indicator = true; + else + { + if(pending_constraints[0] === false) + { + pending_constraints[0] = true; + explicit_policy_pending = _this.certs[i].extensions[j].parsedValue.requireExplicitPolicy; + } + else + { + explicit_policy_pending = (explicit_policy_pending > _this.certs[i].extensions[j].parsedValue.requireExplicitPolicy) ? _this.certs[i].extensions[j].parsedValue.requireExplicitPolicy : explicit_policy_pending; + } + } + // #endregion + + // #region inhibitPolicyMapping + if(_this.certs[i].extensions[j].parsedValue.inhibitPolicyMapping === 0) + policy_mapping_inhibit_indicator = true; + else + { + if(pending_constraints[1] === false) + { + pending_constraints[1] = true; + policy_mapping_inhibit_pending = _this.certs[i].extensions[j].parsedValue.requireExplicitPolicy; + } + else + { + policy_mapping_inhibit_pending = (policy_mapping_inhibit_pending > _this.certs[i].extensions[j].parsedValue.requireExplicitPolicy) ? _this.certs[i].extensions[j].parsedValue.requireExplicitPolicy : policy_mapping_inhibit_pending; + } + } + // #endregion + } + } + // #endregion + + // #region InhibitAnyPolicy + if(_this.certs[i].extensions[j].extnID === "2.5.29.54") + { + if(inhibit_any_policy_indicator === false) + { + if(_this.certs[i].extensions[j].parsedValue.value_block.value_dec === 0) + inhibit_any_policy_indicator = true; + else + { + if(pending_constraints[2] === false) + { + pending_constraints[2] = true; + inhibit_any_policy_pending = _this.certs[i].extensions[j].parsedValue.value_block.value_dec; + } + else + { + inhibit_any_policy_pending = (inhibit_any_policy_pending > _this.certs[i].extensions[j].parsedValue.value_block.value_dec) ? _this.certs[i].extensions[j].parsedValue.value_block.value_dec : inhibit_any_policy_pending; + } + } + } + } + // #endregion + } + + // #region Check "inhibit_any_policy_indicator" + if(inhibit_any_policy_indicator === true) + delete (policies_and_certs[0])[i]; // Unset value to "undefined" for "anyPolicies" value for current certificate + // #endregion + + // #region Combine information from certificate policies and policy mappings + if((typeof cert_policies[i] !== "undefined") && + (typeof policy_mappings[i] !== "undefined") && + (policy_mapping_inhibit_indicator === false)) + { + for(var m = 0; m < cert_policies[i].certificatePolicies.length; m++) + { + var domainPolicy = ""; + + // #region Find if current policy is in "mappings" array + for(var n = 0; n < policy_mappings[i].mappings.length; n++) + { + if(policy_mappings[i].mappings[n].subjectDomainPolicy === cert_policies[i].certificatePolicies[m].policyIdentifier) + { + domainPolicy = policy_mappings[i].mappings[n].issuerDomainPolicy; + break; + } + + // #region Could be the case for some reasons + if(policy_mappings[i].mappings[n].issuerDomainPolicy === cert_policies[i].certificatePolicies[m].policyIdentifier) + { + domainPolicy = policy_mappings[i].mappings[n].subjectDomainPolicy; + break; + } + // #endregion + } + + if(domainPolicy === "") + continue; + // #endregion + + // #region Find the index of "domainPolicy" + var domainPolicy_index = (-1); + + for(var p = 0; p < all_policies.length; p++) + { + if(all_policies[p] === domainPolicy) + { + domainPolicy_index = p; + break; + } + } + // #endregion + + // #region Change array value for "domainPolicy" + if(domainPolicy_index !== (-1)) + (policies_and_certs[domainPolicy_index])[i] = true; // Put "set" in "domainPolicy" cell for specific certificate + // #endregion + } + } + // #endregion + + // #region Process with "pending constraints" + if(explicit_policy_indicator === false) + { + if(pending_constraints[0] === true) + { + explicit_policy_pending--; + if(explicit_policy_pending === 0) + { + explicit_policy_indicator = true; + pending_constraints[0] = false; + } + } + } + + if(policy_mapping_inhibit_indicator === false) + { + if(pending_constraints[1] === true) + { + policy_mapping_inhibit_pending--; + if(policy_mapping_inhibit_pending === 0) + { + policy_mapping_inhibit_indicator = true; + pending_constraints[1] = false; + } + } + } + + if(inhibit_any_policy_indicator === false) + { + if(pending_constraints[2] === true) + { + inhibit_any_policy_pending--; + if(inhibit_any_policy_pending === 0) + { + inhibit_any_policy_indicator = true; + pending_constraints[2] = false; + } + } + } + // #endregion + } + } + + // #region Create "set of authorities-constrained policies" + var auth_constr_policies = new Array(); + + for(var i = 0; i < policies_and_certs.length; i++) + { + var found = true; + + for(var j = 0; j < (_this.certs.length - 1) ; j++) + { + if(typeof (policies_and_certs[i])[j] === "undefined") + { + found = false; + break; + } + } + + if(found === true) + auth_constr_policies.push(all_policies[i]); + } + // #endregion + + // #region Create "set of user-constrained policies" + var user_constr_policies = new Array(); + + for(var i = 0; i < auth_constr_policies.length; i++) + { + for(var j = 0; j < initial_policy_set.length; j++) + { + if(initial_policy_set[j] === auth_constr_policies[i]) + { + user_constr_policies.push(initial_policy_set[j]); + break; + } + } + } + // #endregion + + // #region Combine output object + return { + result: (user_constr_policies.length > 0), + result_code: 0, + result_message: (user_constr_policies.length > 0) ? "" : "Zero \"user_constr_policies\" array, no intersections with \"auth_constr_policies\"", + auth_constr_policies: auth_constr_policies, + user_constr_policies: user_constr_policies, + explicit_policy_indicator: explicit_policy_indicator, + policy_mappings: policy_mappings + }; + // #endregion + } + ); + // #endregion + + // #region Work with name constraints + sequence = sequence.then( + function(policy_result) + { + // #region Auxiliary functions for name constraints checking + function compare_dNSName(name, constraint) + { + /// Compare two dNSName values + /// DNS from name + /// Constraint for DNS from name + /// Boolean result - valid or invalid the "name" against the "constraint" + + // #region Make a "string preparation" for both name and constrain + var name_prepared = in_window.org.pkijs.stringPrep(name); + var constraint_prepared = in_window.org.pkijs.stringPrep(constraint); + // #endregion + + // #region Make a "splitted" versions of "constraint" and "name" + var name_splitted = name_prepared.split("."); + var constraint_splitted = constraint_prepared.split("."); + // #endregion + + // #region Length calculation and additional check + var name_len = name_splitted.length; + var constr_len = constraint_splitted.length; + + if((name_len === 0) || (constr_len === 0) || (name_len < constr_len)) + return false; + // #endregion + + // #region Check that no part of "name" has zero length + for(var i = 0; i < name_len; i++) + { + if(name_splitted[i].length === 0) + return false; + } + // #endregion + + // #region Check that no part of "constraint" has zero length + for(var i = 0; i < constr_len; i++) + { + if(constraint_splitted[i].length === 0) + { + if(i === 0) + { + if(constr_len === 1) + return false; + else + continue; + } + + return false; + } + } + // #endregion + + // #region Check that "name" has a tail as "constraint" + + for(var i = 0; i < constr_len; i++) + { + if(constraint_splitted[constr_len - 1 - i].length === 0) + continue; + + if(name_splitted[name_len - 1 - i].localeCompare(constraint_splitted[constr_len - 1 - i]) !== 0) + return false; + } + // #endregion + + return true; + } + + function compare_rfc822Name(name, constraint) + { + /// Compare two rfc822Name values + /// E-mail address from name + /// Constraint for e-mail address from name + /// Boolean result - valid or invalid the "name" against the "constraint" + + // #region Make a "string preparation" for both name and constrain + var name_prepared = in_window.org.pkijs.stringPrep(name); + var constraint_prepared = in_window.org.pkijs.stringPrep(constraint); + // #endregion + + // #region Make a "splitted" versions of "constraint" and "name" + var name_splitted = name_prepared.split("@"); + var constraint_splitted = constraint_prepared.split("@"); + // #endregion + + // #region Splitted array length checking + if((name_splitted.length === 0) || (constraint_splitted.length === 0) || (name_splitted.length < constraint_splitted.length)) + return false; + // #endregion + + if(constraint_splitted.length === 1) + { + var result = compare_dNSName(name_splitted[1], constraint_splitted[0]); + + if(result) + { + // #region Make a "splitted" versions of domain name from "constraint" and "name" + var ns = name_splitted[1].split("."); + var cs = constraint_splitted[0].split("."); + // #endregion + + if(cs[0].length === 0) + return true; + + return ns.length === cs.length; + } + else + return false; + } + else + return (name_prepared.localeCompare(constraint_prepared) === 0); + + return false; + } + + function compare_uniformResourceIdentifier(name, constraint) + { + /// Compare two uniformResourceIdentifier values + /// uniformResourceIdentifier from name + /// Constraint for uniformResourceIdentifier from name + /// Boolean result - valid or invalid the "name" against the "constraint" + + // #region Make a "string preparation" for both name and constrain + var name_prepared = in_window.org.pkijs.stringPrep(name); + var constraint_prepared = in_window.org.pkijs.stringPrep(constraint); + // #endregion + + // #region Find out a major URI part to compare with + var ns = name_prepared.split("/"); + var cs = constraint_prepared.split("/"); + + if(cs.length > 1) // Malformed constraint + return false; + + if(ns.length > 1) // Full URI string + { + for(var i = 0; i < ns.length; i++) + { + if((ns[i].length > 0) && (ns[i].charAt(ns[i].length - 1) !== ':')) + { + var ns_port = ns[i].split(":"); + name_prepared = ns_port[0]; + break; + } + } + } + // #endregion + + var result = compare_dNSName(name_prepared, constraint_prepared); + + if(result) + { + // #region Make a "splitted" versions of "constraint" and "name" + var name_splitted = name_prepared.split("."); + var constraint_splitted = constraint_prepared.split("."); + // #endregion + + if(constraint_splitted[0].length === 0) + return true; + + return name_splitted.length === constraint_splitted.length; + } + else + return false; + + return false; + } + + function compare_iPAddress(name, constraint) + { + /// Compare two iPAddress values + /// iPAddress from name + /// Constraint for iPAddress from name + /// Boolean result - valid or invalid the "name" against the "constraint" + + // #region Common variables + var name_view = new Uint8Array(name.value_block.value_hex); + var constraint_view = new Uint8Array(constraint.value_block.value_hex); + // #endregion + + // #region Work with IPv4 addresses + if((name_view.length === 4) && (constraint_view.length === 8)) + { + for(var i = 0; i < 4; i++) + { + if((name_view[i] ^ constraint_view[i]) & constraint_view[i + 4]) + return false; + } + + return true; + } + // #endregion + + // #region Work with IPv6 addresses + if((name_view.length === 16) && (constraint_view.length === 32)) + { + for(var i = 0; i < 16; i++) + { + if((name_view[i] ^ constraint_view[i]) & constraint_view[i + 16]) + return false; + } + + return true; + } + // #endregion + + return false; + } + + function compare_directoryName(name, constraint) + { + /// Compare two directoryName values + /// directoryName from name + /// Constraint for directoryName from name + /// Boolean flag - should be comparision interrupted after first match or we need to match all "constraints" parts + /// Boolean result - valid or invalid the "name" against the "constraint" + + // #region Initial check + if((name.types_and_values.length === 0) || (constraint.types_and_values.length === 0)) + return true; + + if(name.types_and_values.length < constraint.types_and_values.length) + return false; + // #endregion + + // #region Initial variables + var result = true; + var name_start = 0; + // #endregion + + for(var i = 0; i < constraint.types_and_values.length; i++) + { + var local_result = false; + + for(var j = name_start; j < name.types_and_values.length; j++) + { + local_result = name.types_and_values[j].isEqual(constraint.types_and_values[i]); + + if(name.types_and_values[j].type === constraint.types_and_values[i].type) + result = result && local_result; + + if(local_result === true) + { + if((name_start === 0) || (name_start === j)) + { + name_start = j + 1; + break; + } + else // Structure of "name" must be the same with "constraint" + return false; + } + } + + if(local_result === false) + return false; + } + + return (name_start === 0) ? false : result; + } + // #endregion + + // #region Check a result from "policy checking" part + if(policy_result.result === false) + return policy_result; + // #endregion + + // #region Check all certificates, excluding "trust anchor" + path_depth = 1; + + for(var i = (_this.certs.length - 2) ; i >= 0 ; i--, path_depth++) + { + // #region Support variables + var subject_alt_names = new Array(); + + var cert_permitted_subtrees = new Array(); + var cert_excluded_subtrees = new Array(); + // #endregion + + if("extensions" in _this.certs[i]) + { + for(var j = 0; j < _this.certs[i].extensions.length; j++) + { + // #region NameConstraints + if(_this.certs[i].extensions[j].extnID === "2.5.29.30") + { + if("permittedSubtrees" in _this.certs[i].extensions[j].parsedValue) + cert_permitted_subtrees = cert_permitted_subtrees.concat(_this.certs[i].extensions[j].parsedValue.permittedSubtrees); + + if("excludedSubtrees" in _this.certs[i].extensions[j].parsedValue) + cert_excluded_subtrees = cert_excluded_subtrees.concat(_this.certs[i].extensions[j].parsedValue.excludedSubtrees); + } + // #endregion + + // #region SubjectAltName + if(_this.certs[i].extensions[j].extnID === "2.5.29.17") + subject_alt_names = subject_alt_names.concat(_this.certs[i].extensions[j].parsedValue.altNames); + // #endregion + } + } + + // #region Checking for "required name forms" + var form_found = (required_name_forms.length <= 0); + + for(var j = 0; j < required_name_forms.length; j++) + { + switch(required_name_forms[j].base.NameType) + { + case 4: // directoryName + { + if(required_name_forms[j].base.Name.types_and_values.length !== _this.certs[i].subject.types_and_values.length) + continue; + + form_found = true; + + for(var k = 0; k < _this.certs[i].subject.types_and_values.length; k++) + { + if(_this.certs[i].subject.types_and_values[k].type !== required_name_forms[j].base.Name.types_and_values[k].type) + { + form_found = false; + break; + } + } + + if(form_found === true) + break; + } + break; + default: // ??? Probably here we should reject the certificate ??? + } + } + + if(form_found === false) + { + policy_result.result = false; + policy_result.result_code = 21; + policy_result.result_message = "No neccessary name form found"; + + return new Promise(function(resolve, reject) + { + reject(policy_result); + }); + } + // #endregion + + // #region Checking for "permited sub-trees" + // #region Make groups for all types of constraints + var constr_groups = new Array(); // Array of array for groupped constraints + constr_groups[0] = new Array(); // rfc822Name + constr_groups[1] = new Array(); // dNSName + constr_groups[2] = new Array(); // directoryName + constr_groups[3] = new Array(); // uniformResourceIdentifier + constr_groups[4] = new Array(); // iPAddress + + for(var j = 0; j < permitted_subtrees.length; j++) + { + switch(permitted_subtrees[j].base.NameType) + { + // #region rfc822Name + case 1: + constr_groups[0].push(permitted_subtrees[j]); + break; + // #endregion + // #region dNSName + case 2: + constr_groups[1].push(permitted_subtrees[j]); + break; + // #endregion + // #region directoryName + case 4: + constr_groups[2].push(permitted_subtrees[j]); + break; + // #endregion + // #region uniformResourceIdentifier + case 6: + constr_groups[3].push(permitted_subtrees[j]); + break; + // #endregion + // #region iPAddress + case 7: + constr_groups[4].push(permitted_subtrees[j]); + break; + // #endregion + // #region default + + default: + // #endregion + } + } + // #endregion + + // #region Check name constraints groupped by type, one-by-one + for(var p = 0; p < 5; p++) + { + var group_permitted = false; + var valueExists = false; + var group = constr_groups[p]; + + for(var j = 0; j < group.length; j++) + { + switch(p) + { + // #region rfc822Name + case 0: + if(subject_alt_names.length > 0) + { + for(var k = 0; k < subject_alt_names.length; k++) + { + if(subject_alt_names[k].NameType === 1) // rfc822Name + { + valueExists = true; + group_permitted = group_permitted || compare_rfc822Name(subject_alt_names[k].Name, group[j].base.Name); + } + } + } + else // Try to find out "emailAddress" inside "subject" + { + for(var k = 0; k < _this.certs[i].subject.types_and_values.length; k++) + { + if((_this.certs[i].subject.types_and_values[k].type === "1.2.840.113549.1.9.1") || // PKCS#9 e-mail address + (_this.certs[i].subject.types_and_values[k].type === "0.9.2342.19200300.100.1.3")) // RFC1274 "rfc822Mailbox" e-mail address + { + valueExists = true; + group_permitted = group_permitted || compare_rfc822Name(_this.certs[i].subject.types_and_values[k].value.value_block.value, group[j].base.Name); + } + } + } + break; + // #endregion + // #region dNSName + case 1: + if(subject_alt_names.length > 0) + { + for(var k = 0; k < subject_alt_names.length; k++) + { + if(subject_alt_names[k].NameType === 2) // dNSName + { + valueExists = true; + group_permitted = group_permitted || compare_dNSName(subject_alt_names[k].Name, group[j].base.Name); + } + } + } + break; + // #endregion + // #region directoryName + case 2: + valueExists = true; + group_permitted = compare_directoryName(_this.certs[i].subject, group[j].base.Name); + break; + // #endregion + // #region uniformResourceIdentifier + case 3: + if(subject_alt_names.length > 0) + { + for(var k = 0; k < subject_alt_names.length; k++) + { + if(subject_alt_names[k].NameType === 6) // uniformResourceIdentifier + { + valueExists = true; + group_permitted = group_permitted || compare_uniformResourceIdentifier(subject_alt_names[k].Name, group[j].base.Name); + } + } + } + break; + // #endregion + // #region iPAddress + case 4: + if(subject_alt_names.length > 0) + { + for(var k = 0; k < subject_alt_names.length; k++) + { + if(subject_alt_names[k].NameType === 7) // iPAddress + { + valueExists = true; + group_permitted = group_permitted || compare_iPAddress(subject_alt_names[k].Name, group[j].base.Name); + } + } + } + break; + // #endregion + // #region default + + default: + // #endregion + } + + if(group_permitted) + break; + } + + if((group_permitted === false) && (group.length > 0) && valueExists) + { + policy_result.result = false; + policy_result.result_code = 41; + policy_result.result_message = "Failed to meet \"permitted sub-trees\" name constraint"; + + return new Promise(function(resolve, reject) + { + reject(policy_result); + }); + } + } + // #endregion + // #endregion + + // #region Checking for "excluded sub-trees" + var excluded = false; + + for(var j = 0; j < excluded_subtrees.length; j++) + { + switch(excluded_subtrees[j].base.NameType) + { + // #region rfc822Name + case 1: + if(subject_alt_names.length >= 0) + { + for(var k = 0; k < subject_alt_names.length; k++) + { + if(subject_alt_names[k].NameType === 1) // rfc822Name + excluded = excluded || compare_rfc822Name(subject_alt_names[k].Name, excluded_subtrees[j].base.Name); + } + } + else // Try to find out "emailAddress" inside "subject" + { + for(var k = 0; k < _this.subject.types_and_values.length; k++) + { + if((_this.subject.types_and_values[k].type === "1.2.840.113549.1.9.1") || // PKCS#9 e-mail address + (_this.subject.types_and_values[k].type === "0.9.2342.19200300.100.1.3")) // RFC1274 "rfc822Mailbox" e-mail address + { + excluded = excluded || compare_rfc822Name(_this.subject.types_and_values[k].value.value_block.value, excluded_subtrees[j].base.Name); + } + } + } + break; + // #endregion + // #region dNSName + case 2: + if(subject_alt_names.length > 0) + { + for(var k = 0; k < subject_alt_names.length; k++) + { + if(subject_alt_names[k].NameType === 2) // dNSName + excluded = excluded || compare_dNSName(subject_alt_names[k].Name, excluded_subtrees[j].base.Name); + } + } + break; + // #endregion + // #region directoryName + case 4: + excluded = excluded || compare_directoryName(_this.certs[i].subject, excluded_subtrees[j].base.Name); + break; + // #endregion + // #region uniformResourceIdentifier + case 6: + if(subject_alt_names.length > 0) + { + for(var k = 0; k < subject_alt_names.length; k++) + { + if(subject_alt_names[k].NameType === 6) // uniformResourceIdentifier + excluded = excluded || compare_uniformResourceIdentifier(subject_alt_names[k].Name, excluded_subtrees[j].base.Name); + } + } + break; + // #endregion + // #region iPAddress + case 7: + if(subject_alt_names.length > 0) + { + for(var k = 0; k < subject_alt_names.length; k++) + { + if(subject_alt_names[k].NameType === 7) // iPAddress + excluded = excluded || compare_iPAddress(subject_alt_names[k].Name, excluded_subtrees[j].base.Name); + } + } + break; + // #endregion + // #region default + + default: // No action, but probably here we need to create a warning for "malformed constraint" + // #endregion + } + + if(excluded) + break; + } + + if(excluded === true) + { + policy_result.result = false; + policy_result.result_code = 42; + policy_result.result_message = "Failed to meet \"excluded sub-trees\" name constraint"; + + return new Promise(function(resolve, reject) + { + reject(policy_result); + }); + } + // #endregion + + // #region Append "cert_..._subtrees" to "..._subtrees" + permitted_subtrees = permitted_subtrees.concat(cert_permitted_subtrees); + excluded_subtrees = excluded_subtrees.concat(cert_excluded_subtrees); + // #endregion + } + // #endregion + + return policy_result; + } + ); + // #endregion + + return sequence; + }; + //************************************************************************************** + // #endregion + //************************************************************************************** +} +)(typeof exports !== "undefined" ? exports : window); diff --git a/dom/u2f/tests/test_frame.html b/dom/u2f/tests/test_frame.html index b71be18445..7740c37b84 100644 --- a/dom/u2f/tests/test_frame.html +++ b/dom/u2f/tests/test_frame.html @@ -9,9 +9,6 @@ Mozilla Bug 1231681 -

- -
@@ -19,48 +16,49 @@

 
-
 
 
   
diff --git a/dom/u2f/tests/test_frame_appid_facet.html b/dom/u2f/tests/test_frame_appid_facet.html
index 1e5a0078cf..94afd9d6aa 100644
--- a/dom/u2f/tests/test_frame_appid_facet.html
+++ b/dom/u2f/tests/test_frame_appid_facet.html
@@ -8,9 +8,6 @@
 
diff --git a/dom/u2f/tests/test_frame_appid_facet_insecure.html b/dom/u2f/tests/test_frame_appid_facet_insecure.html
index c0c9e1c2c0..d2e3fea3d4 100644
--- a/dom/u2f/tests/test_frame_appid_facet_insecure.html
+++ b/dom/u2f/tests/test_frame_appid_facet_insecure.html
@@ -8,9 +8,6 @@
 
 
diff --git a/dom/u2f/tests/test_frame_register.html b/dom/u2f/tests/test_frame_register.html
index 4205bb8a9b..0ccaddbf10 100644
--- a/dom/u2f/tests/test_frame_register.html
+++ b/dom/u2f/tests/test_frame_register.html
@@ -8,9 +8,6 @@
 
+  
+  
+  
+  
+
+
+

Register and Sign Test for FIDO Universal Second Factor

+ + + diff --git a/dom/u2f/tests/test_no_token.html b/dom/u2f/tests/test_no_token.html index 216b1fcd5e..16e82892ff 100644 --- a/dom/u2f/tests/test_no_token.html +++ b/dom/u2f/tests/test_no_token.html @@ -8,31 +8,29 @@ Mozilla Bug 1231681 -

-
 
diff --git a/dom/u2f/tests/u2futil.js b/dom/u2f/tests/u2futil.js
index 3106961f65..7e90b8f66c 100644
--- a/dom/u2f/tests/u2futil.js
+++ b/dom/u2f/tests/u2futil.js
@@ -98,24 +98,38 @@ function importPublicKey(keyBytes) {
   return crypto.subtle.importKey("jwk", jwk, {name: "ECDSA", namedCurve: "P-256"}, true, ["verify"])
 }
 
-function assembleSignedData(appId, presenceAndCounter, clientData) {
+function deriveAppAndChallengeParam(appId, clientData) {
   var appIdBuf = string2buffer(appId);
   return Promise.all([
     crypto.subtle.digest("SHA-256", appIdBuf),
     crypto.subtle.digest("SHA-256", clientData)
   ])
   .then(function(digests) {
-    var appParam = new Uint8Array(digests[0]);
-    var clientParam = new Uint8Array(digests[1]);
-
-    var signedData = new Uint8Array(32 + 1 + 4 + 32);
-    appParam.map((x, i) => signedData[0 + i] = x);
-    presenceAndCounter.map((x, i) => signedData[32 + i] = x);
-    clientParam.map((x, i) => signedData[37 + i] = x);
-    return signedData;
+    return {
+      appParam: new Uint8Array(digests[0]),
+      challengeParam: new Uint8Array(digests[1]),
+    };
   });
 }
 
+function assembleSignedData(appParam, presenceAndCounter, challengeParam) {
+  var signedData = new Uint8Array(32 + 1 + 4 + 32);
+  appParam.map((x, i) => signedData[0 + i] = x);
+  presenceAndCounter.map((x, i) => signedData[32 + i] = x);
+  challengeParam.map((x, i) => signedData[37 + i] = x);
+  return signedData;
+}
+
+function assembleRegistrationSignedData(appParam, challengeParam, keyHandle, pubKey) {
+  var signedData = new Uint8Array(1 + 32 + 32 + keyHandle.length + 65);
+  signedData[0] = 0x00;
+  appParam.map((x, i) => signedData[1 + i] = x);
+  challengeParam.map((x, i) => signedData[33 + i] = x);
+  keyHandle.map((x, i) => signedData[65 + i] = x);
+  pubKey.map((x, i) => signedData[65 + keyHandle.length + i] = x);
+  return signedData;
+}
+
 function verifySignature(key, data, derSig) {
   if (derSig.byteLength < 70) {
     console.log("bad sig: " + hexEncode(derSig))
@@ -141,4 +155,4 @@ function verifySignature(key, data, derSig) {
 
   var alg = {name: "ECDSA", hash: "SHA-256"};
   return crypto.subtle.verify(alg, key, sig, data);
-}
\ No newline at end of file
+}
diff --git a/dom/webidl/Directory.webidl b/dom/webidl/Directory.webidl
index da18a53721..4392883b41 100644
--- a/dom/webidl/Directory.webidl
+++ b/dom/webidl/Directory.webidl
@@ -114,6 +114,9 @@ partial interface Directory {
    */
   [Throws]
   Promise> getFilesAndDirectories();
+
+  [Throws]
+  Promise> getFiles(optional boolean recursiveFlag = false);
 };
 
 enum CreateIfExistsMode { "replace", "fail" };
diff --git a/dom/webidl/FileList.webidl b/dom/webidl/FileList.webidl
index 0dc10401c3..84a56be1bf 100644
--- a/dom/webidl/FileList.webidl
+++ b/dom/webidl/FileList.webidl
@@ -11,8 +11,6 @@
  */
 
 interface FileList {
-  [Throws]
-  getter (File or Directory)? item(unsigned long index);
-
+  getter File? item(unsigned long index);
   readonly attribute unsigned long length;
 };
diff --git a/dom/xul/templates/nsXULContentUtils.cpp b/dom/xul/templates/nsXULContentUtils.cpp
index 082817ce4f..d292b75a0b 100644
--- a/dom/xul/templates/nsXULContentUtils.cpp
+++ b/dom/xul/templates/nsXULContentUtils.cpp
@@ -49,7 +49,6 @@
 #include "rdf.h"
 #include "nsContentUtils.h"
 #include "nsIDateTimeFormat.h"
-#include "nsDateTimeFormatCID.h"
 #include "nsIScriptableDateFormat.h"
 #include "nsICollation.h"
 #include "nsCollationCID.h"
@@ -103,9 +102,9 @@ nsXULContentUtils::Init()
 #undef XUL_RESOURCE
 #undef XUL_LITERAL
 
-    rv = CallCreateInstance(NS_DATETIMEFORMAT_CONTRACTID, &gFormat);
-    if (NS_FAILED(rv)) {
-        return rv;
+    gFormat = nsIDateTimeFormat::Create().take();
+    if (!gFormat) {
+        return NS_ERROR_FAILURE;
     }
 
     return NS_OK;
diff --git a/embedding/browser/nsDocShellTreeOwner.cpp b/embedding/browser/nsDocShellTreeOwner.cpp
index 39eed2029e..e6f2881f48 100644
--- a/embedding/browser/nsDocShellTreeOwner.cpp
+++ b/embedding/browser/nsDocShellTreeOwner.cpp
@@ -1187,7 +1187,7 @@ DefaultTooltipTextProvider::GetNodeText(nsIDOMNode* aNode, char16_t** aText,
                 NS_ENSURE_SUCCESS(rv, rv);
               } else {
                 FileList* fl = static_cast(fileList.get());
-                fl->UnsafeItem(0).GetAsFile()->GetName(outText);
+                fl->Item(0)->GetName(outText);
 
                 // For UX and performance (jank) reasons we cap the number of
                 // files that we list in the tooltip to 20 plus a "and xxx more"
@@ -1196,7 +1196,7 @@ DefaultTooltipTextProvider::GetNodeText(nsIDOMNode* aNode, char16_t** aText,
                 uint32_t count = std::min(listLength, TRUNCATED_FILE_COUNT);
                 for (uint32_t i = 1; i < count; ++i) {
                   nsString fileName;
-                  fl->UnsafeItem(i).GetAsFile()->GetName(fileName);
+                  fl->Item(i)->GetName(fileName);
                   outText.Append(NS_LITERAL_STRING("\n"));
                   outText.Append(fileName);
                 }
diff --git a/gfx/2d/2D.h b/gfx/2d/2D.h
index fb837db22a..db9f985b35 100644
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -955,6 +955,15 @@ public:
                            Point aOffset,
                            const DrawOptions &aOptions = DrawOptions()) = 0;
 
+  /**
+   * Draw aSurface using the 3D transform aMatrix. The DrawTarget's transform
+   * and clip are applied after the 3D transform.
+   *
+   * If the transform fails (i.e. because aMatrix is singular), false is returned and nothing is drawn.
+   */
+  virtual bool Draw3DTransformedSurface(SourceSurface* aSurface,
+                                        const Matrix4x4& aMatrix);
+
   /**
    * Push a clip to the DrawTarget.
    *
diff --git a/gfx/2d/DrawTargetCairo.cpp b/gfx/2d/DrawTargetCairo.cpp
index a2ed29e55b..55096c0084 100644
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -39,6 +39,9 @@
 #include "cairo-win32.h"
 #endif
 
+#define PIXMAN_DONT_DEFINE_STDINT
+#include "pixman.h"
+
 #include 
 
 // 2^23
@@ -614,7 +617,8 @@ DrawTargetCairo::~DrawTargetCairo()
 bool
 DrawTargetCairo::IsValid() const
 {
-  return mSurface && !cairo_surface_status(mSurface);
+  return mSurface && !cairo_surface_status(mSurface) &&
+         mContext && !cairo_surface_status(cairo_get_group_target(mContext));
 }
 
 DrawTargetType
@@ -717,10 +721,19 @@ DrawTargetCairo::LockBits(uint8_t** aData, IntSize* aSize,
                           int32_t* aStride, SurfaceFormat* aFormat,
                           IntPoint* aOrigin)
 {
-  cairo_surface_t* surf = cairo_get_group_target(mContext);
+  cairo_surface_t* target = cairo_get_group_target(mContext);
+  cairo_surface_t* surf = target;
+#ifdef CAIRO_HAS_WIN32_SURFACE
+  if (cairo_surface_get_type(surf) == CAIRO_SURFACE_TYPE_WIN32) {
+    cairo_surface_t* imgsurf = cairo_win32_surface_get_image(surf);
+    if (imgsurf) {
+      surf = imgsurf;
+    }
+  }
+#endif
   if (cairo_surface_get_type(surf) == CAIRO_SURFACE_TYPE_IMAGE) {
     PointDouble offset;
-    cairo_surface_get_device_offset(surf, &offset.x, &offset.y);
+    cairo_surface_get_device_offset(target, &offset.x, &offset.y);
     // verify the device offset can be converted to integers suitable for a bounds rect
     IntPoint origin(int32_t(-offset.x), int32_t(-offset.y));
     if (-PointDouble(origin) != offset ||
@@ -752,6 +765,14 @@ DrawTargetCairo::ReleaseBits(uint8_t* aData)
   MOZ_ASSERT(mLockedBits == aData);
   mLockedBits = nullptr;
   cairo_surface_t* surf = cairo_get_group_target(mContext);
+#ifdef CAIRO_HAS_WIN32_SURFACE
+  if (cairo_surface_get_type(surf) == CAIRO_SURFACE_TYPE_WIN32) {
+    cairo_surface_t* imgsurf = cairo_win32_surface_get_image(surf);
+    if (imgsurf) {
+      cairo_surface_mark_dirty(imgsurf);
+    }
+  }
+#endif
   cairo_surface_mark_dirty(surf);
 }
 
@@ -808,12 +829,12 @@ DrawTargetCairo::DrawSurface(SourceSurface *aSurface,
                              const DrawSurfaceOptions &aSurfOptions,
                              const DrawOptions &aOptions)
 {
-  if (mTransformSingular) {
+  if (mTransformSingular || aDest.IsEmpty()) {
     return;
   }
 
   if (!IsValid() || !aSurface) {
-    gfxCriticalNote << "DrawSurface with bad surface " << cairo_surface_status(mSurface);
+    gfxCriticalNote << "DrawSurface with bad surface " << cairo_surface_status(cairo_get_group_target(mContext));
     return;
   }
 
@@ -1267,7 +1288,7 @@ DrawTargetCairo::SetPermitSubpixelAA(bool aPermitSubpixelAA)
 {
   DrawTarget::SetPermitSubpixelAA(aPermitSubpixelAA);
 #ifdef MOZ_TREE_CAIRO
-  cairo_surface_set_subpixel_antialiasing(mSurface,
+  cairo_surface_set_subpixel_antialiasing(cairo_get_group_target(mContext),
     aPermitSubpixelAA ? CAIRO_SUBPIXEL_ANTIALIASING_ENABLED : CAIRO_SUBPIXEL_ANTIALIASING_DISABLED);
 #endif
 }
@@ -1284,7 +1305,12 @@ DrawTargetCairo::FillGlyphs(ScaledFont *aFont,
   }
 
   if (!IsValid()) {
-    gfxDebug() << "FillGlyphs bad surface " << cairo_surface_status(mSurface);
+    gfxDebug() << "FillGlyphs bad surface " << cairo_surface_status(cairo_get_group_target(mContext));
+    return;
+  }
+
+  if (!aFont) {
+    gfxDevCrash(LogReason::InvalidFont) << "Invalid scaled font";
     return;
   }
 
@@ -1322,8 +1348,8 @@ DrawTargetCairo::FillGlyphs(ScaledFont *aFont,
 
   cairo_show_glyphs(mContext, &glyphs[0], aBuffer.mNumGlyphs);
 
-  if (mSurface && cairo_surface_status(mSurface)) {
-    gfxDebug() << "Ending FillGlyphs with a bad surface " << cairo_surface_status(mSurface);
+  if (cairo_surface_status(cairo_get_group_target(mContext))) {
+    gfxDebug() << "Ending FillGlyphs with a bad surface " << cairo_surface_status(cairo_get_group_target(mContext));
   }
 }
 
@@ -1736,16 +1762,25 @@ DrawTargetCairo::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurf
 already_AddRefed
 DrawTargetCairo::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
 {
-  if (cairo_surface_status(mSurface)) {
+  if (cairo_surface_status(cairo_get_group_target(mContext))) {
     RefPtr target = new DrawTargetCairo();
     if (target->Init(aSize, aFormat)) {
       return target.forget();
     }
   }
 
-  cairo_surface_t* similar = cairo_surface_create_similar(mSurface,
-                                                          GfxFormatToCairoContent(aFormat),
-                                                          aSize.width, aSize.height);
+  cairo_surface_t* similar;
+#ifdef CAIRO_HAS_WIN32_SURFACE
+  if (cairo_surface_get_type(mSurface) == CAIRO_SURFACE_TYPE_WIN32) {
+    similar = cairo_win32_surface_create_with_dib(GfxFormatToCairoFormat(aFormat),
+                                                  aSize.width, aSize.height);
+  } else
+#endif
+  {
+    similar = cairo_surface_create_similar(mSurface,
+                                           GfxFormatToCairoContent(aFormat),
+                                           aSize.width, aSize.height);
+  }
 
   if (!cairo_surface_status(similar)) {
     RefPtr target = new DrawTargetCairo();
@@ -1754,7 +1789,7 @@ DrawTargetCairo::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFo
     }
   }
 
-  gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) << "Failed to create similar cairo surface! Size: " << aSize << " Status: " << cairo_surface_status(similar) << cairo_surface_status(mSurface) << " format " << (int)aFormat;
+  gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) << "Failed to create similar cairo surface! Size: " << aSize << " Status: " << cairo_surface_status(similar) << cairo_surface_status(cairo_get_group_target(mContext)) << " format " << (int)aFormat;
 
   return nullptr;
 }
@@ -1840,6 +1875,244 @@ DrawTargetCairo::CreateShadowDrawTarget(const IntSize &aSize, SurfaceFormat aFor
   return nullptr;
 }
 
+static inline pixman_format_code_t
+GfxFormatToPixmanFormat(SurfaceFormat aFormat)
+{
+  switch (aFormat) {
+  case SurfaceFormat::A8R8G8B8_UINT32:
+    return PIXMAN_a8r8g8b8;
+  case SurfaceFormat::X8R8G8B8_UINT32:
+    return PIXMAN_x8r8g8b8;
+  case SurfaceFormat::R5G6B5_UINT16:
+    return PIXMAN_r5g6b5;
+  case SurfaceFormat::A8:
+    return PIXMAN_a8;
+  default:
+    // Allow both BGRA and ARGB formats to be passed through unmodified,
+    // even though even though we are actually rendering to A8R8G8B8_UINT32.
+    if (aFormat == SurfaceFormat::B8G8R8A8 ||
+        aFormat == SurfaceFormat::A8R8G8B8) {
+      return PIXMAN_a8r8g8b8;
+    }
+    return (pixman_format_code_t)0;
+  }
+}
+
+static inline bool
+GfxMatrixToPixmanTransform(const Matrix4x4 &aMatrix, pixman_transform* aResult)
+{
+  pixman_f_transform fTransform = {{
+    { aMatrix._11, aMatrix._21, aMatrix._41 },
+    { aMatrix._12, aMatrix._22, aMatrix._42 },
+    { aMatrix._14, aMatrix._24, aMatrix._44 }
+  }};
+  return pixman_transform_from_pixman_f_transform(aResult, &fTransform);
+}
+
+#ifndef USE_SKIA
+bool
+DrawTarget::Draw3DTransformedSurface(SourceSurface* aSurface, const Matrix4x4& aMatrix)
+{
+  // Composite the 3D transform with the DT's transform.
+  Matrix4x4 fullMat = aMatrix * Matrix4x4::From2D(mTransform);
+  // Transform the surface bounds and clip to this DT.
+  IntRect xformBounds =
+    RoundedOut(
+      fullMat.TransformAndClipBounds(Rect(Point(0, 0), Size(aSurface->GetSize())),
+                                     Rect(Point(0, 0), Size(GetSize()))));
+  if (xformBounds.IsEmpty()) {
+    return true;
+  }
+  // Offset the matrix by the transformed origin.
+  fullMat.PostTranslate(-xformBounds.x, -xformBounds.y, 0);
+  // Invert the matrix into a pattern matrix for pixman.
+  if (!fullMat.Invert()) {
+    return false;
+  }
+  pixman_transform xform;
+  if (!GfxMatrixToPixmanTransform(fullMat, &xform)) {
+    return false;
+  }
+
+  // Read in the source data.
+  RefPtr srcSurf = aSurface->GetDataSurface();
+  pixman_format_code_t srcFormat = GfxFormatToPixmanFormat(srcSurf->GetFormat());
+  if (!srcFormat) {
+    return false;
+  }
+  DataSourceSurface::ScopedMap srcMap(srcSurf, DataSourceSurface::READ);
+  if (!srcMap.IsMapped()) {
+    return false;
+  }
+
+  // Set up an intermediate destination surface only the size of the transformed bounds.
+  // Try to pass through the source's format unmodified in both the BGRA and ARGB cases.
+  RefPtr dstSurf =
+    Factory::CreateDataSourceSurface(xformBounds.Size(),
+                                     srcFormat == PIXMAN_a8r8g8b8 ?
+                                       srcSurf->GetFormat() : SurfaceFormat::A8R8G8B8_UINT32);
+  if (!dstSurf) {
+    return false;
+  }
+
+  // Wrap the surfaces in pixman images and do the transform.
+  pixman_image_t* dst =
+    pixman_image_create_bits(PIXMAN_a8r8g8b8,
+                             xformBounds.width, xformBounds.height,
+                             (uint32_t*)dstSurf->GetData(), dstSurf->Stride());
+  pixman_image_t* src =
+    pixman_image_create_bits(srcFormat,
+                             srcSurf->GetSize().width, srcSurf->GetSize().height,
+                             (uint32_t*)srcMap.GetData(), srcMap.GetStride());
+  MOZ_ASSERT(src && dst, "Failed to create pixman images?");
+
+  pixman_image_set_filter(src, PIXMAN_FILTER_BILINEAR, nullptr, 0);
+  pixman_image_set_transform(src, &xform);
+
+  pixman_image_composite32(PIXMAN_OP_SRC,
+                           src, nullptr, dst,
+                           0, 0, 0, 0, 0, 0,
+                           xformBounds.width, xformBounds.height);
+
+  pixman_image_unref(dst);
+  pixman_image_unref(src);
+
+  // Temporarily reset the DT's transform, since it has already been composed above.
+  Matrix origTransform = mTransform;
+  SetTransform(Matrix());
+
+  // Draw the transformed surface within the transformed bounds.
+  DrawSurface(dstSurf, Rect(xformBounds), Rect(Point(0, 0), Size(xformBounds.Size())));
+
+  SetTransform(origTransform);
+
+  return true;
+}
+#endif
+
+#ifdef CAIRO_HAS_XLIB_SURFACE
+static bool gXRenderInitialized = false;
+static bool gXRenderHasTransform = false;
+
+static bool
+SupportsXRender(cairo_surface_t* surface)
+{
+  if (!surface ||
+      cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_XLIB ||
+      !cairo_xlib_surface_get_xrender_format(surface)) {
+    return false;
+  }
+
+  if (gXRenderInitialized) {
+    return true;
+  }
+  gXRenderInitialized = true;
+
+  cairo_device_t* device = cairo_surface_get_device(surface);
+  if (cairo_device_acquire(device) != CAIRO_STATUS_SUCCESS) {
+    return false;
+  }
+
+  Display* display = cairo_xlib_surface_get_display(surface);
+  int major, minor;
+  if (XRenderQueryVersion(display, &major, &minor)) {
+    if (major > 0 || (major == 0 && minor >= 6)) {
+      gXRenderHasTransform = true;
+    }
+  }
+
+  cairo_device_release(device);
+
+  return true;
+}
+#endif
+
+bool
+DrawTargetCairo::Draw3DTransformedSurface(SourceSurface* aSurface, const Matrix4x4& aMatrix)
+{
+#if CAIRO_HAS_XLIB_SURFACE
+  cairo_surface_t* srcSurf =
+    aSurface->GetType() == SurfaceType::CAIRO ?
+      static_cast(aSurface)->GetSurface() : nullptr;
+  if (!SupportsXRender(srcSurf) || !gXRenderHasTransform) {
+    return DrawTarget::Draw3DTransformedSurface(aSurface, aMatrix);
+  }
+
+  Matrix4x4 fullMat = aMatrix * Matrix4x4::From2D(mTransform);
+  IntRect xformBounds =
+    RoundedOut(
+      fullMat.TransformAndClipBounds(Rect(Point(0, 0), Size(aSurface->GetSize())),
+                                     Rect(Point(0, 0), Size(GetSize()))));
+  if (xformBounds.IsEmpty()) {
+    return true;
+  }
+  fullMat.PostTranslate(-xformBounds.x, -xformBounds.y, 0);
+  if (!fullMat.Invert()) {
+    return false;
+  }
+  pixman_transform xform;
+  if (!GfxMatrixToPixmanTransform(fullMat, &xform)) {
+    return false;
+  }
+
+  cairo_surface_t* xformSurf =
+    cairo_surface_create_similar(srcSurf, CAIRO_CONTENT_COLOR_ALPHA,
+                                 xformBounds.width, xformBounds.height);
+  if (!SupportsXRender(xformSurf)) {
+    cairo_surface_destroy(xformSurf);
+    return false;
+  }
+  cairo_device_t* device = cairo_surface_get_device(xformSurf);
+  if (cairo_device_acquire(device) != CAIRO_STATUS_SUCCESS) {
+    cairo_surface_destroy(xformSurf);
+    return false;
+  }
+
+  Display* display = cairo_xlib_surface_get_display(xformSurf);
+
+  Picture srcPict = XRenderCreatePicture(display,
+                                         cairo_xlib_surface_get_drawable(srcSurf),
+                                         cairo_xlib_surface_get_xrender_format(srcSurf),
+                                         0, nullptr);
+  XRenderSetPictureFilter(display, srcPict, FilterBilinear, nullptr, 0);
+  XRenderSetPictureTransform(display, srcPict, (XTransform*)&xform);
+
+  Picture dstPict = XRenderCreatePicture(display,
+                                         cairo_xlib_surface_get_drawable(xformSurf),
+                                         cairo_xlib_surface_get_xrender_format(xformSurf),
+                                         0, nullptr);
+
+  XRenderComposite(display, PictOpSrc,
+                   srcPict, None, dstPict,
+                   0, 0, 0, 0, 0, 0,
+                   xformBounds.width, xformBounds.height);
+
+  XRenderFreePicture(display, srcPict);
+  XRenderFreePicture(display, dstPict);
+
+  cairo_device_release(device);
+  cairo_surface_mark_dirty(xformSurf);
+
+  AutoPrepareForDrawing(this, mContext);
+
+  cairo_identity_matrix(mContext);
+
+  cairo_set_operator(mContext, CAIRO_OPERATOR_OVER);
+  cairo_set_antialias(mContext, CAIRO_ANTIALIAS_DEFAULT);
+  cairo_set_source_surface(mContext, xformSurf, xformBounds.x, xformBounds.y);
+
+  cairo_new_path(mContext);
+  cairo_rectangle(mContext, xformBounds.x, xformBounds.y, xformBounds.width, xformBounds.height);
+  cairo_fill(mContext);
+
+  cairo_surface_destroy(xformSurf);
+
+  return true;
+#else
+  return DrawTarget::Draw3DTransformedSurface(aSurface, aMatrix);
+#endif
+}
+
 bool
 DrawTargetCairo::Init(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat)
 {
diff --git a/gfx/2d/DrawTargetCairo.h b/gfx/2d/DrawTargetCairo.h
index 0b2b300a83..70fa7303ce 100644
--- a/gfx/2d/DrawTargetCairo.h
+++ b/gfx/2d/DrawTargetCairo.h
@@ -134,6 +134,9 @@ public:
                            Point aOffset,
                            const DrawOptions &aOptions = DrawOptions()) override;
 
+  virtual bool Draw3DTransformedSurface(SourceSurface* aSurface,
+                                        const Matrix4x4& aMatrix) override;
+
   virtual void PushClip(const Path *aPath) override;
   virtual void PushClipRect(const Rect &aRect) override;
   virtual void PopClip() override;
diff --git a/gfx/2d/DrawTargetD2D1.cpp b/gfx/2d/DrawTargetD2D1.cpp
index 759eb85520..fb79d72a69 100644
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -40,6 +40,7 @@ ID2D1Factory1 *D2DFactory1()
 
 DrawTargetD2D1::DrawTargetD2D1()
   : mPushedLayers(1)
+  , mUsedCommandListsSincePurge(0)
 {
 }
 
@@ -87,7 +88,7 @@ DrawTargetD2D1::Snapshot()
   }
   PopAllClips();
 
-  mDC->Flush();
+  Flush();
 
   mSnapshot = new SourceSurfaceD2D1(mBitmap, mDC, mFormat, mSize, this);
 
@@ -95,10 +96,28 @@ DrawTargetD2D1::Snapshot()
   return snapshot.forget();
 }
 
+// Command lists are kept around by device contexts until EndDraw is called,
+// this can cause issues with memory usage (see bug 1238328). EndDraw/BeginDraw
+// are expensive though, especially relatively when little work is done, so
+// we try to reduce the amount of times we execute these purges.
+static const uint32_t kPushedLayersBeforePurge = 25;
+
 void
 DrawTargetD2D1::Flush()
 {
-  mDC->Flush();
+  if ((mUsedCommandListsSincePurge >= kPushedLayersBeforePurge) &&
+      mPushedLayers.size() == 1) {
+    // It's important to pop all clips as otherwise layers can forget about
+    // their clip when doing an EndDraw. When we have layers pushed we cannot
+    // easily pop all underlying clips to delay the purge until we have no
+    // layers pushed.
+    PopAllClips();
+    mUsedCommandListsSincePurge = 0;
+    mDC->EndDraw();
+    mDC->BeginDraw();
+  } else {
+    mDC->Flush();
+  }
 
   // We no longer depend on any target.
   for (TargetSet::iterator iter = mDependingOnTargets.begin();
@@ -263,6 +282,7 @@ DrawTargetD2D1::ClearRect(const Rect &aRect)
   }
 
   RefPtr list;
+  mUsedCommandListsSincePurge++;
   mDC->CreateCommandList(getter_AddRefs(list));
   mDC->SetTarget(list);
 
@@ -791,6 +811,8 @@ DrawTargetD2D1::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask,
   mPushedLayers.push_back(pushedLayer);
 
   mDC->SetTarget(CurrentTarget());
+
+  mUsedCommandListsSincePurge++;
 }
 
 void
@@ -1071,7 +1093,7 @@ DrawTargetD2D1::factory()
                                 getter_AddRefs(factory));
 
   if (FAILED(hr) || !factory) {
-    gfxWarning() << "Failed to create Direct2D factory.";
+    gfxCriticalNote << "Failed to create a D2D1 content device: " << hexa(hr);
     return nullptr;
   }
 
@@ -1149,12 +1171,24 @@ DrawTargetD2D1::MarkChanged()
   }
 }
 
+bool
+DrawTargetD2D1::ShouldClipTemporarySurfaceDrawing(CompositionOp aOp,
+                                                  const Pattern& aPattern,
+                                                  bool aClipIsComplex)
+{
+  bool patternSupported = IsPatternSupportedByD2D(aPattern);
+  return patternSupported && !CurrentLayer().mIsOpaque && D2DSupportsCompositeMode(aOp) &&
+         IsOperatorBoundByMask(aOp) && aClipIsComplex;
+}
+
 void
 DrawTargetD2D1::PrepareForDrawing(CompositionOp aOp, const Pattern &aPattern)
 {
   MarkChanged();
 
-  if (D2DSupportsPrimitiveBlendMode(aOp) && IsPatternSupportedByD2D(aPattern)) {
+  bool patternSupported = IsPatternSupportedByD2D(aPattern);
+
+  if (D2DSupportsPrimitiveBlendMode(aOp) && patternSupported) {
     // It's important to do this before FlushTransformToDC! As this will cause
     // the transform to become dirty.
     PushAllClips();
@@ -1167,12 +1201,24 @@ DrawTargetD2D1::PrepareForDrawing(CompositionOp aOp, const Pattern &aPattern)
     return;
   }
 
-  PopAllClips();
-
-  mDC->CreateCommandList(getter_AddRefs(mCommandList));
+  HRESULT result = mDC->CreateCommandList(getter_AddRefs(mCommandList));
   mDC->SetTarget(mCommandList);
+  mUsedCommandListsSincePurge++;
+
+  // This is where we should have a valid command list.  If we don't, something is
+  // wrong, and it's likely an OOM.
+  if (!mCommandList) {
+    gfxDevCrash(LogReason::InvalidCommandList) << "Invalid D2D1.1 command list on creation " << mUsedCommandListsSincePurge << ", " << gfx::hexa(result);
+  }
+
+  D2D1_RECT_F rect;
+  bool isAligned;
+  bool clipIsComplex = CurrentLayer().mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned);
+
+  if (ShouldClipTemporarySurfaceDrawing(aOp, aPattern, clipIsComplex)) {
+    PushClipsToDC(mDC);
+  }
 
-  PushAllClips();
   FlushTransformToDC();
 }
 
@@ -1187,9 +1233,19 @@ DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern)
     return;
   }
 
-  PopAllClips();
+  D2D1_RECT_F rect;
+  bool isAligned;
+  bool clipIsComplex = CurrentLayer().mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned);
+
+  if (ShouldClipTemporarySurfaceDrawing(aOp, aPattern, clipIsComplex)) {
+    PopClipsFromDC(mDC);
+  }
 
   mDC->SetTarget(CurrentTarget());
+  if (!mCommandList) {
+    gfxDevCrash(LogReason::InvalidCommandList) << "Invalid D21.1 command list on finalize";
+    return;
+  }
   mCommandList->Close();
 
   RefPtr source = mCommandList;
@@ -1200,17 +1256,12 @@ DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern)
 
   if (patternSupported) {
     if (D2DSupportsCompositeMode(aOp)) {
-      D2D1_RECT_F rect;
-      bool isAligned;
       RefPtr tmpImage;
-      bool clipIsComplex = CurrentLayer().mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned);
-
       if (clipIsComplex) {
+        PopAllClips();
         if (!IsOperatorBoundByMask(aOp)) {
           tmpImage = GetImageForLayerContent();
         }
-      } else {
-        PushAllClips();
       }
       mDC->DrawImage(source, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp));
 
@@ -1241,8 +1292,6 @@ DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern)
     blendEffect->SetInput(1, source);
     blendEffect->SetValue(D2D1_BLEND_PROP_MODE, D2DBlendMode(aOp));
 
-    PushAllClips();
-
     mDC->DrawImage(blendEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
     return;
   }
@@ -1253,8 +1302,6 @@ DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern)
     return;
   }
 
-  PushAllClips();
-
   RefPtr radialGradientEffect;
 
   HRESULT hr = mDC->CreateEffect(CLSID_RadialGradientEffect, getter_AddRefs(radialGradientEffect));
@@ -1337,6 +1384,8 @@ DrawTargetD2D1::GetImageForLayerContent()
     tmpBitmap->CopyFromBitmap(nullptr, mBitmap, nullptr);
     return tmpBitmap.forget();
   } else {
+    PopAllClips();
+
     RefPtr list = CurrentLayer().mCurrentList;
     mDC->CreateCommandList(getter_AddRefs(CurrentLayer().mCurrentList));
     mDC->SetTarget(CurrentTarget());
@@ -1345,6 +1394,8 @@ DrawTargetD2D1::GetImageForLayerContent()
     DCCommandSink sink(mDC);
     list->Stream(&sink);
 
+    PushAllClips();
+
     return list.forget();
   }
 }
@@ -1607,6 +1658,29 @@ DrawTargetD2D1::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha)
 
     RefPtr image = GetImageForSurface(pat->mSurface, mat, pat->mExtendMode, !pat->mSamplingRect.IsEmpty() ? &pat->mSamplingRect : nullptr);
 
+    if (pat->mSurface->GetFormat() == SurfaceFormat::A8) {
+      // See bug 1251431, at least FillOpacityMask does not appear to allow a source bitmapbrush
+      // with source format A8. This creates a BGRA surface with the same alpha values that
+      // the A8 surface has.
+      RefPtr bitmap;
+      image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
+      if (bitmap) {
+        RefPtr oldTarget;
+        RefPtr tmpBitmap;
+        mDC->CreateBitmap(D2D1::SizeU(pat->mSurface->GetSize().width, pat->mSurface->GetSize().height), nullptr, 0,
+                          D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET, D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)),
+                          getter_AddRefs(tmpBitmap));
+        mDC->GetTarget(getter_AddRefs(oldTarget));
+        mDC->SetTarget(tmpBitmap);
+
+        RefPtr brush;
+        mDC->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), getter_AddRefs(brush));
+        mDC->FillOpacityMask(bitmap, brush);
+        mDC->SetTarget(oldTarget);
+        image = tmpBitmap;
+      }
+    }
+
     if (!image) {
       return CreateTransparentBlackBrush();
     }
@@ -1740,7 +1814,7 @@ DrawTargetD2D1::PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry,
 {
   D2D1_LAYER_OPTIONS1 options = D2D1_LAYER_OPTIONS1_NONE;
 
-  if (aDC->GetPixelFormat().alphaMode == D2D1_ALPHA_MODE_IGNORE || aForceIgnoreAlpha) {
+  if (CurrentLayer().mIsOpaque || aForceIgnoreAlpha) {
     options = D2D1_LAYER_OPTIONS1_IGNORE_ALPHA | D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND;
   }
 
diff --git a/gfx/2d/DrawTargetD2D1.h b/gfx/2d/DrawTargetD2D1.h
index def9ffa64b..7e69c62039 100644
--- a/gfx/2d/DrawTargetD2D1.h
+++ b/gfx/2d/DrawTargetD2D1.h
@@ -164,6 +164,7 @@ private:
   // This function will mark the surface as changing, and make sure any
   // copy-on-write snapshots are notified.
   void MarkChanged();
+  bool ShouldClipTemporarySurfaceDrawing(CompositionOp aOp, const Pattern& aPattern, bool aClipIsComplex);
   void PrepareForDrawing(CompositionOp aOp, const Pattern &aPattern);
   void FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern);
   void FlushTransformToDC() {
@@ -266,6 +267,8 @@ private:
   // A list of targets which have this object in their mDependentTargets set
   TargetSet mDependingOnTargets;
 
+  uint32_t mUsedCommandListsSincePurge;
+
   static ID2D1Factory1 *mFactory;
   static IDWriteFactory *mDWriteFactory;
 };
diff --git a/gfx/2d/DrawTargetRecording.cpp b/gfx/2d/DrawTargetRecording.cpp
index 84f3764271..8ee65bb0ac 100644
--- a/gfx/2d/DrawTargetRecording.cpp
+++ b/gfx/2d/DrawTargetRecording.cpp
@@ -389,6 +389,10 @@ DrawTargetRecording::FillGlyphs(ScaledFont *aFont,
 {
   EnsurePatternDependenciesStored(aPattern);
 
+  if (aFont->GetType() != FontType::DWRITE && aFont->GetType() != FontType::GDI) {
+    gfxDevCrash(LogReason::GetFontFileDataFailed) << "Unexpected ScaledFont type " << (int)aFont->GetType();
+  }
+
   if (!aFont->GetUserData(reinterpret_cast(mRecorder.get()))) {
   // TODO support font in b2g recordings
 #ifndef MOZ_WIDGET_GONK
diff --git a/gfx/2d/DrawTargetSkia.cpp b/gfx/2d/DrawTargetSkia.cpp
index 68fb7ce18c..2fe2667547 100644
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -669,6 +669,102 @@ DrawTargetSkia::MaskSurface(const Pattern &aSource,
   mCanvas->drawBitmap(bitmap, aOffset.x, aOffset.y, &paint.mPaint);
 }
 
+bool
+DrawTarget::Draw3DTransformedSurface(SourceSurface* aSurface, const Matrix4x4& aMatrix)
+{
+  // Composite the 3D transform with the DT's transform.
+  Matrix4x4 fullMat = aMatrix * Matrix4x4::From2D(mTransform);
+  if (fullMat.IsSingular()) {
+    return false;
+  }
+  // Transform the surface bounds and clip to this DT.
+  IntRect xformBounds =
+    RoundedOut(
+      fullMat.TransformAndClipBounds(Rect(Point(0, 0), Size(aSurface->GetSize())),
+                                     Rect(Point(0, 0), Size(GetSize()))));
+  if (xformBounds.IsEmpty()) {
+    return true;
+  }
+  // Offset the matrix by the transformed origin.
+  fullMat.PostTranslate(-xformBounds.x, -xformBounds.y, 0);
+
+  // Read in the source data.
+  SkBitmap srcBitmap = GetBitmapForSurface(aSurface);
+
+  // Set up an intermediate destination surface only the size of the transformed bounds.
+  // Try to pass through the source's format unmodified in both the BGRA and ARGB cases.
+  RefPtr dstSurf =
+    Factory::CreateDataSourceSurface(xformBounds.Size(),
+                                     srcBitmap.alphaType() == kPremul_SkAlphaType ?
+                                       aSurface->GetFormat() : SurfaceFormat::A8R8G8B8_UINT32,
+                                     true);
+  if (!dstSurf) {
+    return false;
+  }
+  SkAutoTUnref dstCanvas(
+    SkCanvas::NewRasterDirect(
+      SkImageInfo::Make(xformBounds.width, xformBounds.height,
+                        srcBitmap.alphaType() == kPremul_SkAlphaType ?
+                          srcBitmap.colorType() : kBGRA_8888_SkColorType,
+                        kPremul_SkAlphaType),
+      dstSurf->GetData(), dstSurf->Stride()));
+  if (!dstCanvas) {
+    return false;
+  }
+
+  // Do the transform.
+  SkPaint paint;
+  paint.setAntiAlias(true);
+  paint.setFilterQuality(kLow_SkFilterQuality);
+  paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+
+  SkMatrix xform;
+  GfxMatrixToSkiaMatrix(fullMat, xform);
+  dstCanvas->setMatrix(xform);
+
+  dstCanvas->drawBitmap(srcBitmap, 0, 0, &paint);
+  dstCanvas->flush();
+
+  // Temporarily reset the DT's transform, since it has already been composed above.
+  Matrix origTransform = mTransform;
+  SetTransform(Matrix());
+
+  // Draw the transformed surface within the transformed bounds.
+  DrawSurface(dstSurf, Rect(xformBounds), Rect(Point(0, 0), Size(xformBounds.Size())));
+
+  SetTransform(origTransform);
+
+  return true;
+}
+
+bool
+DrawTargetSkia::Draw3DTransformedSurface(SourceSurface* aSurface, const Matrix4x4& aMatrix)
+{
+  if (aMatrix.IsSingular()) {
+    return false;
+  }
+
+  MarkChanged();
+
+  SkBitmap bitmap = GetBitmapForSurface(aSurface);
+
+  mCanvas->save();
+
+  SkPaint paint;
+  paint.setAntiAlias(true);
+  paint.setFilterQuality(kLow_SkFilterQuality);
+
+  SkMatrix xform;
+  GfxMatrixToSkiaMatrix(aMatrix, xform);
+  mCanvas->concat(xform);
+
+  mCanvas->drawBitmap(bitmap, 0, 0, &paint);
+
+  mCanvas->restore();
+
+  return true;
+}
+
 already_AddRefed
 DrawTargetSkia::CreateSourceSurfaceFromData(unsigned char *aData,
                                             const IntSize &aSize,
diff --git a/gfx/2d/DrawTargetSkia.h b/gfx/2d/DrawTargetSkia.h
index d5bc47f297..854b3d51c0 100644
--- a/gfx/2d/DrawTargetSkia.h
+++ b/gfx/2d/DrawTargetSkia.h
@@ -86,6 +86,8 @@ public:
                            SourceSurface *aMask,
                            Point aOffset,
                            const DrawOptions &aOptions = DrawOptions()) override;
+  virtual bool Draw3DTransformedSurface(SourceSurface* aSurface,
+                                        const Matrix4x4& aMatrix) override;
   virtual void PushClip(const Path *aPath) override;
   virtual void PushClipRect(const Rect& aRect) override;
   virtual void PopClip() override;
diff --git a/gfx/2d/HelpersSkia.h b/gfx/2d/HelpersSkia.h
index c2e1ddfc4a..ce0bf380d0 100644
--- a/gfx/2d/HelpersSkia.h
+++ b/gfx/2d/HelpersSkia.h
@@ -105,6 +105,14 @@ GfxMatrixToSkiaMatrix(const Matrix& mat, SkMatrix& retval)
                   0, 0, SK_Scalar1);
 }
 
+static inline void
+GfxMatrixToSkiaMatrix(const Matrix4x4& aMatrix, SkMatrix& aResult)
+{
+  aResult.setAll(SkFloatToScalar(aMatrix._11), SkFloatToScalar(aMatrix._21), SkFloatToScalar(aMatrix._41),
+                 SkFloatToScalar(aMatrix._12), SkFloatToScalar(aMatrix._22), SkFloatToScalar(aMatrix._42),
+                 SkFloatToScalar(aMatrix._14), SkFloatToScalar(aMatrix._24), SkFloatToScalar(aMatrix._44));
+}
+
 static inline SkPaint::Cap
 CapStyleToSkiaCap(CapStyle aCap)
 {
diff --git a/gfx/2d/Logging.h b/gfx/2d/Logging.h
index c3c3e35d78..94e314c668 100644
--- a/gfx/2d/Logging.h
+++ b/gfx/2d/Logging.h
@@ -131,6 +131,13 @@ enum class LogReason : int {
   InvalidRect,
   CannotDraw3D, // 20
   IncompatibleBasicTexturedEffect,
+  InvalidFont,
+  PAllocTextureBackendMismatch,
+  GetFontFileDataFailed,
+  MessageChannelCloseFailure,
+  TextureAliveAfterShutdown,
+  InvalidContext,
+  InvalidCommandList,
   // End
   MustBeLessThanThis = 101,
 };
diff --git a/gfx/2d/SFNTData.cpp b/gfx/2d/SFNTData.cpp
index 1978577eb0..2e0fa6e0fe 100644
--- a/gfx/2d/SFNTData.cpp
+++ b/gfx/2d/SFNTData.cpp
@@ -116,7 +116,7 @@ SFNTData::Create(const uint8_t *aFontData, uint32_t aDataLength)
 
   // Check to see if this is a font collection.
   if (aDataLength < sizeof(TTCHeader)) {
-    gfxWarning() << "Font data too short.";
+    gfxDevCrash(LogReason::GetFontFileDataFailed) << "Font data too short: length = " << aDataLength;
     return nullptr;
   }
 
@@ -124,7 +124,7 @@ SFNTData::Create(const uint8_t *aFontData, uint32_t aDataLength)
   if (ttcHeader->ttcTag == TRUETYPE_TAG('t', 't', 'c', 'f')) {
     uint32_t numFonts = ttcHeader->numFonts;
     if (aDataLength < sizeof(TTCHeader) + (numFonts * sizeof(BigEndianUint32))) {
-      gfxWarning() << "Font data too short to contain full TTC Header.";
+      gfxDevCrash(LogReason::GetFontFileDataFailed) << "Font data too short to contain full TTC Header: numFonts = " << numFonts << "; length = " << aDataLength;
       return nullptr;
     }
 
@@ -134,6 +134,7 @@ SFNTData::Create(const uint8_t *aFontData, uint32_t aDataLength)
     const BigEndianUint32* endOfOffsets = offset + numFonts;
     while (offset != endOfOffsets) {
       if (!sfntData->AddFont(aFontData, aDataLength, *offset)) {
+        gfxDevCrash(LogReason::GetFontFileDataFailed) << "Failed to add font data from TTC";
         return nullptr;
       }
       ++offset;
@@ -144,6 +145,7 @@ SFNTData::Create(const uint8_t *aFontData, uint32_t aDataLength)
 
   UniquePtr sfntData(new SFNTData);
   if (!sfntData->AddFont(aFontData, aDataLength, 0)) {
+    gfxDevCrash(LogReason::GetFontFileDataFailed) << "Failed to add single font data";
     return nullptr;
   }
 
@@ -223,7 +225,7 @@ SFNTData::AddFont(const uint8_t *aFontData, uint32_t aDataLength,
 {
   uint32_t remainingLength = aDataLength - aOffset;
   if (remainingLength < sizeof(OffsetTable)) {
-    gfxWarning() << "Font data too short to contain OffsetTable " << aOffset;
+    gfxCriticalError() << "Font data too short to contain OffsetTable: offset = " << aOffset << "; length = " << aDataLength;
     return false;
   }
 
@@ -231,7 +233,7 @@ SFNTData::AddFont(const uint8_t *aFontData, uint32_t aDataLength,
     reinterpret_cast(aFontData + aOffset);
   if (remainingLength <
       sizeof(OffsetTable) + (offsetTable->numTables * sizeof(TableDirEntry))) {
-    gfxWarning() << "Font data too short to contain tables.";
+    gfxCriticalError() << "Font data too short to contain tables. numTables = " << offsetTable->numTables << "; offset = " << aOffset << "; length = " << aDataLength;
     return false;
   }
 
diff --git a/gfx/2d/ScaledFontWin.cpp b/gfx/2d/ScaledFontWin.cpp
index 4b22dc2fdc..1c6d81049b 100644
--- a/gfx/2d/ScaledFontWin.cpp
+++ b/gfx/2d/ScaledFontWin.cpp
@@ -40,6 +40,7 @@ ScaledFontWin::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton)
     table = 0;
     tableSize = ::GetFontData(dc.GetDC(), table, 0, nullptr, 0);
     if (tableSize == GDI_ERROR) {
+      gfxDevCrash(LogReason::GetFontFileDataFailed) << "Failed to get font data from GDI";
       return false;
     }
   }
@@ -49,6 +50,7 @@ ScaledFontWin::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton)
   uint32_t sizeGot =
     ::GetFontData(dc.GetDC(), table, 0, fontData.get(), tableSize);
   if (sizeGot != tableSize) {
+    gfxDevCrash(LogReason::GetFontFileDataFailed) << "GDI did not return enough data for font: wanted " << tableSize << ", got " << sizeGot;
     return false;
   }
 
@@ -58,7 +60,7 @@ ScaledFontWin::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton)
     UniquePtr sfntData = SFNTData::Create(fontData.get(),
                                                     tableSize);
     if (!sfntData) {
-      gfxWarning() << "Failed to create SFNTData for GetFontFileData.";
+      gfxDevCrash(LogReason::GetFontFileDataFailed) << "Failed to create SFNTData for GetFontFileData.";
       return false;
     }
 
@@ -66,7 +68,7 @@ ScaledFontWin::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton)
     // both 16 bit.
     if (!sfntData->GetIndexForU16Name(
           reinterpret_cast(mLogFont.lfFaceName), &index)) {
-      gfxWarning() << "Failed to get index for face name.";
+      gfxDevCrash(LogReason::GetFontFileDataFailed) << "Failed to get index for face name.";
       return false;
     }
   }
diff --git a/gfx/gl/GLLibraryEGL.cpp b/gfx/gl/GLLibraryEGL.cpp
index f344be15e2..110d672eaf 100644
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -163,7 +163,7 @@ GetAndInitDisplayForAccelANGLE(GLLibraryEGL& egl)
     // D3D11 ANGLE only works with OMTC; there's a bug in the non-OMTC layer
     // manager, and it's pointless to try to fix it.  We also don't try
     // D3D11 ANGLE if the layer manager is prefering D3D9 (hrm, do we care?)
-    if (gfxPrefs::LayersOffMainThreadCompositionEnabled() &&
+    if (!gfxPrefs::LayersOffMainThreadCompositionForceDisabled() &&
         !gfxPrefs::LayersPreferD3D9())
     {
         if (gfxPrefs::WebGLANGLEForceD3D11())
diff --git a/gfx/gl/SharedSurfaceANGLE.cpp b/gfx/gl/SharedSurfaceANGLE.cpp
index de083a2ae8..a5c8290aea 100644
--- a/gfx/gl/SharedSurfaceANGLE.cpp
+++ b/gfx/gl/SharedSurfaceANGLE.cpp
@@ -209,7 +209,11 @@ public:
             }
         }
 
-        ID3D11Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D11Device();
+        RefPtr device;
+        if (!gfxWindowsPlatform::GetPlatform()->GetD3D11Device(&device)) {
+            return;
+        }
+
         device->GetImmediateContext(getter_AddRefs(mDeviceContext));
 
         mTexture->GetDesc(&mDesc);
@@ -260,8 +264,13 @@ bool
 SharedSurface_ANGLEShareHandle::ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface)
 {
     MOZ_ASSERT(out_surface);
+
+    RefPtr device;
+    if (!gfxWindowsPlatform::GetPlatform()->GetD3D11Device(&device)) {
+        return false;
+    }
+
     RefPtr tex;
-    ID3D11Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D11Device();
     HRESULT hr = device->OpenSharedResource(mShareHandle,
                                             __uuidof(ID3D11Texture2D),
                                             (void**)(ID3D11Texture2D**)getter_AddRefs(tex));
diff --git a/gfx/ipc/GfxMessageUtils.h b/gfx/ipc/GfxMessageUtils.h
index 02ecfcb5aa..2158b52cf9 100644
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -7,25 +7,25 @@
 #ifndef __GFXMESSAGEUTILS_H__
 #define __GFXMESSAGEUTILS_H__
 
+#include "FilterSupport.h"
+#include "FrameMetrics.h"
+#include "ImageTypes.h"
+#include "RegionBuilder.h"
 #include "base/process_util.h"
 #include "chrome/common/ipc_message_utils.h"
-#include "ipc/IPCMessageUtils.h"
-
-#include 
-
-#include "mozilla/gfx/Matrix.h"
 #include "gfxPoint.h"
 #include "gfxRect.h"
+#include "gfxTypes.h"
+#include "ipc/IPCMessageUtils.h"
+#include "mozilla/gfx/Matrix.h"
+#include "mozilla/layers/AsyncDragMetrics.h"
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/GeckoContentController.h"
+#include "mozilla/layers/LayersTypes.h"
 #include "nsRect.h"
 #include "nsRegion.h"
-#include "gfxTypes.h"
-#include "mozilla/layers/AsyncDragMetrics.h"
-#include "mozilla/layers/LayersTypes.h"
-#include "mozilla/layers/CompositorTypes.h"
-#include "ImageTypes.h"
-#include "FrameMetrics.h"
-#include "FilterSupport.h"
-#include "mozilla/layers/GeckoContentController.h"
+
+#include 
 
 #ifdef _MSC_VER
 #pragma warning( disable : 4800 )
@@ -367,6 +367,7 @@ struct RegionParamTraits
 
   static void Write(Message* msg, const paramType& param)
   {
+
     for (auto iter = param.RectIter(); !iter.Done(); iter.Next()) {
       const Rect& r = iter.Get();
       MOZ_RELEASE_ASSERT(!r.IsEmpty());
@@ -379,12 +380,16 @@ struct RegionParamTraits
 
   static bool Read(const Message* msg, void** iter, paramType* result)
   {
+    RegionBuilder builder;
     Rect rect;
     while (ReadParam(msg, iter, &rect)) {
-      if (rect.IsEmpty())
+      if (rect.IsEmpty()) {
+        *result = builder.ToRegion();
         return true;
-      result->Or(*result, rect);
+      }
+      builder.Or(rect);
     }
+
     return false;
   }
 };
diff --git a/gfx/layers/Compositor.cpp b/gfx/layers/Compositor.cpp
index b04c8c3df1..56ecb704f0 100644
--- a/gfx/layers/Compositor.cpp
+++ b/gfx/layers/Compositor.cpp
@@ -398,6 +398,18 @@ Compositor::ComputeBackdropCopyRect(const gfx::Rect& aRect,
   return result;
 }
 
+void
+Compositor::SetInvalid()
+{
+  mParent = nullptr;
+}
+
+bool
+Compositor::IsValid() const
+{
+  return !mParent;
+}
+
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
 void
 Compositor::SetDispAcquireFence(Layer* aLayer, nsIWidget* aWidget)
diff --git a/gfx/layers/Compositor.h b/gfx/layers/Compositor.h
index fe622e7e81..1a070323c5 100644
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -518,6 +518,11 @@ public:
     return mCompositeUntilTime;
   }
 
+  // A stale Compositor has no CompositorBridgeParent; it will not process
+  // frames and should not be used.
+  void SetInvalid();
+  bool IsValid() const;
+
 protected:
   void DrawDiagnosticsInternal(DiagnosticFlags aFlags,
                                const gfx::Rect& aVisibleRect,
diff --git a/gfx/layers/CompositorTypes.h b/gfx/layers/CompositorTypes.h
index f549386467..6a974485de 100644
--- a/gfx/layers/CompositorTypes.h
+++ b/gfx/layers/CompositorTypes.h
@@ -63,9 +63,12 @@ enum class TextureFlags : uint32_t {
   IMMEDIATE_UPLOAD   = 1 << 10,
   // The texture is part of a component-alpha pair
   COMPONENT_ALPHA    = 1 << 11,
+  // The texture is being allocated for a compositor that no longer exists.
+  // This flag is only used in the parent process.
+  INVALID_COMPOSITOR = 1 << 12,
 
   // OR union of all valid bits
-  ALL_BITS           = (1 << 12) - 1,
+  ALL_BITS           = (1 << 13) - 1,
   // the default flags
   DEFAULT = NO_FLAGS
 };
diff --git a/gfx/layers/IMFYCbCrImage.cpp b/gfx/layers/IMFYCbCrImage.cpp
index 520b0bd24d..4b823917fe 100644
--- a/gfx/layers/IMFYCbCrImage.cpp
+++ b/gfx/layers/IMFYCbCrImage.cpp
@@ -224,9 +224,10 @@ IMFYCbCrImage::GetTextureClient(CompositableClient* aClient)
     return mTextureClient;
   }
 
-  LayersBackend backend = aClient->GetForwarder()->GetCompositorBackendType();
-  ID3D11Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D11ImageBridgeDevice();
+  RefPtr device;
+  gfxWindowsPlatform::GetPlatform()->GetD3D11ImageBridgeDevice(&device);
 
+  LayersBackend backend = aClient->GetForwarder()->GetCompositorBackendType();
   if (!device || backend != LayersBackend::LAYERS_D3D11) {
     if (backend == LayersBackend::LAYERS_D3D9 ||
         backend == LayersBackend::LAYERS_D3D11) {
diff --git a/gfx/layers/Layers.cpp b/gfx/layers/Layers.cpp
index 1f7a3d78ac..ff5f609776 100644
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -1963,6 +1963,9 @@ Layer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
   if (Is3DContextLeaf()) {
     aStream << " [is3DContextLeaf]";
   }
+  if (IsScrollbarContainer()) {
+    aStream << " [scrollbar]";
+  }
   if (GetScrollbarDirection() == VERTICAL) {
     aStream << nsPrintfCString(" [vscrollbar=%lld]", GetScrollbarTargetContainerId()).get();
   }
diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp
index 5448aa1033..645ded798e 100644
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -156,6 +156,14 @@ using mozilla::gfx::PointTyped;
  * pixels would make us drop to low-res at y=490...990.\n
  * This value is in layer pixels.
  *
+ * \li\b apz.disable_for_scroll_linked_effects
+ * Setting this pref to true will disable APZ scrolling on documents where
+ * scroll-linked effects are detected. A scroll linked effect is detected if
+ * positioning or transform properties are updated inside a scroll event
+ * dispatch; we assume that such an update is in response to the scroll event
+ * and is therefore a scroll-linked effect which will be laggy with APZ
+ * scrolling.
+ *
  * \li\b apz.displayport_expiry_ms
  * While a scrollable frame is scrolling async, we set a displayport on it
  * to make sure it is layerized. However this takes up memory, so once the
diff --git a/gfx/layers/basic/BasicCompositor.cpp b/gfx/layers/basic/BasicCompositor.cpp
index deddd7166e..0237473d5e 100644
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -17,13 +17,6 @@
 #include 
 #include "ImageContainer.h"
 #include "gfxPrefs.h"
-#ifdef MOZ_ENABLE_SKIA
-#include "skia/include/core/SkCanvas.h"              // for SkCanvas
-#include "skia/include/core/SkBitmapDevice.h"        // for SkBitmapDevice
-#else
-#define PIXMAN_DONT_DEFINE_STDINT
-#include "pixman.h"                     // for pixman_f_transform, etc
-#endif
 
 namespace mozilla {
 using namespace mozilla::gfx;
@@ -85,8 +78,9 @@ public:
   bool mWrappingExistingData;
 };
 
-BasicCompositor::BasicCompositor(nsIWidget *aWidget)
-  : mWidget(aWidget)
+BasicCompositor::BasicCompositor(CompositorBridgeParent* aParent, nsIWidget *aWidget)
+  : Compositor(aParent)
+  , mWidget(aWidget)
   , mDidExternalComposition(false)
 {
   MOZ_COUNT_CTOR(BasicCompositor);
@@ -261,144 +255,6 @@ DrawSurfaceWithTextureCoords(DrawTarget *aDest,
                    mode, aMask, aMaskTransform, &matrix);
 }
 
-#ifdef MOZ_ENABLE_SKIA
-static SkMatrix
-Matrix3DToSkia(const Matrix4x4& aMatrix)
-{
-  SkMatrix transform;
-  transform.setAll(aMatrix._11,
-                   aMatrix._21,
-                   aMatrix._41,
-                   aMatrix._12,
-                   aMatrix._22,
-                   aMatrix._42,
-                   aMatrix._14,
-                   aMatrix._24,
-                   aMatrix._44);
-
-  return transform;
-}
-
-static void
-Transform(DataSourceSurface* aDest,
-          DataSourceSurface* aSource,
-          const Matrix4x4& aTransform,
-          const Point& aDestOffset)
-{
-  if (aTransform.IsSingular()) {
-    return;
-  }
-
-  IntSize destSize = aDest->GetSize();
-  SkImageInfo destInfo = SkImageInfo::Make(destSize.width,
-                                           destSize.height,
-                                           kBGRA_8888_SkColorType,
-                                           kPremul_SkAlphaType);
-  SkBitmap destBitmap;
-  destBitmap.setInfo(destInfo, aDest->Stride());
-  destBitmap.setPixels((uint32_t*)aDest->GetData());
-  SkCanvas destCanvas(destBitmap);
-
-  IntSize srcSize = aSource->GetSize();
-  SkImageInfo srcInfo = SkImageInfo::Make(srcSize.width,
-                                          srcSize.height,
-                                          kBGRA_8888_SkColorType,
-                                          kPremul_SkAlphaType);
-  SkBitmap src;
-  src.setInfo(srcInfo, aSource->Stride());
-  src.setPixels((uint32_t*)aSource->GetData());
-
-  Matrix4x4 transform = aTransform;
-  transform.PostTranslate(Point3D(-aDestOffset.x, -aDestOffset.y, 0));
-  destCanvas.setMatrix(Matrix3DToSkia(transform));
-
-  SkPaint paint;
-  paint.setXfermodeMode(SkXfermode::kSrc_Mode);
-  paint.setAntiAlias(true);
-  paint.setFilterQuality(kLow_SkFilterQuality);
-  SkRect destRect = SkRect::MakeXYWH(0, 0, srcSize.width, srcSize.height);
-  destCanvas.drawBitmapRect(src, destRect, &paint);
-}
-#else
-static pixman_transform
-Matrix3DToPixman(const Matrix4x4& aMatrix)
-{
-  pixman_f_transform transform;
-
-  transform.m[0][0] = aMatrix._11;
-  transform.m[0][1] = aMatrix._21;
-  transform.m[0][2] = aMatrix._41;
-  transform.m[1][0] = aMatrix._12;
-  transform.m[1][1] = aMatrix._22;
-  transform.m[1][2] = aMatrix._42;
-  transform.m[2][0] = aMatrix._14;
-  transform.m[2][1] = aMatrix._24;
-  transform.m[2][2] = aMatrix._44;
-
-  pixman_transform result;
-  pixman_transform_from_pixman_f_transform(&result, &transform);
-
-  return result;
-}
-
-static void
-Transform(DataSourceSurface* aDest,
-          DataSourceSurface* aSource,
-          const Matrix4x4& aTransform,
-          const Point& aDestOffset)
-{
-  IntSize destSize = aDest->GetSize();
-  pixman_image_t* dest = pixman_image_create_bits(PIXMAN_a8r8g8b8,
-                                                  destSize.width,
-                                                  destSize.height,
-                                                  (uint32_t*)aDest->GetData(),
-                                                  aDest->Stride());
-
-  IntSize srcSize = aSource->GetSize();
-  pixman_image_t* src = pixman_image_create_bits(PIXMAN_a8r8g8b8,
-                                                 srcSize.width,
-                                                 srcSize.height,
-                                                 (uint32_t*)aSource->GetData(),
-                                                 aSource->Stride());
-
-  MOZ_ASSERT(src !=0 && dest != 0, "Failed to create pixman images?");
-
-  pixman_transform pixTransform = Matrix3DToPixman(aTransform);
-  pixman_transform pixTransformInverted;
-
-  // If the transform is singular then nothing would be drawn anyway, return here
-  if (!pixman_transform_invert(&pixTransformInverted, &pixTransform)) {
-    pixman_image_unref(dest);
-    pixman_image_unref(src);
-    return;
-  }
-  pixman_image_set_transform(src, &pixTransformInverted);
-
-  pixman_image_composite32(PIXMAN_OP_SRC,
-                           src,
-                           nullptr,
-                           dest,
-                           aDestOffset.x,
-                           aDestOffset.y,
-                           0,
-                           0,
-                           0,
-                           0,
-                           destSize.width,
-                           destSize.height);
-
-  pixman_image_unref(dest);
-  pixman_image_unref(src);
-}
-#endif
-
-static inline IntRect
-RoundOut(Rect r)
-{
-  r.RoundOut();
-  return IntRect(r.x, r.y, r.width, r.height);
-}
-
 static void
 SetupMask(const EffectChain& aEffectChain,
           DrawTarget* aDest,
@@ -443,7 +299,7 @@ BasicCompositor::DrawQuad(const gfx::Rect& aRect,
     newTransform = aTransform.As2D();
   } else {
     // Create a temporary surface for the transform.
-    dest = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(RoundOut(aRect).Size(), SurfaceFormat::B8G8R8A8);
+    dest = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(RoundedOut(aRect).Size(), SurfaceFormat::B8G8R8A8);
     if (!dest) {
       return;
     }
@@ -458,8 +314,7 @@ BasicCompositor::DrawQuad(const gfx::Rect& aRect,
       return;
     }
 
-    // Propagate the coordinate offset to our 2D draw target.
-    newTransform = Matrix::Translation(transformBounds.x, transformBounds.y);
+    newTransform = Matrix();
 
     // When we apply the 3D transformation, we do it against a temporary
     // surface, so undo the coordinate offset.
@@ -567,54 +422,44 @@ BasicCompositor::DrawQuad(const gfx::Rect& aRect,
   if (!aTransform.Is2D()) {
     dest->Flush();
 
-    RefPtr snapshot = dest->Snapshot();
-    RefPtr source = snapshot->GetDataSurface();
-    RefPtr temp =
-      Factory::CreateDataSourceSurface(RoundOut(transformBounds).Size(), SurfaceFormat::B8G8R8A8
-#ifdef MOZ_ENABLE_SKIA
-        , true
-#endif
-        );
-    if (NS_WARN_IF(!temp)) {
-      buffer->PopClip();
-      return;
-    }
-
-    Transform(temp, source, new3DTransform, transformBounds.TopLeft());
+    RefPtr destSnapshot = dest->Snapshot();
 
     SetupMask(aEffectChain, buffer, offset, sourceMask, maskTransform);
 
-    // Adjust for the fact that our content now start at 0,0 instead
-    // of the top left of transformBounds.
-    transformBounds.MoveTo(0, 0);
-    maskTransform.PostTranslate(-transformBounds.x, -transformBounds.y);
-
     if (sourceMask) {
-      // Transform the source by it's normal transform, and then the inverse
-      // of the mask transform so that it's in the mask's untransformed
-      // coordinate space.
-      Matrix old = buffer->GetTransform();
-      Matrix sourceTransform = old;
+      RefPtr transformDT =
+        dest->CreateSimilarDrawTarget(IntSize(transformBounds.width, transformBounds.height),
+                                      SurfaceFormat::B8G8R8A8);
+      new3DTransform.PostTranslate(-transformBounds.x, -transformBounds.y, 0);
+      if (transformDT &&
+          transformDT->Draw3DTransformedSurface(destSnapshot, new3DTransform)) {
+        RefPtr transformSnapshot = transformDT->Snapshot();
 
-      Matrix inverseMask = maskTransform;
-      inverseMask.Invert();
+        // Transform the source by it's normal transform, and then the inverse
+        // of the mask transform so that it's in the mask's untransformed
+        // coordinate space.
+        Matrix sourceTransform = newTransform;
+        sourceTransform.PostTranslate(transformBounds.TopLeft());
 
-      sourceTransform *= inverseMask;
+        Matrix inverseMask = maskTransform;
+        inverseMask.Invert();
 
-      SurfacePattern source(temp, ExtendMode::CLAMP, sourceTransform);
+        sourceTransform *= inverseMask;
 
-      buffer->PushClipRect(transformBounds);
+        SurfacePattern source(transformSnapshot, ExtendMode::CLAMP, sourceTransform);
 
-      // Mask in the untransformed coordinate space, and then transform
-      // by the mask transform to put the result back into destination
-      // coords.
-      buffer->SetTransform(maskTransform);
-      buffer->MaskSurface(source, sourceMask, Point(0, 0));
-      buffer->SetTransform(old);
+        buffer->PushClipRect(transformBounds);
 
-      buffer->PopClip();
+        // Mask in the untransformed coordinate space, and then transform
+        // by the mask transform to put the result back into destination
+        // coords.
+        buffer->SetTransform(maskTransform);
+        buffer->MaskSurface(source, sourceMask, Point(0, 0));
+
+        buffer->PopClip();
+      }
     } else {
-      buffer->DrawSurface(temp, transformBounds, transformBounds);
+      buffer->Draw3DTransformedSurface(destSnapshot, new3DTransform);
     }
   }
 
diff --git a/gfx/layers/basic/BasicCompositor.h b/gfx/layers/basic/BasicCompositor.h
index dbbdc37ce6..c791f94f54 100644
--- a/gfx/layers/basic/BasicCompositor.h
+++ b/gfx/layers/basic/BasicCompositor.h
@@ -42,7 +42,7 @@ public:
 class BasicCompositor : public Compositor
 {
 public:
-  explicit BasicCompositor(nsIWidget *aWidget);
+  explicit BasicCompositor(CompositorBridgeParent* aParent, nsIWidget *aWidget);
 
 protected:
   virtual ~BasicCompositor();
diff --git a/gfx/layers/basic/BasicLayerManager.cpp b/gfx/layers/basic/BasicLayerManager.cpp
index c43b39bb23..1555ef2b9e 100644
--- a/gfx/layers/basic/BasicLayerManager.cpp
+++ b/gfx/layers/basic/BasicLayerManager.cpp
@@ -44,13 +44,6 @@
 #include "nsRect.h"                     // for mozilla::gfx::IntRect
 #include "nsRegion.h"                   // for nsIntRegion, etc
 #include "nsTArray.h"                   // for AutoTArray
-#ifdef MOZ_ENABLE_SKIA
-#include "skia/include/core/SkCanvas.h"         // for SkCanvas
-#include "skia/include/core/SkBitmapDevice.h"   // for SkBitmapDevice
-#else
-#define PIXMAN_DONT_DEFINE_STDINT
-#include "pixman.h"                     // for pixman_f_transform, etc
-#endif
 class nsIWidget;
 
 namespace mozilla {
@@ -114,7 +107,11 @@ BasicLayerManager::PushGroupForLayer(gfxContext* aContext, Layer* aLayer, const
     if (!surfRect.IsEmpty()) {
       RefPtr dt = aContext->GetDrawTarget()->CreateSimilarDrawTarget(surfRect.Size(), SurfaceFormat::B8G8R8A8);
 
-      RefPtr ctx = new gfxContext(dt, ToRect(rect).TopLeft());
+      RefPtr ctx = gfxContext::ForDrawTarget(dt, ToRect(rect).TopLeft());
+      if (!ctx) {
+        gfxDevCrash(LogReason::InvalidContext) << "BasicLayerManager context problem " << gfx::hexa(dt);
+        return group;
+      }
       ctx->SetMatrix(oldMat);
 
       group.mGroupOffset = surfRect.TopLeft();
@@ -701,182 +698,6 @@ BasicLayerManager::SetRoot(Layer* aLayer)
   mRoot = aLayer;
 }
 
-#ifdef MOZ_ENABLE_SKIA
-static SkMatrix
-BasicLayerManager_Matrix3DToSkia(const Matrix4x4& aMatrix)
-{
-  SkMatrix transform;
-  transform.setAll(aMatrix._11,
-                   aMatrix._21,
-                   aMatrix._41,
-                   aMatrix._12,
-                   aMatrix._22,
-                   aMatrix._42,
-                   aMatrix._14,
-                   aMatrix._24,
-                   aMatrix._44);
-
-  return transform;
-}
-
-static void
-Transform(const gfxImageSurface* aDest,
-          RefPtr aSrc,
-          const Matrix4x4& aTransform,
-          gfxPoint aDestOffset)
-{
-  if (aTransform.IsSingular()) {
-    return;
-  }
-
-  IntSize destSize = aDest->GetSize();
-  SkImageInfo destInfo = SkImageInfo::Make(destSize.width,
-                                           destSize.height,
-                                           kBGRA_8888_SkColorType,
-                                           kPremul_SkAlphaType);
-  SkBitmap destBitmap;
-  destBitmap.setInfo(destInfo, aDest->Stride());
-  destBitmap.setPixels((uint32_t*)aDest->Data());
-  SkCanvas destCanvas(destBitmap);
-
-  IntSize srcSize = aSrc->GetSize();
-  SkImageInfo srcInfo = SkImageInfo::Make(srcSize.width,
-                                          srcSize.height,
-                                          kBGRA_8888_SkColorType,
-                                          kPremul_SkAlphaType);
-  SkBitmap src;
-  src.setInfo(srcInfo, aSrc->Stride());
-  src.setPixels((uint32_t*)aSrc->GetData());
-
-  Matrix4x4 transform = aTransform;
-  transform.PostTranslate(Point3D(-aDestOffset.x, -aDestOffset.y, 0));
-  destCanvas.setMatrix(BasicLayerManager_Matrix3DToSkia(transform));
-
-  SkPaint paint;
-  paint.setXfermodeMode(SkXfermode::kSrc_Mode);
-  paint.setAntiAlias(true);
-  paint.setFilterQuality(kLow_SkFilterQuality);
-  SkRect destRect = SkRect::MakeXYWH(0, 0, srcSize.width, srcSize.height);
-  destCanvas.drawBitmapRect(src, destRect, &paint);
-}
-#else
-static pixman_transform
-BasicLayerManager_Matrix3DToPixman(const Matrix4x4& aMatrix)
-{
-  pixman_f_transform transform;
-
-  transform.m[0][0] = aMatrix._11;
-  transform.m[0][1] = aMatrix._21;
-  transform.m[0][2] = aMatrix._41;
-  transform.m[1][0] = aMatrix._12;
-  transform.m[1][1] = aMatrix._22;
-  transform.m[1][2] = aMatrix._42;
-  transform.m[2][0] = aMatrix._14;
-  transform.m[2][1] = aMatrix._24;
-  transform.m[2][2] = aMatrix._44;
-
-  pixman_transform result;
-  pixman_transform_from_pixman_f_transform(&result, &transform);
-
-  return result;
-}
-
-static void
-Transform(const gfxImageSurface* aDest,
-          RefPtr aSrc,
-          const Matrix4x4& aTransform,
-          gfxPoint aDestOffset)
-{
-  IntSize destSize = aDest->GetSize();
-  pixman_image_t* dest = pixman_image_create_bits(aDest->Format() == SurfaceFormat::A8R8G8B8_UINT32 ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8,
-                                                  destSize.width,
-                                                  destSize.height,
-                                                  (uint32_t*)aDest->Data(),
-                                                  aDest->Stride());
-
-  IntSize srcSize = aSrc->GetSize();
-  pixman_image_t* src = pixman_image_create_bits(aSrc->GetFormat() == SurfaceFormat::B8G8R8A8 ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8,
-                                                 srcSize.width,
-                                                 srcSize.height,
-                                                 (uint32_t*)aSrc->GetData(),
-                                                 aSrc->Stride());
-
-  MOZ_ASSERT(src != 0 && dest !=0, "Failed to create pixman images?");
-
-  pixman_transform pixTransform = BasicLayerManager_Matrix3DToPixman(aTransform);
-  pixman_transform pixTransformInverted;
-
-  // If the transform is singular then nothing would be drawn anyway, return here
-  if (!pixman_transform_invert(&pixTransformInverted, &pixTransform)) {
-    pixman_image_unref(dest);
-    pixman_image_unref(src);
-    return;
-  }
-  pixman_image_set_transform(src, &pixTransformInverted);
-
-  pixman_image_composite32(PIXMAN_OP_SRC,
-                           src,
-                           nullptr,
-                           dest,
-                           aDestOffset.x,
-                           aDestOffset.y,
-                           0,
-                           0,
-                           0,
-                           0,
-                           destSize.width,
-                           destSize.height);
-
-  pixman_image_unref(dest);
-  pixman_image_unref(src);
-}
-#endif
-
-/**
- * Transform a surface using a Matrix4x4 and blit to the destination if
- * it is efficient to do so.
- *
- * @param aSource       Source surface.
- * @param aDest         Desintation context.
- * @param aBounds       Area represented by aSource.
- * @param aTransform    Transformation matrix.
- * @param aDestRect     Output: rectangle in which to draw returned surface on aDest
- *                      (same size as aDest). Only filled in if this returns
- *                      a surface.
- * @return              Transformed surface
- */
-static already_AddRefed
-Transform3D(RefPtr aSource,
-            gfxContext* aDest,
-            const gfxRect& aBounds,
-            const Matrix4x4& aTransform,
-            gfxRect& aDestRect)
-{
-  // Find the transformed rectangle of our layer, intersected with the
-  // destination rectangle.
-  // This is in device space since we have an identity transform set on aTarget.
-  aDestRect = ThebesRect(aTransform.TransformAndClipBounds(
-                ToRectDouble(aBounds),
-                ToRectDouble(aDest->GetClipExtents())));
-  aDestRect.RoundOut();
-
-  // Create a surface the size of the transformed object.
-  RefPtr destImage = new gfxImageSurface(IntSize(aDestRect.width,
-                                                                    aDestRect.height),
-                                                            SurfaceFormat::A8R8G8B8_UINT32);
-  gfxPoint offset = aDestRect.TopLeft();
-
-  // Include a translation to the correct origin.
-  Matrix4x4 translation = Matrix4x4::Translation(aBounds.x, aBounds.y, 0);
-
-  // Transform the content and offset it such that the content begins at the origin.
-  Transform(destImage, aSource->GetDataSurface(), translation * aTransform, offset);
-
-  // If we haven't actually drawn to aDest then return our temporary image so
-  // that the caller can do this.
-  return destImage.forget();
-}
-
 void
 BasicLayerManager::PaintSelfOrChildren(PaintLayerContext& aPaintContext,
                                        gfxContext* aGroupTarget)
@@ -1080,43 +901,60 @@ BasicLayerManager::PaintLayer(gfxContext* aTarget,
     RefPtr untransformedDT =
       gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(bounds.width, bounds.height),
                                                                    SurfaceFormat::B8G8R8A8);
-    if (!untransformedDT) {
+    if (!untransformedDT || !untransformedDT->IsValid()) {
       return;
     }
 
-    RefPtr groupTarget = new gfxContext(untransformedDT,
-                                                      Point(bounds.x, bounds.y));
+    RefPtr groupTarget = gfxContext::ForDrawTarget(untransformedDT,
+                                                               Point(bounds.x, bounds.y));
+    MOZ_ASSERT(groupTarget); // already checked the target above
 
     PaintSelfOrChildren(paintLayerContext, groupTarget);
 
     // Temporary fast fix for bug 725886
     // Revert these changes when 725886 is ready
-    MOZ_ASSERT(untransformedDT,
+    MOZ_ASSERT(untransformedDT && untransformedDT->IsValid(),
                "We should always allocate an untransformed surface with 3d transforms!");
-    gfxRect destRect;
 #ifdef DEBUG
     if (aLayer->GetDebugColorIndex() != 0) {
       Color color((aLayer->GetDebugColorIndex() & 1) ? 1.f : 0.f,
                   (aLayer->GetDebugColorIndex() & 2) ? 1.f : 0.f,
                   (aLayer->GetDebugColorIndex() & 4) ? 1.f : 0.f);
 
-      RefPtr temp = new gfxContext(untransformedDT, Point(bounds.x, bounds.y));
+      RefPtr temp = gfxContext::ForDrawTarget(untransformedDT,
+                                                          Point(bounds.x, bounds.y));
+      MOZ_ASSERT(temp); // already checked for target above
       temp->SetColor(color);
       temp->Paint();
     }
 #endif
     Matrix4x4 effectiveTransform = aLayer->GetEffectiveTransform();
-    RefPtr result =
-      Transform3D(untransformedDT->Snapshot(), aTarget, ThebesRect(bounds),
-                  effectiveTransform, destRect);
+    Rect xformBounds =
+      effectiveTransform.TransformAndClipBounds(Rect(bounds),
+                                                ToRect(aTarget->GetClipExtents()));
+    xformBounds.RoundOut();
+    effectiveTransform.PostTranslate(-xformBounds.x, -xformBounds.y, 0);
+
+    RefPtr untransformedSurf = untransformedDT->Snapshot();
+    RefPtr xformDT =
+      untransformedDT->CreateSimilarDrawTarget(IntSize(xformBounds.width, xformBounds.height),
+                                               SurfaceFormat::B8G8R8A8);
+    RefPtr xformSurf;
+    if(xformDT && untransformedSurf &&
+       xformDT->Draw3DTransformedSurface(untransformedSurf, effectiveTransform)) {
+      xformSurf = xformDT->Snapshot();
+    }
+
+    if (xformSurf) {
+      aTarget->SetPattern(
+        new gfxPattern(xformSurf,
+                       Matrix::Translation(xformBounds.TopLeft())));
 
-    if (result) {
-      aTarget->SetSource(result, destRect.TopLeft());
       // Azure doesn't support EXTEND_NONE, so to avoid extending the edges
       // of the source surface out to the current clip region, clip to
       // the rectangle of the result surface now.
       aTarget->NewPath();
-      aTarget->SnappedRectangle(destRect);
+      aTarget->SnappedRectangle(ThebesRect(xformBounds));
       aTarget->Clip();
       FlushGroup(paintLayerContext, needsClipToVisibleRegion);
     }
diff --git a/gfx/layers/basic/BasicPaintedLayer.cpp b/gfx/layers/basic/BasicPaintedLayer.cpp
index 551ce97a07..928971b4e3 100644
--- a/gfx/layers/basic/BasicPaintedLayer.cpp
+++ b/gfx/layers/basic/BasicPaintedLayer.cpp
@@ -174,7 +174,9 @@ BasicPaintedLayer::Validate(LayerManager::DrawPaintedLayerCallback aCallback,
 
     RenderTraceInvalidateStart(this, "FFFF00", state.mRegionToDraw.GetBounds());
 
-    RefPtr ctx = gfxContext::ContextForDrawTarget(target);
+    RefPtr ctx = gfxContext::ForDrawTargetWithTransform(target);
+    MOZ_ASSERT(ctx); // already checked the target above
+
     PaintBuffer(ctx,
                 state.mRegionToDraw, state.mRegionToDraw, state.mRegionToInvalidate,
                 state.mDidSelfCopy,
diff --git a/gfx/layers/basic/X11BasicCompositor.h b/gfx/layers/basic/X11BasicCompositor.h
index 5f7574d420..d9efc568d7 100644
--- a/gfx/layers/basic/X11BasicCompositor.h
+++ b/gfx/layers/basic/X11BasicCompositor.h
@@ -46,8 +46,9 @@ private:
 class X11BasicCompositor : public BasicCompositor
 {
 public:
-
-  explicit X11BasicCompositor(nsIWidget *aWidget) : BasicCompositor(aWidget) {}
+  explicit X11BasicCompositor(CompositorBridgeParent* aParent, nsIWidget *aWidget)
+    : BasicCompositor(aParent, aWidget)
+  {}
 
   virtual already_AddRefed
   CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
diff --git a/gfx/layers/client/ClientLayerManager.cpp b/gfx/layers/client/ClientLayerManager.cpp
index 30a4af3c83..7903b109c7 100644
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -108,6 +108,7 @@ ClientLayerManager::ClientLayerManager(nsIWidget* aWidget)
   mMemoryPressureObserver = new MemoryPressureObserver(this);
 }
 
+
 ClientLayerManager::~ClientLayerManager()
 {
   if (mTransactionIdAllocator) {
@@ -128,6 +129,18 @@ ClientLayerManager::~ClientLayerManager()
   MOZ_COUNT_DTOR(ClientLayerManager);
 }
 
+void
+ClientLayerManager::Destroy()
+{
+  // It's important to call ClearCachedResource before Destroy because the
+  // former will early-return if the later has already run.
+  ClearCachedResources();
+  for (size_t i = 0; i < mTexturePools.Length(); i++) {
+    mTexturePools[i]->Destroy();
+  }
+  LayerManager::Destroy();
+}
+
 int32_t
 ClientLayerManager::GetMaxTextureSize() const
 {
@@ -707,6 +720,8 @@ ClientLayerManager::SetIsFirstPaint()
 TextureClientPool*
 ClientLayerManager::GetTexturePool(SurfaceFormat aFormat, TextureFlags aFlags)
 {
+  MOZ_DIAGNOSTIC_ASSERT(!mDestroyed);
+
   for (size_t i = 0; i < mTexturePools.Length(); i++) {
     if (mTexturePools[i]->GetFormat() == aFormat &&
         mTexturePools[i]->GetFlags() == aFlags) {
diff --git a/gfx/layers/client/ClientLayerManager.h b/gfx/layers/client/ClientLayerManager.h
index f4629a7793..a18f39f97a 100644
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -44,13 +44,7 @@ class ClientLayerManager final : public LayerManager
 public:
   explicit ClientLayerManager(nsIWidget* aWidget);
 
-  virtual void Destroy() override
-  {
-    // It's important to call ClearCachedResource before Destroy because the
-    // former will early-return if the later has already run.
-    ClearCachedResources();
-    LayerManager::Destroy();
-  }
+  virtual void Destroy() override;
 
 protected:
   virtual ~ClientLayerManager();
diff --git a/gfx/layers/client/ClientPaintedLayer.cpp b/gfx/layers/client/ClientPaintedLayer.cpp
index 4df9b6bf9b..93c6da7076 100644
--- a/gfx/layers/client/ClientPaintedLayer.cpp
+++ b/gfx/layers/client/ClientPaintedLayer.cpp
@@ -89,7 +89,8 @@ ClientPaintedLayer::PaintThebes()
     
     SetAntialiasingFlags(this, target);
 
-    RefPtr ctx = gfxContext::ContextForDrawTarget(target);
+    RefPtr ctx = gfxContext::ForDrawTargetWithTransform(target);
+    MOZ_ASSERT(ctx); // already checked the target above
 
     ClientManager()->GetPaintedLayerCallback()(this,
                                               ctx,
diff --git a/gfx/layers/client/SingleTiledContentClient.cpp b/gfx/layers/client/SingleTiledContentClient.cpp
index 4f22726aca..149a1cdc93 100644
--- a/gfx/layers/client/SingleTiledContentClient.cpp
+++ b/gfx/layers/client/SingleTiledContentClient.cpp
@@ -185,7 +185,11 @@ ClientSingleTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion,
   }
 
   {
-    RefPtr ctx = new gfxContext(dt);
+    RefPtr ctx = gfxContext::ForDrawTarget(dt);
+    if (!ctx) {
+      gfxDevCrash(LogReason::InvalidContext) << "SingleTiledContextClient context problem " << gfx::hexa(dt);
+      return;
+    }
     ctx->SetMatrix(ctx->CurrentMatrix().Translate(-mTilingOrigin.x, -mTilingOrigin.y));
 
     aCallback(mPaintedLayer, ctx, paintRegion, paintRegion, DrawRegionClip::DRAW, nsIntRegion(), aCallbackData);
diff --git a/gfx/layers/client/TextureClientPool.cpp b/gfx/layers/client/TextureClientPool.cpp
index 689b4e99d3..aec63e311b 100644
--- a/gfx/layers/client/TextureClientPool.cpp
+++ b/gfx/layers/client/TextureClientPool.cpp
@@ -194,9 +194,9 @@ TextureClientPool::ShrinkToMaximumSize()
     } else {
       if (!mTextureClients.size()) {
         // Getting here means we're over our desired number of TextureClients
-        // with none in the pool. This can happen for pathological cases, or
-        // it could mean that mMaxTextureClients needs adjusting for whatever
-        // device we're running on.
+        // with none in the pool. This can happen during shutdown, or for
+        // pathological cases, or it could mean that mMaxTextureClients needs
+        // adjusting for whatever device we're running on.
         TCP_LOG("TexturePool %p encountering pathological case!\n", this);
         break;
       }
@@ -269,5 +269,11 @@ TextureClientPool::Clear()
   }
 }
 
+void TextureClientPool::Destroy()
+{
+  Clear();
+  mMaxTextureClients = 0;
+}
+
 } // namespace layers
 } // namespace mozilla
diff --git a/gfx/layers/client/TextureClientPool.h b/gfx/layers/client/TextureClientPool.h
index d146ed6302..9c72030055 100644
--- a/gfx/layers/client/TextureClientPool.h
+++ b/gfx/layers/client/TextureClientPool.h
@@ -105,6 +105,11 @@ public:
   gfx::SurfaceFormat GetFormat() { return mFormat; }
   TextureFlags GetFlags() const { return mFlags; }
 
+  /**
+   * Clear the pool and put it in a state where it won't recycle any new texture.
+   */
+  void Destroy();
+
 private:
   // The minimum size of the pool (the number of tiles that will be kept after
   // shrinking).
diff --git a/gfx/layers/client/TiledContentClient.cpp b/gfx/layers/client/TiledContentClient.cpp
index 8dcda1f08c..5bd07ac5b7 100644
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -933,11 +933,12 @@ ClientMultiTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion,
             gfxPlatform::GetPlatform()->Optimal2DFormatForContent(
               GetContentType()));
 
-        if (!mSinglePaintDrawTarget) {
+        if (!mSinglePaintDrawTarget || !mSinglePaintDrawTarget->IsValid()) {
           return;
         }
 
-        ctxt = new gfxContext(mSinglePaintDrawTarget);
+        ctxt = gfxContext::ForDrawTarget(mSinglePaintDrawTarget);
+        MOZ_ASSERT(ctxt); // already checked draw target above
 
         mSinglePaintBufferOffset = nsIntPoint(bounds.x, bounds.y);
       }
@@ -1158,9 +1159,14 @@ void ClientMultiTiledLayerBuffer::Update(const nsIntRegion& newValidRegion,
       tileset.mTiles = &mMoz2DTiles[0];
       tileset.mTileCount = mMoz2DTiles.size();
       RefPtr drawTarget = gfx::Factory::CreateTiledDrawTarget(tileset);
+      if (!drawTarget || !drawTarget->IsValid()) {
+        gfxDevCrash(LogReason::InvalidContext) << "Invalid tiled draw target";
+        return;
+      }
       drawTarget->SetTransform(Matrix());
 
-      RefPtr ctx = new gfxContext(drawTarget);
+      RefPtr ctx = gfxContext::ForDrawTarget(drawTarget);
+      MOZ_ASSERT(ctx); // already checked the draw target above
       ctx->SetMatrix(
         ctx->CurrentMatrix().Scale(mResolution, mResolution).Translate(ThebesPoint(-mTilingOrigin)));
 
diff --git a/gfx/layers/d3d11/CompositorD3D11.cpp b/gfx/layers/d3d11/CompositorD3D11.cpp
index 5bf230cf98..c0d50b4a37 100644
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -158,8 +158,9 @@ private:
   bool mInitOkay;
 };
 
-CompositorD3D11::CompositorD3D11(nsIWidget* aWidget)
-  : mAttachments(nullptr)
+CompositorD3D11::CompositorD3D11(CompositorBridgeParent* aParent, nsIWidget* aWidget)
+  : Compositor(aParent)
+  , mAttachments(nullptr)
   , mWidget(aWidget)
   , mHwnd(nullptr)
   , mDisableSequenceForNextFrame(false)
@@ -203,9 +204,7 @@ CompositorD3D11::Initialize()
 
   HRESULT hr;
 
-  mDevice = gfxWindowsPlatform::GetPlatform()->GetD3D11Device();
-
-  if (!mDevice) {
+  if (!gfxWindowsPlatform::GetPlatform()->GetD3D11Device(&mDevice)) {
     return false;
   }
 
@@ -495,10 +494,6 @@ CompositorD3D11::CreateRenderTarget(const gfx::IntRect& aRect,
   CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, aRect.width, aRect.height, 1, 1,
                              D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET);
 
-  if (mDevice != gfxWindowsPlatform::GetPlatform()->GetD3D11Device()) {
-    gfxCriticalError() << "Out of sync D3D11 devices in CreateRenderTarget";
-  }
-
   RefPtr texture;
   HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
   if (FAILED(hr) || !texture) {
@@ -532,10 +527,6 @@ CompositorD3D11::CreateTexture(const gfx::IntRect& aRect,
                              aRect.width, aRect.height, 1, 1,
                              D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET);
 
-  if (mDevice != gfxWindowsPlatform::GetPlatform()->GetD3D11Device()) {
-    gfxCriticalError() << "Out of sync D3D11 devices in CreateRenderTargetFromSource";
-  }
-
   RefPtr texture;
   HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
 
@@ -1128,10 +1119,6 @@ CompositorD3D11::BeginFrame(const nsIntRegion& aInvalidRegion,
                             Rect* aClipRectOut,
                             Rect* aRenderBoundsOut)
 {
-  if (mDevice != gfxWindowsPlatform::GetPlatform()->GetD3D11Device()) {
-    gfxCriticalError() << "Out of sync D3D11 devices in BeginFrame";
-  }
-
   // Don't composite if we are minimised. Other than for the sake of efficency,
   // this is important because resizing our buffers when mimised will fail and
   // cause a crash when we're restored.
@@ -1398,10 +1385,6 @@ CompositorD3D11::VerifyBufferSize()
 bool
 CompositorD3D11::UpdateRenderTarget()
 {
-  if (mDevice != gfxWindowsPlatform::GetPlatform()->GetD3D11Device()) {
-    gfxCriticalError() << "Out of sync D3D11 devices in UpdateRenderTarget";
-  }
-
   EnsureSize();
   if (!VerifyBufferSize()) {
     gfxCriticalNote << "Failed VerifyBufferSize in UpdateRenderTarget " << mSize;
@@ -1456,10 +1439,6 @@ DeviceAttachmentsD3D11::InitSyncObject()
                              D3D11_BIND_RENDER_TARGET);
   desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
 
-  if (mDevice != gfxWindowsPlatform::GetPlatform()->GetD3D11Device()) {
-    gfxCriticalError() << "Out of sync D3D11 devices in InitSyncObject";
-  }
-
   RefPtr texture;
   HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
   if (Failed(hr, "create sync texture")) {
@@ -1602,10 +1581,6 @@ CompositorD3D11::PaintToTarget()
 
   RefPtr readTexture;
 
-  if (mDevice != gfxWindowsPlatform::GetPlatform()->GetD3D11Device()) {
-    gfxCriticalError() << "Out of sync D3D11 devices in PaintToTarget";
-  }
-
   hr = mDevice->CreateTexture2D(&softDesc, nullptr, getter_AddRefs(readTexture));
   if (FAILED(hr)) {
     gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Failed in PaintToTarget 2";
@@ -1655,7 +1630,8 @@ CompositorD3D11::HandleError(HRESULT hr, Severity aSeverity)
     MOZ_CRASH("GFX: Unrecoverable D3D11 error");
   }
 
-  if (mDevice != gfxWindowsPlatform::GetPlatform()->GetD3D11Device()) {
+  RefPtr device;
+  if (!gfxWindowsPlatform::GetPlatform()->GetD3D11Device(&device) || device != mDevice) {
     gfxCriticalError() << "Out of sync D3D11 devices in HandleError, " << (int)mVerifyBuffersFailed;
   }
 
diff --git a/gfx/layers/d3d11/CompositorD3D11.h b/gfx/layers/d3d11/CompositorD3D11.h
index 2158c0f7a7..1260b2f21e 100644
--- a/gfx/layers/d3d11/CompositorD3D11.h
+++ b/gfx/layers/d3d11/CompositorD3D11.h
@@ -42,7 +42,7 @@ struct DeviceAttachmentsD3D11;
 class CompositorD3D11 : public Compositor
 {
 public:
-  CompositorD3D11(nsIWidget* aWidget);
+  CompositorD3D11(CompositorBridgeParent* aParent, nsIWidget* aWidget);
   ~CompositorD3D11();
 
   virtual bool Initialize() override;
diff --git a/gfx/layers/d3d11/TextureD3D11.cpp b/gfx/layers/d3d11/TextureD3D11.cpp
index 21d95f07b1..049a081f37 100644
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -347,11 +347,10 @@ DXGITextureData*
 D3D11TextureData::Create(IntSize aSize, SurfaceFormat aFormat, TextureAllocationFlags aFlags,
                          ID3D11Device* aDevice)
 {
-  RefPtr texture11;
-  ID3D11Device* d3d11device = aDevice ? aDevice
-                            : gfxWindowsPlatform::GetPlatform()->GetD3D11DeviceForCurrentThread();
-  MOZ_ASSERT(d3d11device != nullptr);
-  if (!d3d11device) {
+  RefPtr device = aDevice;
+  if (!device &&
+      !gfxWindowsPlatform::GetPlatform()->GetD3D11DeviceForCurrentThread(&device))
+  {
     return nullptr;
   }
 
@@ -367,7 +366,8 @@ D3D11TextureData::Create(IntSize aSize, SurfaceFormat aFormat, TextureAllocation
     }
   }
 
-  HRESULT hr = d3d11device->CreateTexture2D(&newDesc, nullptr, getter_AddRefs(texture11));
+  RefPtr texture11;
+  HRESULT hr = device->CreateTexture2D(&newDesc, nullptr, getter_AddRefs(texture11));
   if (FAILED(hr)) {
     gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize)))
       << "[D3D11] 2 CreateTexture2D failure " << aSize << " Code: " << gfx::hexa(hr);
@@ -626,10 +626,16 @@ DXGITextureHostD3D11::OpenSharedHandle()
   return true;
 }
 
-ID3D11Device*
+RefPtr
 DXGITextureHostD3D11::GetDevice()
 {
-  return gfxWindowsPlatform::GetPlatform()->GetD3D11Device();
+  if (mFlags & TextureFlags::INVALID_COMPOSITOR) {
+    return nullptr;
+  }
+
+  RefPtr device;
+  gfxWindowsPlatform::GetPlatform()->GetD3D11Device(&device);
+  return device;
 }
 
 void
@@ -692,31 +698,32 @@ DXGIYCbCrTextureHostD3D11::DXGIYCbCrTextureHostD3D11(TextureFlags aFlags,
 bool
 DXGIYCbCrTextureHostD3D11::OpenSharedHandle()
 {
-  if (!GetDevice()) {
+  RefPtr device = GetDevice();
+  if (!device) {
     return false;
   }
 
   RefPtr textures[3];
 
-  HRESULT hr = GetDevice()->OpenSharedResource((HANDLE)mHandles[0],
-                                               __uuidof(ID3D11Texture2D),
-                                               (void**)(ID3D11Texture2D**)getter_AddRefs(textures[0]));
+  HRESULT hr = device->OpenSharedResource((HANDLE)mHandles[0],
+                                          __uuidof(ID3D11Texture2D),
+                                          (void**)(ID3D11Texture2D**)getter_AddRefs(textures[0]));
   if (FAILED(hr)) {
     NS_WARNING("Failed to open shared texture for Y Plane");
     return false;
   }
 
-  hr = GetDevice()->OpenSharedResource((HANDLE)mHandles[1],
-                                       __uuidof(ID3D11Texture2D),
-                                       (void**)(ID3D11Texture2D**)getter_AddRefs(textures[1]));
+  hr = device->OpenSharedResource((HANDLE)mHandles[1],
+                                  __uuidof(ID3D11Texture2D),
+                                  (void**)(ID3D11Texture2D**)getter_AddRefs(textures[1]));
   if (FAILED(hr)) {
     NS_WARNING("Failed to open shared texture for Cb Plane");
     return false;
   }
 
-  hr = GetDevice()->OpenSharedResource((HANDLE)mHandles[2],
-                                       __uuidof(ID3D11Texture2D),
-                                       (void**)(ID3D11Texture2D**)getter_AddRefs(textures[2]));
+  hr = device->OpenSharedResource((HANDLE)mHandles[2],
+                                  __uuidof(ID3D11Texture2D),
+                                  (void**)(ID3D11Texture2D**)getter_AddRefs(textures[2]));
   if (FAILED(hr)) {
     NS_WARNING("Failed to open shared texture for Cr Plane");
     return false;
@@ -729,10 +736,16 @@ DXGIYCbCrTextureHostD3D11::OpenSharedHandle()
   return true;
 }
 
-ID3D11Device*
+RefPtr
 DXGIYCbCrTextureHostD3D11::GetDevice()
 {
-  return gfxWindowsPlatform::GetPlatform()->GetD3D11Device();
+  if (mFlags & TextureFlags::INVALID_COMPOSITOR) {
+    return nullptr;
+  }
+
+  RefPtr device;
+  gfxWindowsPlatform::GetPlatform()->GetD3D11Device(&device);
+  return device;
 }
 
 void
diff --git a/gfx/layers/d3d11/TextureD3D11.h b/gfx/layers/d3d11/TextureD3D11.h
index 3d28217a75..a844a3b932 100644
--- a/gfx/layers/d3d11/TextureD3D11.h
+++ b/gfx/layers/d3d11/TextureD3D11.h
@@ -310,7 +310,7 @@ public:
   }
 
 protected:
-  ID3D11Device* GetDevice();
+  RefPtr GetDevice();
 
   bool OpenSharedHandle();
 
@@ -349,7 +349,7 @@ public:
   }
 
 protected:
-  ID3D11Device* GetDevice();
+  RefPtr GetDevice();
 
   bool OpenSharedHandle();
 
diff --git a/gfx/layers/d3d9/TextureD3D9.cpp b/gfx/layers/d3d9/TextureD3D9.cpp
index 975167913b..9948143559 100644
--- a/gfx/layers/d3d9/TextureD3D9.cpp
+++ b/gfx/layers/d3d9/TextureD3D9.cpp
@@ -901,6 +901,9 @@ TextureHostD3D9::UpdatedInternal(const nsIntRegion* aRegion)
 IDirect3DDevice9*
 TextureHostD3D9::GetDevice()
 {
+  if (mFlags & TextureFlags::INVALID_COMPOSITOR) {
+    return nullptr;
+  }
   return mCompositor ? mCompositor->device() : nullptr;
 }
 
@@ -961,6 +964,9 @@ DXGITextureHostD3D9::DXGITextureHostD3D9(TextureFlags aFlags,
 IDirect3DDevice9*
 DXGITextureHostD3D9::GetDevice()
 {
+  if (mFlags & TextureFlags::INVALID_COMPOSITOR) {
+    return nullptr;
+  }
   return mCompositor ? mCompositor->device() : nullptr;
 }
 
@@ -1051,6 +1057,9 @@ DXGIYCbCrTextureHostD3D9::DXGIYCbCrTextureHostD3D9(TextureFlags aFlags,
 IDirect3DDevice9*
 DXGIYCbCrTextureHostD3D9::GetDevice()
 {
+  if (mFlags & TextureFlags::INVALID_COMPOSITOR) {
+    return nullptr;
+  }
   return mCompositor ? mCompositor->device() : nullptr;
 }
 
diff --git a/gfx/layers/ipc/CompositableTransactionParent.cpp b/gfx/layers/ipc/CompositableTransactionParent.cpp
index ac31b1be2c..2236e27dbd 100644
--- a/gfx/layers/ipc/CompositableTransactionParent.cpp
+++ b/gfx/layers/ipc/CompositableTransactionParent.cpp
@@ -35,12 +35,6 @@ namespace layers {
 class ClientTiledLayerBuffer;
 class Compositor;
 
-template
-CompositableHost* AsCompositable(const Op& op)
-{
-  return CompositableHost::FromIPDLActor(op.compositableParent());
-}
-
 // This function can in some cases fail and return false without it being a bug.
 // This can theoretically happen if the ImageBridge sends frames before
 // we created the layer tree. Since we can't enforce that the layer
@@ -52,12 +46,11 @@ CompositableHost* AsCompositable(const Op& op)
 // big deal.
 // Note that Layers transactions do not need to call this because they always
 // schedule the composition, in LayerManagerComposite::EndTransaction.
-template
-bool ScheduleComposition(const T& op)
+static bool
+ScheduleComposition(CompositableHost* aCompositable)
 {
-  CompositableHost* comp = AsCompositable(op);
-  uint64_t id = comp->GetCompositorID();
-  if (!comp || !id) {
+  uint64_t id = aCompositable->GetCompositorID();
+  if (!id) {
     return false;
   }
   CompositorBridgeParent* cp = CompositorBridgeParent::GetCompositor(id);
@@ -81,12 +74,18 @@ bool
 CompositableParentManager::ReceiveCompositableUpdate(const CompositableOperation& aEdit,
                                                      EditReplyVector& replyv)
 {
-  switch (aEdit.type()) {
-    case CompositableOperation::TOpPaintTextureRegion: {
+  // Ignore all operations on compositables created on stale compositors. We
+  // return true because the child is unable to handle errors.
+  CompositableHost* compositable = CompositableHost::FromIPDLActor(aEdit.compositableParent());
+  if (compositable->GetCompositor() && compositable->GetCompositor()->IsValid()) {
+    return true;
+  }
+
+  switch (aEdit.detail().type()) {
+    case CompositableOperationDetail::TOpPaintTextureRegion: {
       MOZ_LAYERS_LOG(("[ParentSide] Paint PaintedLayer"));
 
-      const OpPaintTextureRegion& op = aEdit.get_OpPaintTextureRegion();
-      CompositableHost* compositable = AsCompositable(op);
+      const OpPaintTextureRegion& op = aEdit.detail().get_OpPaintTextureRegion();
       Layer* layer = compositable->GetLayer();
       if (!layer || layer->GetType() != Layer::TYPE_PAINTED) {
         return false;
@@ -106,28 +105,28 @@ CompositableParentManager::ReceiveCompositableUpdate(const CompositableOperation
         return false;
       }
       replyv.push_back(
-        OpContentBufferSwap(op.compositableParent(), nullptr, frontUpdatedRegion));
+        OpContentBufferSwap(aEdit.compositableParent(), nullptr, frontUpdatedRegion));
 
       RenderTraceInvalidateEnd(thebes, "FF00FF");
       break;
     }
-    case CompositableOperation::TOpUseTiledLayerBuffer: {
+    case CompositableOperationDetail::TOpUseTiledLayerBuffer: {
       MOZ_LAYERS_LOG(("[ParentSide] Paint TiledLayerBuffer"));
-      const OpUseTiledLayerBuffer& op = aEdit.get_OpUseTiledLayerBuffer();
-      TiledContentHost* compositable = AsCompositable(op)->AsTiledContentHost();
+      const OpUseTiledLayerBuffer& op = aEdit.detail().get_OpUseTiledLayerBuffer();
+      TiledContentHost* tiledHost = compositable->AsTiledContentHost();
 
-      NS_ASSERTION(compositable, "The compositable is not tiled");
+      NS_ASSERTION(tiledHost, "The compositable is not tiled");
 
       const SurfaceDescriptorTiles& tileDesc = op.tileLayerDescriptor();
-      bool success = compositable->UseTiledLayerBuffer(this, tileDesc);
+      bool success = tiledHost->UseTiledLayerBuffer(this, tileDesc);
       if (!success) {
         return false;
       }
       break;
     }
-    case CompositableOperation::TOpRemoveTexture: {
-      const OpRemoveTexture& op = aEdit.get_OpRemoveTexture();
-      CompositableHost* compositable = AsCompositable(op);
+    case CompositableOperationDetail::TOpRemoveTexture: {
+      const OpRemoveTexture& op = aEdit.detail().get_OpRemoveTexture();
+
       RefPtr tex = TextureHost::AsTextureHost(op.textureParent());
 
       MOZ_ASSERT(tex.get());
@@ -136,9 +135,8 @@ CompositableParentManager::ReceiveCompositableUpdate(const CompositableOperation
       SendFenceHandleIfPresent(op.textureParent());
       break;
     }
-    case CompositableOperation::TOpRemoveTextureAsync: {
-      const OpRemoveTextureAsync& op = aEdit.get_OpRemoveTextureAsync();
-      CompositableHost* compositable = AsCompositable(op);
+    case CompositableOperationDetail::TOpRemoveTextureAsync: {
+      const OpRemoveTextureAsync& op = aEdit.detail().get_OpRemoveTextureAsync();
       RefPtr tex = TextureHost::AsTextureHost(op.textureParent());
 
       MOZ_ASSERT(tex.get());
@@ -167,9 +165,8 @@ CompositableParentManager::ReceiveCompositableUpdate(const CompositableOperation
       }
       break;
     }
-    case CompositableOperation::TOpUseTexture: {
-      const OpUseTexture& op = aEdit.get_OpUseTexture();
-      CompositableHost* compositable = AsCompositable(op);
+    case CompositableOperationDetail::TOpUseTexture: {
+      const OpUseTexture& op = aEdit.detail().get_OpUseTexture();
 
       AutoTArray textures;
       for (auto& timedTexture : op.textures()) {
@@ -192,16 +189,17 @@ CompositableParentManager::ReceiveCompositableUpdate(const CompositableOperation
           }
         }
       }
-      compositable->UseTextureHost(textures);
+      if (textures.Length() > 0) {
+        compositable->UseTextureHost(textures);
+      }
 
       if (UsesImageBridge() && compositable->GetLayer()) {
-        ScheduleComposition(op);
+        ScheduleComposition(compositable);
       }
       break;
     }
-    case CompositableOperation::TOpUseComponentAlphaTextures: {
-      const OpUseComponentAlphaTextures& op = aEdit.get_OpUseComponentAlphaTextures();
-      CompositableHost* compositable = AsCompositable(op);
+    case CompositableOperationDetail::TOpUseComponentAlphaTextures: {
+      const OpUseComponentAlphaTextures& op = aEdit.detail().get_OpUseComponentAlphaTextures();
       RefPtr texOnBlack = TextureHost::AsTextureHost(op.textureOnBlackParent());
       RefPtr texOnWhite = TextureHost::AsTextureHost(op.textureOnWhiteParent());
 
@@ -209,14 +207,13 @@ CompositableParentManager::ReceiveCompositableUpdate(const CompositableOperation
       compositable->UseComponentAlphaTextures(texOnBlack, texOnWhite);
 
       if (UsesImageBridge()) {
-        ScheduleComposition(op);
+        ScheduleComposition(compositable);
       }
       break;
     }
 #ifdef MOZ_WIDGET_GONK
-    case CompositableOperation::TOpUseOverlaySource: {
-      const OpUseOverlaySource& op = aEdit.get_OpUseOverlaySource();
-      CompositableHost* compositable = AsCompositable(op);
+    case CompositableOperationDetail::TOpUseOverlaySource: {
+      const OpUseOverlaySource& op = aEdit.detail().get_OpUseOverlaySource();
       if (!ValidatePictureRect(op.overlay().size(), op.picture())) {
         return false;
       }
diff --git a/gfx/layers/ipc/CompositorBridgeChild.cpp b/gfx/layers/ipc/CompositorBridgeChild.cpp
index 287ccc1234..abc93eed65 100644
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -234,7 +234,7 @@ CompositorBridgeChild::RecvInvalidateLayers(const uint64_t& aLayersId)
 
 bool
 CompositorBridgeChild::RecvCompositorUpdated(const uint64_t& aLayersId,
-                                      const TextureFactoryIdentifier& aNewIdentifier)
+                                             const TextureFactoryIdentifier& aNewIdentifier)
 {
   if (mLayerManager) {
     // This case is handled directly by nsBaseWidget.
@@ -243,6 +243,7 @@ CompositorBridgeChild::RecvCompositorUpdated(const uint64_t& aLayersId,
     if (dom::TabChild* child = dom::TabChild::GetFrom(aLayersId)) {
       child->CompositorUpdated(aNewIdentifier);
     }
+    SendAcknowledgeCompositorUpdate(aLayersId);
   }
   return true;
 }
diff --git a/gfx/layers/ipc/CompositorBridgeParent.cpp b/gfx/layers/ipc/CompositorBridgeParent.cpp
index 69293cf124..4f03019ee9 100644
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -67,6 +67,7 @@
 #endif
 #include "GeckoProfiler.h"
 #include "mozilla/ipc/ProtocolTypes.h"
+#include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/unused.h"
 #include "mozilla/Hal.h"
 #include "mozilla/HalTypes.h"
@@ -109,6 +110,7 @@ CompositorBridgeParent::LayerTreeState::LayerTreeState()
   , mCrossProcessParent(nullptr)
   , mLayerTree(nullptr)
   , mUpdatedPluginDataAvailable(false)
+  , mPendingCompositorUpdates(0)
 {
 }
 
@@ -1610,22 +1612,23 @@ CompositorBridgeParent::NewCompositor(const nsTArray& aBackendHin
   for (size_t i = 0; i < aBackendHints.Length(); ++i) {
     RefPtr compositor;
     if (aBackendHints[i] == LayersBackend::LAYERS_OPENGL) {
-      compositor = new CompositorOGL(mWidget,
+      compositor = new CompositorOGL(this,
+                                     mWidget,
                                      mEGLSurfaceSize.width,
                                      mEGLSurfaceSize.height,
                                      mUseExternalSurfaceSize);
     } else if (aBackendHints[i] == LayersBackend::LAYERS_BASIC) {
 #ifdef MOZ_WIDGET_GTK
       if (gfxPlatformGtk::GetPlatform()->UseXRender()) {
-        compositor = new X11BasicCompositor(mWidget);
+        compositor = new X11BasicCompositor(this, mWidget);
       } else
 #endif
       {
-        compositor = new BasicCompositor(mWidget);
+        compositor = new BasicCompositor(this, mWidget);
       }
 #ifdef XP_WIN
     } else if (aBackendHints[i] == LayersBackend::LAYERS_D3D11) {
-      compositor = new CompositorD3D11(mWidget);
+      compositor = new CompositorD3D11(this, mWidget);
     } else if (aBackendHints[i] == LayersBackend::LAYERS_D3D9) {
       compositor = new CompositorD3D9(this, mWidget);
 #endif
@@ -1932,6 +1935,7 @@ class CrossProcessCompositorBridgeParent final : public PCompositorBridgeParent,
 public:
   explicit CrossProcessCompositorBridgeParent(Transport* aTransport)
     : mTransport(aTransport)
+    , mSubprocess(nullptr)
     , mNotifyAfterRemotePaint(false)
     , mDestroyCalled(false)
   {
@@ -2047,6 +2051,7 @@ public:
 
   virtual AsyncCompositionManager* GetCompositionManager(LayerTransactionParent* aParent) override;
   virtual bool RecvRemotePluginsReady()  override { return false; }
+  virtual bool RecvAcknowledgeCompositorUpdate(const uint64_t& aLayersId) override;
 
   void DidComposite(uint64_t aId,
                     TimeStamp& aCompositeStart,
@@ -2065,6 +2070,7 @@ private:
   // ourself.  This is released (deferred) in ActorDestroy().
   RefPtr mSelfRef;
   Transport* mTransport;
+  ipc::GeckoChildProcessHost* mSubprocess;
 
   RefPtr mCompositorThreadHolder;
   // If true, we should send a RemotePaintIsReady message when the layer transaction
@@ -2175,6 +2181,11 @@ CompositorBridgeParent::ResetCompositorTask(const nsTArray& aBack
   ForEachIndirectLayerTree([&] (LayerTreeState* lts, uint64_t layersId) -> void {
     if (CrossProcessCompositorBridgeParent* cpcp = lts->mCrossProcessParent) {
       Unused << cpcp->SendCompositorUpdated(layersId, newIdentifier.value());
+
+      if (LayerTransactionParent* ltp = lts->mLayerTree) {
+        ltp->AddPendingCompositorUpdate();
+      }
+      lts->mPendingCompositorUpdates++;
     }
   });
 }
@@ -2199,6 +2210,9 @@ CompositorBridgeParent::ResetCompositorImpl(const nsTArray& aBack
     return Nothing();
   }
 
+  if (mCompositor) {
+    mCompositor->SetInvalid();
+  }
   mCompositor = compositor;
   mLayerManager->ChangeCompositor(compositor);
 
@@ -2215,13 +2229,18 @@ OpenCompositor(CrossProcessCompositorBridgeParent* aCompositor,
 }
 
 /*static*/ PCompositorBridgeParent*
-CompositorBridgeParent::Create(Transport* aTransport, ProcessId aOtherPid)
+CompositorBridgeParent::Create(Transport* aTransport, ProcessId aOtherPid, GeckoChildProcessHost* aProcessHost)
 {
   gfxPlatform::InitLayersIPC();
 
   RefPtr cpcp =
     new CrossProcessCompositorBridgeParent(aTransport);
 
+  if (aProcessHost) {
+    cpcp->mSubprocess = aProcessHost;
+    aProcessHost->AssociateActor();
+  }
+
   cpcp->mSelfRef = cpcp;
   CompositorLoop()->PostTask(
     FROM_HERE,
@@ -2279,6 +2298,12 @@ CrossProcessCompositorBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   RefPtr lru = CompositorLRU::GetSingleton();
   lru->Remove(this);
+
+  if (mSubprocess) {
+    mSubprocess->DissociateActor();
+    mSubprocess = nullptr;
+  }
+
   // We must keep this object alive untill the code handling message
   // reception is finished on this thread.
   MessageLoop::current()->PostTask(FROM_HERE,
@@ -2310,6 +2335,7 @@ CrossProcessCompositorBridgeParent::AllocPLayerTransactionParent(
     LayerTransactionParent* p = new LayerTransactionParent(lm, this, aId);
     p->AddIPDLReference();
     sIndirectLayerTrees[aId].mLayerTree = p;
+    p->SetPendingCompositorUpdates(state->mPendingCompositorUpdates);
     return p;
   }
 
@@ -2709,6 +2735,20 @@ CrossProcessCompositorBridgeParent::GetCompositionManager(LayerTransactionParent
   return state->mParent->GetCompositionManager(aLayerTree);
 }
 
+bool
+CrossProcessCompositorBridgeParent::RecvAcknowledgeCompositorUpdate(const uint64_t& aLayersId)
+{
+  MonitorAutoLock lock(*sIndirectLayerTreesLock);
+  CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[aLayersId];
+
+  if (LayerTransactionParent* ltp = state.mLayerTree) {
+    ltp->AcknowledgeCompositorUpdate();
+  }
+  MOZ_ASSERT(state.mPendingCompositorUpdates > 0);
+  state.mPendingCompositorUpdates--;
+  return true;
+}
+
 void
 CrossProcessCompositorBridgeParent::DeferredDestroy()
 {
@@ -2735,7 +2775,7 @@ CrossProcessCompositorBridgeParent::CloneToplevel(
       Transport* transport = OpenDescriptor(aFds[i].fd(),
                                             Transport::MODE_SERVER);
       PCompositorBridgeParent* compositor =
-        CompositorBridgeParent::Create(transport, base::GetProcId(aPeerProcess));
+        CompositorBridgeParent::Create(transport, base::GetProcId(aPeerProcess), mSubprocess);
       compositor->CloneManagees(this, aCtx);
       compositor->IToplevelProtocol::SetTransport(transport);
       // The reference to the compositor thread is held in OnChannelConnected().
diff --git a/gfx/layers/ipc/CompositorBridgeParent.h b/gfx/layers/ipc/CompositorBridgeParent.h
index 86fb89b6b4..f26fc77061 100644
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -48,6 +48,10 @@ namespace gfx {
 class DrawTarget;
 } // namespace gfx
 
+namespace ipc {
+class GeckoChildProcessHost;
+} // namespace ipc
+
 namespace layers {
 
 class APZCTreeManager;
@@ -240,6 +244,11 @@ public:
   virtual bool RecvFlushRendering() override;
   virtual bool RecvForcePresent() override;
 
+  virtual bool RecvAcknowledgeCompositorUpdate(const uint64_t& aLayersId) override {
+    MOZ_ASSERT_UNREACHABLE("This message is only sent cross-process");
+    return true;
+  }
+
   virtual bool RecvGetTileSize(int32_t* aWidth, int32_t* aHeight) override;
 
   virtual bool RecvNotifyRegionInvalidated(const nsIntRegion& aRegion) override;
@@ -408,7 +417,7 @@ public:
    * directly to us.  Transport is to its thread context.
    */
   static PCompositorBridgeParent*
-  Create(Transport* aTransport, ProcessId aOtherProcess);
+  Create(Transport* aTransport, ProcessId aOtherProcess, mozilla::ipc::GeckoChildProcessHost* aProcessHost);
 
   struct LayerTreeState {
     LayerTreeState();
@@ -429,6 +438,10 @@ public:
     RefPtr mLayerTreeReadyObserver;
     RefPtr mLayerTreeClearedObserver;
 
+    // Number of times the compositor has been reset without having been
+    // acknowledged by the child.
+    uint32_t mPendingCompositorUpdates;
+
     PCompositorBridgeParent* CrossProcessPCompositorBridge() const;
   };
 
diff --git a/gfx/layers/ipc/ImageBridgeChild.cpp b/gfx/layers/ipc/ImageBridgeChild.cpp
index 883e6975c3..22a6f2cb4e 100644
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -159,8 +159,8 @@ ImageBridgeChild::UseTextures(CompositableClient* aCompositable,
                                         t.mTimeStamp, t.mPictureRect,
                                         t.mFrameID, t.mProducerID, t.mInputFrameID));
   }
-  mTxn->AddNoSwapEdit(OpUseTexture(nullptr, aCompositable->GetIPDLActor(),
-                                   textures));
+  mTxn->AddNoSwapEdit(CompositableOperation(nullptr, aCompositable->GetIPDLActor(),
+                                            OpUseTexture(textures)));
 }
 
 void
@@ -175,9 +175,13 @@ ImageBridgeChild::UseComponentAlphaTextures(CompositableClient* aCompositable,
   MOZ_ASSERT(aTextureOnWhite->GetIPDLActor());
   MOZ_ASSERT(aTextureOnBlack->GetIPDLActor());
   MOZ_ASSERT(aTextureOnBlack->GetSize() == aTextureOnWhite->GetSize());
-  mTxn->AddNoSwapEdit(OpUseComponentAlphaTextures(nullptr, aCompositable->GetIPDLActor(),
-                                                  nullptr, aTextureOnBlack->GetIPDLActor(),
-                                                  nullptr, aTextureOnWhite->GetIPDLActor()));
+  mTxn->AddNoSwapEdit(
+    CompositableOperation(
+      nullptr,
+      aCompositable->GetIPDLActor(),
+      OpUseComponentAlphaTextures(
+        nullptr ,aTextureOnBlack->GetIPDLActor(),
+        nullptr, aTextureOnWhite->GetIPDLActor())));
 }
 
 #ifdef MOZ_WIDGET_GONK
@@ -1154,12 +1158,15 @@ ImageBridgeChild::RemoveTextureFromCompositable(CompositableClient* aCompositabl
   if (!aTexture || !aTexture->IsSharedWithCompositor() || !aCompositable->IsConnected()) {
     return;
   }
+
+  CompositableOperation op(
+    nullptr, aCompositable->GetIPDLActor(),
+    OpRemoveTexture(nullptr, aTexture->GetIPDLActor()));
+
   if (aTexture->GetFlags() & TextureFlags::DEALLOCATE_CLIENT) {
-    mTxn->AddEdit(OpRemoveTexture(nullptr, aCompositable->GetIPDLActor(),
-                                  nullptr, aTexture->GetIPDLActor()));
+    mTxn->AddEdit(op);
   } else {
-    mTxn->AddNoSwapEdit(OpRemoveTexture(nullptr, aCompositable->GetIPDLActor(),
-                                        nullptr, aTexture->GetIPDLActor()));
+    mTxn->AddNoSwapEdit(op);
   }
 }
 
@@ -1175,10 +1182,16 @@ ImageBridgeChild::RemoveTextureFromCompositableAsync(AsyncTransactionTracker* aA
   if (!aTexture || !aTexture->IsSharedWithCompositor() || !aCompositable->IsConnected()) {
     return;
   }
-  mTxn->AddNoSwapEdit(OpRemoveTextureAsync(CompositableClient::GetTrackersHolderId(aCompositable->GetIPDLActor()),
-                                           aAsyncTransactionTracker->GetId(),
-                                           nullptr, aCompositable->GetIPDLActor(),
-                                           nullptr, aTexture->GetIPDLActor()));
+
+  CompositableOperation op(
+    nullptr, aCompositable->GetIPDLActor(),
+    OpRemoveTextureAsync(
+      CompositableClient::GetTrackersHolderId(aCompositable->GetIPDLActor()),
+      aAsyncTransactionTracker->GetId(),
+      nullptr, aCompositable->GetIPDLActor(),
+      nullptr, aTexture->GetIPDLActor()));
+
+  mTxn->AddNoSwapEdit(op);
   // Hold AsyncTransactionTracker until receving reply
   CompositableClient::HoldUntilComplete(aCompositable->GetIPDLActor(),
                                         aAsyncTransactionTracker);
diff --git a/gfx/layers/ipc/ImageBridgeParent.cpp b/gfx/layers/ipc/ImageBridgeParent.cpp
index b4643bae21..4aade51385 100644
--- a/gfx/layers/ipc/ImageBridgeParent.cpp
+++ b/gfx/layers/ipc/ImageBridgeParent.cpp
@@ -17,6 +17,7 @@
 #include "mozilla/ipc/MessageChannel.h" // for MessageChannel, etc
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/ipc/Transport.h"      // for Transport
+#include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/media/MediaSystemResourceManagerParent.h" // for MediaSystemResourceManagerParent
 #include "mozilla/layers/CompositableTransactionParent.h"
 #include "mozilla/layers/CompositorBridgeParent.h"  // for CompositorBridgeParent
@@ -59,6 +60,7 @@ ImageBridgeParent::ImageBridgeParent(MessageLoop* aLoop,
   , mTransport(aTransport)
   , mSetChildThreadPriority(false)
   , mClosed(false)
+  , mSubprocess(nullptr)
 {
   MOZ_ASSERT(NS_IsMainThread());
   sMainLoop = MessageLoop::current();
@@ -100,6 +102,11 @@ ImageBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
   // Can't alloc/dealloc shmems from now on.
   mClosed = true;
 
+  if (mSubprocess) {
+    mSubprocess->DissociateActor();
+    mSubprocess = nullptr;
+  }
+
   MessageLoop::current()->PostTask(
     FROM_HERE,
     NewRunnableMethod(this, &ImageBridgeParent::DeferredDestroy));
@@ -194,10 +201,16 @@ ConnectImageBridgeInParentProcess(ImageBridgeParent* aBridge,
 }
 
 /*static*/ PImageBridgeParent*
-ImageBridgeParent::Create(Transport* aTransport, ProcessId aChildProcessId)
+ImageBridgeParent::Create(Transport* aTransport, ProcessId aChildProcessId, GeckoChildProcessHost* aProcessHost)
 {
   MessageLoop* loop = CompositorBridgeParent::CompositorLoop();
   RefPtr bridge = new ImageBridgeParent(loop, aTransport, aChildProcessId);
+
+  if (aProcessHost) {
+    bridge->mSubprocess = aProcessHost;
+    aProcessHost->AssociateActor();
+  }
+
   loop->PostTask(FROM_HERE,
                  NewRunnableFunction(ConnectImageBridgeInParentProcess,
                                      bridge.get(), aTransport, aChildProcessId));
@@ -359,7 +372,7 @@ ImageBridgeParent::CloneToplevel(const InfallibleTArray& aFds
     if (aFds[i].protocolId() == unsigned(GetProtocolId())) {
       Transport* transport = OpenDescriptor(aFds[i].fd(),
                                             Transport::MODE_SERVER);
-      PImageBridgeParent* bridge = Create(transport, base::GetProcId(aPeerProcess));
+      PImageBridgeParent* bridge = Create(transport, base::GetProcId(aPeerProcess), mSubprocess);
       bridge->CloneManagees(this, aCtx);
       bridge->IToplevelProtocol::SetTransport(transport);
       // The reference to the compositor thread is held in OnChannelConnected().
diff --git a/gfx/layers/ipc/ImageBridgeParent.h b/gfx/layers/ipc/ImageBridgeParent.h
index da1968f394..b0f3c286a5 100644
--- a/gfx/layers/ipc/ImageBridgeParent.h
+++ b/gfx/layers/ipc/ImageBridgeParent.h
@@ -29,6 +29,7 @@ class Thread;
 namespace mozilla {
 namespace ipc {
 class Shmem;
+class GeckoChildProcessHost;
 } // namespace ipc
 
 namespace layers {
@@ -56,7 +57,7 @@ public:
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   static PImageBridgeParent*
-  Create(Transport* aTransport, ProcessId aChildProcessId);
+  Create(Transport* aTransport, ProcessId aChildProcessId, ipc::GeckoChildProcessHost* aProcessHost);
 
   // CompositableParentManager
   virtual void SendFenceHandleIfPresent(PTextureParent* aTexture) override;
@@ -156,6 +157,8 @@ private:
   bool mSetChildThreadPriority;
   bool mClosed;
 
+  ipc::GeckoChildProcessHost* mSubprocess;
+
   /**
    * Map of all living ImageBridgeParent instances
    */
diff --git a/gfx/layers/ipc/LayerTransactionChild.cpp b/gfx/layers/ipc/LayerTransactionChild.cpp
index 9f288830b8..b044fb9b9b 100644
--- a/gfx/layers/ipc/LayerTransactionChild.cpp
+++ b/gfx/layers/ipc/LayerTransactionChild.cpp
@@ -6,6 +6,7 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "LayerTransactionChild.h"
+#include "mozilla/gfx/Logging.h"
 #include "mozilla/layers/CompositableClient.h"  // for CompositableChild
 #include "mozilla/layers/PCompositableChild.h"  // for PCompositableChild
 #include "mozilla/layers/PLayerChild.h"  // for PLayerChild
@@ -39,7 +40,11 @@ LayerTransactionChild::Destroy()
   const ManagedContainer& textures = ManagedPTextureChild();
   for (auto iter = textures.ConstIter(); !iter.Done(); iter.Next()) {
     TextureClient* texture = TextureClient::AsTextureClient(iter.Get()->GetKey());
+
     if (texture) {
+      // TODO: cf bug 1242448.
+      //gfxDevCrash(gfx::LogReason::TextureAliveAfterShutdown)
+      //  << "A texture is held alive after shutdown (PCompositorBridge)";
       texture->Destroy();
     }
   }
diff --git a/gfx/layers/ipc/LayerTransactionParent.cpp b/gfx/layers/ipc/LayerTransactionParent.cpp
index 9ba742dcc0..a448a8d4d3 100644
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -149,6 +149,7 @@ LayerTransactionParent::LayerTransactionParent(LayerManagerComposite* aManager,
   , mShadowLayersManager(aLayersManager)
   , mId(aId)
   , mPendingTransaction(0)
+  , mPendingCompositorUpdates(0)
   , mDestroyed(false)
   , mIPCOpen(false)
 {
@@ -178,6 +179,8 @@ LayerTransactionParent::Destroy()
   }
   InfallibleTArray textures;
   ManagedPTextureParent(textures);
+  // We expect all textures to be destroyed by now.
+  MOZ_DIAGNOSTIC_ASSERT(textures.Length() == 0);
   for (unsigned int i = 0; i < textures.Length(); ++i) {
     RefPtr tex = TextureHost::AsTextureHost(textures[i]);
     tex->DeallocateDeviceData();
@@ -358,6 +361,9 @@ LayerTransactionParent::RecvUpdate(InfallibleTArray&& cset,
       layer->SetScrollbarData(common.scrollbarTargetContainerId(),
         static_cast(common.scrollbarDirection()),
         common.scrollbarThumbRatio());
+      if (common.isScrollbarContainer()) {
+        layer->SetIsScrollbarContainer();
+      }
       layer->SetMixBlendMode((gfx::CompositionOp)common.mixBlendMode());
       layer->SetForceIsolatedGroup(common.forceIsolatedGroup());
       if (PLayerParent* maskLayer = common.maskLayerParent()) {
@@ -586,6 +592,11 @@ LayerTransactionParent::RecvUpdate(InfallibleTArray&& cset,
     case Edit::TOpAttachCompositable: {
       const OpAttachCompositable& op = edit.get_OpAttachCompositable();
       CompositableHost* host = CompositableHost::FromIPDLActor(op.compositableParent());
+      if (mPendingCompositorUpdates) {
+        // Do not attach compositables from old layer trees. Return true since
+        // content cannot handle errors.
+        return true;
+      }
       if (!Attach(cast(op.layerParent()), host, false)) {
         return false;
       }
@@ -599,6 +610,11 @@ LayerTransactionParent::RecvUpdate(InfallibleTArray&& cset,
         NS_ERROR("CompositableParent not found in the map");
         return false;
       }
+      if (mPendingCompositorUpdates) {
+        // Do not attach compositables from old layer trees. Return true since
+        // content cannot handle errors.
+        return true;
+      }
       CompositableHost* host = CompositableHost::FromIPDLActor(compositableParent);
       if (!Attach(cast(op.layerParent()), host, true)) {
         return false;
@@ -949,8 +965,20 @@ LayerTransactionParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData
                                             const LayersBackend& aLayersBackend,
                                             const TextureFlags& aFlags)
 {
-  MOZ_ASSERT(aLayersBackend == mLayerManager->GetCompositor()->GetBackendType());
-  return TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags);
+  TextureFlags flags = aFlags;
+
+  if (mPendingCompositorUpdates) {
+    // The compositor was recreated, and we're receiving layers updates for a
+    // a layer manager that will soon be discarded or invalidated. We can't
+    // return null because this will mess up deserialization later and we'll
+    // kill the content process. Instead, we signal that the underlying
+    // TextureHost should not attempt to access the compositor.
+    flags |= TextureFlags::INVALID_COMPOSITOR;
+  } else if (aLayersBackend != mLayerManager->GetCompositor()->GetBackendType()) {
+    gfxDevCrash(gfx::LogReason::PAllocTextureBackendMismatch) << "Texture backend is wrong";
+  }
+
+  return TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, flags);
 }
 
 bool
@@ -968,8 +996,16 @@ LayerTransactionParent::RecvChildAsyncMessages(InfallibleTArray tex = TextureHost::AsTextureHost(op.textureParent());
 
diff --git a/gfx/layers/ipc/LayerTransactionParent.h b/gfx/layers/ipc/LayerTransactionParent.h
index 24b504980b..353bf77743 100644
--- a/gfx/layers/ipc/LayerTransactionParent.h
+++ b/gfx/layers/ipc/LayerTransactionParent.h
@@ -91,6 +91,19 @@ public:
 
   virtual void ReplyRemoveTexture(const OpReplyRemoveTexture& aReply) override;
 
+  void AddPendingCompositorUpdate() {
+    mPendingCompositorUpdates++;
+  }
+  void SetPendingCompositorUpdates(uint32_t aCount) {
+    // Only called after construction.
+    MOZ_ASSERT(mPendingCompositorUpdates == 0);
+    mPendingCompositorUpdates = aCount;
+  }
+  void AcknowledgeCompositorUpdate() {
+    MOZ_ASSERT(mPendingCompositorUpdates > 0);
+    mPendingCompositorUpdates--;
+  }
+
 protected:
   virtual bool RecvSyncWithCompositor() override { return true; }
 
@@ -187,6 +200,11 @@ private:
   uint64_t mId;
 
   uint64_t mPendingTransaction;
+
+  // Number of compositor updates we're waiting for the child to
+  // acknowledge.
+  uint32_t mPendingCompositorUpdates;
+
   // When the widget/frame/browser stuff in this process begins its
   // destruction process, we need to Disconnect() all the currently
   // live shadow layers, because some of them might be orphaned from
diff --git a/gfx/layers/ipc/LayersMessages.ipdlh b/gfx/layers/ipc/LayersMessages.ipdlh
index 424562af91..f8027e2ab6 100644
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -230,6 +230,7 @@ struct CommonLayerAttributes {
   uint64_t scrollbarTargetContainerId;
   uint32_t scrollbarDirection;
   float scrollbarThumbRatio;
+  bool isScrollbarContainer;
   int8_t mixBlendMode;
   bool forceIsolatedGroup;
   nullable PLayer maskLayer;
@@ -350,18 +351,15 @@ struct SurfaceDescriptorTiles {
 };
 
 struct OpUseTiledLayerBuffer {
-  PCompositable compositable;
   SurfaceDescriptorTiles tileLayerDescriptor;
 };
 
 struct OpUseOverlaySource {
-  PCompositable compositable;
   OverlaySource overlay;
   IntRect picture;
 };
 
 struct OpPaintTextureRegion {
-  PCompositable compositable;
   ThebesBufferData bufferData;
   nsIntRegion updatedRegion;
 };
@@ -370,7 +368,6 @@ struct OpPaintTextureRegion {
  * Tells the CompositableHost to remove the corresponding TextureHost
  */
 struct OpRemoveTexture {
-  PCompositable compositable;
   PTexture texture;
 };
 
@@ -412,12 +409,10 @@ struct TimedTexture {
  * The list must not be empty.
  */
 struct OpUseTexture {
-  PCompositable compositable;
   TimedTexture[] textures;
 };
 
 struct OpUseComponentAlphaTextures {
-  PCompositable compositable;
   PTexture textureOnBlack;
   PTexture textureOnWhite;
 };
@@ -438,7 +433,7 @@ struct OpDeliverFenceToTracker {
   FenceHandle fence;
 };
 
-union CompositableOperation {
+union CompositableOperationDetail {
   OpPaintTextureRegion;
 
   OpUseTiledLayerBuffer;
@@ -451,6 +446,11 @@ union CompositableOperation {
   OpUseOverlaySource;
 };
 
+struct CompositableOperation {
+  PCompositable compositable;
+  CompositableOperationDetail detail;
+};
+
 // A unit of a changeset; a set of these comprise a changeset
 union Edit {
   OpCreatePaintedLayer;
@@ -516,7 +516,7 @@ union AsyncParentMessageData {
 };
 
 union AsyncChildMessageData {
-  OpRemoveTextureAsync;
+  CompositableOperation;
 };
 
 } // namespace
diff --git a/gfx/layers/ipc/PCompositorBridge.ipdl b/gfx/layers/ipc/PCompositorBridge.ipdl
index c6dac5d9b2..fc58aacec6 100644
--- a/gfx/layers/ipc/PCompositorBridge.ipdl
+++ b/gfx/layers/ipc/PCompositorBridge.ipdl
@@ -100,6 +100,10 @@ parent:
    */
   async RemotePluginsReady();
 
+  // Confirmation that the child has invalidated all its layers, and will not
+  // request layers against an old compositor.
+  async AcknowledgeCompositorUpdate(uint64_t id);
+
   // Child sends the parent a request for fill ratio numbers.
   async RequestOverfill();
 
diff --git a/gfx/layers/ipc/ShadowLayers.cpp b/gfx/layers/ipc/ShadowLayers.cpp
index b2d4546071..c81284d8bd 100644
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -546,8 +546,8 @@ ShadowLayerForwarder::UseTiledLayerBuffer(CompositableClient* aCompositable,
 {
   MOZ_ASSERT(aCompositable && aCompositable->IsConnected());
 
-  mTxn->AddNoSwapPaint(OpUseTiledLayerBuffer(nullptr, aCompositable->GetIPDLActor(),
-                                             aTileLayerDescriptor));
+  mTxn->AddNoSwapPaint(CompositableOperation(nullptr, aCompositable->GetIPDLActor(),
+                                             OpUseTiledLayerBuffer(aTileLayerDescriptor)));
 }
 
 void
@@ -559,9 +559,10 @@ ShadowLayerForwarder::UpdateTextureRegion(CompositableClient* aCompositable,
   MOZ_ASSERT(aCompositable);
   MOZ_ASSERT(aCompositable->IsConnected());
 
-  mTxn->AddPaint(OpPaintTextureRegion(nullptr, aCompositable->GetIPDLActor(),
-                                      aThebesBufferData,
-                                      aUpdatedRegion));
+  mTxn->AddPaint(
+    CompositableOperation(
+      nullptr, aCompositable->GetIPDLActor(),
+      OpPaintTextureRegion(aThebesBufferData, aUpdatedRegion)));
 }
 
 void
@@ -589,8 +590,8 @@ ShadowLayerForwarder::UseTextures(CompositableClient* aCompositable,
       mTxn->MarkSyncTransaction();
     }
   }
-  mTxn->AddEdit(OpUseTexture(nullptr, aCompositable->GetIPDLActor(),
-                             textures));
+  mTxn->AddEdit(CompositableOperation(nullptr, aCompositable->GetIPDLActor(),
+                                      OpUseTexture(textures)));
 }
 
 void
@@ -607,9 +608,12 @@ ShadowLayerForwarder::UseComponentAlphaTextures(CompositableClient* aCompositabl
   MOZ_ASSERT(aTextureOnWhite->GetIPDLActor());
   MOZ_ASSERT(aTextureOnBlack->GetSize() == aTextureOnWhite->GetSize());
 
-  mTxn->AddEdit(OpUseComponentAlphaTextures(nullptr, aCompositable->GetIPDLActor(),
-                                            nullptr, aTextureOnBlack->GetIPDLActor(),
-                                            nullptr, aTextureOnWhite->GetIPDLActor()));
+  mTxn->AddEdit(
+    CompositableOperation(
+      nullptr, aCompositable->GetIPDLActor(),
+      OpUseComponentAlphaTextures(
+        nullptr, aTextureOnBlack->GetIPDLActor(),
+        nullptr, aTextureOnWhite->GetIPDLActor())));
 }
 
 #ifdef MOZ_WIDGET_GONK
@@ -621,8 +625,8 @@ ShadowLayerForwarder::UseOverlaySource(CompositableClient* aCompositable,
   MOZ_ASSERT(aCompositable);
   MOZ_ASSERT(aCompositable->IsConnected());
 
-  mTxn->AddEdit(OpUseOverlaySource(nullptr, aCompositable->GetIPDLActor(),
-      aOverlay, aPictureRect));
+  mTxn->AddEdit(CompositableOperation(nullptr, aCompositable->GetIPDLActor(),
+                                      OpUseOverlaySource(aOverlay, aPictureRect)));
 }
 #endif
 
@@ -666,8 +670,10 @@ ShadowLayerForwarder::RemoveTextureFromCompositable(CompositableClient* aComposi
     return;
   }
 
-  mTxn->AddEdit(OpRemoveTexture(nullptr, aCompositable->GetIPDLActor(),
-                                nullptr, aTexture->GetIPDLActor()));
+  mTxn->AddEdit(
+    CompositableOperation(
+      nullptr, aCompositable->GetIPDLActor(),
+      OpRemoveTexture(nullptr, aTexture->GetIPDLActor())));
   if (aTexture->GetFlags() & TextureFlags::DEALLOCATE_CLIENT) {
     mTxn->MarkSyncTransaction();
   }
@@ -675,24 +681,26 @@ ShadowLayerForwarder::RemoveTextureFromCompositable(CompositableClient* aComposi
 
 void
 ShadowLayerForwarder::RemoveTextureFromCompositableAsync(AsyncTransactionTracker* aAsyncTransactionTracker,
-                                                     CompositableClient* aCompositable,
-                                                     TextureClient* aTexture)
+                                                         CompositableClient* aCompositable,
+                                                         TextureClient* aTexture)
 {
   MOZ_ASSERT(aCompositable);
   MOZ_ASSERT(aTexture);
   MOZ_ASSERT(aCompositable->IsConnected());
   MOZ_ASSERT(aTexture->GetIPDLActor());
+
+  CompositableOperation op(
+    nullptr, aCompositable->GetIPDLActor(),
+    OpRemoveTextureAsync(CompositableClient::GetTrackersHolderId(aCompositable->GetIPDLActor()),
+      aAsyncTransactionTracker->GetId(),
+      nullptr, aCompositable->GetIPDLActor(),
+      nullptr, aTexture->GetIPDLActor()));
+
 #ifdef MOZ_WIDGET_GONK
-  mPendingAsyncMessages.push_back(OpRemoveTextureAsync(CompositableClient::GetTrackersHolderId(aCompositable->GetIPDLActor()),
-                                  aAsyncTransactionTracker->GetId(),
-                                  nullptr, aCompositable->GetIPDLActor(),
-                                  nullptr, aTexture->GetIPDLActor()));
+  mPendingAsyncMessages.push_back(op);
 #else
   if (mTxn->Opened() && aCompositable->IsConnected()) {
-    mTxn->AddEdit(OpRemoveTextureAsync(CompositableClient::GetTrackersHolderId(aCompositable->GetIPDLActor()),
-                                       aAsyncTransactionTracker->GetId(),
-                                       nullptr, aCompositable->GetIPDLActor(),
-                                       nullptr, aTexture->GetIPDLActor()));
+    mTxn->AddEdit(op);
   } else {
     NS_RUNTIMEABORT("not reached");
   }
@@ -826,6 +834,7 @@ ShadowLayerForwarder::EndTransaction(InfallibleTArray* aReplies,
     common.scrollbarTargetContainerId() = mutant->GetScrollbarTargetContainerId();
     common.scrollbarDirection() = mutant->GetScrollbarDirection();
     common.scrollbarThumbRatio() = mutant->GetScrollbarThumbRatio();
+    common.isScrollbarContainer() = mutant->IsScrollbarContainer();
     common.mixBlendMode() = (int8_t)mutant->GetMixBlendMode();
     common.forceIsolatedGroup() = mutant->GetForceIsolatedGroup();
     if (Layer* maskLayer = mutant->GetMaskLayer()) {
diff --git a/gfx/layers/ipc/ShadowLayers.h b/gfx/layers/ipc/ShadowLayers.h
index c69bfb8d5e..d7f79fc0c8 100644
--- a/gfx/layers/ipc/ShadowLayers.h
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -397,7 +397,7 @@ private:
 
   Transaction* mTxn;
   MessageLoop* mMessageLoop;
-  std::vector mPendingAsyncMessages;
+  std::vector mPendingAsyncMessages;
   DiagnosticTypes mDiagnosticTypes;
   bool mIsFirstPaint;
   bool mWindowOverlayChanged;
diff --git a/gfx/layers/opengl/CompositorOGL.cpp b/gfx/layers/opengl/CompositorOGL.cpp
index 812c9e17e6..bcc685b7b2 100644
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -83,9 +83,11 @@ CompositorOGL::BindBackdrop(ShaderProgramOGL* aProgram, GLuint aBackdrop, GLenum
   aProgram->SetBackdropTextureUnit(aTexUnit - LOCAL_GL_TEXTURE0);
 }
 
-CompositorOGL::CompositorOGL(nsIWidget *aWidget, int aSurfaceWidth,
+CompositorOGL::CompositorOGL(CompositorBridgeParent* aParent,
+                             nsIWidget *aWidget, int aSurfaceWidth,
                              int aSurfaceHeight, bool aUseExternalSurfaceSize)
-  : mWidget(aWidget)
+  : Compositor(aParent)
+  , mWidget(aWidget)
   , mWidgetSize(-1, -1)
   , mSurfaceSize(aSurfaceWidth, aSurfaceHeight)
   , mHasBGRA(0)
diff --git a/gfx/layers/opengl/CompositorOGL.h b/gfx/layers/opengl/CompositorOGL.h
index a702712225..84c3ad6bf9 100644
--- a/gfx/layers/opengl/CompositorOGL.h
+++ b/gfx/layers/opengl/CompositorOGL.h
@@ -193,7 +193,8 @@ class CompositorOGL final : public Compositor
 
   std::map mPrograms;
 public:
-  explicit CompositorOGL(nsIWidget *aWidget, int aSurfaceWidth = -1, int aSurfaceHeight = -1,
+  explicit CompositorOGL(CompositorBridgeParent* aParent,
+                         nsIWidget *aWidget, int aSurfaceWidth = -1, int aSurfaceHeight = -1,
                          bool aUseExternalSurfaceSize = false);
 
 protected:
diff --git a/gfx/src/RegionBuilder.h b/gfx/src/RegionBuilder.h
new file mode 100644
index 0000000000..c3d88f9f9a
--- /dev/null
+++ b/gfx/src/RegionBuilder.h
@@ -0,0 +1,32 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ *  * License, v. 2.0. If a copy of the MPL was not distributed with this
+ *  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef RegionBuilder_h__
+#define RegionBuilder_h__
+
+#include 
+
+template 
+class RegionBuilder
+{
+public:
+  typedef typename RegionType::RectType RectType;
+
+  RegionBuilder()
+  {}
+
+  void Or(const RectType& aRect) {
+    pixman_box32_t box = { aRect.x, aRect.y, aRect.XMost(), aRect.YMost() };
+    mRects.AppendElement(box);
+  }
+
+  RegionType ToRegion() const {
+    return RegionType(mRects);
+  }
+
+private:
+  nsTArray mRects;
+};
+
+#endif // RegionBuilder_h__
diff --git a/gfx/src/gfxCrashReporterUtils.cpp b/gfx/src/gfxCrashReporterUtils.cpp
index 650488f82f..de7060d0e2 100644
--- a/gfx/src/gfxCrashReporterUtils.cpp
+++ b/gfx/src/gfxCrashReporterUtils.cpp
@@ -16,7 +16,7 @@
 #include "mozilla/Services.h"           // for GetObserverService
 #include "mozilla/StaticMutex.h"
 #include "mozilla/mozalloc.h"           // for operator new, etc
-#include "nsAutoPtr.h"                  // for nsRefPtr
+#include "mozilla/RefPtr.h"             // for RefPtr
 #include "nsCOMPtr.h"                   // for nsCOMPtr
 #include "nsError.h"                    // for NS_OK, NS_FAILED, nsresult
 #include "nsExceptionHandler.h"         // for AppendAppNotesToCrashReport
diff --git a/gfx/src/moz.build b/gfx/src/moz.build
index 9118c1ec04..4f0fa9ca24 100644
--- a/gfx/src/moz.build
+++ b/gfx/src/moz.build
@@ -37,6 +37,7 @@ EXPORTS += [
     'nsSize.h',
     'nsThemeConstants.h',
     'nsTransform2D.h',
+    'RegionBuilder.h',
 ]
 
 EXPORTS.mozilla += [
diff --git a/gfx/src/nsDeviceContext.cpp b/gfx/src/nsDeviceContext.cpp
index 7027629ad0..9b9de1ce4b 100644
--- a/gfx/src/nsDeviceContext.cpp
+++ b/gfx/src/nsDeviceContext.cpp
@@ -336,7 +336,7 @@ nsDeviceContext::CreateRenderingContext()
 
     // This can legitimately happen - CreateDrawTargetForSurface will fail
     // to create a draw target if the size is too large, for instance.
-    if (!dt) {
+    if (!dt || !dt->IsValid()) {
         gfxCriticalNote << "Failed to create draw target in device context sized " << mWidth << "x" << mHeight << " and pointers " << hexa(mPrintingSurface) << " and " << hexa(printingSurface);
         return nullptr;
     }
@@ -345,6 +345,10 @@ nsDeviceContext::CreateRenderingContext()
     nsresult rv = mDeviceContextSpec->GetDrawEventRecorder(getter_AddRefs(recorder));
     if (NS_SUCCEEDED(rv) && recorder) {
       dt = gfx::Factory::CreateRecordingDrawTarget(recorder, dt);
+      if (!dt || !dt->IsValid()) {
+          gfxCriticalNote << "Failed to create a recording draw target";
+          return nullptr;
+      }
     }
 
 #ifdef XP_MACOSX
@@ -352,7 +356,8 @@ nsDeviceContext::CreateRenderingContext()
 #endif
     dt->AddUserData(&sDisablePixelSnapping, (void*)0x1, nullptr);
 
-    RefPtr pContext = new gfxContext(dt);
+    RefPtr pContext = gfxContext::ForDrawTarget(dt);
+    MOZ_ASSERT(pContext); // already checked draw target above
 
     gfxMatrix transform;
     if (printingSurface->GetRotateForLandscape()) {
diff --git a/gfx/src/nsDeviceContext.h b/gfx/src/nsDeviceContext.h
index 8871257346..0523abc7c2 100644
--- a/gfx/src/nsDeviceContext.h
+++ b/gfx/src/nsDeviceContext.h
@@ -11,7 +11,7 @@
 #include "gfxTypes.h"                   // for gfxFloat
 #include "gfxFont.h"                    // for gfxFont::Orientation
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT_HELPER2
-#include "nsAutoPtr.h"                  // for nsRefPtr
+#include "mozilla/RefPtr.h"             // for RefPtr
 #include "nsCOMPtr.h"                   // for nsCOMPtr
 #include "nsCoord.h"                    // for nscoord
 #include "nsError.h"                    // for nsresult
diff --git a/gfx/src/nsFont.cpp b/gfx/src/nsFont.cpp
index 777a320228..c5b1f09f0b 100644
--- a/gfx/src/nsFont.cpp
+++ b/gfx/src/nsFont.cpp
@@ -50,6 +50,7 @@ nsFont::Init()
   variantLigatures = 0;
   variantNumeric = 0;
   variantPosition = NS_FONT_VARIANT_POSITION_NORMAL;
+  variantWidth = NS_FONT_VARIANT_WIDTH_NORMAL;
 }
 
 nsFont::nsFont(const nsFont& aOther) = default;
@@ -81,6 +82,7 @@ bool nsFont::Equals(const nsFont& aOther) const
       (variantLigatures == aOther.variantLigatures) &&
       (variantNumeric == aOther.variantNumeric) &&
       (variantPosition == aOther.variantPosition) &&
+      (variantWidth == aOther.variantWidth) &&
       (alternateValues == aOther.alternateValues) &&
       (featureValueLookup == aOther.featureValueLookup) &&
       (smoothing == aOther.smoothing)) {
@@ -170,6 +172,23 @@ AddFontFeaturesBitmask(uint32_t aValue, uint32_t aMin, uint32_t aMax,
   }
 }
 
+static uint32_t
+FontFeatureTagForVariantWidth(uint32_t aVariantWidth)
+{
+  switch (aVariantWidth) {
+    case NS_FONT_VARIANT_WIDTH_FULL:
+      return TRUETYPE_TAG('f','w','i','d');
+    case NS_FONT_VARIANT_WIDTH_HALF:
+      return TRUETYPE_TAG('h','w','i','d');
+    case NS_FONT_VARIANT_WIDTH_THIRD:
+      return TRUETYPE_TAG('t','w','i','d');
+    case NS_FONT_VARIANT_WIDTH_QUARTER:
+      return TRUETYPE_TAG('q','w','i','d');
+    default:
+      return 0;
+  }
+}
+
 void nsFont::AddFontFeaturesToStyle(gfxFontStyle *aStyle) const
 {
   // add in font-variant features
@@ -256,6 +275,13 @@ void nsFont::AddFontFeaturesToStyle(gfxFontStyle *aStyle) const
   // -- position
   aStyle->variantSubSuper = variantPosition;
 
+  // -- width
+  setting.mTag = FontFeatureTagForVariantWidth(variantWidth);
+  if (setting.mTag) {
+    setting.mValue = 1;
+    aStyle->featureSettings.AppendElement(setting);
+  }
+
   // indicate common-path case when neither variantCaps or variantSubSuper are set
   aStyle->noFallbackVariantFeatures =
     (aStyle->variantCaps == NS_FONT_VARIANT_CAPS_NORMAL) &&
diff --git a/gfx/src/nsFont.h b/gfx/src/nsFont.h
index 48cbbfc6c5..d21ce2593a 100644
--- a/gfx/src/nsFont.h
+++ b/gfx/src/nsFont.h
@@ -10,7 +10,7 @@
 #include                   // for int16_t
 #include "gfxFontFamilyList.h"
 #include "gfxFontFeatures.h"
-#include "nsAutoPtr.h"                  // for nsRefPtr
+#include "mozilla/RefPtr.h"             // for RefPtr
 #include "nsCoord.h"                    // for nscoord
 #include "nsStringFwd.h"                // for nsSubstring
 #include "nsString.h"               // for nsString
@@ -55,6 +55,7 @@ struct nsFont {
   uint8_t variantCaps;
   uint8_t variantNumeric;
   uint8_t variantPosition;
+  uint8_t variantWidth;
 
   uint16_t variantLigatures;
   uint16_t variantEastAsian;
diff --git a/gfx/src/nsFontMetrics.cpp b/gfx/src/nsFontMetrics.cpp
index 523b68fe87..6a954bc223 100644
--- a/gfx/src/nsFontMetrics.cpp
+++ b/gfx/src/nsFontMetrics.cpp
@@ -20,8 +20,10 @@
 #include "nsString.h"                   // for nsString
 #include "nsStyleConsts.h"              // for NS_STYLE_HYPHENS_NONE
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT
+#include "mozilla/UniquePtr.h"          // for UniquePtr
 
 class gfxUserFontSet;
+using namespace mozilla;
 
 namespace {
 
@@ -51,8 +53,8 @@ public:
             nullptr);
     }
 
-    gfxTextRun *get() { return mTextRun; }
-    gfxTextRun *operator->() { return mTextRun; }
+    gfxTextRun *get() { return mTextRun.get(); }
+    gfxTextRun *operator->() { return mTextRun.get(); }
 
 private:
     static uint32_t ComputeFlags(nsFontMetrics* aMetrics) {
@@ -76,7 +78,7 @@ private:
         return flags;
     }
 
-    nsAutoPtr mTextRun;
+    UniquePtr mTextRun;
 };
 
 class StubPropertyProvider : public gfxTextRun::PropertyProvider {
diff --git a/gfx/src/nsFontMetrics.h b/gfx/src/nsFontMetrics.h
index 886deed791..163380c004 100644
--- a/gfx/src/nsFontMetrics.h
+++ b/gfx/src/nsFontMetrics.h
@@ -10,7 +10,7 @@
 #include                   // for int32_t
 #include "gfxTextRun.h"                 // for gfxFont, gfxFontGroup
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT_HELPER2
-#include "nsAutoPtr.h"                  // for nsRefPtr
+#include "mozilla/RefPtr.h"             // for RefPtr
 #include "nsCOMPtr.h"                   // for nsCOMPtr
 #include "nsCoord.h"                    // for nscoord
 #include "nsError.h"                    // for nsresult
diff --git a/gfx/src/nsRegion.h b/gfx/src/nsRegion.h
index 00184f6259..ec9c5f7d06 100644
--- a/gfx/src/nsRegion.h
+++ b/gfx/src/nsRegion.h
@@ -19,6 +19,7 @@
 #include "nsMargin.h"                   // for nsIntMargin
 #include "nsRegionFwd.h"                // for nsIntRegion
 #include "nsStringGlue.h"               // for nsCString
+#include "nsTArray.h"                   // for nsTArray, nsTArray_Impl
 #include "xpcom-config.h"               // for CPP_THROW_NEW
 #include "mozilla/Move.h"               // for mozilla::Move
 #include "mozilla/gfx/MatrixFwd.h"      // for mozilla::gfx::Matrix4x4
@@ -49,12 +50,20 @@ enum class VisitSide {
 class nsRegion
 {
 public:
+  typedef nsRect RectType;
+  typedef nsPoint PointType;
+  typedef nsMargin MarginType;
+
   nsRegion () { pixman_region32_init(&mImpl); }
   MOZ_IMPLICIT nsRegion (const nsRect& aRect) { pixman_region32_init_rect(&mImpl,
                                                                           aRect.x,
                                                                           aRect.y,
                                                                           aRect.width,
                                                                           aRect.height); }
+  explicit nsRegion (const nsTArray& aRects)
+  {
+    pixman_region32_init_rects(&mImpl, aRects.Elements(), aRects.Length());
+  }
   nsRegion (const nsRegion& aRegion) { pixman_region32_init(&mImpl); pixman_region32_copy(&mImpl,aRegion.Impl()); }
   nsRegion (nsRegion&& aRegion) { mImpl = aRegion.mImpl; pixman_region32_init(&aRegion.mImpl); }
   nsRegion& operator = (nsRegion&& aRegion) {
@@ -471,6 +480,7 @@ public:
 
   BaseIntRegion () {}
   MOZ_IMPLICIT BaseIntRegion (const Rect& aRect) : mImpl (ToRect(aRect)) {}
+  explicit BaseIntRegion (const nsTArray& aRects) : mImpl (aRects) {}
   BaseIntRegion (const BaseIntRegion& aRegion) : mImpl (aRegion.mImpl) {}
   BaseIntRegion (BaseIntRegion&& aRegion) : mImpl (mozilla::Move(aRegion.mImpl)) {}
   Derived& operator = (const Rect& aRect) { mImpl = ToRect (aRect); return This(); }
@@ -814,10 +824,15 @@ class IntRegionTyped :
   static_assert(IsPixel::value, "'units' must be a coordinate system tag");
 
 public:
+  typedef IntRectTyped RectType;
+  typedef IntPointTyped PointType;
+  typedef IntMarginTyped MarginType;
+
   // Forward constructors.
   IntRegionTyped() {}
   MOZ_IMPLICIT IntRegionTyped(const IntRectTyped& aRect) : Super(aRect) {}
   IntRegionTyped(const IntRegionTyped& aRegion) : Super(aRegion) {}
+  explicit IntRegionTyped(const nsTArray& aRects) : Super(aRects) {}
   IntRegionTyped(IntRegionTyped&& aRegion) : Super(mozilla::Move(aRegion)) {}
 
   // Assignment operators need to be forwarded as well, otherwise the compiler
diff --git a/gfx/tests/gtest/TestCompositor.cpp b/gfx/tests/gtest/TestCompositor.cpp
index a21a3b1bd5..b2a184450a 100644
--- a/gfx/tests/gtest/TestCompositor.cpp
+++ b/gfx/tests/gtest/TestCompositor.cpp
@@ -3,8 +3,10 @@
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
+#include "gfxPrefs.h"
 #include "gfxUtils.h"
 #include "gtest/gtest.h"
+#include "gtest/MozGTestBench.h"
 #include "TestLayers.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/RefPtr.h"
@@ -110,13 +112,14 @@ static already_AddRefed CreateTestCompositor(LayersBackend backend,
   RefPtr compositor;
 
   if (backend == LayersBackend::LAYERS_OPENGL) {
-    compositor = new CompositorOGL(widget,
+    compositor = new CompositorOGL(nullptr,
+                                   widget,
                                    gCompWidth,
                                    gCompHeight,
                                    true);
     compositor->SetDestinationSurfaceSize(IntSize(gCompWidth, gCompHeight));
   } else if (backend == LayersBackend::LAYERS_BASIC) {
-    compositor = new BasicCompositor(widget);
+    compositor = new BasicCompositor(nullptr, widget);
 #ifdef XP_WIN
   } else if (backend == LayersBackend::LAYERS_D3D11) {
     //compositor = new CompositorD3D11();
@@ -226,44 +229,51 @@ TEST(Gfx, CompositorConstruct)
   auto layerManagers = GetLayerManagers(GetPlatformBackends());
 }
 
-TEST(Gfx, CompositorSimpleTree)
-{
+static void CompositorSimpleTree() {
+  const int benchmarkRepeatCount = 30;
+
+  RefPtr refDT = CreateDT();
+  refDT->FillRect(Rect(0, 0, gCompWidth, gCompHeight), ColorPattern(Color(1.f, 0.f, 1.f, 1.f)));
+  refDT->FillRect(Rect(0, 0, 100, 100), ColorPattern(Color(1.f, 0.f, 0.f, 1.f)));
+  refDT->FillRect(Rect(0, 50, 100, 100), ColorPattern(Color(0.f, 0.f, 1.f, 1.f)));
+
   auto layerManagers = GetLayerManagers(GetPlatformBackends());
   for (size_t i = 0; i < layerManagers.size(); i++) {
-    RefPtr layerManager = layerManagers[i].mLayerManager;
-    RefPtr lmBase = layerManager.get();
-    nsTArray> layers;
-    nsIntRegion layerVisibleRegion[] = {
-      nsIntRegion(IntRect(0, 0, gCompWidth, gCompHeight)),
-      nsIntRegion(IntRect(0, 0, gCompWidth, gCompHeight)),
-      nsIntRegion(IntRect(0, 0, 100, 100)),
-      nsIntRegion(IntRect(0, 50, 100, 100)),
-    };
-    RefPtr root = CreateLayerTree("c(ooo)", layerVisibleRegion, nullptr, lmBase, layers);
+    // Benchmark n composites
+    for (size_t n = 0; n < benchmarkRepeatCount; n++) {
+      RefPtr layerManager = layerManagers[i].mLayerManager;
+      RefPtr lmBase = layerManager.get();
+      nsTArray> layers;
+      nsIntRegion layerVisibleRegion[] = {
+        nsIntRegion(IntRect(0, 0, gCompWidth, gCompHeight)),
+        nsIntRegion(IntRect(0, 0, gCompWidth, gCompHeight)),
+        nsIntRegion(IntRect(0, 0, 100, 100)),
+        nsIntRegion(IntRect(0, 50, 100, 100)),
+      };
+      RefPtr root = CreateLayerTree("c(ooo)", layerVisibleRegion, nullptr, lmBase, layers);
 
-    { // background
-      ColorLayer* colorLayer = layers[1]->AsColorLayer();
-      colorLayer->SetColor(Color(1.f, 0.f, 1.f, 1.f));
-      colorLayer->SetBounds(colorLayer->GetVisibleRegion().ToUnknownRegion().GetBounds());
+      { // background
+        ColorLayer* colorLayer = layers[1]->AsColorLayer();
+        colorLayer->SetColor(Color(1.f, 0.f, 1.f, 1.f));
+        colorLayer->SetBounds(colorLayer->GetVisibleRegion().ToUnknownRegion().GetBounds());
+      }
+
+      {
+        ColorLayer* colorLayer = layers[2]->AsColorLayer();
+        colorLayer->SetColor(Color(1.f, 0.f, 0.f, 1.f));
+        colorLayer->SetBounds(colorLayer->GetVisibleRegion().ToUnknownRegion().GetBounds());
+      }
+
+      {
+        ColorLayer* colorLayer = layers[3]->AsColorLayer();
+        colorLayer->SetColor(Color(0.f, 0.f, 1.f, 1.f));
+        colorLayer->SetBounds(colorLayer->GetVisibleRegion().ToUnknownRegion().GetBounds());
+      }
+
+      EXPECT_TRUE(CompositeAndCompare(layerManager, refDT));
     }
-
-    {
-      ColorLayer* colorLayer = layers[2]->AsColorLayer();
-      colorLayer->SetColor(Color(1.f, 0.f, 0.f, 1.f));
-      colorLayer->SetBounds(colorLayer->GetVisibleRegion().ToUnknownRegion().GetBounds());
-    }
-
-    {
-      ColorLayer* colorLayer = layers[3]->AsColorLayer();
-      colorLayer->SetColor(Color(0.f, 0.f, 1.f, 1.f));
-      colorLayer->SetBounds(colorLayer->GetVisibleRegion().ToUnknownRegion().GetBounds());
-    }
-
-    RefPtr refDT = CreateDT();
-    refDT->FillRect(Rect(0, 0, gCompWidth, gCompHeight), ColorPattern(Color(1.f, 0.f, 1.f, 1.f)));
-    refDT->FillRect(Rect(0, 0, 100, 100), ColorPattern(Color(1.f, 0.f, 0.f, 1.f)));
-    refDT->FillRect(Rect(0, 50, 100, 100), ColorPattern(Color(0.f, 0.f, 1.f, 1.f)));
-    EXPECT_TRUE(CompositeAndCompare(layerManager, refDT));
   }
-}
+};
+
+MOZ_GTEST_BENCH(GfxBench, CompositorSimpleTree, &CompositorSimpleTree);
 
diff --git a/gfx/tests/gtest/TestRegion.cpp b/gfx/tests/gtest/TestRegion.cpp
index 5fb708bfde..1c6b640245 100644
--- a/gfx/tests/gtest/TestRegion.cpp
+++ b/gfx/tests/gtest/TestRegion.cpp
@@ -6,7 +6,10 @@
 #include 
 
 #include "gtest/gtest.h"
+#include "gtest/MozGTestBench.h"
+#include "nsRect.h"
 #include "nsRegion.h"
+#include "RegionBuilder.h"
 
 using namespace std;
 
@@ -562,3 +565,42 @@ TEST(Gfx, RegionVisitEdges) {
   }
 
 }
+
+MOZ_GTEST_BENCH(GfxBench, RegionOr, []{
+  const int size = 5000;
+  nsRegion r;
+  for (int i = 0; i < size; i++) {
+    r = r.Or(r, nsRect(i, i, i + 10, i + 10));
+  }
+});
+
+MOZ_GTEST_BENCH(GfxBench, RegionAnd, []{
+  const int size = 5000;
+  nsRegion r(nsRect(0, 0, size, size));
+  for (int i = 0; i < size; i++) {
+    nsRegion rMissingPixel(nsRect(0, 0, size, size));
+    rMissingPixel = rMissingPixel.Sub(rMissingPixel, nsRect(i, i, 1, 1));
+    r = r.And(r, rMissingPixel);
+  }
+});
+
+void TestExec() {
+  const int size = 5000;
+
+  RegionBuilder r;
+  for (int i = 0; i < size; i++) {
+    r.Or(nsRect(i, i, i + 10, i + 10));
+  }
+  r.ToRegion();
+
+  RegionBuilder rInt;
+  for (int i = 0; i < size; i++) {
+    rInt.Or(nsIntRect(i, i, i + 10, i + 10));
+  }
+  rInt.ToRegion();
+}
+
+MOZ_GTEST_BENCH(GfxBench, RegionBuilderOr, []{
+  TestExec();
+});
+
diff --git a/gfx/tests/gtest/gfxFontSelectionTest.cpp b/gfx/tests/gtest/gfxFontSelectionTest.cpp
index 9d0ea9a762..d5c3444359 100644
--- a/gfx/tests/gtest/gfxFontSelectionTest.cpp
+++ b/gfx/tests/gtest/gfxFontSelectionTest.cpp
@@ -7,6 +7,7 @@
 
 #include "mozilla/gfx/2D.h"
 #include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
 #include "nsCOMPtr.h"
 #include "nsTArray.h"
 #include "nsString.h"
@@ -191,7 +192,7 @@ MakeContext ()
     RefPtr drawTarget = gfxPlatform::GetPlatform()->
         CreateOffscreenContentDrawTarget(IntSize(size, size),
                                          SurfaceFormat::B8G8R8X8);
-    RefPtr ctx = new gfxContext(drawTarget);
+    RefPtr ctx = gfxContext::ForDrawTarget(drawTarget);
 
     return ctx.forget();
 }
@@ -251,7 +252,7 @@ RunTest (TestEntry *test, gfxContext *ctx) {
 
     fontGroup = gfxPlatform::GetPlatform()->CreateFontGroup(NS_ConvertUTF8toUTF16(test->utf8FamilyString), &test->fontStyle, nullptr, nullptr, 1.0);
 
-    nsAutoPtr textRun;
+    UniquePtr textRun;
     gfxTextRunFactory::Parameters params = {
       ctx, nullptr, nullptr, nullptr, 0, 60
     };
@@ -263,7 +264,8 @@ RunTest (TestEntry *test, gfxContext *ctx) {
     if (test->stringType == S_ASCII) {
         flags |= gfxTextRunFactory::TEXT_IS_ASCII | gfxTextRunFactory::TEXT_IS_8BIT;
         length = strlen(test->string);
-        textRun = fontGroup->MakeTextRun(reinterpret_cast(test->string), length, ¶ms, flags);
+        textRun = fontGroup->MakeTextRun(
+            reinterpret_cast(test->string), length, ¶ms, flags);
     } else {
         NS_ConvertUTF8toUTF16 str(nsDependentCString(test->string));
         length = str.Length();
diff --git a/gfx/tests/gtest/gfxTextRunPerfTest.cpp b/gfx/tests/gtest/gfxTextRunPerfTest.cpp
index 0e485754cc..ebe79bddff 100644
--- a/gfx/tests/gtest/gfxTextRunPerfTest.cpp
+++ b/gfx/tests/gtest/gfxTextRunPerfTest.cpp
@@ -43,7 +43,7 @@ MakeContext ()
     RefPtr drawTarget = gfxPlatform::GetPlatform()->
         CreateOffscreenContentDrawTarget(IntSize(size, size),
                                          SurfaceFormat::B8G8R8X8);
-    RefPtr ctx = new gfxContext(drawTarget);
+    RefPtr ctx = gfxContext::ForDrawTarget(drawTarget);
 
     return ctx.forget();
 }
@@ -66,7 +66,7 @@ RunTest (TestEntry *test, gfxContext *ctx) {
         fontGroup = gfxPlatform::GetPlatform()->CreateFontGroup(NS_ConvertUTF8toUTF16(test->mFamilies), &style_western_normal_16, nullptr, nullptr, 1.0);
     }
 
-    nsAutoPtr textRun;
+    UniquePtr textRun;
     uint32_t i;
     bool isASCII = true;
     for (i = 0; test->mString[i]; ++i) {
@@ -84,7 +84,8 @@ RunTest (TestEntry *test, gfxContext *ctx) {
         flags |= gfxTextRunFactory::TEXT_IS_ASCII |
                  gfxTextRunFactory::TEXT_IS_8BIT;
         length = strlen(test->mString);
-        textRun = fontGroup->MakeTextRun(reinterpret_cast(test->mString), length, ¶ms, flags);
+        textRun = fontGroup->MakeTextRun(
+            reinterpret_cast(test->mString), length, ¶ms, flags);
     } else {
         NS_ConvertUTF8toUTF16 str(nsDependentCString(test->mString));
         length = str.Length();
diff --git a/gfx/tests/gtest/gfxWordCacheTest.cpp b/gfx/tests/gtest/gfxWordCacheTest.cpp
index 68e5c4c6a9..74ca305ffe 100644
--- a/gfx/tests/gtest/gfxWordCacheTest.cpp
+++ b/gfx/tests/gtest/gfxWordCacheTest.cpp
@@ -60,7 +60,7 @@ static gfxTextRun *
 MakeTextRun(const char16_t *aText, uint32_t aLength, gfxFontGroup *aFontGroup,
             const gfxFontGroup::Parameters* aParams, uint32_t aFlags)
 {
-  nsAutoPtr textRun;
+  UniquePtr textRun;
   if (aLength == 0) {
     abort();
     //textRun = aFontGroup->MakeEmptyTextRun(aParams, aFlags);
@@ -73,12 +73,12 @@ MakeTextRun(const char16_t *aText, uint32_t aLength, gfxFontGroup *aFontGroup,
   if (!textRun) {
     return nullptr;
   }
-  nsresult rv = gTextRuns->AddObject(textRun);
+  nsresult rv = gTextRuns->AddObject(textRun.get());
   if (NS_FAILED(rv)) {
-    gTextRuns->RemoveFromCache(textRun);
+    gTextRuns->RemoveFromCache(textRun.get());
     return nullptr;
   }
-  return textRun.forget();
+  return textRun.release();
 }
 
 static already_AddRefed
diff --git a/gfx/thebes/gfxAndroidPlatform.cpp b/gfx/thebes/gfxAndroidPlatform.cpp
index 9c1633b9db..f8b15bd869 100644
--- a/gfx/thebes/gfxAndroidPlatform.cpp
+++ b/gfx/thebes/gfxAndroidPlatform.cpp
@@ -246,37 +246,12 @@ gfxAndroidPlatform::GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh,
     aFontList.AppendElement("Droid Sans Fallback");
 }
 
-nsresult
-gfxAndroidPlatform::GetFontList(nsIAtom *aLangGroup,
-                                const nsACString& aGenericFamily,
-                                nsTArray& aListOfFonts)
-{
-    gfxPlatformFontList::PlatformFontList()->GetFontList(aLangGroup,
-                                                         aGenericFamily,
-                                                         aListOfFonts);
-    return NS_OK;
-}
-
 void
 gfxAndroidPlatform::GetSystemFontList(InfallibleTArray* retValue)
 {
     gfxFT2FontList::PlatformFontList()->GetSystemFontList(retValue);
 }
 
-nsresult
-gfxAndroidPlatform::UpdateFontList()
-{
-    gfxPlatformFontList::PlatformFontList()->UpdateFontList();
-    return NS_OK;
-}
-
-nsresult
-gfxAndroidPlatform::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
-{
-    gfxPlatformFontList::PlatformFontList()->GetStandardFamilyName(aFontName, aFamilyName);
-    return NS_OK;
-}
-
 gfxPlatformFontList*
 gfxAndroidPlatform::CreatePlatformFontList()
 {
@@ -326,34 +301,6 @@ gfxAndroidPlatform::GetFTLibrary()
     return gPlatformFTLibrary;
 }
 
-gfxFontEntry*
-gfxAndroidPlatform::LookupLocalFont(const nsAString& aFontName,
-                                    uint16_t aWeight,
-                                    int16_t aStretch,
-                                    uint8_t aStyle)
-{
-    return gfxPlatformFontList::PlatformFontList()->LookupLocalFont(aFontName,
-                                                                    aWeight,
-                                                                    aStretch,
-                                                                    aStyle);
-}
-
-gfxFontEntry* 
-gfxAndroidPlatform::MakePlatformFont(const nsAString& aFontName,
-                                     uint16_t aWeight,
-                                     int16_t aStretch,
-                                     uint8_t aStyle,
-                                     const uint8_t* aFontData,
-                                     uint32_t aLength)
-{
-    return gfxPlatformFontList::PlatformFontList()->MakePlatformFont(aFontName,
-                                                                     aWeight,
-                                                                     aStretch,
-                                                                     aStyle,
-                                                                     aFontData,
-                                                                     aLength);
-}
-
 already_AddRefed
 gfxAndroidPlatform::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont)
 {
diff --git a/gfx/thebes/gfxAndroidPlatform.h b/gfx/thebes/gfxAndroidPlatform.h
index a902d68159..c5fbc52a94 100644
--- a/gfx/thebes/gfxAndroidPlatform.h
+++ b/gfx/thebes/gfxAndroidPlatform.h
@@ -45,30 +45,11 @@ public:
     // platform implementations of font functions
     virtual bool IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags);
     virtual gfxPlatformFontList* CreatePlatformFontList();
-    virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
-                                          uint16_t aWeight,
-                                          int16_t aStretch,
-                                          uint8_t aStyle);
-    virtual gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
-                                           uint16_t aWeight,
-                                           int16_t aStretch,
-                                           uint8_t aStyle,
-                                           const uint8_t* aFontData,
-                                           uint32_t aLength);
 
     virtual void GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh,
                                         int32_t aRunScript,
                                         nsTArray& aFontList);
 
-    virtual nsresult GetFontList(nsIAtom *aLangGroup,
-                                 const nsACString& aGenericFamily,
-                                 nsTArray& aListOfFonts);
-
-    virtual nsresult UpdateFontList();
-
-    virtual nsresult GetStandardFamilyName(const nsAString& aFontName,
-                                           nsAString& aFamilyName);
-
     gfxFontGroup*
     CreateFontGroup(const mozilla::FontFamilyList& aFontFamilyList,
                     const gfxFontStyle *aStyle,
diff --git a/gfx/thebes/gfxBlur.cpp b/gfx/thebes/gfxBlur.cpp
index 578491980c..7faf07afae 100644
--- a/gfx/thebes/gfxBlur.cpp
+++ b/gfx/thebes/gfxBlur.cpp
@@ -74,14 +74,15 @@ gfxAlphaBoxBlur::Init(const gfxRect& aRect,
         gfxPlatform::GetPlatform()->CreateDrawTargetForData(mData.get(), size,
                                                             mBlur->GetStride(),
                                                             SurfaceFormat::A8);
-    if (!dt) {
+    if (!dt || !dt->IsValid()) {
         return nullptr;
     }
 
     IntRect irect = mBlur->GetRect();
     gfxPoint topleft(irect.TopLeft().x, irect.TopLeft().y);
 
-    mContext = new gfxContext(dt);
+    mContext = gfxContext::ForDrawTarget(dt);
+    MOZ_ASSERT(mContext); // already checked for target above
     mContext->SetMatrix(gfxMatrix::Translation(-topleft));
 
     return mContext;
diff --git a/gfx/thebes/gfxBlur.h b/gfx/thebes/gfxBlur.h
index 563bf71b99..9cc5f37166 100644
--- a/gfx/thebes/gfxBlur.h
+++ b/gfx/thebes/gfxBlur.h
@@ -8,7 +8,6 @@
 
 #include "gfxTypes.h"
 #include "nsSize.h"
-#include "nsAutoPtr.h"
 #include "gfxPoint.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtr.h"
diff --git a/gfx/thebes/gfxContext.cpp b/gfx/thebes/gfxContext.cpp
index fc3b298d13..f255f73ba1 100644
--- a/gfx/thebes/gfxContext.cpp
+++ b/gfx/thebes/gfxContext.cpp
@@ -72,7 +72,9 @@ gfxContext::gfxContext(DrawTarget *aTarget, const Point& aDeviceOffset)
   , mTransformChanged(false)
   , mDT(aTarget)
 {
-  MOZ_ASSERT(aTarget, "Don't create a gfxContext without a DrawTarget");
+  if (!aTarget) {
+    gfxCriticalError() << "Don't create a gfxContext without a DrawTarget";
+  }
 
   MOZ_COUNT_CTOR(gfxContext);
 
@@ -83,10 +85,23 @@ gfxContext::gfxContext(DrawTarget *aTarget, const Point& aDeviceOffset)
 }
 
 /* static */ already_AddRefed
-gfxContext::ContextForDrawTarget(DrawTarget* aTarget)
+gfxContext::ForDrawTarget(DrawTarget* aTarget,
+                          const mozilla::gfx::Point& aDeviceOffset)
 {
   if (!aTarget || !aTarget->IsValid()) {
-    gfxWarning() << "Invalid target in gfxContext::ContextForDrawTarget";
+    gfxCriticalNote << "Invalid target in gfxContext::ForDrawTarget " << hexa(aTarget);
+    return nullptr;
+  }
+
+  RefPtr result = new gfxContext(aTarget, aDeviceOffset);
+  return result.forget();
+}
+
+/* static */ already_AddRefed
+gfxContext::ForDrawTargetWithTransform(DrawTarget* aTarget)
+{
+  if (!aTarget || !aTarget->IsValid()) {
+    gfxCriticalNote << "Invalid target in gfxContext::ForDrawTargetWithTransform " << hexa(aTarget);
     return nullptr;
   }
 
diff --git a/gfx/thebes/gfxContext.h b/gfx/thebes/gfxContext.h
index 1b0e8f1797..432420ec5f 100644
--- a/gfx/thebes/gfxContext.h
+++ b/gfx/thebes/gfxContext.h
@@ -14,7 +14,6 @@
 #include "gfxMatrix.h"
 #include "gfxPattern.h"
 #include "nsTArray.h"
-#include "nsAutoPtr.h"
 
 #include "mozilla/gfx/2D.h"
 
@@ -57,21 +56,26 @@ class gfxContext final {
     NS_INLINE_DECL_REFCOUNTING(gfxContext)
 
 public:
-
     /**
      * Initialize this context from a DrawTarget.
      * Strips any transform from aTarget.
      * aTarget will be flushed in the gfxContext's destructor.
+     * If aTarget is null or invalid, nullptr is returned.  The caller
+     * is responsible for handling this scenario as appropriate.
      */
-    explicit gfxContext(mozilla::gfx::DrawTarget *aTarget,
-                        const mozilla::gfx::Point& aDeviceOffset = mozilla::gfx::Point());
-
+    static already_AddRefed
+        ForDrawTarget(mozilla::gfx::DrawTarget* aTarget,
+                      const mozilla::gfx::Point& aDeviceOffset = mozilla::gfx::Point());
+    
     /**
      * Create a new gfxContext wrapping aTarget and preserving aTarget's
      * transform. Note that the transform is moved from aTarget to the resulting
      * gfxContext, aTarget will no longer have its transform.
+     * If aTarget is null or invalid, nullptr is returned.  The caller
+     * is responsible for handling this scenario as appropriate.
      */
-    static already_AddRefed ContextForDrawTarget(mozilla::gfx::DrawTarget* aTarget);
+    static already_AddRefed
+        ForDrawTargetWithTransform(mozilla::gfx::DrawTarget* aTarget);
 
     /**
      * Return the current transparency group target, if any. If no group is
@@ -457,6 +461,16 @@ public:
     static mozilla::gfx::UserDataKey sDontUseAsSourceKey;
 
 private:
+
+    /**
+     * Initialize this context from a DrawTarget.
+     * Strips any transform from aTarget.
+     * aTarget will be flushed in the gfxContext's destructor.  Use the static
+     * ContextForDrawTargetNoTransform() when you want this behavior, as that
+     * version deals with null DrawTarget better.
+     */
+    explicit gfxContext(mozilla::gfx::DrawTarget *aTarget,
+                        const mozilla::gfx::Point& aDeviceOffset = mozilla::gfx::Point());
     ~gfxContext();
 
   friend class PatternFromState;
@@ -627,6 +641,8 @@ public:
         return mMatrix;
     }
 
+    bool HasMatrix() const { return !!mContext; }
+
 private:
     gfxContext *mContext;
     gfxMatrix   mMatrix;
diff --git a/gfx/thebes/gfxDWriteCommon.h b/gfx/thebes/gfxDWriteCommon.h
index fd46ed555b..a355b59c67 100644
--- a/gfx/thebes/gfxDWriteCommon.h
+++ b/gfx/thebes/gfxDWriteCommon.h
@@ -10,7 +10,6 @@
 #include "nscore.h"
 #include "nsIServiceManager.h"
 #include "nsCOMPtr.h"
-#include "nsAutoPtr.h"
 #include "cairo-features.h"
 #include "gfxFontConstants.h"
 #include "nsTArray.h"
diff --git a/gfx/thebes/gfxDWriteFontList.cpp b/gfx/thebes/gfxDWriteFontList.cpp
index 0585850e7f..8948219407 100644
--- a/gfx/thebes/gfxDWriteFontList.cpp
+++ b/gfx/thebes/gfxDWriteFontList.cpp
@@ -1257,23 +1257,27 @@ gfxDWriteFontList::GetStandardFamilyName(const nsAString& aFontName,
     return false;
 }
 
-gfxFontFamily*
-gfxDWriteFontList::FindFamily(const nsAString& aFamily, gfxFontStyle* aStyle,
-                              gfxFloat aDevToCssSize)
+bool
+gfxDWriteFontList::FindAndAddFamilies(const nsAString& aFamily,
+                                      nsTArray* aOutput,
+                                      gfxFontStyle* aStyle,
+                                      gfxFloat aDevToCssSize)
 {
     nsAutoString keyName(aFamily);
     BuildKeyNameFromFontName(keyName);
 
     gfxFontFamily *ff = mFontSubstitutes.GetWeak(keyName);
     if (ff) {
-        return ff;
+        aOutput->AppendElement(ff);
+        return true;
     }
 
     if (mNonExistingFonts.Contains(keyName)) {
-        return nullptr;
+        return false;
     }
 
-    return gfxPlatformFontList::FindFamily(aFamily);
+    return gfxPlatformFontList::FindAndAddFamilies(aFamily, aOutput, aStyle,
+                                                   aDevToCssSize);
 }
 
 void
diff --git a/gfx/thebes/gfxDWriteFontList.h b/gfx/thebes/gfxDWriteFontList.h
index e9d992b0d2..afe19c2c73 100644
--- a/gfx/thebes/gfxDWriteFontList.h
+++ b/gfx/thebes/gfxDWriteFontList.h
@@ -378,9 +378,10 @@ public:
     IDWriteGdiInterop *GetGDIInterop() { return mGDIInterop; }
     bool UseGDIFontTableAccess() { return mGDIFontTableAccess; }
 
-    gfxFontFamily* FindFamily(const nsAString& aFamily,
-                              gfxFontStyle* aStyle = nullptr,
-                              gfxFloat aDevToCssSize = 1.0) override;
+    bool FindAndAddFamilies(const nsAString& aFamily,
+                            nsTArray* aOutput,
+                            gfxFontStyle* aStyle = nullptr,
+                            gfxFloat aDevToCssSize = 1.0) override;
 
     gfxFloat GetForceGDIClassicMaxFontSize() { return mForceGDIClassicMaxFontSize; }
 
diff --git a/gfx/thebes/gfxDWriteFonts.cpp b/gfx/thebes/gfxDWriteFonts.cpp
index 2594a6a322..abd17f8efd 100644
--- a/gfx/thebes/gfxDWriteFonts.cpp
+++ b/gfx/thebes/gfxDWriteFonts.cpp
@@ -597,7 +597,7 @@ int32_t
 gfxDWriteFont::GetGlyphWidth(DrawTarget& aDrawTarget, uint16_t aGID)
 {
     if (!mGlyphWidths) {
-        mGlyphWidths = new nsDataHashtable(128);
+        mGlyphWidths = MakeUnique>(128);
     }
 
     int32_t width = -1;
diff --git a/gfx/thebes/gfxDWriteFonts.h b/gfx/thebes/gfxDWriteFonts.h
index beb2d99e1f..9fa33ecb87 100644
--- a/gfx/thebes/gfxDWriteFonts.h
+++ b/gfx/thebes/gfxDWriteFonts.h
@@ -7,6 +7,7 @@
 #define GFX_WINDOWSDWRITEFONTS_H
 
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/UniquePtr.h"
 #include 
 
 #include "gfxFont.h"
@@ -98,7 +99,7 @@ protected:
     Metrics *mMetrics;
 
     // cache of glyph widths in 16.16 fixed-point pixels
-    nsAutoPtr > mGlyphWidths;
+    mozilla::UniquePtr> mGlyphWidths;
 
     uint32_t mSpaceGlyph;
 
diff --git a/gfx/thebes/gfxDrawable.cpp b/gfx/thebes/gfxDrawable.cpp
index 41b00317bb..9d3a30a1c1 100644
--- a/gfx/thebes/gfxDrawable.cpp
+++ b/gfx/thebes/gfxDrawable.cpp
@@ -122,10 +122,11 @@ gfxCallbackDrawable::MakeSurfaceDrawable(const Filter aFilter)
     RefPtr dt =
         gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(mSize,
                                                                      format);
-    if (!dt)
+    if (!dt || !dt->IsValid())
         return nullptr;
 
-    RefPtr ctx = new gfxContext(dt);
+    RefPtr ctx = gfxContext::ForDrawTarget(dt);
+    MOZ_ASSERT(ctx); // already checked for target above
     Draw(ctx, gfxRect(0, 0, mSize.width, mSize.height), ExtendMode::CLAMP, aFilter);
 
     RefPtr surface = dt->Snapshot();
diff --git a/gfx/thebes/gfxDrawable.h b/gfx/thebes/gfxDrawable.h
index aefb8d56a0..c310f353e3 100644
--- a/gfx/thebes/gfxDrawable.h
+++ b/gfx/thebes/gfxDrawable.h
@@ -6,7 +6,6 @@
 #ifndef GFX_DRAWABLE_H
 #define GFX_DRAWABLE_H
 
-#include "nsAutoPtr.h"
 #include "gfxRect.h"
 #include "gfxMatrix.h"
 #include "mozilla/gfx/2D.h"
diff --git a/gfx/thebes/gfxFT2FontList.cpp b/gfx/thebes/gfxFT2FontList.cpp
index 85c9a8ba00..84ff698570 100644
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -1405,7 +1405,7 @@ PreloadAsUserFontFaces(nsStringHashKey::KeyType aKey,
              crc);
 #endif
 
-        fe->mUserFontData = new gfxUserFontData;
+        fe->mUserFontData = MakeUnique();
         fe->mUserFontData->mRealName = fe->Name();
         fe->mUserFontData->mCRC32 = crc;
         fe->mUserFontData->mLength = buf.st_size;
diff --git a/gfx/thebes/gfxFcPlatformFontList.cpp b/gfx/thebes/gfxFcPlatformFontList.cpp
index b45f23b807..48189f9480 100644
--- a/gfx/thebes/gfxFcPlatformFontList.cpp
+++ b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -733,10 +733,16 @@ gfxFontconfigFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle,
     nsAutoRef renderPattern
         (FcFontRenderPrepare(nullptr, pattern, mFontPattern));
 
+    FcBool autohint;
+    if (FcPatternGetBool(renderPattern, FC_AUTOHINT, 0, &autohint) != FcResultMatch) {
+      autohint = FcFalse;
+    }
+
     cairo_scaled_font_t* scaledFont =
         CreateScaledFont(renderPattern, aFontStyle, aNeedsBold);
     gfxFont* newFont =
-        new gfxFontconfigFont(scaledFont, this, aFontStyle, aNeedsBold);
+        new gfxFontconfigFont(scaledFont, this, aFontStyle, aNeedsBold,
+                              bool(autohint));
     cairo_scaled_font_destroy(scaledFont);
 
     return newFont;
@@ -850,8 +856,10 @@ gfxFontconfigFontFamily::AddFontPattern(FcPattern* aFontPattern)
 gfxFontconfigFont::gfxFontconfigFont(cairo_scaled_font_t *aScaledFont,
                                      gfxFontEntry *aFontEntry,
                                      const gfxFontStyle *aFontStyle,
-                                     bool aNeedsBold) :
-    gfxFT2FontBase(aScaledFont, aFontEntry, aFontStyle)
+                                     bool aNeedsBold,
+                                     bool aAutoHinting) :
+    gfxFT2FontBase(aScaledFont, aFontEntry, aFontStyle),
+    mAutoHinting(aAutoHinting)
 {
 }
 
@@ -876,9 +884,10 @@ gfxFontconfigFont::GetGlyphRenderingOptions(const TextRunDrawParams* aRunParams)
   mozilla::gfx::AntialiasMode aaMode =
     mozilla::gfx::CairoAntialiasToGfxAntialias(antialias);
 
-  // We don't want to force the use of the autohinter over the font's built in hints
+  bool autohint = GetAutoHinting();
+
   // The fontconfig AA mode must be passed along because it may override the hinting style.
-  return mozilla::gfx::Factory::CreateCairoGlyphRenderingOptions(hinting, false, aaMode);
+  return mozilla::gfx::Factory::CreateCairoGlyphRenderingOptions(hinting, autohint, aaMode);
 }
 #endif
 
@@ -1184,9 +1193,11 @@ gfxFcPlatformFontList::MakePlatformFont(const nsAString& aFontName,
                                       aStyle, aFontData, face);
 }
 
-gfxFontFamily*
-gfxFcPlatformFontList::FindFamily(const nsAString& aFamily, gfxFontStyle* aStyle,
-                                  gfxFloat aDevToCssSize)
+bool
+gfxFcPlatformFontList::FindAndAddFamilies(const nsAString& aFamily,
+                                          nsTArray* aOutput,
+                                          gfxFontStyle* aStyle,
+                                          gfxFloat aDevToCssSize)
 {
     nsAutoString familyName(aFamily);
     ToLowerCase(familyName);
@@ -1208,9 +1219,10 @@ gfxFcPlatformFontList::FindFamily(const nsAString& aFamily, gfxFontStyle* aStyle
         mozilla::FontFamilyName::Convert(familyName).IsGeneric()) {
         PrefFontList* prefFonts = FindGenericFamilies(familyName, language);
         if (prefFonts && !prefFonts->IsEmpty()) {
-            return (*prefFonts)[0];
+            aOutput->AppendElements(*prefFonts);
+            return true;
         }
-        return nullptr;
+        return false;
     }
 
     // fontconfig allows conditional substitutions in such a way that it's
@@ -1228,13 +1240,18 @@ gfxFcPlatformFontList::FindFamily(const nsAString& aFamily, gfxFontStyle* aStyle
     // Nimbus Sans L as alternatives for Helvetica.
 
     // Because the FcConfigSubstitute call is quite expensive, we cache the
-    // actual font family found via this process. So check the cache first:
+    // actual font families found via this process. So check the cache first:
     NS_ConvertUTF16toUTF8 familyToFind(familyName);
-    gfxFontFamily* cached = mFcSubstituteCache.GetWeak(familyToFind);
-    if (cached) {
-        return cached;
+    AutoTArray cachedFamilies;
+    if (mFcSubstituteCache.Get(familyToFind, &cachedFamilies)) {
+        if (cachedFamilies.IsEmpty()) {
+            return false;
+        }
+        aOutput->AppendElements(cachedFamilies);
+        return true;
     }
 
+    // It wasn't in the cache, so we need to ask fontconfig...
     const FcChar8* kSentinelName = ToFcChar8Ptr("-moz-sentinel");
     FcChar8* sentinelFirstFamily = nullptr;
     nsAutoRef sentinelSubst(FcPatternCreate());
@@ -1249,7 +1266,7 @@ gfxFcPlatformFontList::FindFamily(const nsAString& aFamily, gfxFontStyle* aStyle
     FcPatternAddString(fontWithSentinel, FC_FAMILY, kSentinelName);
     FcConfigSubstitute(nullptr, fontWithSentinel, FcMatchPattern);
 
-    // iterate through substitutions until hitting the sentinel
+    // Add all font family matches until reaching the sentinel.
     FcChar8* substName = nullptr;
     for (int i = 0;
          FcPatternGetString(fontWithSentinel, FC_FAMILY,
@@ -1261,16 +1278,17 @@ gfxFcPlatformFontList::FindFamily(const nsAString& aFamily, gfxFontStyle* aStyle
             FcStrCmp(substName, sentinelFirstFamily) == 0) {
             break;
         }
-        gfxFontFamily* foundFamily = gfxPlatformFontList::FindFamily(subst);
-        if (foundFamily) {
-            // We've figured out what family the given name maps to, after any
-            // fontconfig subsitutions. Cache it to speed up future lookups.
-            mFcSubstituteCache.Put(familyToFind, foundFamily);
-            return foundFamily;
-        }
+        gfxPlatformFontList::FindAndAddFamilies(subst, &cachedFamilies);
     }
 
-    return nullptr;
+    // Cache the resulting list, so we don't have to do this again.
+    mFcSubstituteCache.Put(familyToFind, cachedFamilies);
+
+    if (cachedFamilies.IsEmpty()) {
+        return false;
+    }
+    aOutput->AppendElements(cachedFamilies);
+    return true;
 }
 
 bool
@@ -1547,18 +1565,21 @@ gfxFcPlatformFontList::FindGenericFamilies(const nsAString& aGeneric,
         FcPatternGetString(font, FC_FAMILY, 0, &mappedGeneric);
         if (mappedGeneric) {
             NS_ConvertUTF8toUTF16 mappedGenericName(ToCharPtr(mappedGeneric));
-            gfxFontFamily* genericFamily =
-                gfxPlatformFontList::FindFamily(mappedGenericName);
-            if (genericFamily && !prefFonts->Contains(genericFamily)) {
-                prefFonts->AppendElement(genericFamily);
-                bool foundLang = !fcLang.IsEmpty() &&
-                                 PatternHasLang(font, ToFcChar8Ptr(fcLang.get()));
-                foundFontWithLang = foundFontWithLang || foundLang;
-                // stop at the first family for which the lang matches (or
-                // when there is no lang)
-                if (fcLang.IsEmpty() ||
-                    prefFonts->Length() >= limit || foundLang) {
-                    break;
+            AutoTArray genericFamilies;
+            if (gfxPlatformFontList::FindAndAddFamilies(mappedGenericName,
+                                                        &genericFamilies)) {
+                MOZ_ASSERT(genericFamilies.Length() == 1,
+                           "expected a single family");
+                if (!prefFonts->Contains(genericFamilies[0])) {
+                    prefFonts->AppendElement(genericFamilies[0]);
+                    bool foundLang =
+                        !fcLang.IsEmpty() &&
+                        PatternHasLang(font, ToFcChar8Ptr(fcLang.get()));
+                    foundFontWithLang = foundFontWithLang || foundLang;
+                    // check to see if the list is full
+                    if (prefFonts->Length() >= limit) {
+                        break;
+                    }
                 }
             }
         }
diff --git a/gfx/thebes/gfxFcPlatformFontList.h b/gfx/thebes/gfxFcPlatformFontList.h
index 8873a59093..5e93f4ba40 100644
--- a/gfx/thebes/gfxFcPlatformFontList.h
+++ b/gfx/thebes/gfxFcPlatformFontList.h
@@ -11,6 +11,7 @@
 #include "gfxFT2FontBase.h"
 #include "gfxPlatformFontList.h"
 #include "mozilla/mozalloc.h"
+#include "nsClassHashtable.h"
 
 #include 
 #include "ft2build.h"
@@ -181,7 +182,10 @@ public:
     gfxFontconfigFont(cairo_scaled_font_t *aScaledFont,
                       gfxFontEntry *aFontEntry,
                       const gfxFontStyle *aFontStyle,
-                      bool aNeedsBold);
+                      bool aNeedsBold,
+                      bool aAutoHinting = false);
+
+    bool GetAutoHinting() const { return mAutoHinting; }
 
 #ifdef USE_SKIA
     virtual already_AddRefed
@@ -190,6 +194,9 @@ public:
 
 protected:
     virtual ~gfxFontconfigFont();
+
+private:
+    bool mAutoHinting;
 };
 
 class nsILanguageAtomService;
@@ -224,9 +231,10 @@ public:
                      const uint8_t* aFontData,
                      uint32_t aLength) override;
 
-    gfxFontFamily* FindFamily(const nsAString& aFamily,
-                              gfxFontStyle* aStyle = nullptr,
-                              gfxFloat aDevToCssSize = 1.0) override;
+    bool FindAndAddFamilies(const nsAString& aFamily,
+                            nsTArray* aOutput,
+                            gfxFontStyle* aStyle = nullptr,
+                            gfxFloat aDevToCssSize = 1.0) override;
 
     bool GetStandardFamilyName(const nsAString& aFontName,
                                nsAString& aFamilyName) override;
@@ -276,12 +284,17 @@ protected:
                     FcPattern*> mLocalNames;
 
     // caching generic/lang ==> font family list
-    nsBaseHashtable,
-                    PrefFontList*> mGenericMappings;
+    nsClassHashtable mGenericMappings;
 
-    // caching family lookups as found by FindFamily after resolving substitutions
-    nsRefPtrHashtable mFcSubstituteCache;
+    // Caching family lookups as found by FindAndAddFamilies after resolving
+    // substitutions. The gfxFontFamily objects cached here are owned by the
+    // gfxFcPlatformFontList via its mFamilies table; note that if the main
+    // font list is rebuilt (e.g. due to a fontconfig configuration change),
+    // these pointers will be invalidated. InitFontList() flushes the cache
+    // in this case.
+    nsDataHashtable> mFcSubstituteCache;
 
     nsCOMPtr mCheckFontUpdatesTimer;
     nsCountedRef mLastConfig;
diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp
index 2ba4522b96..cb0a3a1c0c 100644
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -699,7 +699,7 @@ gfxShapedText::AllocateDetailedGlyphs(uint32_t aIndex, uint32_t aCount)
     NS_ASSERTION(aIndex < GetLength(), "Index out of range");
 
     if (!mDetailedGlyphs) {
-        mDetailedGlyphs = new DetailedGlyphStore();
+        mDetailedGlyphs = MakeUnique();
     }
 
     return mDetailedGlyphs->Allocate(aIndex, aCount);
@@ -847,7 +847,7 @@ gfxFont::~gfxFont()
 {
     uint32_t i, count = mGlyphExtentsArray.Length();
     // We destroy the contents of mGlyphExtentsArray explicitly instead of
-    // using nsAutoPtr because VC++ can't deal with nsTArrays of nsAutoPtrs
+    // using UniquePtr because VC++ can't deal with nsTArrays of UniquePtrs
     // of classes that lack a proper copy constructor
     for (i = 0; i < count; ++i) {
         delete mGlyphExtentsArray[i];
@@ -877,7 +877,7 @@ gfxFont::GetGlyphHAdvance(DrawTarget* aDrawTarget, uint16_t aGID)
     NS_ASSERTION(mFUnitsConvFactor >= 0.0f,
                  "missing font unit conversion factor");
     if (!mHarfBuzzShaper) {
-        mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
+        mHarfBuzzShaper = MakeUnique(this);
     }
     gfxHarfBuzzShaper* shaper =
         static_cast(mHarfBuzzShaper.get());
@@ -1451,7 +1451,7 @@ gfxFont::SupportsSubSuperscript(uint32_t aSubSuperscript,
     }
 
     if (!mHarfBuzzShaper) {
-        mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
+        mHarfBuzzShaper = MakeUnique(this);
     }
     gfxHarfBuzzShaper* shaper =
         static_cast(mHarfBuzzShaper.get());
@@ -2039,16 +2039,16 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
         aRunParams.context->SetMatrix(mat);
     }
 
-    nsAutoPtr contextPaint;
+    UniquePtr contextPaint;
     if (fontParams.haveSVGGlyphs && !fontParams.contextPaint) {
         // If no pattern is specified for fill, use the current pattern
         NS_ASSERTION((int(aRunParams.drawMode) & int(DrawMode::GLYPH_STROKE)) == 0,
                      "no pattern supplied for stroking text");
         RefPtr fillPattern = aRunParams.context->GetPattern();
-        contextPaint =
+        contextPaint.reset(
             new SimpleTextContextPaint(fillPattern, nullptr,
-                                       aRunParams.context->CurrentMatrix());
-        fontParams.contextPaint = contextPaint;
+                                       aRunParams.context->CurrentMatrix()));
+        fontParams.contextPaint = contextPaint.get();
     }
 
     // Synthetic-bold strikes are each offset one device pixel in run direction.
@@ -2263,7 +2263,7 @@ gfxFont::Measure(gfxTextRun *aTextRun,
     if (aBoundingBoxType == TIGHT_HINTED_OUTLINE_EXTENTS &&
         mAntialiasOption != kAntialiasNone) {
         if (!mNonAAFont) {
-            mNonAAFont = CopyWithAntialiasOption(kAntialiasNone);
+            mNonAAFont.reset(CopyWithAntialiasOption(kAntialiasNone));
         }
         // if font subclass doesn't implement CopyWithAntialiasOption(),
         // it will return null and we'll proceed to use the existing font
@@ -2529,7 +2529,7 @@ gfxFont::GetShapedWord(DrawTarget *aDrawTarget,
         NS_WARNING("failed to create word cache entry - expect missing text");
         return nullptr;
     }
-    gfxShapedWord *sw = entry->mShapedWord;
+    gfxShapedWord* sw = entry->mShapedWord.get();
 
     bool isContent = !mStyle.systemFont;
 
@@ -2555,10 +2555,9 @@ gfxFont::GetShapedWord(DrawTarget *aDrawTarget,
     }
 #endif
 
-    sw = entry->mShapedWord = gfxShapedWord::Create(aText, aLength,
-                                                    aRunScript,
-                                                    aAppUnitsPerDevUnit,
-                                                    aFlags);
+    sw = gfxShapedWord::Create(aText, aLength, aRunScript, aAppUnitsPerDevUnit,
+                               aFlags);
+    entry->mShapedWord.reset(sw);
     if (!sw) {
         NS_WARNING("failed to create gfxShapedWord - expect missing text");
         return nullptr;
@@ -2586,7 +2585,7 @@ gfxFont::GetShapedWord(DrawTarget *aDrawTarget,
 bool
 gfxFont::CacheHashEntry::KeyEquals(const KeyTypePointer aKey) const
 {
-    const gfxShapedWord *sw = mShapedWord;
+    const gfxShapedWord* sw = mShapedWord.get();
     if (!sw) {
         return false;
     }
@@ -2655,7 +2654,7 @@ gfxFont::ShapeText(DrawTarget      *aDrawTarget,
     if (FontCanSupportGraphite() && !aVertical) {
         if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
             if (!mGraphiteShaper) {
-                mGraphiteShaper = new gfxGraphiteShaper(this);
+                mGraphiteShaper = MakeUnique(this);
             }
             ok = mGraphiteShaper->ShapeText(aDrawTarget, aText, aOffset, aLength,
                                             aScript, aVertical, aShapedText);
@@ -2664,7 +2663,7 @@ gfxFont::ShapeText(DrawTarget      *aDrawTarget,
 
     if (!ok) {
         if (!mHarfBuzzShaper) {
-            mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
+            mHarfBuzzShaper = MakeUnique(this);
         }
         ok = mHarfBuzzShaper->ShapeText(aDrawTarget, aText, aOffset, aLength,
                                         aScript, aVertical, aShapedText);
@@ -3180,26 +3179,24 @@ gfxFont::InitFakeSmallCapsRun(DrawTarget     *aDrawTarget,
                         aDrawTarget, nullptr, nullptr, nullptr, 0,
                         aTextRun->GetAppUnitsPerDevUnit()
                     };
-                    nsAutoPtr tempRun;
-                    tempRun =
+                    UniquePtr tempRun(
                         gfxTextRun::Create(¶ms, convertedString.Length(),
-                                           aTextRun->GetFontGroup(), 0);
+                                           aTextRun->GetFontGroup(), 0));
                     tempRun->AddGlyphRun(f, aMatchType, 0, true, aOrientation);
-                    if (!f->SplitAndInitTextRun(aDrawTarget, tempRun,
+                    if (!f->SplitAndInitTextRun(aDrawTarget, tempRun.get(),
                                                 convertedString.BeginReading(),
                                                 0, convertedString.Length(),
                                                 aScript, vertical)) {
                         ok = false;
                     } else {
-                        nsAutoPtr mergedRun;
-                        mergedRun =
+                        UniquePtr mergedRun(
                             gfxTextRun::Create(¶ms, runLength,
-                                               aTextRun->GetFontGroup(), 0);
-                        MergeCharactersInTextRun(mergedRun, tempRun,
+                                               aTextRun->GetFontGroup(), 0));
+                        MergeCharactersInTextRun(mergedRun.get(), tempRun.get(),
                                                  charsToMergeArray.Elements(),
                                                  deletedCharsArray.Elements());
                         gfxTextRun::Range runRange(0, runLength);
-                        aTextRun->CopyGlyphDataFrom(mergedRun, runRange,
+                        aTextRun->CopyGlyphDataFrom(mergedRun.get(), runRange,
                                                     aOffset + runStart);
                     }
                 } else {
@@ -3801,7 +3798,8 @@ void
 gfxFont::AddGlyphChangeObserver(GlyphChangeObserver *aObserver)
 {
     if (!mGlyphChangeObservers) {
-        mGlyphChangeObservers = new nsTHashtable >;
+        mGlyphChangeObservers.reset(
+            new nsTHashtable>);
     }
     mGlyphChangeObservers->PutEntry(aObserver);
 }
diff --git a/gfx/thebes/gfxFont.h b/gfx/thebes/gfxFont.h
index 57ef82e699..a977f5578d 100644
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -654,7 +654,7 @@ protected:
     static void GetRoundOffsetsToPixels(DrawTarget* aDrawTarget,
                                         bool* aRoundX, bool* aRoundY);
 
-    // the font this shaper is working with. The font owns a nsAutoPtr reference
+    // the font this shaper is working with. The font owns a UniquePtr reference
     // to this object, and will destroy it before it dies. Thus, mFont will always
     // be valid.
     gfxFont* MOZ_NON_OWNING_REF mFont;
@@ -1146,7 +1146,7 @@ protected:
         nsTArray::index_type mLastUsed;
     };
 
-    nsAutoPtr   mDetailedGlyphs;
+    mozilla::UniquePtr   mDetailedGlyphs;
 
     // Number of char16_t characters and CompressedGlyph glyph records
     uint32_t                        mLength;
@@ -1535,7 +1535,7 @@ public:
             return GetHorizontalMetrics();
         }
         if (!mVerticalMetrics) {
-            mVerticalMetrics = CreateVerticalMetrics();
+            mVerticalMetrics.reset(CreateVerticalMetrics());
         }
         return *mVerticalMetrics;
     }
@@ -1752,7 +1752,7 @@ public:
     // any attempt to use GetShapedWord().
     void InitWordCache() {
         if (!mWordCache) {
-            mWordCache = new nsTHashtable;
+            mWordCache = mozilla::MakeUnique>();
         }
     }
 
@@ -2066,10 +2066,10 @@ protected:
 
         enum { ALLOW_MEMMOVE = true };
 
-        nsAutoPtr mShapedWord;
+        mozilla::UniquePtr mShapedWord;
     };
 
-    nsAutoPtr > mWordCache;
+    mozilla::UniquePtr > mWordCache;
 
     static const uint32_t  kShapedWordCacheMaxAge = 3;
 
@@ -2085,7 +2085,8 @@ protected:
     nsExpirationState          mExpirationState;
     gfxFontStyle               mStyle;
     AutoTArray mGlyphExtentsArray;
-    nsAutoPtr > > mGlyphChangeObservers;
+    mozilla::UniquePtr>>
+                               mGlyphChangeObservers;
 
     gfxFloat                   mAdjustedSize;
 
@@ -2099,13 +2100,13 @@ protected:
 
     // a copy of the font without antialiasing, if needed for separate
     // measurement by mathml code
-    nsAutoPtr         mNonAAFont;
+    mozilla::UniquePtr         mNonAAFont;
 
     // we create either or both of these shapers when needed, depending
     // whether the font has graphite tables, and whether graphite shaping
     // is actually enabled
-    nsAutoPtr   mHarfBuzzShaper;
-    nsAutoPtr   mGraphiteShaper;
+    mozilla::UniquePtr   mHarfBuzzShaper;
+    mozilla::UniquePtr   mGraphiteShaper;
 
     // if a userfont with unicode-range specified, contains map of *possible*
     // ranges supported by font
@@ -2114,7 +2115,7 @@ protected:
     RefPtr mAzureScaledFont;
 
     // For vertical metrics, created on demand.
-    nsAutoPtr mVerticalMetrics;
+    mozilla::UniquePtr mVerticalMetrics;
 
     // Helper for subclasses that want to initialize standard metrics from the
     // tables of sfnt (TrueType/OpenType) fonts.
diff --git a/gfx/thebes/gfxFontConstants.h b/gfx/thebes/gfxFontConstants.h
index ca2adca43e..5aa1eb0c78 100644
--- a/gfx/thebes/gfxFontConstants.h
+++ b/gfx/thebes/gfxFontConstants.h
@@ -203,6 +203,12 @@ enum {
 #define NS_FONT_VARIANT_POSITION_SUPER              1
 #define NS_FONT_VARIANT_POSITION_SUB                2
 
+#define NS_FONT_VARIANT_WIDTH_NORMAL  0
+#define NS_FONT_VARIANT_WIDTH_FULL    1
+#define NS_FONT_VARIANT_WIDTH_HALF    2
+#define NS_FONT_VARIANT_WIDTH_THIRD   3
+#define NS_FONT_VARIANT_WIDTH_QUARTER 4
+
 // based on fixed offset values used within WebKit
 #define NS_FONT_SUBSCRIPT_OFFSET_RATIO     (0.20)
 #define NS_FONT_SUPERSCRIPT_OFFSET_RATIO   (0.34)
diff --git a/gfx/thebes/gfxFontEntry.cpp b/gfx/thebes/gfxFontEntry.cpp
index 8d7ed4653b..ae95bc8e84 100644
--- a/gfx/thebes/gfxFontEntry.cpp
+++ b/gfx/thebes/gfxFontEntry.cpp
@@ -385,7 +385,7 @@ gfxFontEntry::TryGetSVGData(gfxFont* aFont)
 
         // gfxSVGGlyphs will hb_blob_destroy() the table when it is finished
         // with it.
-        mSVGGlyphs = new gfxSVGGlyphs(svgTable, this);
+        mSVGGlyphs = MakeUnique(svgTable, this);
     }
 
     if (!mFontsUsingSVGGlyphs.Contains(aFont)) {
@@ -430,9 +430,9 @@ gfxFontEntry::TryGetMathTable()
 
         // gfxMathTable will hb_blob_destroy() the table when it is finished
         // with it.
-        mMathTable = new gfxMathTable(mathTable);
+        mMathTable = MakeUnique(mathTable);
         if (!mMathTable->HasValidHeaders()) {
-            mMathTable = nullptr;
+            mMathTable.reset(nullptr);
             return false;
         }
     }
@@ -643,7 +643,7 @@ gfxFontEntry::GetExistingFontTable(uint32_t aTag, hb_blob_t **aBlob)
     if (!mFontTableCache) {
         // we do this here rather than on fontEntry construction
         // because not all shapers will access the table cache at all
-        mFontTableCache = new nsTHashtable(8);
+        mFontTableCache = MakeUnique>(8);
     }
 
     FontTableHashEntry *entry = mFontTableCache->GetEntry(aTag);
@@ -662,7 +662,7 @@ gfxFontEntry::ShareFontTableAndGetBlob(uint32_t aTag,
     if (MOZ_UNLIKELY(!mFontTableCache)) {
         // we do this here rather than on fontEntry construction
         // because not all shapers will access the table cache at all
-      mFontTableCache = new nsTHashtable(8);
+      mFontTableCache = MakeUnique>(8);
     }
 
     FontTableHashEntry *entry = mFontTableCache->PutEntry(aTag);
@@ -676,7 +676,7 @@ gfxFontEntry::ShareFontTableAndGetBlob(uint32_t aTag,
         return nullptr;
     }
 
-    return entry->ShareTableAndGetBlob(Move(*aBuffer), mFontTableCache);
+    return entry->ShareTableAndGetBlob(Move(*aBuffer), mFontTableCache.get());
 }
 
 static int
@@ -893,7 +893,7 @@ bool
 gfxFontEntry::SupportsOpenTypeFeature(int32_t aScript, uint32_t aFeatureTag)
 {
     if (!mSupportedFeatures) {
-        mSupportedFeatures = new nsDataHashtable();
+        mSupportedFeatures = MakeUnique>();
     }
 
     // note: high-order three bytes *must* be unique for each feature
@@ -971,7 +971,7 @@ const hb_set_t*
 gfxFontEntry::InputsForOpenTypeFeature(int32_t aScript, uint32_t aFeatureTag)
 {
     if (!mFeatureInputs) {
-        mFeatureInputs = new nsDataHashtable();
+        mFeatureInputs = MakeUnique>();
     }
 
     NS_ASSERTION(aFeatureTag == HB_TAG('s','u','p','s') ||
@@ -1032,7 +1032,7 @@ bool
 gfxFontEntry::SupportsGraphiteFeature(uint32_t aFeatureTag)
 {
     if (!mSupportedFeatures) {
-        mSupportedFeatures = new nsDataHashtable();
+        mSupportedFeatures = MakeUnique>();
     }
 
     // note: high-order three bytes *must* be unique for each feature
diff --git a/gfx/thebes/gfxFontEntry.h b/gfx/thebes/gfxFontEntry.h
index f177c4fdaf..0036de2cbb 100644
--- a/gfx/thebes/gfxFontEntry.h
+++ b/gfx/thebes/gfxFontEntry.h
@@ -433,14 +433,14 @@ public:
     RefPtr mCharacterMap;
     uint32_t         mUVSOffset;
     mozilla::UniquePtr mUVSData;
-    nsAutoPtr mUserFontData;
-    nsAutoPtr mSVGGlyphs;
+    mozilla::UniquePtr mUserFontData;
+    mozilla::UniquePtr mSVGGlyphs;
     // list of gfxFonts that are using SVG glyphs
     nsTArray mFontsUsingSVGGlyphs;
-    nsAutoPtr mMathTable;
+    mozilla::UniquePtr mMathTable;
     nsTArray mFeatureSettings;
-    nsAutoPtr> mSupportedFeatures;
-    nsAutoPtr> mFeatureInputs;
+    mozilla::UniquePtr> mSupportedFeatures;
+    mozilla::UniquePtr> mFeatureInputs;
     uint32_t         mLanguageOverride;
 
     // Color Layer font support
@@ -625,7 +625,7 @@ private:
         hb_blob_t *mBlob;
     };
 
-    nsAutoPtr > mFontTableCache;
+    mozilla::UniquePtr > mFontTableCache;
 
     gfxFontEntry(const gfxFontEntry&);
     gfxFontEntry& operator=(const gfxFontEntry&);
diff --git a/gfx/thebes/gfxFontInfoLoader.h b/gfx/thebes/gfxFontInfoLoader.h
index cde5d3ea35..40b655aa5b 100644
--- a/gfx/thebes/gfxFontInfoLoader.h
+++ b/gfx/thebes/gfxFontInfoLoader.h
@@ -6,7 +6,6 @@
 #ifndef GFX_FONT_INFO_LOADER_H
 #define GFX_FONT_INFO_LOADER_H
 
-#include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsIObserver.h"
 #include "nsITimer.h"
diff --git a/gfx/thebes/gfxFontUtils.cpp b/gfx/thebes/gfxFontUtils.cpp
index af7b896a10..7e62e3b6f7 100644
--- a/gfx/thebes/gfxFontUtils.cpp
+++ b/gfx/thebes/gfxFontUtils.cpp
@@ -66,8 +66,10 @@ gfxSparseBitSet::Dump(const char* aPrefix, eGfxLog aWhichLog) const
     uint32_t b, numBlocks = mBlocks.Length();
 
     for (b = 0; b < numBlocks; b++) {
-        Block *block = mBlocks[b];
-        if (!block) continue;
+        Block *block = mBlocks[b].get();
+        if (!block) {
+            continue;
+        }
         const int BUFSIZE = 256;
         char outStr[BUFSIZE];
         int index = 0;
diff --git a/gfx/thebes/gfxFontUtils.h b/gfx/thebes/gfxFontUtils.h
index 816a570150..2c22d1ff92 100644
--- a/gfx/thebes/gfxFontUtils.h
+++ b/gfx/thebes/gfxFontUtils.h
@@ -9,7 +9,6 @@
 #include "gfxPlatform.h"
 #include "nsComponentManagerUtils.h"
 #include "nsTArray.h"
-#include "nsAutoPtr.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Endian.h"
 #include "mozilla/MemoryReporting.h"
@@ -44,9 +43,10 @@ public:
         uint32_t len = aBitset.mBlocks.Length();
         mBlocks.AppendElements(len);
         for (uint32_t i = 0; i < len; ++i) {
-            Block *block = aBitset.mBlocks[i];
-            if (block)
-                mBlocks[i] = new Block(*block);
+            Block *block = aBitset.mBlocks[i].get();
+            if (block) {
+                mBlocks[i] = mozilla::MakeUnique(*block);
+            }
         }
     }
 
@@ -56,8 +56,8 @@ public:
         }
         size_t n = mBlocks.Length();
         for (size_t i = 0; i < n; ++i) {
-            const Block *b1 = mBlocks[i];
-            const Block *b2 = aOther->mBlocks[i];
+            const Block *b1 = mBlocks[i].get();
+            const Block *b2 = aOther->mBlocks[i].get();
             if (!b1 != !b2) {
                 return false;
             }
@@ -74,11 +74,13 @@ public:
     bool test(uint32_t aIndex) const {
         NS_ASSERTION(mBlocks.DebugGetHeader(), "mHdr is null, this is bad");
         uint32_t blockIndex = aIndex/BLOCK_SIZE_BITS;
-        if (blockIndex >= mBlocks.Length())
+        if (blockIndex >= mBlocks.Length()) {
             return false;
-        Block *block = mBlocks[blockIndex];
-        if (!block)
+        }
+        const Block *block = mBlocks[blockIndex].get();
+        if (!block) {
             return false;
+        }
         return ((block->mBits[(aIndex>>3) & (BLOCK_SIZE - 1)]) & (1 << (aIndex & 0x7))) != 0;
     }
 
@@ -99,43 +101,54 @@ public:
 
         endBlock = aEnd >> BLOCK_INDEX_SHIFT;
         for (blockIndex = startBlock; blockIndex <= endBlock; blockIndex++) {
-            if (blockIndex < blockLen && mBlocks[blockIndex])
+            if (blockIndex < blockLen && mBlocks[blockIndex]) {
                 hasBlocksInRange = true;
+            }
+        }
+        if (!hasBlocksInRange) {
+            return false;
         }
-        if (!hasBlocksInRange) return false;
 
         Block *block;
         uint32_t i, start, end;
         
         // first block, check bits
-        if ((block = mBlocks[startBlock])) {
+        if ((block = mBlocks[startBlock].get())) {
             start = aStart;
             end = std::min(aEnd, ((startBlock+1) << BLOCK_INDEX_SHIFT) - 1);
             for (i = start; i <= end; i++) {
-                if ((block->mBits[(i>>3) & (BLOCK_SIZE - 1)]) & (1 << (i & 0x7)))
+                if ((block->mBits[(i>>3) & (BLOCK_SIZE - 1)]) & (1 << (i & 0x7))) {
                     return true;
+                }
             }
         }
-        if (endBlock == startBlock) return false;
+        if (endBlock == startBlock) {
+            return false;
+        }
 
         // [2..n-1] blocks check bytes
         for (blockIndex = startBlock + 1; blockIndex < endBlock; blockIndex++) {
             uint32_t index;
             
-            if (blockIndex >= blockLen || !(block = mBlocks[blockIndex])) continue;
+            if (blockIndex >= blockLen ||
+                !(block = mBlocks[blockIndex].get())) {
+                continue;
+            }
             for (index = 0; index < BLOCK_SIZE; index++) {
-                if (block->mBits[index]) 
+                if (block->mBits[index]) {
                     return true;
+                }
             }
         }
         
         // last block, check bits
-        if (endBlock < blockLen && (block = mBlocks[endBlock])) {
+        if (endBlock < blockLen && (block = mBlocks[endBlock].get())) {
             start = endBlock << BLOCK_INDEX_SHIFT;
             end = aEnd;
             for (i = start; i <= end; i++) {
-                if ((block->mBits[(i>>3) & (BLOCK_SIZE - 1)]) & (1 << (i & 0x7)))
+                if ((block->mBits[(i>>3) & (BLOCK_SIZE - 1)]) & (1 << (i & 0x7))) {
                     return true;
+                }
             }
         }
         
@@ -145,14 +158,12 @@ public:
     void set(uint32_t aIndex) {
         uint32_t blockIndex = aIndex/BLOCK_SIZE_BITS;
         if (blockIndex >= mBlocks.Length()) {
-            nsAutoPtr *blocks = mBlocks.AppendElements(blockIndex + 1 - mBlocks.Length());
-            if (MOZ_UNLIKELY(!blocks)) // OOM
-                return;
+            mBlocks.AppendElements(blockIndex + 1 - mBlocks.Length());
         }
-        Block *block = mBlocks[blockIndex];
+        Block *block = mBlocks[blockIndex].get();
         if (!block) {
             block = new Block;
-            mBlocks[blockIndex] = block;
+            mBlocks[blockIndex].reset(block);
         }
         block->mBits[(aIndex>>3) & (BLOCK_SIZE - 1)] |= 1 << (aIndex & 0x7);
     }
@@ -170,26 +181,24 @@ public:
 
         if (endIndex >= mBlocks.Length()) {
             uint32_t numNewBlocks = endIndex + 1 - mBlocks.Length();
-            nsAutoPtr *blocks = mBlocks.AppendElements(numNewBlocks);
-            if (MOZ_UNLIKELY(!blocks)) // OOM
-                return;
+            mBlocks.AppendElements(numNewBlocks);
         }
 
         for (uint32_t i = startIndex; i <= endIndex; ++i) {
             const uint32_t blockFirstBit = i * BLOCK_SIZE_BITS;
             const uint32_t blockLastBit = blockFirstBit + BLOCK_SIZE_BITS - 1;
 
-            Block *block = mBlocks[i];
+            Block *block = mBlocks[i].get();
             if (!block) {
-                bool fullBlock = false;
-                if (aStart <= blockFirstBit && aEnd >= blockLastBit)
-                    fullBlock = true;
+                bool fullBlock =
+                    (aStart <= blockFirstBit && aEnd >= blockLastBit);
 
                 block = new Block(fullBlock ? 0xFF : 0);
-                mBlocks[i] = block;
+                mBlocks[i].reset(block);
 
-                if (fullBlock)
+                if (fullBlock) {
                     continue;
+                }
             }
 
             const uint32_t start = aStart > blockFirstBit ? aStart - blockFirstBit : 0;
@@ -204,11 +213,9 @@ public:
     void clear(uint32_t aIndex) {
         uint32_t blockIndex = aIndex/BLOCK_SIZE_BITS;
         if (blockIndex >= mBlocks.Length()) {
-            nsAutoPtr *blocks = mBlocks.AppendElements(blockIndex + 1 - mBlocks.Length());
-            if (MOZ_UNLIKELY(!blocks)) // OOM
-                return;
+            mBlocks.AppendElements(blockIndex + 1 - mBlocks.Length());
         }
-        Block *block = mBlocks[blockIndex];
+        Block *block = mBlocks[blockIndex].get();
         if (!block) {
             return;
         }
@@ -221,15 +228,13 @@ public:
 
         if (endIndex >= mBlocks.Length()) {
             uint32_t numNewBlocks = endIndex + 1 - mBlocks.Length();
-            nsAutoPtr *blocks = mBlocks.AppendElements(numNewBlocks);
-            if (MOZ_UNLIKELY(!blocks)) // OOM
-                return;
+            mBlocks.AppendElements(numNewBlocks);
         }
 
         for (uint32_t i = startIndex; i <= endIndex; ++i) {
             const uint32_t blockFirstBit = i * BLOCK_SIZE_BITS;
 
-            Block *block = mBlocks[i];
+            Block *block = mBlocks[i].get();
             if (!block) {
                 // any nonexistent block is implicitly all clear,
                 // so there's no need to even create it
@@ -249,7 +254,7 @@ public:
         size_t total = mBlocks.ShallowSizeOfExcludingThis(aMallocSizeOf);
         for (uint32_t i = 0; i < mBlocks.Length(); i++) {
             if (mBlocks[i]) {
-                total += aMallocSizeOf(mBlocks[i]);
+                total += aMallocSizeOf(mBlocks[i].get());
             }
         }
         return total;
@@ -262,8 +267,9 @@ public:
     // clear out all blocks in the array
     void reset() {
         uint32_t i;
-        for (i = 0; i < mBlocks.Length(); i++)
-            mBlocks[i] = nullptr;    
+        for (i = 0; i < mBlocks.Length(); i++) {
+            mBlocks[i] = nullptr;
+        }
     }
 
     // set this bitset to the union of its current contents and another
@@ -272,10 +278,7 @@ public:
         uint32_t blockCount = aBitset.mBlocks.Length();
         if (blockCount > mBlocks.Length()) {
             uint32_t needed = blockCount - mBlocks.Length();
-            nsAutoPtr *blocks = mBlocks.AppendElements(needed);
-            if (MOZ_UNLIKELY(!blocks)) { // OOM
-                return;
-            }
+            mBlocks.AppendElements(needed);
         }
         // for each block that may be present in aBitset...
         for (uint32_t i = 0; i < blockCount; ++i) {
@@ -285,7 +288,7 @@ public:
             }
             // if the block is missing in this set, just copy the other
             if (!mBlocks[i]) {
-                mBlocks[i] = new Block(*aBitset.mBlocks[i]);
+                mBlocks[i] = mozilla::MakeUnique(*aBitset.mBlocks[i]);
                 continue;
             }
             // else set existing block to the union of both
@@ -306,7 +309,7 @@ public:
         uint32_t check = adler32(0, Z_NULL, 0);
         for (uint32_t i = 0; i < mBlocks.Length(); i++) {
             if (mBlocks[i]) {
-                const Block *block = mBlocks[i];
+                const Block *block = mBlocks[i].get();
                 check = adler32(check, (uint8_t*) (&i), 4);
                 check = adler32(check, (uint8_t*) block, sizeof(Block));
             }
@@ -315,7 +318,7 @@ public:
     }
 
 private:
-    nsTArray< nsAutoPtr > mBlocks;
+    nsTArray> mBlocks;
 };
 
 #define TRUETYPE_TAG(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d))
diff --git a/gfx/thebes/gfxGDIFont.cpp b/gfx/thebes/gfxGDIFont.cpp
index 82072b3065..4105e7f715 100644
--- a/gfx/thebes/gfxGDIFont.cpp
+++ b/gfx/thebes/gfxGDIFont.cpp
@@ -469,7 +469,7 @@ gfxGDIFont::GetGlyph(uint32_t aUnicode, uint32_t aVarSelector)
     }
 
     if (!mGlyphIDs) {
-        mGlyphIDs = new nsDataHashtable(64);
+        mGlyphIDs = MakeUnique>(64);
     }
 
     uint32_t gid;
@@ -506,7 +506,7 @@ int32_t
 gfxGDIFont::GetGlyphWidth(DrawTarget& aDrawTarget, uint16_t aGID)
 {
     if (!mGlyphWidths) {
-        mGlyphWidths = new nsDataHashtable(128);
+        mGlyphWidths = MakeUnique>(128);
     }
 
     int32_t width;
diff --git a/gfx/thebes/gfxGDIFont.h b/gfx/thebes/gfxGDIFont.h
index 50190d6887..aee2ad2a7d 100644
--- a/gfx/thebes/gfxGDIFont.h
+++ b/gfx/thebes/gfxGDIFont.h
@@ -106,11 +106,11 @@ protected:
     bool                  mNeedsBold;
 
     // cache of glyph IDs (used for non-sfnt fonts only)
-    nsAutoPtr > mGlyphIDs;
+    mozilla::UniquePtr > mGlyphIDs;
     SCRIPT_CACHE          mScriptCache;
 
     // cache of glyph widths in 16.16 fixed-point pixels
-    nsAutoPtr > mGlyphWidths;
+    mozilla::UniquePtr > mGlyphWidths;
 };
 
 #endif /* GFX_GDIFONT_H */
diff --git a/gfx/thebes/gfxGDIFontList.cpp b/gfx/thebes/gfxGDIFontList.cpp
index 1e4499dcc6..2db1028b14 100644
--- a/gfx/thebes/gfxGDIFontList.cpp
+++ b/gfx/thebes/gfxGDIFontList.cpp
@@ -130,7 +130,7 @@ GDIFontEntry::GDIFontEntry(const nsAString& aFaceName,
       mFamilyHasItalicFace(aFamilyHasItalicFace),
       mCharset(), mUnicodeRanges()
 {
-    mUserFontData = aUserFontData;
+    mUserFontData.reset(aUserFontData);
     mStyle = aStyle;
     mWeight = aWeight;
     mStretch = aStretch;
@@ -895,23 +895,27 @@ gfxGDIFontList::MakePlatformFont(const nsAString& aFontName,
     return fe;
 }
 
-gfxFontFamily*
-gfxGDIFontList::FindFamily(const nsAString& aFamily, gfxFontStyle* aStyle,
-                           gfxFloat aDevToCssSize)
+bool
+gfxGDIFontList::FindAndAddFamilies(const nsAString& aFamily,
+                                   nsTArray* aOutput,
+                                   gfxFontStyle* aStyle,
+                                   gfxFloat aDevToCssSize)
 {
     nsAutoString keyName(aFamily);
     BuildKeyNameFromFontName(keyName);
 
     gfxFontFamily *ff = mFontSubstitutes.GetWeak(keyName);
     if (ff) {
-        return ff;
+        aOutput->AppendElement(ff);
+        return true;
     }
 
     if (mNonExistingFonts.Contains(keyName)) {
-        return nullptr;
+        return false;
     }
 
-    return gfxPlatformFontList::FindFamily(aFamily);
+    return gfxPlatformFontList::FindAndAddFamilies(aFamily, aOutput, aStyle,
+                                                   aDevToCssSize);
 }
 
 gfxFontFamily*
diff --git a/gfx/thebes/gfxGDIFontList.h b/gfx/thebes/gfxGDIFontList.h
index b9172c8d35..f6890fbb7b 100644
--- a/gfx/thebes/gfxGDIFontList.h
+++ b/gfx/thebes/gfxGDIFontList.h
@@ -309,9 +309,10 @@ public:
 
     virtual gfxFontFamily* GetDefaultFont(const gfxFontStyle* aStyle);
 
-    gfxFontFamily* FindFamily(const nsAString& aFamily,
-                              gfxFontStyle* aStyle = nullptr,
-                              gfxFloat aDevToCssSize = 1.0) override;
+    bool FindAndAddFamilies(const nsAString& aFamily,
+                            nsTArray* aOutput,
+                            gfxFontStyle* aStyle = nullptr,
+                            gfxFloat aDevToCssSize = 1.0) override;
 
     virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
                                           uint16_t aWeight,
diff --git a/gfx/thebes/gfxImageSurface.h b/gfx/thebes/gfxImageSurface.h
index 829fa421c8..dea580f5e5 100644
--- a/gfx/thebes/gfxImageSurface.h
+++ b/gfx/thebes/gfxImageSurface.h
@@ -9,7 +9,6 @@
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/RefPtr.h"
 #include "gfxASurface.h"
-#include "nsAutoPtr.h"
 #include "nsSize.h"
 
 // ARGB -- raw buffer.. wont be changed.. good for storing data.
diff --git a/gfx/thebes/gfxMacFont.cpp b/gfx/thebes/gfxMacFont.cpp
index 29b4dd092b..b15c98df0e 100644
--- a/gfx/thebes/gfxMacFont.cpp
+++ b/gfx/thebes/gfxMacFont.cpp
@@ -138,7 +138,7 @@ gfxMacFont::ShapeText(DrawTarget     *aDrawTarget,
     if (static_cast(GetFontEntry())->RequiresAATLayout() &&
         !aVertical) {
         if (!mCoreTextShaper) {
-            mCoreTextShaper = new gfxCoreTextShaper(this);
+            mCoreTextShaper = MakeUnique(this);
         }
         if (mCoreTextShaper->ShapeText(aDrawTarget, aText, aOffset, aLength,
                                        aScript, aVertical, aShapedText)) {
diff --git a/gfx/thebes/gfxMacFont.h b/gfx/thebes/gfxMacFont.h
index 142403780e..4ace6e37c6 100644
--- a/gfx/thebes/gfxMacFont.h
+++ b/gfx/thebes/gfxMacFont.h
@@ -93,7 +93,7 @@ protected:
 
     cairo_font_face_t    *mFontFace;
 
-    nsAutoPtr mCoreTextShaper;
+    mozilla::UniquePtr mCoreTextShaper;
 
     Metrics               mMetrics;
     uint32_t              mSpaceGlyph;
diff --git a/gfx/thebes/gfxMacPlatformFontList.h b/gfx/thebes/gfxMacPlatformFontList.h
index 7a09542c74..6d5cdeee0e 100644
--- a/gfx/thebes/gfxMacPlatformFontList.h
+++ b/gfx/thebes/gfxMacPlatformFontList.h
@@ -128,9 +128,10 @@ public:
                                    const uint8_t* aFontData,
                                    uint32_t aLength) override;
 
-    gfxFontFamily* FindFamily(const nsAString& aFamily,
-                              gfxFontStyle* aStyle = nullptr,
-                              gfxFloat aDevToCssSize = 1.0) override;
+    bool FindAndAddFamilies(const nsAString& aFamily,
+                            nsTArray* aOutput,
+                            gfxFontStyle* aStyle = nullptr,
+                            gfxFloat aDevToCssSize = 1.0) override;
 
     // lookup the system font for a particular system font type and set
     // the name and style characteristics
diff --git a/gfx/thebes/gfxMacPlatformFontList.mm b/gfx/thebes/gfxMacPlatformFontList.mm
index 573eb6c892..c3221f540a 100644
--- a/gfx/thebes/gfxMacPlatformFontList.mm
+++ b/gfx/thebes/gfxMacPlatformFontList.mm
@@ -1664,14 +1664,14 @@ gfxMacPlatformFontList::MakePlatformFont(const nsAString& aFontName,
         return nullptr;
     }
 
-    nsAutoPtr
-        newFontEntry(new MacOSFontEntry(uniqueName, fontRef, aWeight,
-                                        aStretch, aStyle, true, false));
+    auto newFontEntry =
+        MakeUnique(uniqueName, fontRef, aWeight, aStretch,
+                                   aStyle, true, false);
     ::CFRelease(fontRef);
 
     // if succeeded and font cmap is good, return the new font
     if (newFontEntry->mIsValid && NS_SUCCEEDED(newFontEntry->ReadCMAP())) {
-        return newFontEntry.forget();
+        return newFontEntry.release();
     }
 
     // if something is funky about this font, delete immediately
@@ -1688,20 +1688,25 @@ gfxMacPlatformFontList::MakePlatformFont(const nsAString& aFontName,
 // WebCore/platform/graphics/mac/FontCacheMac.mm
 static const char kSystemFont_system[] = "-apple-system";
 
-gfxFontFamily*
-gfxMacPlatformFontList::FindFamily(const nsAString& aFamily, gfxFontStyle* aStyle,
-                                   gfxFloat aDevToCssSize)
+bool
+gfxMacPlatformFontList::FindAndAddFamilies(const nsAString& aFamily,
+                                           nsTArray* aOutput,
+                                           gfxFontStyle* aStyle,
+                                           gfxFloat aDevToCssSize)
 {
     // search for special system font name, -apple-system
     if (aFamily.EqualsLiteral(kSystemFont_system)) {
         if (mUseSizeSensitiveSystemFont &&
             aStyle && (aStyle->size * aDevToCssSize) >= kTextDisplayCrossover) {
-            return mSystemDisplayFontFamily;
+            aOutput->AppendElement(mSystemDisplayFontFamily);
+            return true;
         }
-        return mSystemTextFontFamily;
+        aOutput->AppendElement(mSystemTextFontFamily);
+        return true;
     }
 
-    return gfxPlatformFontList::FindFamily(aFamily, aStyle, aDevToCssSize);
+    return gfxPlatformFontList::FindAndAddFamilies(aFamily, aOutput, aStyle,
+                                                   aDevToCssSize);
 }
 
 void
diff --git a/gfx/thebes/gfxPattern.h b/gfx/thebes/gfxPattern.h
index 8dfc111d8b..6463040367 100644
--- a/gfx/thebes/gfxPattern.h
+++ b/gfx/thebes/gfxPattern.h
@@ -13,7 +13,6 @@
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/PatternHelpers.h"
 #include "nsISupportsImpl.h"
-#include "nsAutoPtr.h"
 #include "nsTArray.h"
 
 typedef struct _cairo_pattern cairo_pattern_t;
diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp
index 7ec1bab6e8..2984b80ff5 100644
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -470,7 +470,8 @@ MemoryPressureObserver::Observe(nsISupports *aSubject,
     Factory::PurgeAllCaches();
     gfxGradientCache::PurgeAllCaches();
 
-    gfxPlatform::GetPlatform()->PurgeSkiaCache();
+    gfxPlatform::PurgeSkiaFontCache();
+    gfxPlatform::GetPlatform()->PurgeSkiaGPUCache();
     return NS_OK;
 }
 
@@ -599,13 +600,12 @@ gfxPlatform::Init()
                                gfxPrefs::Direct2DForceEnabled(),
                                gfxPrefs::DirectWriteFontRenderingForceEnabled());
       // Layers prefs
-      forcedPrefs.AppendPrintf("-L%d%d%d%d%d%d",
+      forcedPrefs.AppendPrintf("-L%d%d%d%d%d",
                                gfxPrefs::LayersAMDSwitchableGfxEnabled(),
                                gfxPrefs::LayersAccelerationDisabled(),
                                gfxPrefs::LayersAccelerationForceEnabled(),
                                gfxPrefs::LayersD3D11DisableWARP(),
-                               gfxPrefs::LayersD3D11ForceWARP(),
-                               gfxPrefs::LayersOffMainThreadCompositionForceEnabled());
+                               gfxPrefs::LayersD3D11ForceWARP());
       // WebGL prefs
       forcedPrefs.AppendPrintf("-W%d%d%d%d%d%d%d%d",
                                gfxPrefs::WebGLANGLEForceD3D11(),
@@ -878,8 +878,11 @@ gfxPlatform::ShutdownLayersIPC()
     if (XRE_IsContentProcess()) {
 
         gfx::VRManagerChild::ShutDown();
-        layers::ImageBridgeChild::ShutDown();
-        layers::CompositorBridgeChild::ShutDown();
+        // cf bug 1215265.
+        if (gfxPrefs::ChildProcessShutdown()) {
+          layers::CompositorBridgeChild::ShutDown();
+          layers::ImageBridgeChild::ShutDown();
+        }
 
     } else if (XRE_IsParentProcess()) {
 
@@ -938,24 +941,6 @@ gfxPlatform::CreateDrawTargetForSurface(gfxASurface *aSurface, const IntSize& aS
   return drawTarget.forget();
 }
 
-// This is a temporary function used by ContentClient to build a DrawTarget
-// around the gfxASurface. This should eventually be replaced by plumbing
-// the DrawTarget through directly
-already_AddRefed
-gfxPlatform::CreateDrawTargetForUpdateSurface(gfxASurface *aSurface, const IntSize& aSize)
-{
-#ifdef XP_MACOSX
-  // this is a bit of a hack that assumes that the buffer associated with the CGContext
-  // will live around long enough that nothing bad will happen.
-  if (aSurface->GetType() == gfxSurfaceType::Quartz) {
-    return Factory::CreateDrawTargetForCairoCGContext(static_cast(aSurface)->GetCGContext(), aSize);
-  }
-#endif
-  MOZ_CRASH("GFX: unused function");
-  return nullptr;
-}
-
-
 cairo_user_data_key_t kSourceSurface;
 
 /**
@@ -1335,7 +1320,17 @@ gfxPlatform::GetSkiaGLGlue()
 }
 
 void
-gfxPlatform::PurgeSkiaCache()
+gfxPlatform::PurgeSkiaFontCache()
+{
+#ifdef USE_SKIA
+  if (gfxPlatform::GetPlatform()->GetDefaultContentBackend() == BackendType::SKIA) {
+    SkGraphics::PurgeFontCache();
+  }
+#endif
+}
+
+void
+gfxPlatform::PurgeSkiaGPUCache()
 {
 #ifdef USE_SKIA_GPU
   if (!mSkiaGlue)
@@ -1446,13 +1441,26 @@ gfxPlatform::GetFontList(nsIAtom *aLangGroup,
                          const nsACString& aGenericFamily,
                          nsTArray& aListOfFonts)
 {
-    return NS_ERROR_NOT_IMPLEMENTED;
+    gfxPlatformFontList::PlatformFontList()->GetFontList(aLangGroup,
+                                                         aGenericFamily,
+                                                         aListOfFonts);
+    return NS_OK;
 }
 
 nsresult
 gfxPlatform::UpdateFontList()
 {
-    return NS_ERROR_NOT_IMPLEMENTED;
+    gfxPlatformFontList::PlatformFontList()->UpdateFontList();
+    return NS_OK;
+}
+
+nsresult
+gfxPlatform::GetStandardFamilyName(const nsAString& aFontName,
+                                   nsAString& aFamilyName)
+{
+    gfxPlatformFontList::PlatformFontList()->GetStandardFamilyName(aFontName,
+                                                                   aFamilyName);
+    return NS_OK;
 }
 
 bool
@@ -1527,6 +1535,18 @@ gfxPlatform::UseGraphiteShaping()
     return mGraphiteShapingEnabled;
 }
 
+gfxFontEntry*
+gfxPlatform::LookupLocalFont(const nsAString& aFontName,
+                             uint16_t aWeight,
+                             int16_t aStretch,
+                             uint8_t aStyle)
+{
+    return gfxPlatformFontList::PlatformFontList()->LookupLocalFont(aFontName,
+                                                                    aWeight,
+                                                                    aStretch,
+                                                                    aStyle);
+}
+
 gfxFontEntry*
 gfxPlatform::MakePlatformFont(const nsAString& aFontName,
                               uint16_t aWeight,
@@ -1535,15 +1555,12 @@ gfxPlatform::MakePlatformFont(const nsAString& aFontName,
                               const uint8_t* aFontData,
                               uint32_t aLength)
 {
-    // Default implementation does not handle activating downloaded fonts;
-    // just free the data and return.
-    // Platforms that support @font-face must override this,
-    // using the data to instantiate the font, and taking responsibility
-    // for freeing it when no longer required.
-    if (aFontData) {
-        NS_Free((void*)aFontData);
-    }
-    return nullptr;
+    return gfxPlatformFontList::PlatformFontList()->MakePlatformFont(aFontName,
+                                                                     aWeight,
+                                                                     aStretch,
+                                                                     aStyle,
+                                                                     aFontData,
+                                                                     aLength);
 }
 
 mozilla::layers::DiagnosticTypes
@@ -1929,6 +1946,8 @@ gfxPlatform::FlushFontAndWordCaches()
         fontCache->AgeAllGenerations();
         fontCache->FlushShapedWordCaches();
     }
+
+    gfxPlatform::PurgeSkiaFontCache();
 }
 
 void
@@ -2208,15 +2227,14 @@ gfxPlatform::GetScaledFontForFontWithCairoSkia(DrawTarget* aTarget, gfxFont* aFo
 /* static */ bool
 gfxPlatform::UsesOffMainThreadCompositing()
 {
-  InitLayersAccelerationPrefs();
   static bool firstTime = true;
   static bool result = false;
 
   if (firstTime) {
+    InitLayersAccelerationPrefs();
     result =
       sPrefBrowserTabsRemoteAutostart ||
-      gfxPrefs::LayersOffMainThreadCompositionEnabled() ||
-      gfxPrefs::LayersOffMainThreadCompositionForceEnabled();
+      !gfxPrefs::LayersOffMainThreadCompositionForceDisabled();
 #if defined(MOZ_WIDGET_GTK)
     // Linux users who chose OpenGL are being grandfathered in to OMTC
     result |= gfxPrefs::LayersAccelerationForceEnabled();
diff --git a/gfx/thebes/gfxPlatform.h b/gfx/thebes/gfxPlatform.h
index 3a6eba29e0..4ad05a4ae9 100644
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -11,7 +11,6 @@
 #include "nsTArray.h"
 #include "nsString.h"
 #include "nsCOMPtr.h"
-#include "nsAutoPtr.h"
 
 #include "gfxTypes.h"
 #include "gfxFontFamilyList.h"
@@ -183,7 +182,7 @@ public:
                              gfxImageFormat aFormat) = 0;
 
     /**
-     * Beware that these methods may return DrawTargets which are not fully supported
+     * Beware that this method may return DrawTargets which are not fully supported
      * on the current platform and might fail silently in subtle ways. This is a massive
      * potential footgun. You should only use these methods for canvas drawing really.
      * Use extreme caution if you use them for content where you are not 100% sure we
@@ -193,9 +192,6 @@ public:
     virtual already_AddRefed
       CreateDrawTargetForSurface(gfxASurface *aSurface, const mozilla::gfx::IntSize& aSize);
 
-    virtual already_AddRefed
-      CreateDrawTargetForUpdateSurface(gfxASurface *aSurface, const mozilla::gfx::IntSize& aSize);
-
     /*
      * Creates a SourceSurface for a gfxASurface. This function does no caching,
      * so the caller should cache the gfxASurface if it will be used frequently.
@@ -332,7 +328,7 @@ public:
      * Resolving a font name to family name. The result MUST be in the result of GetFontList().
      * If the name doesn't in the system, aFamilyName will be empty string, but not failed.
      */
-    virtual nsresult GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName) = 0;
+    virtual nsresult GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName);
 
     /**
      * Create the appropriate platform font group
@@ -353,8 +349,7 @@ public:
     virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
                                           uint16_t aWeight,
                                           int16_t aStretch,
-                                          uint8_t aStyle)
-    { return nullptr; }
+                                          uint8_t aStyle);
 
     /**
      * Activate a platform font.  (Needed to support @font-face src url().)
@@ -556,7 +551,8 @@ public:
     }
 
     mozilla::gl::SkiaGLGlue* GetSkiaGLGlue();
-    void PurgeSkiaCache();
+    void PurgeSkiaGPUCache();
+    static void PurgeSkiaFontCache();
 
     virtual bool IsInGonkEmulator() const { return false; }
 
diff --git a/gfx/thebes/gfxPlatformFontList.cpp b/gfx/thebes/gfxPlatformFontList.cpp
index 9ed85639bc..7db562920c 100644
--- a/gfx/thebes/gfxPlatformFontList.cpp
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -184,7 +184,7 @@ gfxPlatformFontList::gfxPlatformFontList(bool aNeedFullnamePostscriptNames)
     mOtherFamilyNamesInitialized = false;
 
     if (aNeedFullnamePostscriptNames) {
-        mExtraNames = new ExtraNames();
+        mExtraNames = MakeUnique();
     }
     mFaceNameListsInitialized = false;
 
@@ -228,6 +228,8 @@ gfxPlatformFontList::InitFontList()
         fontCache->FlushShapedWordCaches();
     }
 
+    gfxPlatform::PurgeSkiaFontCache();
+
     mFontFamilies.Clear();
     mOtherFamilyNames.Clear();
     mOtherFamilyNamesInitialized = false;
@@ -379,7 +381,7 @@ gfxPlatformFontList::LookupInFaceNameLists(const nsAString& aFaceName)
         // names not completely initialized, so keep track of lookup misses
         if (!mFaceNameListsInitialized) {
             if (!mFaceNamesMissed) {
-                mFaceNamesMissed = new nsTHashtable(2);
+                mFaceNamesMissed = MakeUnique>(2);
             }
             mFaceNamesMissed->PutEntry(aFaceName);
         }
@@ -640,46 +642,50 @@ gfxPlatformFontList::CheckFamily(gfxFontFamily *aFamily)
     return aFamily;
 }
 
-gfxFontFamily* 
-gfxPlatformFontList::FindFamily(const nsAString& aFamily, gfxFontStyle* aStyle,
-                                gfxFloat aDevToCssSize)
+bool 
+gfxPlatformFontList::FindAndAddFamilies(const nsAString& aFamily,
+                                        nsTArray* aOutput,
+                                        gfxFontStyle* aStyle,
+                                        gfxFloat aDevToCssSize)
 {
     nsAutoString key;
-    gfxFontFamily *familyEntry;
     GenerateFontListKey(aFamily, key);
 
     NS_ASSERTION(mFontFamilies.Count() != 0, "system font list was not initialized correctly");
 
     // lookup in canonical (i.e. English) family name list
-    if ((familyEntry = mFontFamilies.GetWeak(key))) {
-        return CheckFamily(familyEntry);
+    gfxFontFamily *familyEntry = mFontFamilies.GetWeak(key);
+
+    // if not found, lookup in other family names list (mostly localized names)
+    if (!familyEntry) {
+        familyEntry = mOtherFamilyNames.GetWeak(key);
     }
 
-    // lookup in other family names list (mostly localized names)
-    if ((familyEntry = mOtherFamilyNames.GetWeak(key)) != nullptr) {
-        return CheckFamily(familyEntry);
-    }
-
-    // name not found and other family names not yet fully initialized so
+    // if still not found and other family names not yet fully initialized,
     // initialize the rest of the list and try again.  this is done lazily
     // since reading name table entries is expensive.
     // although ASCII localized family names are possible they don't occur
     // in practice so avoid pulling in names at startup
-    if (!mOtherFamilyNamesInitialized && !IsASCII(aFamily)) {
+    if (!familyEntry && !mOtherFamilyNamesInitialized && !IsASCII(aFamily)) {
         InitOtherFamilyNames();
-        if ((familyEntry = mOtherFamilyNames.GetWeak(key)) != nullptr) {
-            return CheckFamily(familyEntry);
-        } else if (!mOtherFamilyNamesInitialized) {
+        familyEntry = mOtherFamilyNames.GetWeak(key);
+        if (!familyEntry && !mOtherFamilyNamesInitialized) {
             // localized family names load timed out, add name to list of
             // names to check after localized names are loaded
             if (!mOtherNamesMissed) {
-                mOtherNamesMissed = new nsTHashtable(2);
+                mOtherNamesMissed = MakeUnique>(2);
             }
             mOtherNamesMissed->PutEntry(key);
         }
     }
 
-    return nullptr;
+    familyEntry = CheckFamily(familyEntry);
+    if (familyEntry) {
+        aOutput->AppendElement(familyEntry);
+        return true;
+    }
+
+    return false;
 }
 
 gfxFontEntry*
@@ -785,8 +791,7 @@ void
 gfxPlatformFontList::ResolveGenericFontNames(
     FontFamilyType aGenericType,
     eFontPrefLang aPrefLang,
-    nsTArray>* aGenericFamilies
-)
+    nsTArray>* aGenericFamilies)
 {
     const char* langGroupStr = GetPrefLangName(aPrefLang);
     const char* generic = GetGenericName(aGenericType);
@@ -819,18 +824,11 @@ gfxPlatformFontList::ResolveGenericFontNames(
         gfxFontStyle style;
         style.language = langGroup;
         style.systemFont = false;
-        RefPtr family =
-            FindFamily(genericFamily, &style);
-        if (family) {
-            bool notFound = true;
-            for (const gfxFontFamily* f : *aGenericFamilies) {
-                if (f == family) {
-                    notFound = false;
-                    break;
-                }
-            }
-            if (notFound) {
-                aGenericFamilies->AppendElement(family);
+        AutoTArray families;
+        FindAndAddFamilies(genericFamily, &families, &style);
+        for (gfxFontFamily* f : families) {
+            if (!aGenericFamilies->Contains(f)) {
+                aGenericFamilies->AppendElement(f);
             }
         }
     }
@@ -854,11 +852,12 @@ gfxPlatformFontList::GetPrefFontsLangGroup(mozilla::FontFamilyType aGenericType,
         aGenericType = eFamily_monospace;
     }
 
-    PrefFontList* prefFonts = mLangGroupPrefFonts[aPrefLang][aGenericType];
+    PrefFontList* prefFonts =
+        mLangGroupPrefFonts[aPrefLang][aGenericType].get();
     if (MOZ_UNLIKELY(!prefFonts)) {
         prefFonts = new PrefFontList;
         ResolveGenericFontNames(aGenericType, aPrefLang, prefFonts);
-        mLangGroupPrefFonts[aPrefLang][aGenericType] = prefFonts;
+        mLangGroupPrefFonts[aPrefLang][aGenericType].reset(prefFonts);
     }
     return prefFonts;
 }
@@ -1572,7 +1571,7 @@ gfxPlatformFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
         auto& prefFontsLangGroup = mLangGroupPrefFonts[i];
         for (uint32_t j = eFamily_generic_first;
              j < eFamily_generic_first + eFamily_generic_count; j++) {
-            PrefFontList* pf = prefFontsLangGroup[j];
+            PrefFontList* pf = prefFontsLangGroup[j].get();
             if (pf) {
                 aSizes->mFontListSize +=
                     pf->ShallowSizeOfExcludingThis(aMallocSizeOf);
diff --git a/gfx/thebes/gfxPlatformFontList.h b/gfx/thebes/gfxPlatformFontList.h
index 394524eebf..c211737cf2 100644
--- a/gfx/thebes/gfxPlatformFontList.h
+++ b/gfx/thebes/gfxPlatformFontList.h
@@ -129,9 +129,14 @@ public:
                           int32_t aRunScript,
                           const gfxFontStyle* aStyle);
 
-    virtual gfxFontFamily*
-    FindFamily(const nsAString& aFamily, gfxFontStyle* aStyle = nullptr,
-               gfxFloat aDevToCssSize = 1.0);
+    // Find family(ies) matching aFamily and append to the aOutput array
+    // (there may be multiple results in the case of fontconfig aliases, etc).
+    // Return true if any match was found and appended, false if none.
+    virtual bool
+    FindAndAddFamilies(const nsAString& aFamily,
+                       nsTArray* aOutput,
+                       gfxFontStyle* aStyle = nullptr,
+                       gfxFloat aDevToCssSize = 1.0);
 
     gfxFontEntry* FindFontForFamily(const nsAString& aFamily, const gfxFontStyle* aStyle, bool& aNeedsBold);
 
@@ -254,6 +259,17 @@ protected:
 
     static gfxPlatformFontList *sPlatformFontList;
 
+    // Convenience method to return the first matching family (if any) as found
+    // by FindAndAddFamilies().
+    gfxFontFamily*
+    FindFamily(const nsAString& aFamily, gfxFontStyle* aStyle = nullptr,
+               gfxFloat aDevToCssSize = 1.0)
+    {
+        AutoTArray families;
+        return FindAndAddFamilies(aFamily, &families, aStyle, aDevToCssSize)
+               ? families[0] : nullptr;
+    }
+
     // Lookup family name in global family list without substitutions or
     // localized family name lookup. Used for common font fallback families.
     gfxFontFamily* FindFamilyByCanonicalName(const nsAString& aFamily) {
@@ -375,16 +391,16 @@ protected:
       // Postscript name ==> font entry (unique, one name per font entry)
       FontEntryTable mPostscriptNames;
     };
-    nsAutoPtr mExtraNames;
+    mozilla::UniquePtr mExtraNames;
 
     // face names missed when face name loading takes a long time
-    nsAutoPtr > mFaceNamesMissed;
+    mozilla::UniquePtr > mFaceNamesMissed;
 
     // localized family names missed when face name loading takes a long time
-    nsAutoPtr > mOtherNamesMissed;
+    mozilla::UniquePtr > mOtherNamesMissed;
 
     typedef nsTArray> PrefFontList;
-    typedef mozilla::RangedArray,
+    typedef mozilla::RangedArray,
                                  mozilla::eFamily_generic_first,
                                  mozilla::eFamily_generic_count> PrefFontsForLangGroup;
     mozilla::RangedArrayGetScaledFont(aTarget);
 }
 
-nsresult
-gfxPlatformMac::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
-{
-    gfxPlatformFontList::PlatformFontList()->GetStandardFamilyName(aFontName, aFamilyName);
-    return NS_OK;
-}
-
 gfxFontGroup *
 gfxPlatformMac::CreateFontGroup(const FontFamilyList& aFontFamilyList,
                                 const gfxFontStyle *aStyle,
@@ -178,38 +171,6 @@ gfxPlatformMac::CreateFontGroup(const FontFamilyList& aFontFamilyList,
                             aUserFontSet, aDevToCssSize);
 }
 
-// these will move to gfxPlatform once all platforms support the fontlist
-gfxFontEntry* 
-gfxPlatformMac::LookupLocalFont(const nsAString& aFontName,
-                                uint16_t aWeight,
-                                int16_t aStretch,
-                                uint8_t aStyle)
-{
-    return gfxPlatformFontList::PlatformFontList()->LookupLocalFont(aFontName,
-                                                                    aWeight,
-                                                                    aStretch,
-                                                                    aStyle);
-}
-
-gfxFontEntry* 
-gfxPlatformMac::MakePlatformFont(const nsAString& aFontName,
-                                 uint16_t aWeight,
-                                 int16_t aStretch,
-                                 uint8_t aStyle,
-                                 const uint8_t* aFontData,
-                                 uint32_t aLength)
-{
-    // Ownership of aFontData is received here, and passed on to
-    // gfxPlatformFontList::MakePlatformFont(), which must ensure the data
-    // is released with NS_Free when no longer needed
-    return gfxPlatformFontList::PlatformFontList()->MakePlatformFont(aFontName,
-                                                                     aWeight,
-                                                                     aStretch,
-                                                                     aStyle,
-                                                                     aFontData,
-                                                                     aLength);
-}
-
 bool
 gfxPlatformMac::IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags)
 {
@@ -232,23 +193,6 @@ gfxPlatformMac::IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags)
     return true;
 }
 
-// these will also move to gfxPlatform once all platforms support the fontlist
-nsresult
-gfxPlatformMac::GetFontList(nsIAtom *aLangGroup,
-                            const nsACString& aGenericFamily,
-                            nsTArray& aListOfFonts)
-{
-    gfxPlatformFontList::PlatformFontList()->GetFontList(aLangGroup, aGenericFamily, aListOfFonts);
-    return NS_OK;
-}
-
-nsresult
-gfxPlatformMac::UpdateFontList()
-{
-    gfxPlatformFontList::PlatformFontList()->UpdateFontList();
-    return NS_OK;
-}
-
 static const char kFontArialUnicodeMS[] = "Arial Unicode MS";
 static const char kFontAppleBraille[] = "Apple Braille";
 static const char kFontAppleColorEmoji[] = "Apple Color Emoji";
diff --git a/gfx/thebes/gfxPlatformMac.h b/gfx/thebes/gfxPlatformMac.h
index d89f39c479..5e4b8e26d8 100644
--- a/gfx/thebes/gfxPlatformMac.h
+++ b/gfx/thebes/gfxPlatformMac.h
@@ -57,8 +57,6 @@ public:
     already_AddRefed
       GetScaledFontForFont(mozilla::gfx::DrawTarget* aTarget, gfxFont *aFont) override;
 
-    nsresult GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName) override;
-
     gfxFontGroup*
     CreateFontGroup(const mozilla::FontFamilyList& aFontFamilyList,
                     const gfxFontStyle *aStyle,
@@ -66,27 +64,10 @@ public:
                     gfxUserFontSet *aUserFontSet,
                     gfxFloat aDevToCssSize) override;
 
-    virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
-                                          uint16_t aWeight,
-                                          int16_t aStretch,
-                                          uint8_t aStyle) override;
-
     virtual gfxPlatformFontList* CreatePlatformFontList() override;
 
-    virtual gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
-                                           uint16_t aWeight,
-                                           int16_t aStretch,
-                                           uint8_t aStyle,
-                                           const uint8_t* aFontData,
-                                           uint32_t aLength) override;
-
     bool IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags) override;
 
-    nsresult GetFontList(nsIAtom *aLangGroup,
-                         const nsACString& aGenericFamily,
-                         nsTArray& aListOfFonts) override;
-    nsresult UpdateFontList() override;
-
     virtual void GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh,
                                         int32_t aRunScript,
                                         nsTArray& aFontList) override;
diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h
index 8203d627b9..83ccd56cc2 100644
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -148,6 +148,7 @@ private:
   DECL_GFX_PREF(Live, "apz.content_response_timeout",          APZContentResponseTimeout, int32_t, 300);
   DECL_GFX_PREF(Live, "apz.danger_zone_x",                     APZDangerZoneX, int32_t, 50);
   DECL_GFX_PREF(Live, "apz.danger_zone_y",                     APZDangerZoneY, int32_t, 100);
+  DECL_GFX_PREF(Live, "apz.disable_for_scroll_linked_effects", APZDisableForScrollLinkedEffects, bool, false);
   DECL_GFX_PREF(Live, "apz.displayport_expiry_ms",             APZDisplayPortExpiryTime, uint32_t, 15000);
   DECL_GFX_PREF(Live, "apz.drag.enabled",                      APZDragEnabled, bool, false);
   DECL_GFX_PREF(Live, "apz.enlarge_displayport_when_clipped",  APZEnlargeDisplayPortWhenClipped, bool, false);
@@ -313,7 +314,8 @@ private:
   DECL_GFX_PREF(Once, "image.multithreaded_decoding.limit",    ImageMTDecodingLimit, int32_t, -1);
   DECL_GFX_PREF(Live, "image.single-color-optimization.enabled", ImageSingleColorOptimizationEnabled, bool, true);
   DECL_GFX_PREF(Live, "image.webp.enabled",                    ImageWebPEnabled, bool, true);
-  
+
+  DECL_GFX_PREF(Live, "layers.child-process-shutdown",         ChildProcessShutdown, bool, true);
   DECL_GFX_PREF(Once, "layers.acceleration.disabled",          LayersAccelerationDisabled, bool, false);
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps",          LayersDrawFPS, bool, false);
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.print-histogram",  FPSPrintHistogram, bool, false);
@@ -363,8 +365,7 @@ private:
   DECL_GFX_PREF(Live, "layers.low-precision-opacity",          LowPrecisionOpacity, float, 1.0f);
   DECL_GFX_PREF(Live, "layers.low-precision-resolution",       LowPrecisionResolution, float, 0.25f);
   DECL_GFX_PREF(Live, "layers.max-active",                     MaxActiveLayers, int32_t, -1);
-  DECL_GFX_PREF(Once, "layers.offmainthreadcomposition.enabled", LayersOffMainThreadCompositionEnabled, bool, false);
-  DECL_GFX_PREF(Once, "layers.offmainthreadcomposition.force-enabled", LayersOffMainThreadCompositionForceEnabled, bool, false);
+  DECL_GFX_PREF(Once, "layers.offmainthreadcomposition.force-disabled", LayersOffMainThreadCompositionForceDisabled, bool, false);
   DECL_GFX_PREF(Live, "layers.offmainthreadcomposition.frame-rate", LayersCompositionFrameRate, int32_t,-1);
   DECL_GFX_PREF(Live, "layers.orientation.sync.timeout",       OrientationSyncMillis, uint32_t, (uint32_t)0);
   DECL_GFX_PREF(Once, "layers.overzealous-gralloc-unlocking",  OverzealousGrallocUnlocking, bool, false);
diff --git a/gfx/thebes/gfxReusableSurfaceWrapper.h b/gfx/thebes/gfxReusableSurfaceWrapper.h
index 6b79fc8b4c..831bb1d51d 100644
--- a/gfx/thebes/gfxReusableSurfaceWrapper.h
+++ b/gfx/thebes/gfxReusableSurfaceWrapper.h
@@ -7,7 +7,6 @@
 
 #include "gfxImageSurface.h"
 #include "nsISupportsImpl.h"
-#include "nsAutoPtr.h"
 
 
 /**
diff --git a/gfx/thebes/gfxSVGGlyphs.h b/gfx/thebes/gfxSVGGlyphs.h
index d07649ad7f..f308a20100 100644
--- a/gfx/thebes/gfxSVGGlyphs.h
+++ b/gfx/thebes/gfxSVGGlyphs.h
@@ -8,7 +8,6 @@
 #include "gfxFontUtils.h"
 #include "mozilla/gfx/2D.h"
 #include "nsString.h"
-#include "nsAutoPtr.h"
 #include "nsClassHashtable.h"
 #include "nsBaseHashtable.h"
 #include "nsHashKeys.h"
diff --git a/gfx/thebes/gfxTeeSurface.cpp b/gfx/thebes/gfxTeeSurface.cpp
index 834475d695..20598badb0 100644
--- a/gfx/thebes/gfxTeeSurface.cpp
+++ b/gfx/thebes/gfxTeeSurface.cpp
@@ -4,7 +4,6 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gfxTeeSurface.h"
-#include "nsAutoPtr.h"
 #include "nsTArray.h"
 
 #include "cairo-tee.h"
diff --git a/gfx/thebes/gfxTextRun.cpp b/gfx/thebes/gfxTextRun.cpp
index 3749c07cc4..af5f88ee03 100644
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -131,7 +131,7 @@ gfxTextRun::AllocateStorageForTextRun(size_t aSize, uint32_t aLength)
     return storage;
 }
 
-gfxTextRun *
+UniquePtr
 gfxTextRun::Create(const gfxTextRunFactory::Parameters *aParams,
                    uint32_t aLength, gfxFontGroup *aFontGroup, uint32_t aFlags)
 {
@@ -140,7 +140,8 @@ gfxTextRun::Create(const gfxTextRunFactory::Parameters *aParams,
         return nullptr;
     }
 
-    return new (storage) gfxTextRun(aParams, aLength, aFontGroup, aFlags);
+    return UniquePtr(new (storage) gfxTextRun(aParams, aLength,
+                                                          aFontGroup, aFlags));
 }
 
 gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters *aParams,
@@ -1611,7 +1612,7 @@ gfxFontGroup::BuildFontList()
     }
 
     // initialize fonts in the font family list
-    AutoTArray fonts;
+    AutoTArray fonts;
     gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
 
     // lookup fonts in the fontlist
@@ -1646,8 +1647,6 @@ void
 gfxFontGroup::AddPlatformFont(const nsAString& aName,
                               nsTArray& aFamilyList)
 {
-    gfxFontFamily* family = nullptr;
-
     // First, look up in the user font set...
     // If the fontSet matches the family, we must not look for a platform
     // font of the same name, even if we fail to actually get a fontEntry
@@ -1655,18 +1654,16 @@ gfxFontGroup::AddPlatformFont(const nsAString& aName,
     if (mUserFontSet) {
         // Add userfonts to the fontlist whether already loaded
         // or not. Loading is initiated during font matching.
-        family = mUserFontSet->LookupFamily(aName);
+        gfxFontFamily* family = mUserFontSet->LookupFamily(aName);
+        if (family) {
+            aFamilyList.AppendElement(family);
+            return;
+        }
     }
 
     // Not known in the user font set ==> check system fonts
-    gfxPlatformFontList* fontList = gfxPlatformFontList::PlatformFontList();
-    if (!family) {
-        family = fontList->FindFamily(aName, &mStyle, mDevToCssSize);
-    }
-
-    if (family) {
-        aFamilyList.AppendElement(family);
-    }
+    gfxPlatformFontList::PlatformFontList()
+        ->FindAndAddFamilies(aName, &aFamilyList, &mStyle, mDevToCssSize);
 }
 
 void
@@ -1965,19 +1962,20 @@ gfxFontGroup::IsInvalidChar(char16_t ch)
             IsBidiControl(ch));
 }
 
-gfxTextRun *
+UniquePtr
 gfxFontGroup::MakeEmptyTextRun(const Parameters *aParams, uint32_t aFlags)
 {
     aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT;
     return gfxTextRun::Create(aParams, 0, this, aFlags);
 }
 
-gfxTextRun *
+UniquePtr
 gfxFontGroup::MakeSpaceTextRun(const Parameters *aParams, uint32_t aFlags)
 {
     aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT;
 
-    gfxTextRun *textRun = gfxTextRun::Create(aParams, 1, this, aFlags);
+    UniquePtr textRun =
+        gfxTextRun::Create(aParams, 1, this, aFlags);
     if (!textRun) {
         return nullptr;
     }
@@ -2021,11 +2019,11 @@ gfxFontGroup::MakeSpaceTextRun(const Parameters *aParams, uint32_t aFlags)
     return textRun;
 }
 
-gfxTextRun *
+UniquePtr
 gfxFontGroup::MakeBlankTextRun(uint32_t aLength,
                                const Parameters *aParams, uint32_t aFlags)
 {
-    gfxTextRun *textRun =
+    UniquePtr textRun =
         gfxTextRun::Create(aParams, aLength, this, aFlags);
     if (!textRun) {
         return nullptr;
@@ -2040,7 +2038,7 @@ gfxFontGroup::MakeBlankTextRun(uint32_t aLength,
     return textRun;
 }
 
-gfxTextRun *
+UniquePtr
 gfxFontGroup::MakeHyphenTextRun(DrawTarget* aDrawTarget,
                                 uint32_t aAppUnitsPerDevUnit)
 {
@@ -2065,7 +2063,7 @@ gfxFontGroup::GetHyphenWidth(gfxTextRun::PropertyProvider *aProvider)
     if (mHyphenWidth < 0) {
         RefPtr dt(aProvider->GetDrawTarget());
         if (dt) {
-            nsAutoPtr
+            UniquePtr
                 hyphRun(MakeHyphenTextRun(dt,
                                           aProvider->GetAppUnitsPerDevUnit()));
             mHyphenWidth = hyphRun.get() ? hyphRun->GetAdvanceWidth() : 0;
@@ -2074,7 +2072,7 @@ gfxFontGroup::GetHyphenWidth(gfxTextRun::PropertyProvider *aProvider)
     return mHyphenWidth;
 }
 
-gfxTextRun *
+UniquePtr
 gfxFontGroup::MakeTextRun(const uint8_t *aString, uint32_t aLength,
                           const Parameters *aParams, uint32_t aFlags,
                           gfxMissingFontRecorder *aMFR)
@@ -2096,20 +2094,20 @@ gfxFontGroup::MakeTextRun(const uint8_t *aString, uint32_t aLength,
         return MakeBlankTextRun(aLength, aParams, aFlags);
     }
 
-    gfxTextRun *textRun = gfxTextRun::Create(aParams, aLength,
-                                             this, aFlags);
+    UniquePtr textRun = gfxTextRun::Create(aParams, aLength,
+                                                       this, aFlags);
     if (!textRun) {
         return nullptr;
     }
 
-    InitTextRun(aParams->mDrawTarget, textRun, aString, aLength, aMFR);
+    InitTextRun(aParams->mDrawTarget, textRun.get(), aString, aLength, aMFR);
 
     textRun->FetchGlyphExtents(aParams->mDrawTarget);
 
     return textRun;
 }
 
-gfxTextRun *
+UniquePtr
 gfxFontGroup::MakeTextRun(const char16_t *aString, uint32_t aLength,
                           const Parameters *aParams, uint32_t aFlags,
                           gfxMissingFontRecorder *aMFR)
@@ -2125,13 +2123,13 @@ gfxFontGroup::MakeTextRun(const char16_t *aString, uint32_t aLength,
         return MakeBlankTextRun(aLength, aParams, aFlags);
     }
 
-    gfxTextRun *textRun = gfxTextRun::Create(aParams, aLength,
-                                             this, aFlags);
+    UniquePtr textRun = gfxTextRun::Create(aParams, aLength,
+                                                       this, aFlags);
     if (!textRun) {
         return nullptr;
     }
 
-    InitTextRun(aParams->mDrawTarget, textRun, aString, aLength, aMFR);
+    InitTextRun(aParams->mDrawTarget, textRun.get(), aString, aLength, aMFR);
 
     textRun->FetchGlyphExtents(aParams->mDrawTarget);
 
@@ -2552,7 +2550,7 @@ gfxFontGroup::GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel, uint32_t aFlags,
     if (mCachedEllipsisTextRun &&
         (mCachedEllipsisTextRun->GetFlags() & TEXT_ORIENT_MASK) == aFlags &&
         mCachedEllipsisTextRun->GetAppUnitsPerDevUnit() == aAppUnitsPerDevPixel) {
-        return mCachedEllipsisTextRun;
+        return mCachedEllipsisTextRun.get();
     }
 
     // Use a Unicode ellipsis if the font supports it,
@@ -2568,36 +2566,16 @@ gfxFontGroup::GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel, uint32_t aFlags,
     Parameters params = {
         refDT, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel
     };
-    gfxTextRun* textRun =
+    mCachedEllipsisTextRun =
         MakeTextRun(ellipsis.get(), ellipsis.Length(), ¶ms,
                     aFlags | TEXT_IS_PERSISTENT, nullptr);
-    if (!textRun) {
+    if (!mCachedEllipsisTextRun) {
         return nullptr;
     }
-    mCachedEllipsisTextRun = textRun;
-    textRun->ReleaseFontGroup(); // don't let the presence of a cached ellipsis
-                                 // textrun prolong the fontgroup's life
-    return textRun;
-}
-
-already_AddRefed
-gfxFontGroup::FindNonItalicFaceForChar(gfxFontFamily* aFamily, uint32_t aCh)
-{
-    NS_ASSERTION(mStyle.style != NS_FONT_STYLE_NORMAL,
-                 "should only be called in the italic/oblique case");
-
-    gfxFontStyle regularStyle = mStyle;
-    regularStyle.style = NS_FONT_STYLE_NORMAL;
-    bool needsBold;
-    gfxFontEntry *fe = aFamily->FindFontForStyle(regularStyle, needsBold);
-    NS_ASSERTION(!fe->mIsUserFontContainer,
-                 "should only be searching platform fonts");
-    if (!fe->HasCharacter(aCh)) {
-        return nullptr;
-    }
-
-    RefPtr font = fe->FindOrMakeFont(&mStyle, needsBold);
-    return font.forget();
+    // don't let the presence of a cached ellipsis textrun prolong the
+    // fontgroup's life
+    mCachedEllipsisTextRun->ReleaseFontGroup();
+    return mCachedEllipsisTextRun.get();
 }
 
 already_AddRefed
@@ -2681,24 +2659,30 @@ gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh, uint32_t aNextCh,
                 return firstFont.forget();
             }
 
+            RefPtr font;
             if (mFonts[0].CheckForFallbackFaces()) {
-                RefPtr font =
-                    FindFallbackFaceForChar(mFonts[0].Family(), aCh, aRunScript);
-                if (font) {
-                    *aMatchType = gfxTextRange::kFontGroup;
-                    return font.forget();
-                }
-            } else if (mStyle.style != NS_FONT_STYLE_NORMAL &&
-                       !firstFont->GetFontEntry()->IsUserFont()) {
-                // If italic, test the regular face to see if it supports
-                // character. Only do this for platform fonts, not userfonts.
-                RefPtr font =
-                    FindNonItalicFaceForChar(mFonts[0].Family(), aCh);
-                if (font) {
-                    *aMatchType = gfxTextRange::kFontGroup;
-                    return font.forget();
+                font = FindFallbackFaceForChar(mFonts[0].Family(), aCh,
+                                               aRunScript);
+            } else if (!firstFont->GetFontEntry()->IsUserFont()) {
+                // For platform fonts (but not userfonts), we may need to do
+                // fallback within the family to handle cases where some faces
+                // such as Italic or Black have reduced character sets compared
+                // to the family's Regular face.
+                gfxFontEntry* fe = firstFont->GetFontEntry();
+                if (!fe->IsUpright() ||
+                    fe->Weight() != NS_FONT_WEIGHT_NORMAL ||
+                    fe->Stretch() != NS_FONT_STRETCH_NORMAL) {
+                    // If style/weight/stretch was not Normal, see if we can
+                    // fall back to a next-best face (e.g. Arial Black -> Bold,
+                    // or Arial Narrow -> Regular).
+                    font = FindFallbackFaceForChar(mFonts[0].Family(), aCh,
+                                                   aRunScript);
                 }
             }
+            if (font) {
+                *aMatchType = gfxTextRange::kFontGroup;
+                return font.forget();
+            }
         }
 
         // we don't need to check the first font again below
@@ -2803,13 +2787,15 @@ gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh, uint32_t aNextCh,
                 return font.forget();
             }
         } else {
-            // If italic, test the regular face to see if it supports the
-            // character. Only do this for platform fonts, not userfonts.
+            // For platform fonts, but not user fonts, consider intra-family
+            // fallback to handle styles with reduced character sets (see
+            // also above).
             fe = ff.FontEntry();
-            if (mStyle.style != NS_FONT_STYLE_NORMAL &&
-                !fe->mIsUserFontContainer &&
-                !fe->IsUserFont()) {
-                font = FindNonItalicFaceForChar(ff.Family(), aCh);
+            if (!fe->mIsUserFontContainer && !fe->IsUserFont() &&
+                (!fe->IsUpright() ||
+                 fe->Weight() != NS_FONT_WEIGHT_NORMAL ||
+                 fe->Stretch() != NS_FONT_STRETCH_NORMAL)) {
+                font = FindFallbackFaceForChar(ff.Family(), aCh, aRunScript);
                 if (font) {
                     *aMatchType = gfxTextRange::kFontGroup;
                     return font.forget();
diff --git a/gfx/thebes/gfxTextRun.h b/gfx/thebes/gfxTextRun.h
index 60e2d3a39f..d2bad10b0b 100644
--- a/gfx/thebes/gfxTextRun.h
+++ b/gfx/thebes/gfxTextRun.h
@@ -434,9 +434,10 @@ public:
 
     // Call this, don't call "new gfxTextRun" directly. This does custom
     // allocation and initialization
-    static gfxTextRun *Create(const gfxTextRunFactory::Parameters *aParams,
-                              uint32_t aLength, gfxFontGroup *aFontGroup,
-                              uint32_t aFlags);
+    static mozilla::UniquePtr
+    Create(const gfxTextRunFactory::Parameters *aParams,
+           uint32_t aLength, gfxFontGroup *aFontGroup,
+           uint32_t aFlags);
 
     // The text is divided into GlyphRuns as necessary
     struct GlyphRun {
@@ -797,29 +798,32 @@ public:
      * textrun will copy it.
      * This calls FetchGlyphExtents on the textrun.
      */
-    virtual gfxTextRun *MakeTextRun(const char16_t *aString, uint32_t aLength,
-                                    const Parameters *aParams, uint32_t aFlags,
-                                    gfxMissingFontRecorder *aMFR);
+    virtual mozilla::UniquePtr
+    MakeTextRun(const char16_t *aString, uint32_t aLength,
+                const Parameters *aParams, uint32_t aFlags,
+                gfxMissingFontRecorder *aMFR);
     /**
      * Make a textrun for a given string.
      * If aText is not persistent (aFlags & TEXT_IS_PERSISTENT), the
      * textrun will copy it.
      * This calls FetchGlyphExtents on the textrun.
      */
-    virtual gfxTextRun *MakeTextRun(const uint8_t *aString, uint32_t aLength,
-                                    const Parameters *aParams, uint32_t aFlags,
-                                    gfxMissingFontRecorder *aMFR);
+    virtual mozilla::UniquePtr
+    MakeTextRun(const uint8_t *aString, uint32_t aLength,
+                const Parameters *aParams, uint32_t aFlags,
+                gfxMissingFontRecorder *aMFR);
 
     /**
      * Textrun creation helper for clients that don't want to pass
      * a full Parameters record.
      */
     template
-    gfxTextRun* MakeTextRun(const T* aString, uint32_t aLength,
-                            DrawTarget* aRefDrawTarget,
-                            int32_t aAppUnitsPerDevUnit,
-                            uint32_t aFlags,
-                            gfxMissingFontRecorder *aMFR)
+    mozilla::UniquePtr
+    MakeTextRun(const T* aString, uint32_t aLength,
+                DrawTarget* aRefDrawTarget,
+                int32_t aAppUnitsPerDevUnit,
+                uint32_t aFlags,
+                gfxMissingFontRecorder *aMFR)
     {
         gfxTextRunFactory::Parameters params = {
             aRefDrawTarget, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevUnit
@@ -842,8 +846,8 @@ public:
      * The caller is responsible for deleting the returned text run
      * when no longer required.
      */
-    gfxTextRun* MakeHyphenTextRun(DrawTarget* aDrawTarget,
-                                  uint32_t aAppUnitsPerDevUnit);
+    mozilla::UniquePtr
+    MakeHyphenTextRun(DrawTarget* aDrawTarget, uint32_t aAppUnitsPerDevUnit);
 
     /**
      * Check whether a given font (specified by its gfxFontEntry)
@@ -1076,7 +1080,7 @@ protected:
 
     // Cache a textrun representing an ellipsis (useful for CSS text-overflow)
     // at a specific appUnitsPerDevPixel size and orientation
-    nsAutoPtr   mCachedEllipsisTextRun;
+    mozilla::UniquePtr   mCachedEllipsisTextRun;
 
     // cache the most recent pref font to avoid general pref font lookup
     RefPtr mLastPrefFamily;
@@ -1096,10 +1100,15 @@ protected:
      * Textrun creation short-cuts for special cases where we don't need to
      * call a font shaper to generate glyphs.
      */
-    gfxTextRun *MakeEmptyTextRun(const Parameters *aParams, uint32_t aFlags);
-    gfxTextRun *MakeSpaceTextRun(const Parameters *aParams, uint32_t aFlags);
-    gfxTextRun *MakeBlankTextRun(uint32_t aLength,
-                                 const Parameters *aParams, uint32_t aFlags);
+    mozilla::UniquePtr
+    MakeEmptyTextRun(const Parameters *aParams, uint32_t aFlags);
+
+    mozilla::UniquePtr
+    MakeSpaceTextRun(const Parameters *aParams, uint32_t aFlags);
+
+    mozilla::UniquePtr
+    MakeBlankTextRun(uint32_t aLength, const Parameters *aParams,
+                     uint32_t aFlags);
 
     // Initialize the list of fonts
     void BuildFontList();
@@ -1142,12 +1151,6 @@ protected:
                        gfxMissingFontRecorder *aMFR);
 
     // Helper for font-matching:
-    // When matching the italic case, allow use of the regular face
-    // if it supports a character but the italic one doesn't.
-    // Return null if regular face doesn't support aCh
-    already_AddRefed
-    FindNonItalicFaceForChar(gfxFontFamily* aFamily, uint32_t aCh);
-
     // search all faces in a family for a fallback in cases where it's unclear
     // whether the family might have a font for a given character
     already_AddRefed
diff --git a/gfx/thebes/gfxUserFontSet.cpp b/gfx/thebes/gfxUserFontSet.cpp
index c6e01dceb0..ad3b86c358 100644
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -268,9 +268,9 @@ gfxUserFontEntry::StoreUserFontData(gfxFontEntry* aFontEntry,
                                     uint8_t aCompression)
 {
     if (!aFontEntry->mUserFontData) {
-        aFontEntry->mUserFontData = new gfxUserFontData;
+        aFontEntry->mUserFontData = MakeUnique();
     }
-    gfxUserFontData* userFontData = aFontEntry->mUserFontData;
+    gfxUserFontData* userFontData = aFontEntry->mUserFontData.get();
     userFontData->mSrcIndex = mSrcIndex;
     const gfxFontFaceSrc& src = mSrcList[mSrcIndex];
     switch (src.mSourceType) {
@@ -1118,7 +1118,7 @@ gfxUserFontSet::UserFontCache::CacheFont(gfxFontEntry* aFontEntry,
         return;
     }
 
-    gfxUserFontData* data = aFontEntry->mUserFontData;
+    gfxUserFontData* data = aFontEntry->mUserFontData.get();
     if (data->mIsBuffer) {
 #ifdef DEBUG_USERFONT_CACHE
         printf("userfontcache skipped fontentry with buffer source: %p\n",
diff --git a/gfx/thebes/gfxUserFontSet.h b/gfx/thebes/gfxUserFontSet.h
index 7583c82a5e..d9be9a9648 100644
--- a/gfx/thebes/gfxUserFontSet.h
+++ b/gfx/thebes/gfxUserFontSet.h
@@ -9,7 +9,6 @@
 #include "gfxFont.h"
 #include "gfxFontFamilyList.h"
 #include "nsRefPtrHashtable.h"
-#include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsIURI.h"
 #include "nsIPrincipal.h"
diff --git a/gfx/thebes/gfxUtils.cpp b/gfx/thebes/gfxUtils.cpp
index 34a738d67e..f9fadaa163 100644
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -446,11 +446,13 @@ CreateSamplingRestrictedDrawable(gfxDrawable* aDrawable,
 
     RefPtr target =
       gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(size, aFormat);
-    if (!target) {
+    if (!target || !target->IsValid()) {
       return nullptr;
     }
 
-    RefPtr tmpCtx = new gfxContext(target);
+    RefPtr tmpCtx = gfxContext::ForDrawTarget(target);
+    MOZ_ASSERT(tmpCtx); // already checked the target above
+
     tmpCtx->SetOp(OptimalFillOp());
     aDrawable->Draw(tmpCtx, needed - needed.TopLeft(), ExtendMode::REPEAT, Filter::LINEAR,
                     1.0, gfxMatrix::Translation(needed.TopLeft()));
@@ -592,11 +594,13 @@ PrescaleAndTileDrawable(gfxDrawable* aDrawable,
 
   RefPtr scaledDT =
     gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(scaledImageSize, aFormat);
-  if (!scaledDT) {
+  if (!scaledDT || !scaledDT->IsValid()) {
     return false;
   }
 
-  RefPtr tmpCtx = new gfxContext(scaledDT);
+  RefPtr tmpCtx = gfxContext::ForDrawTarget(scaledDT);
+  MOZ_ASSERT(tmpCtx); // already checked the target above
+
   scaledDT->SetTransform(ToMatrix(scaleMatrix));
   gfxRect gfxImageRect(aImageRect.x, aImageRect.y, aImageRect.width, aImageRect.height);
   aDrawable->Draw(tmpCtx, gfxImageRect, ExtendMode::REPEAT, aFilter, 1.0, gfxMatrix());
@@ -1342,9 +1346,10 @@ gfxUtils::WriteAsPNG(nsIPresShell* aShell, const char* aFile)
   RefPtr dt = gfxPlatform::GetPlatform()->
     CreateOffscreenContentDrawTarget(IntSize(width, height),
                                      SurfaceFormat::B8G8R8A8);
-  NS_ENSURE_TRUE(dt, /*void*/);
+  NS_ENSURE_TRUE(dt && dt->IsValid(), /*void*/);
 
-  RefPtr context = new gfxContext(dt);
+  RefPtr context = gfxContext::ForDrawTarget(dt);
+  MOZ_ASSERT(context); // already checked the draw target above
   aShell->RenderDocument(r, 0, NS_RGB(255, 255, 0), context);
   WriteAsPNG(dt.get(), aFile);
 }
diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp
index 031a2f3e72..1313a0d331 100644
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -79,6 +79,12 @@ using namespace mozilla::layers;
 using namespace mozilla::widget;
 using namespace mozilla::image;
 
+enum class TelemetryDeviceCode : uint32_t {
+  Content = 0,
+  Image = 1,
+  D2D1 = 2
+};
+
 DCFromDrawTarget::DCFromDrawTarget(DrawTarget& aDrawTarget)
 {
   mDC = nullptr;
@@ -362,6 +368,7 @@ GetParentDevicePrefs()
 
 gfxWindowsPlatform::gfxWindowsPlatform()
   : mRenderMode(RENDER_GDI)
+  , mDeviceLock("gfxWindowsPlatform.mDeviceLock")
   , mIsWARP(false)
   , mHasDeviceReset(false)
   , mHasFakeDeviceReset(false)
@@ -643,24 +650,6 @@ gfxWindowsPlatform::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont)
     return Factory::CreateScaledFontForNativeFont(nativeFont, aFont->GetAdjustedSize());
 }
 
-nsresult
-gfxWindowsPlatform::GetFontList(nsIAtom *aLangGroup,
-                                const nsACString& aGenericFamily,
-                                nsTArray& aListOfFonts)
-{
-    gfxPlatformFontList::PlatformFontList()->GetFontList(aLangGroup, aGenericFamily, aListOfFonts);
-
-    return NS_OK;
-}
-
-nsresult
-gfxWindowsPlatform::UpdateFontList()
-{
-    gfxPlatformFontList::PlatformFontList()->UpdateFontList();
-
-    return NS_OK;
-}
-
 static const char kFontAparajita[] = "Aparajita";
 static const char kFontArabicTypesetting[] = "Arabic Typesetting";
 static const char kFontArial[] = "Arial";
@@ -897,13 +886,6 @@ gfxWindowsPlatform::GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh,
     aFontList.AppendElement(kFontArialUnicodeMS);
 }
 
-nsresult
-gfxWindowsPlatform::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
-{
-    gfxPlatformFontList::PlatformFontList()->GetStandardFamilyName(aFontName, aFamilyName);
-    return NS_OK;
-}
-
 gfxFontGroup *
 gfxWindowsPlatform::CreateFontGroup(const FontFamilyList& aFontFamilyList,
                                     const gfxFontStyle *aStyle,
@@ -915,34 +897,6 @@ gfxWindowsPlatform::CreateFontGroup(const FontFamilyList& aFontFamilyList,
                             aUserFontSet, aDevToCssSize);
 }
 
-gfxFontEntry* 
-gfxWindowsPlatform::LookupLocalFont(const nsAString& aFontName,
-                                    uint16_t aWeight,
-                                    int16_t aStretch,
-                                    uint8_t aStyle)
-{
-    return gfxPlatformFontList::PlatformFontList()->LookupLocalFont(aFontName,
-                                                                    aWeight,
-                                                                    aStretch,
-                                                                    aStyle);
-}
-
-gfxFontEntry* 
-gfxWindowsPlatform::MakePlatformFont(const nsAString& aFontName,
-                                     uint16_t aWeight,
-                                     int16_t aStretch,
-                                     uint8_t aStyle,
-                                     const uint8_t* aFontData,
-                                     uint32_t aLength)
-{
-    return gfxPlatformFontList::PlatformFontList()->MakePlatformFont(aFontName,
-                                                                     aWeight,
-                                                                     aStretch,
-                                                                     aStyle,
-                                                                     aFontData,
-                                                                     aLength);
-}
-
 bool
 gfxWindowsPlatform::IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags)
 {
@@ -1449,10 +1403,12 @@ gfxWindowsPlatform::GetD3D9DeviceManager()
   return mDeviceManager;
 }
 
-ID3D11Device*
-gfxWindowsPlatform::GetD3D11Device()
+bool
+gfxWindowsPlatform::GetD3D11Device(RefPtr* aOutDevice)
 {
-  return mD3D11Device;
+  MutexAutoLock lock(mDeviceLock);
+  *aOutDevice = mD3D11Device;
+  return !!mD3D11Device;
 }
 
 ID3D11Device*
@@ -1461,20 +1417,22 @@ gfxWindowsPlatform::GetD3D11ContentDevice()
   return mD3D11ContentDevice;
 }
 
-ID3D11Device*
-gfxWindowsPlatform::GetD3D11ImageBridgeDevice()
+bool
+gfxWindowsPlatform::GetD3D11ImageBridgeDevice(RefPtr* aOutDevice)
 {
-  return mD3D11ImageBridgeDevice;
+  MutexAutoLock lock(mDeviceLock);
+  *aOutDevice = mD3D11ImageBridgeDevice;
+  return !!mD3D11ImageBridgeDevice;
 }
 
-ID3D11Device*
-gfxWindowsPlatform::GetD3D11DeviceForCurrentThread()
+bool
+gfxWindowsPlatform::GetD3D11DeviceForCurrentThread(RefPtr* aOutDevice)
 {
   if (NS_IsMainThread()) {
-    return GetD3D11ContentDevice();
-  } else {
-    return GetD3D11ImageBridgeDevice();
+    *aOutDevice = mD3D11ContentDevice;
+    return !!mD3D11ContentDevice;
   }
+  return GetD3D11ImageBridgeDevice(aOutDevice);
 }
 
 ReadbackManagerD3D11*
@@ -2009,7 +1967,7 @@ decltype(D3D11CreateDevice)* sD3D11CreateDeviceFn = nullptr;
 
 bool
 gfxWindowsPlatform::AttemptD3D11DeviceCreationHelper(
-  IDXGIAdapter1* aAdapter, HRESULT& aResOut)
+  IDXGIAdapter1* aAdapter, RefPtr& aOutDevice, HRESULT& aResOut)
 {
   MOZ_SEH_TRY {
     aResOut =
@@ -2019,7 +1977,7 @@ gfxWindowsPlatform::AttemptD3D11DeviceCreationHelper(
         // to prevent bug 1092260. IE 11 also uses this flag.
         D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
         mFeatureLevels.Elements(), mFeatureLevels.Length(),
-        D3D11_SDK_VERSION, getter_AddRefs(mD3D11Device), nullptr, nullptr);
+        D3D11_SDK_VERSION, getter_AddRefs(aOutDevice), nullptr, nullptr);
   } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
     return false;
   }
@@ -2035,22 +1993,23 @@ gfxWindowsPlatform::AttemptD3D11DeviceCreation()
   }
 
   HRESULT hr;
-  if (!AttemptD3D11DeviceCreationHelper(adapter, hr)) {
+  RefPtr device;
+  if (!AttemptD3D11DeviceCreationHelper(adapter, device, hr)) {
     gfxCriticalError() << "Crash during D3D11 device creation";
     return FeatureStatus::Crashed;
   }
 
-  if (FAILED(hr) || !mD3D11Device) {
-    mD3D11Device = nullptr;
+  if (FAILED(hr) || !device) {
     gfxCriticalError() << "D3D11 device creation failed: " << hexa(hr);
     return FeatureStatus::Failed;
   }
   if (!DoesD3D11DeviceWork()) {
-    mD3D11Device = nullptr;
     return FeatureStatus::Blocked;
   }
-  if (!mD3D11Device) {
-    return FeatureStatus::Failed;
+
+  {
+    MutexAutoLock lock(mDeviceLock);
+    mD3D11Device = device;
   }
 
   // Only test this when not using WARP since it can fail and cause
@@ -2068,7 +2027,9 @@ gfxWindowsPlatform::AttemptD3D11DeviceCreation()
 
 bool
 gfxWindowsPlatform::AttemptWARPDeviceCreationHelper(
-  ScopedGfxFeatureReporter& aReporterWARP, HRESULT& aResOut)
+  ScopedGfxFeatureReporter& aReporterWARP,
+  RefPtr& aOutDevice,
+  HRESULT& aResOut)
 {
   MOZ_SEH_TRY {
     aResOut =
@@ -2078,7 +2039,7 @@ gfxWindowsPlatform::AttemptWARPDeviceCreationHelper(
         // to prevent bug 1092260. IE 11 also uses this flag.
         D3D11_CREATE_DEVICE_BGRA_SUPPORT,
         mFeatureLevels.Elements(), mFeatureLevels.Length(),
-        D3D11_SDK_VERSION, getter_AddRefs(mD3D11Device), nullptr, nullptr);
+        D3D11_SDK_VERSION, getter_AddRefs(aOutDevice), nullptr, nullptr);
 
     aReporterWARP.SetSuccessful();
   } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
@@ -2093,17 +2054,23 @@ gfxWindowsPlatform::AttemptWARPDeviceCreation()
   ScopedGfxFeatureReporter reporterWARP("D3D11-WARP", gfxPrefs::LayersD3D11ForceWARP());
 
   HRESULT hr;
-  if (!AttemptWARPDeviceCreationHelper(reporterWARP, hr)) {
+  RefPtr device;
+  if (!AttemptWARPDeviceCreationHelper(reporterWARP, device, hr)) {
     gfxCriticalError() << "Exception occurred initializing WARP D3D11 device!";
     return FeatureStatus::Crashed;
   }
 
-  if (FAILED(hr) || !mD3D11Device) {
+  if (FAILED(hr) || !device) {
     // This should always succeed... in theory.
     gfxCriticalError() << "Failed to initialize WARP D3D11 device! " << hexa(hr);
     return FeatureStatus::Failed;
   }
 
+  {
+    MutexAutoLock lock(mDeviceLock);
+    mD3D11Device = device;
+  }
+
   // Only test for texture sharing on Windows 8 since it puts the device into
   // an unusable state if used on Windows 7
   if (IsWin8OrLater()) {
@@ -2137,6 +2104,20 @@ gfxWindowsPlatform::ContentAdapterIsParentAdapter(ID3D11Device* device)
   return true;
 }
 
+static void
+RecordContentDeviceFailure(TelemetryDeviceCode aDevice)
+{
+  // If the parent process fails to acquire a device, we record this
+  // normally as part of the environment. The exceptional case we're
+  // looking for here is when the parent process successfully acquires
+  // a device, but the content process fails to acquire the same device.
+  // This would not normally be displayed in about:support.
+  if (!XRE_IsContentProcess()) {
+    return;
+  }
+  Telemetry::Accumulate(Telemetry::GFX_CONTENT_FAILED_TO_ACQUIRE_DEVICE, uint32_t(aDevice));
+}
+
 bool
 gfxWindowsPlatform::AttemptD3D11ContentDeviceCreationHelper(
   IDXGIAdapter1* aAdapter, HRESULT& aResOut)
@@ -2168,10 +2149,14 @@ gfxWindowsPlatform::AttemptD3D11ContentDeviceCreation()
 
   HRESULT hr;
   if (!AttemptD3D11ContentDeviceCreationHelper(adapter, hr)) {
+    gfxCriticalNote << "Recovered from crash while creating a D3D11 content device";
+    RecordContentDeviceFailure(TelemetryDeviceCode::Content);
     return FeatureStatus::Crashed;
   }
 
   if (FAILED(hr) || !mD3D11ContentDevice) {
+    gfxCriticalNote << "Failed to create a D3D11 content device: " << hexa(hr);
+    RecordContentDeviceFailure(TelemetryDeviceCode::Content);
     return FeatureStatus::Failed;
   }
 
@@ -2201,21 +2186,36 @@ gfxWindowsPlatform::AttemptD3D11ContentDeviceCreation()
   return FeatureStatus::Available;
 }
 
-FeatureStatus
-gfxWindowsPlatform::AttemptD3D11ImageBridgeDeviceCreation()
+bool
+gfxWindowsPlatform::AttemptD3D11ImageBridgeDeviceCreationHelper(
+  IDXGIAdapter1* aAdapter,
+  HRESULT& aResOut)
 {
-  HRESULT hr = E_INVALIDARG;
-  MOZ_SEH_TRY{
-    hr =
+  MOZ_SEH_TRY {
+    aResOut =
       sD3D11CreateDeviceFn(GetDXGIAdapter(), D3D_DRIVER_TYPE_UNKNOWN, nullptr,
                            D3D11_CREATE_DEVICE_BGRA_SUPPORT,
                            mFeatureLevels.Elements(), mFeatureLevels.Length(),
                            D3D11_SDK_VERSION, getter_AddRefs(mD3D11ImageBridgeDevice), nullptr, nullptr);
   } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+    return false;
+  }
+  return true;
+}
+
+FeatureStatus
+gfxWindowsPlatform::AttemptD3D11ImageBridgeDeviceCreation()
+{
+  HRESULT hr;
+  if (!AttemptD3D11ImageBridgeDeviceCreationHelper(GetDXGIAdapter(), hr)) {
+    gfxCriticalNote << "Recovered from crash while creating a D3D11 image bridge device";
+    RecordContentDeviceFailure(TelemetryDeviceCode::Image);
     return FeatureStatus::Crashed;
   }
 
   if (FAILED(hr) || !mD3D11ImageBridgeDevice) {
+    gfxCriticalNote << "Failed to create a content image bridge device: " << hexa(hr);
+    RecordContentDeviceFailure(TelemetryDeviceCode::Image);
     return FeatureStatus::Failed;
   }
 
@@ -2409,6 +2409,8 @@ gfxWindowsPlatform::DisableD3D11AfterCrash()
 void
 gfxWindowsPlatform::ResetD3D11Devices()
 {
+  MutexAutoLock lock(mDeviceLock);
+
   mD3D11Device = nullptr;
   mD3D11ContentDevice = nullptr;
   mD3D11ImageBridgeDevice = nullptr;
@@ -2484,6 +2486,9 @@ gfxWindowsPlatform::InitializeD2D()
 
   mD2D1Status = CheckD2D1Support();
   if (IsFeatureStatusFailure(mD2D1Status)) {
+    if (XRE_IsContentProcess() && GetParentDevicePrefs().useD2D1()) {
+      RecordContentDeviceFailure(TelemetryDeviceCode::D2D1);
+    }
     return;
   }
 
@@ -2834,7 +2839,7 @@ gfxWindowsPlatform::GetAcceleratedCompositorBackends(nsTArray& aB
   }
 
   if (!gfxPrefs::LayersPreferD3D9()) {
-    if (gfxPlatform::CanUseDirect3D11() && GetD3D11Device()) {
+    if (gfxPlatform::CanUseDirect3D11() && mD3D11Device) {
       aBackends.AppendElement(LayersBackend::LAYERS_D3D11);
     } else {
       NS_WARNING("Direct3D 11-accelerated layers are not supported on this system.");
@@ -2874,8 +2879,8 @@ gfxWindowsPlatform::GetD2D1Status() const
 unsigned
 gfxWindowsPlatform::GetD3D11Version()
 {
-  ID3D11Device* device = GetD3D11Device();
-  if (!device) {
+  RefPtr device;
+  if (!GetD3D11Device(&device)) {
     return 0;
   }
   return device->GetFeatureLevel();
diff --git a/gfx/thebes/gfxWindowsPlatform.h b/gfx/thebes/gfxWindowsPlatform.h
index 1426f2f871..16e22f55e7 100644
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -26,6 +26,7 @@
 #include "nsTArray.h"
 #include "nsDataHashtable.h"
 
+#include "mozilla/Mutex.h"
 #include "mozilla/RefPtr.h"
 
 #include 
@@ -159,18 +160,10 @@ public:
      */
     void VerifyD2DDevice(bool aAttemptForce);
 
-    nsresult GetFontList(nsIAtom *aLangGroup,
-                         const nsACString& aGenericFamily,
-                         nsTArray& aListOfFonts) override;
-
-    nsresult UpdateFontList();
-
     virtual void GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh,
                                         int32_t aRunScript,
                                         nsTArray& aFontList) override;
 
-    nsresult GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName) override;
-
     gfxFontGroup*
     CreateFontGroup(const mozilla::FontFamilyList& aFontFamilyList,
                     const gfxFontStyle *aStyle,
@@ -178,24 +171,6 @@ public:
                     gfxUserFontSet *aUserFontSet,
                     gfxFloat aDevToCssSize) override;
 
-    /**
-     * Look up a local platform font using the full font face name (needed to support @font-face src local() )
-     */
-    virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
-                                          uint16_t aWeight,
-                                          int16_t aStretch,
-                                          uint8_t aStyle) override;
-
-    /**
-     * Activate a platform font (needed to support @font-face src url() )
-     */
-    virtual gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
-                                           uint16_t aWeight,
-                                           int16_t aStretch,
-                                           uint8_t aStyle,
-                                           const uint8_t* aFontData,
-                                           uint32_t aLength) override;
-
     virtual bool CanUseHardwareVideoDecoding() override;
 
     /**
@@ -230,14 +205,14 @@ public:
     IDWriteRenderingParams *GetRenderingParams(TextRenderingMode aRenderMode)
     { return mRenderingParams[aRenderMode]; }
 
+    bool GetD3D11Device(RefPtr* aOutDevice);
+    bool GetD3D11DeviceForCurrentThread(RefPtr* aOutDevice);
+    bool GetD3D11ImageBridgeDevice(RefPtr* aOutDevice);
+
     void OnDeviceManagerDestroy(mozilla::layers::DeviceManagerD3D9* aDeviceManager);
     mozilla::layers::DeviceManagerD3D9* GetD3D9DeviceManager();
     IDirect3DDevice9* GetD3D9Device();
-    ID3D11Device *GetD3D11Device();
     ID3D11Device *GetD3D11ContentDevice();
-    ID3D11Device* GetD3D11DeviceForCurrentThread();
-    // Device to be used on the ImageBridge thread
-    ID3D11Device *GetD3D11ImageBridgeDevice();
 
     // Create a D3D11 device to be used for DXVA decoding.
     already_AddRefed CreateD3D11DecoderDevice();
@@ -317,12 +292,18 @@ private:
 
     mozilla::gfx::FeatureStatus AttemptD3D11DeviceCreation();
     bool AttemptD3D11DeviceCreationHelper(
-        IDXGIAdapter1* aAdapter, HRESULT& aResOut);
+        IDXGIAdapter1* aAdapter,
+        RefPtr& aOutDevice,
+        HRESULT& aResOut);
 
     mozilla::gfx::FeatureStatus AttemptWARPDeviceCreation();
     bool AttemptWARPDeviceCreationHelper(
-        mozilla::ScopedGfxFeatureReporter& aReporterWARP, HRESULT& aResOut);
+        mozilla::ScopedGfxFeatureReporter& aReporterWARP,
+        RefPtr& aOutDevice,
+        HRESULT& aResOut);
 
+    bool AttemptD3D11ImageBridgeDeviceCreationHelper(
+        IDXGIAdapter1* aAdapter, HRESULT& aResOut);
     mozilla::gfx::FeatureStatus AttemptD3D11ImageBridgeDeviceCreation();
 
     mozilla::gfx::FeatureStatus AttemptD3D11ContentDeviceCreation();
@@ -342,18 +323,20 @@ private:
     RefPtr mRenderingParams[TEXT_RENDERING_COUNT];
     DWRITE_MEASURING_MODE mMeasuringMode;
 
+    mozilla::Mutex mDeviceLock;
     RefPtr mAdapter;
-    RefPtr mDeviceManager;
     RefPtr mD3D11Device;
     RefPtr mD3D11ContentDevice;
     RefPtr mD3D11ImageBridgeDevice;
-    RefPtr mD3D11ReadbackManager;
+    RefPtr mDeviceManager;
     bool mIsWARP;
     bool mHasDeviceReset;
     bool mHasFakeDeviceReset;
     bool mCompositorD3D11TextureSharingWorks;
     DeviceResetReason mDeviceResetReason;
 
+    RefPtr mD3D11ReadbackManager;
+
     // These should not be accessed directly. Use the Get[Feature]Status
     // accessors instead.
     mozilla::gfx::FeatureStatus mAcceleration;
diff --git a/gfx/thebes/gfxXlibSurface.cpp b/gfx/thebes/gfxXlibSurface.cpp
index cdb8db7f8a..1598b506cd 100644
--- a/gfx/thebes/gfxXlibSurface.cpp
+++ b/gfx/thebes/gfxXlibSurface.cpp
@@ -12,7 +12,6 @@
 #undef max // Xlibint.h defines this and it breaks std::max
 #undef min // Xlibint.h defines this and it breaks std::min
 
-#include "nsAutoPtr.h"
 #include "nsTArray.h"
 #include "nsAlgorithm.h"
 #include "mozilla/Preferences.h"
diff --git a/image/ClippedImage.cpp b/image/ClippedImage.cpp
index 57da47e9a2..4846ab62b9 100644
--- a/image/ClippedImage.cpp
+++ b/image/ClippedImage.cpp
@@ -290,12 +290,13 @@ ClippedImage::GetFrameInternal(const nsIntSize& aSize,
     RefPtr target = gfxPlatform::GetPlatform()->
       CreateOffscreenContentDrawTarget(IntSize(aSize.width, aSize.height),
                                        SurfaceFormat::B8G8R8A8);
-    if (!target) {
+    if (!target || !target->IsValid()) {
       NS_ERROR("Could not create a DrawTarget");
       return MakePair(DrawResult::TEMPORARY_ERROR, RefPtr());
     }
 
-    RefPtr ctx = new gfxContext(target);
+    RefPtr ctx = gfxContext::ForDrawTarget(target);
+    MOZ_ASSERT(ctx); // already checked the draw target above
 
     // Create our callback.
     RefPtr drawTileCallback =
diff --git a/image/DynamicImage.cpp b/image/DynamicImage.cpp
index 4fdf836080..c87f110886 100644
--- a/image/DynamicImage.cpp
+++ b/image/DynamicImage.cpp
@@ -180,12 +180,13 @@ DynamicImage::GetFrameAtSize(const IntSize& aSize,
 {
   RefPtr dt = gfxPlatform::GetPlatform()->
     CreateOffscreenContentDrawTarget(aSize, SurfaceFormat::B8G8R8A8);
-  if (!dt) {
+  if (!dt || !dt->IsValid()) {
     gfxWarning() <<
       "DynamicImage::GetFrame failed in CreateOffscreenContentDrawTarget";
     return nullptr;
   }
-  RefPtr context = new gfxContext(dt);
+  RefPtr context = gfxContext::ForDrawTarget(dt);
+  MOZ_ASSERT(context); // already checked the draw target above
 
   auto result = Draw(context, aSize, ImageRegion::Create(aSize),
                      aWhichFrame, Filter::POINT, Nothing(), aFlags);
diff --git a/image/OrientedImage.cpp b/image/OrientedImage.cpp
index 91bc0abef7..1da5b324bf 100644
--- a/image/OrientedImage.cpp
+++ b/image/OrientedImage.cpp
@@ -99,7 +99,7 @@ OrientedImage::GetFrame(uint32_t aWhichFrame,
   RefPtr target =
     gfxPlatform::GetPlatform()->
       CreateOffscreenContentDrawTarget(size, surfaceFormat);
-  if (!target) {
+  if (!target || !target->IsValid()) {
     NS_ERROR("Could not create a DrawTarget");
     return nullptr;
   }
@@ -113,7 +113,8 @@ OrientedImage::GetFrame(uint32_t aWhichFrame,
     new gfxSurfaceDrawable(innerSurface, size);
 
   // Draw.
-  RefPtr ctx = new gfxContext(target);
+  RefPtr ctx = gfxContext::ForDrawTarget(target);
+  MOZ_ASSERT(ctx); // already checked the draw target above
   ctx->Multiply(OrientationMatrix(size));
   gfxUtils::DrawPixelSnapped(ctx, drawable, size, ImageRegion::Create(size),
                              surfaceFormat, Filter::LINEAR);
diff --git a/image/VectorImage.cpp b/image/VectorImage.cpp
index 913940c55a..3f08dd4310 100644
--- a/image/VectorImage.cpp
+++ b/image/VectorImage.cpp
@@ -726,12 +726,13 @@ VectorImage::GetFrameAtSize(const IntSize& aSize,
   // (either the full image size, or the restricted region)
   RefPtr dt = gfxPlatform::GetPlatform()->
     CreateOffscreenContentDrawTarget(aSize, SurfaceFormat::B8G8R8A8);
-  if (!dt) {
+  if (!dt || !dt->IsValid()) {
     NS_ERROR("Could not create a DrawTarget");
     return nullptr;
   }
 
-  RefPtr context = new gfxContext(dt);
+  RefPtr context = gfxContext::ForDrawTarget(dt);
+  MOZ_ASSERT(context); // already checked the draw target above
 
   auto result = Draw(context, aSize, ImageRegion::Create(aSize),
                      aWhichFrame, Filter::POINT, Nothing(), aFlags);
diff --git a/image/imgFrame.cpp b/image/imgFrame.cpp
index af9d534e55..7069f48a82 100644
--- a/image/imgFrame.cpp
+++ b/image/imgFrame.cpp
@@ -295,14 +295,15 @@ imgFrame::InitWithDrawable(gfxDrawable* aDrawable,
       CreateOffscreenContentDrawTarget(mSize, mFormat);
   }
 
-  if (!target) {
+  if (!target || !target->IsValid()) {
     mAborted = true;
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   // Draw using the drawable the caller provided.
   nsIntRect imageRect(0, 0, mSize.width, mSize.height);
-  RefPtr ctx = new gfxContext(target);
+  RefPtr ctx = gfxContext::ForDrawTarget(target);
+  MOZ_ASSERT(ctx); // already checked the draw target above
   gfxUtils::DrawPixelSnapped(ctx, aDrawable, mSize,
                              ImageRegion::Create(ThebesRect(imageRect)),
                              mFormat, aFilter, aImageFlags);
diff --git a/intl/unicharutil/tools/genUnicodePropertyData.pl b/intl/unicharutil/tools/genUnicodePropertyData.pl
index 126563cc08..31553318aa 100644
--- a/intl/unicharutil/tools/genUnicodePropertyData.pl
+++ b/intl/unicharutil/tools/genUnicodePropertyData.pl
@@ -200,6 +200,7 @@ my @numericvalue;
 my @hanVariant;
 my @bidicategory;
 my @fullWidth;
+my @fullWidthInverse;
 my @verticalOrientation;
 for (my $i = 0; $i < 0x110000; ++$i) {
     $script[$i] = $scriptCode{"UNKNOWN"};
@@ -212,6 +213,7 @@ for (my $i = 0; $i < 0x110000; ++$i) {
     $hanVariant[$i] = 0;
     $bidicategory[$i] = $bidicategoryCode{"L"};
     $fullWidth[$i] = 0;
+    $fullWidthInverse[$i] = 0;
     $verticalOrientation[$i] = 1; # default for unlisted codepoints is 'R'
 }
 
@@ -336,11 +338,13 @@ while () {
           my $wideChar = hex(substr($fields[5], 9));
           die "didn't expect supplementary-plane values here" if $usv > 0xffff || $wideChar > 0xffff;
           $fullWidth[$usv] = $wideChar;
+          $fullWidthInverse[$wideChar] = $usv;
         }
         elsif ($fields[5] =~ /^/) {
           my $narrowChar = hex(substr($fields[5], 7));
           die "didn't expect supplementary-plane values here" if $usv > 0xffff || $narrowChar > 0xffff;
           $fullWidth[$narrowChar] = $usv;
+          $fullWidthInverse[$usv] = $narrowChar;
         }
     }
 }
@@ -687,6 +691,13 @@ sub sprintFullWidth
 }
 &genTables("", "", "FullWidth", "", "uint16_t", 10, 6, \&sprintFullWidth, 0, 2, 1);
 
+sub sprintFullWidthInverse
+{
+  my $usv = shift;
+  return sprintf("0x%04x,", $fullWidthInverse[$usv]);
+}
+&genTables("", "", "FullWidthInverse", "", "uint16_t", 10, 6, \&sprintFullWidthInverse, 0, 2, 1);
+
 sub sprintCasemap
 {
   my $usv = shift;
diff --git a/intl/unicharutil/util/nsUnicodeProperties.cpp b/intl/unicharutil/util/nsUnicodeProperties.cpp
index 6beab3a967..634f981f24 100644
--- a/intl/unicharutil/util/nsUnicodeProperties.cpp
+++ b/intl/unicharutil/util/nsUnicodeProperties.cpp
@@ -361,22 +361,22 @@ GetHanVariant(uint32_t aCh)
 }
 #endif
 
-uint32_t
-GetFullWidth(uint32_t aCh)
-{
-    // full-width mappings only exist for BMP characters; all others are
-    // returned unchanged
-    if (aCh < UNICODE_BMP_LIMIT) {
-        uint32_t v =
-            sFullWidthValues[sFullWidthPages[aCh >> kFullWidthCharBits]]
-                            [aCh & ((1 << kFullWidthCharBits) - 1)];
-        if (v) {
-            // return the mapped value if non-zero; else return original char
-            return v;
-        }
-    }
-    return aCh;
-}
+#define DEFINE_BMP_1PLANE_MAPPING_GET_FUNC(prefix_) \
+  uint32_t Get##prefix_(uint32_t aCh) \
+  { \
+    if (aCh >= UNICODE_BMP_LIMIT) { \
+      return aCh; \
+    } \
+    auto page = s##prefix_##Pages[aCh >> k##prefix_##CharBits]; \
+    auto index = aCh & ((1 << k##prefix_##CharBits) - 1); \
+    uint32_t v = s##prefix_##Values[page][index]; \
+    return v ? v : aCh; \
+  }
+
+// full-width mappings only exist for BMP characters; all others are
+// returned unchanged
+DEFINE_BMP_1PLANE_MAPPING_GET_FUNC(FullWidth)
+DEFINE_BMP_1PLANE_MAPPING_GET_FUNC(FullWidthInverse)
 
 bool
 IsClusterExtender(uint32_t aCh, uint8_t aCategory)
@@ -493,6 +493,18 @@ ClusterIterator::Next()
                  "ClusterIterator::Next has overshot the string!");
 }
 
+uint32_t
+CountGraphemeClusters(const char16_t* aText, uint32_t aLength)
+{
+  ClusterIterator iter(aText, aLength);
+  uint32_t result = 0;
+  while (!iter.AtEnd()) {
+    ++result;
+    iter.Next();
+  }
+  return result;
+}
+
 } // end namespace unicode
 
 } // end namespace mozilla
diff --git a/intl/unicharutil/util/nsUnicodeProperties.h b/intl/unicharutil/util/nsUnicodeProperties.h
index 8bee5f847b..82d6e9692f 100644
--- a/intl/unicharutil/util/nsUnicodeProperties.h
+++ b/intl/unicharutil/util/nsUnicodeProperties.h
@@ -101,6 +101,11 @@ HanVariantType GetHanVariant(uint32_t aCh);
 #endif
 
 uint32_t GetFullWidth(uint32_t aCh);
+// This is the reverse function of GetFullWidth which guarantees that
+// for every codepoint c, GetFullWidthInverse(GetFullWidth(c)) == c.
+// Note that, this function does not guarantee to convert all wide
+// form characters to their possible narrow form.
+uint32_t GetFullWidthInverse(uint32_t aCh);
 
 bool IsClusterExtender(uint32_t aCh, uint8_t aCategory);
 
@@ -146,6 +151,9 @@ private:
 #endif
 };
 
+// Count the number of grapheme clusters in the given string
+uint32_t CountGraphemeClusters(const char16_t* aText, uint32_t aLength);
+
 } // end namespace unicode
 
 } // end namespace mozilla
diff --git a/intl/unicharutil/util/nsUnicodePropertyData.cpp b/intl/unicharutil/util/nsUnicodePropertyData.cpp
index 4db4f3c173..9fd6dd1213 100644
--- a/intl/unicharutil/util/nsUnicodePropertyData.cpp
+++ b/intl/unicharutil/util/nsUnicodePropertyData.cpp
@@ -1359,6 +1359,31 @@ static const uint16_t sFullWidthValues[9][64] = {
 
 
 
+#define kFullWidthInverseMaxPlane  0
+#define kFullWidthInverseIndexBits 10
+#define kFullWidthInverseCharBits  6
+static const uint8_t sFullWidthInversePages[1024] = {
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,3,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,6,7,8,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,11,0,12
+};
+
+static const uint16_t sFullWidthInverseValues[13][64] = {
+  {0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000},
+  {0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xffe9,0xffea,0xffeb,0xffec,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000},
+  {0x0000,0x0000,0xffe8,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000},
+  {0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xffed,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000},
+  {0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xffee,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000},
+  {0x0020,0xff64,0xff61,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xff62,0xff63,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000},
+  {0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xff9e,0xff9f,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xff67,0xff71,0xff68,0xff72,0xff69,0xff73,0xff6a,0xff74,0xff6b,0xff75,0xff76,0x0000,0xff77,0x0000,0xff78,0x0000,0xff79,0x0000,0xff7a,0x0000,0xff7b,0x0000,0xff7c,0x0000,0xff7d,0x0000,0xff7e,0x0000,0xff7f,0x0000,0xff80},
+  {0x0000,0xff81,0x0000,0xff6f,0xff82,0x0000,0xff83,0x0000,0xff84,0x0000,0xff85,0xff86,0xff87,0xff88,0xff89,0xff8a,0x0000,0x0000,0xff8b,0x0000,0x0000,0xff8c,0x0000,0x0000,0xff8d,0x0000,0x0000,0xff8e,0x0000,0x0000,0xff8f,0xff90,0xff91,0xff92,0xff93,0xff6c,0xff94,0xff6d,0xff95,0xff6e,0xff96,0xff97,0xff98,0xff99,0xff9a,0xff9b,0x0000,0xff9c,0x0000,0x0000,0xff66,0xff9d,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xff65,0xff70,0x0000,0x0000,0x0000},
+  {0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xffa1,0xffa2,0xffa3,0xffa4,0xffa5,0xffa6,0xffa7,0xffa8,0xffa9,0xffaa,0xffab,0xffac,0xffad,0xffae,0xffaf},
+  {0xffb0,0xffb1,0xffb2,0xffb3,0xffb4,0xffb5,0xffb6,0xffb7,0xffb8,0xffb9,0xffba,0xffbb,0xffbc,0xffbd,0xffbe,0xffc2,0xffc3,0xffc4,0xffc5,0xffc6,0xffc7,0xffca,0xffcb,0xffcc,0xffcd,0xffce,0xffcf,0xffd2,0xffd3,0xffd4,0xffd5,0xffd6,0xffd7,0xffda,0xffdb,0xffdc,0xffa0,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000},
+  {0x0000,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f,0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f,0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f},
+  {0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f,0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x2985,0x2986,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000},
+  {0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x00a2,0x00a3,0x00ac,0x00af,0x00a6,0x00a5,0x20a9,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000}
+};
+
+
+
 #define kCaseMapMaxPlane  1
 #define kCaseMapIndexBits 11
 #define kCaseMapCharBits  5
diff --git a/ipc/glue/GeckoChildProcessHost.cpp b/ipc/glue/GeckoChildProcessHost.cpp
index a30f697c51..d126b26966 100644
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -110,6 +110,7 @@ GeckoChildProcessHost::GeckoChildProcessHost(GeckoProcessType aProcessType,
 #if defined(MOZ_WIDGET_COCOA)
   , mChildTask(MACH_PORT_NULL)
 #endif
+  , mAssociatedActors(1)
 {
     MOZ_COUNT_CTOR(GeckoChildProcessHost);
 }
@@ -471,6 +472,28 @@ GeckoChildProcessHost::SetAlreadyDead()
   mChildProcessHandle = 0;
 }
 
+namespace {
+
+void
+DelayedDeleteSubprocess(GeckoChildProcessHost* aSubprocess)
+{
+  XRE_GetIOMessageLoop()
+    ->PostTask(FROM_HERE,
+       new DeleteTask(aSubprocess));
+}
+
+}
+
+void
+GeckoChildProcessHost::DissociateActor()
+{
+  if (!--mAssociatedActors) {
+    MessageLoop::current()->
+      PostTask(FROM_HERE,
+        NewRunnableFunction(DelayedDeleteSubprocess, this));
+  }
+}
+
 int32_t GeckoChildProcessHost::mChildCounter = 0;
 
 void
diff --git a/ipc/glue/GeckoChildProcessHost.h b/ipc/glue/GeckoChildProcessHost.h
index 441f655c85..07df09880c 100644
--- a/ipc/glue/GeckoChildProcessHost.h
+++ b/ipc/glue/GeckoChildProcessHost.h
@@ -10,6 +10,7 @@
 #include "base/waitable_event.h"
 #include "chrome/common/child_process_host.h"
 
+#include "mozilla/Atomics.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/ipc/FileDescriptor.h"
 #include "mozilla/Monitor.h"
@@ -125,6 +126,14 @@ public:
   // For bug 943174: Skip the EnsureProcessTerminated call in the destructor.
   void SetAlreadyDead();
 
+  // This associates an actor telling the process host to stay alive at least
+  // until DissociateActor has been called.
+  void AssociateActor() { mAssociatedActors++; }
+
+  // This gets called when actors get destroyed and will schedule the object
+  // for deletion when all actors have cleared their associations.
+  void DissociateActor();
+
 protected:
   GeckoProcessType mProcessType;
   ChildPrivileges mPrivileges;
@@ -202,6 +211,10 @@ private:
   // FIXME/cjones: this strongly indicates bad design.  Shame on us.
   std::queue mQueue;
 
+  // This tracks how many actors are associated with this process that require
+  // it to stay alive and have not yet been destroyed.
+  Atomic mAssociatedActors;
+
   static uint32_t sNextUniqueID;
 };
 
diff --git a/ipc/glue/MessageChannel.cpp b/ipc/glue/MessageChannel.cpp
index 1e7f184eef..00b3810659 100644
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -14,11 +14,12 @@
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Move.h"
 #include "mozilla/SizePrintfMacros.h"
+#include "mozilla/Snprintf.h"
 #include "mozilla/Telemetry.h"
+#include "mozilla/Logging.h"
 #include "nsDebug.h"
 #include "nsISupportsImpl.h"
 #include "nsContentUtils.h"
-#include "mozilla/Snprintf.h"
 
 // Undo the damage done by mozzconf.h
 #undef compress
@@ -521,6 +522,11 @@ MessageChannel::~MessageChannel()
     IPC_ASSERT(mCxxStackFrames.empty(), "mismatched CxxStackFrame ctor/dtors");
 #ifdef OS_WIN
     BOOL ok = CloseHandle(mEvent);
+    if (!ok) {
+        gfxDevCrash(mozilla::gfx::LogReason::MessageChannelCloseFailure) <<
+            "MessageChannel failed to close. GetLastError: " <<
+            GetLastError();
+    }
     MOZ_RELEASE_ASSERT(ok);
 #endif
     Clear();
diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp
index de23e404c7..86b835b378 100644
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -3561,7 +3561,12 @@ PaintInactiveLayer(nsDisplayListBuilder* aBuilder,
                                       itemVisibleRect.Size(),
                                       SurfaceFormat::B8G8R8A8);
     if (tempDT) {
-      context = new gfxContext(tempDT);
+      context = gfxContext::ForDrawTarget(tempDT);
+      if (!context) {
+        // Leave this as crash, it's in the debugging code, we want to know
+        gfxDevCrash(LogReason::InvalidContext) << "PaintInactive context problem " << gfx::hexa(tempDT);
+        return;
+      }
       context->SetMatrix(gfxMatrix::Translation(-itemVisibleRect.x,
                                                 -itemVisibleRect.y));
     }
@@ -5499,7 +5504,12 @@ static void DebugPaintItem(DrawTarget& aDrawTarget,
   RefPtr tempDT =
     aDrawTarget.CreateSimilarDrawTarget(IntSize(bounds.width, bounds.height),
                                         SurfaceFormat::B8G8R8A8);
-  RefPtr context = new gfxContext(tempDT);
+  RefPtr context = gfxContext::ForDrawTarget(tempDT);
+  if (!context) {
+    // Leave this as crash, it's in the debugging code, we want to know
+    gfxDevCrash(LogReason::InvalidContext) << "DebugPaintItem context problem " << gfx::hexa(tempDT);
+    return;
+  }
   context->SetMatrix(gfxMatrix::Translation(-bounds.x, -bounds.y));
   nsRenderingContext ctx(context);
 
@@ -6027,12 +6037,13 @@ ContainerState::CreateMaskLayer(Layer *aLayer,
       aLayer->Manager()->CreateOptimalMaskDrawTarget(surfaceSizeInt);
 
     // fail if we can't get the right surface
-    if (!dt) {
+    if (!dt || !dt->IsValid()) {
       NS_WARNING("Could not create DrawTarget for mask layer.");
       return nullptr;
     }
 
-    RefPtr context = new gfxContext(dt);
+    RefPtr context = gfxContext::ForDrawTarget(dt);
+    MOZ_ASSERT(context); // already checked the draw target above
     context->Multiply(ThebesMatrix(imageTransform));
 
     // paint the clipping rects with alpha to create the mask
diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp
index b1963d2dac..ad31bfc8eb 100644
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -3105,7 +3105,7 @@ ElementRestyler::MoveStyleContextsForContentChildren(
         return false;
       }
       nsIAtom* pseudoTag = sc->GetPseudo();
-      if (pseudoTag && pseudoTag != nsCSSAnonBoxes::mozNonElement) {
+      if (pseudoTag && !nsCSSAnonBoxes::IsNonElement(pseudoTag)) {
         return false;
       }
       aContextsToMove.AppendElement(sc);
@@ -3538,7 +3538,7 @@ ElementRestyler::ComputeRestyleResultFromFrame(nsIFrame* aSelf,
   // where we have this kind of inheritance, we keep restyling past
   // pseudos.
   nsIAtom* pseudoTag = oldContext->GetPseudo();
-  if (pseudoTag && pseudoTag != nsCSSAnonBoxes::mozNonElement) {
+  if (pseudoTag && !nsCSSAnonBoxes::IsNonElement(pseudoTag)) {
     LOG_RESTYLE_CONTINUE("the old style context is for a pseudo");
     aRestyleResult = eRestyleResult_Continue;
     aCanStopWithStyleChange = false;
@@ -3552,7 +3552,10 @@ ElementRestyler::ComputeRestyleResultFromFrame(nsIFrame* aSelf,
     // be inheriting from a grandparent frame's style context (or a further
     // ancestor).
     nsIAtom* parentPseudoTag = parent->StyleContext()->GetPseudo();
-    if (parentPseudoTag && parentPseudoTag != nsCSSAnonBoxes::mozNonElement) {
+    if (parentPseudoTag &&
+        parentPseudoTag != nsCSSAnonBoxes::mozOtherNonElement) {
+      MOZ_ASSERT(parentPseudoTag != nsCSSAnonBoxes::mozText,
+                 "Style of text node should not be parent of anything");
       LOG_RESTYLE_CONTINUE("the old style context's parent is for a pseudo");
       aRestyleResult = eRestyleResult_Continue;
       // Parent style context pseudo-ness doesn't affect whether we can
@@ -3652,6 +3655,14 @@ ElementRestyler::ComputeRestyleResultFromNewContext(nsIFrame* aSelf,
     aCanStopWithStyleChange = false;
     return;
   }
+
+  if (oldContext->IsTextCombined() != aNewContext->IsTextCombined()) {
+    LOG_RESTYLE_CONTINUE("NS_STYLE_IS_TEXT_COMBINED differs between "
+                         "old and new style contexts");
+    aRestyleResult = eRestyleResult_Continue;
+    aCanStopWithStyleChange = false;
+    return;
+  }
 }
 
 bool
@@ -3909,11 +3920,10 @@ ElementRestyler::RestyleSelf(nsIFrame* aSelf,
     // continuation.
     LOG_RESTYLE("using previous continuation's context");
     newContext = prevContinuationContext;
-  }
-  else if (pseudoTag == nsCSSAnonBoxes::mozNonElement) {
+  } else if (nsCSSAnonBoxes::IsNonElement(pseudoTag)) {
     NS_ASSERTION(aSelf->GetContent(),
                  "non pseudo-element frame without content node");
-    newContext = styleSet->ResolveStyleForNonElement(parentContext);
+    newContext = styleSet->ResolveStyleForNonElement(parentContext, pseudoTag);
   }
   else {
     Element* element = ElementForStyleContext(mParentContent, aSelf, pseudoType);
@@ -4258,7 +4268,7 @@ ElementRestyler::RestyleSelf(nsIFrame* aSelf,
     const CSSPseudoElementType extraPseudoType =
       oldExtraContext->GetPseudoType();
     NS_ASSERTION(extraPseudoTag &&
-                 extraPseudoTag != nsCSSAnonBoxes::mozNonElement,
+                 !nsCSSAnonBoxes::IsNonElement(extraPseudoTag),
                  "extra style context is not pseudo element");
     Element* element = extraPseudoType != CSSPseudoElementType::AnonBox
                          ? mContent->AsElement() : nullptr;
diff --git a/layout/base/StaticPresData.cpp b/layout/base/StaticPresData.cpp
new file mode 100644
index 0000000000..73acd5440e
--- /dev/null
+++ b/layout/base/StaticPresData.cpp
@@ -0,0 +1,311 @@
+/* -*- 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 "mozilla/StaticPresData.h"
+
+#include "mozilla/Preferences.h"
+#include "nsPresContext.h"
+
+namespace mozilla {
+
+static StaticPresData* sSingleton = nullptr;
+
+void
+StaticPresData::Init()
+{
+  MOZ_ASSERT(!sSingleton);
+  sSingleton = new StaticPresData();
+}
+
+void
+StaticPresData::Shutdown()
+{
+  MOZ_ASSERT(sSingleton);
+  delete sSingleton;
+  sSingleton = nullptr;
+}
+
+StaticPresData*
+StaticPresData::Get()
+{
+  MOZ_ASSERT(sSingleton);
+  return sSingleton;
+}
+
+StaticPresData::StaticPresData()
+{
+  mLangService = do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID);
+
+  mBorderWidthTable[NS_STYLE_BORDER_WIDTH_THIN] = nsPresContext::CSSPixelsToAppUnits(1);
+  mBorderWidthTable[NS_STYLE_BORDER_WIDTH_MEDIUM] = nsPresContext::CSSPixelsToAppUnits(3);
+  mBorderWidthTable[NS_STYLE_BORDER_WIDTH_THICK] = nsPresContext::CSSPixelsToAppUnits(5);
+}
+
+#define MAKE_FONT_PREF_KEY(_pref, _s0, _s1) \
+ _pref.Assign(_s0); \
+ _pref.Append(_s1);
+
+static const char* const kGenericFont[] = {
+  ".variable.",
+  ".fixed.",
+  ".serif.",
+  ".sans-serif.",
+  ".monospace.",
+  ".cursive.",
+  ".fantasy."
+};
+
+// These are private, use the list in nsFont.h if you want a public list.
+enum {
+  eDefaultFont_Variable,
+  eDefaultFont_Fixed,
+  eDefaultFont_Serif,
+  eDefaultFont_SansSerif,
+  eDefaultFont_Monospace,
+  eDefaultFont_Cursive,
+  eDefaultFont_Fantasy,
+  eDefaultFont_COUNT
+};
+
+const LangGroupFontPrefs*
+StaticPresData::GetFontPrefsForLangHelper(nsIAtom *aLanguage,
+                                          const LangGroupFontPrefs* aPrefs) const
+{
+  // Get language group for aLanguage:
+  MOZ_ASSERT(aLanguage);
+  MOZ_ASSERT(mLangService);
+  MOZ_ASSERT(aPrefs);
+
+  nsresult rv = NS_OK;
+  nsIAtom *langGroupAtom = nullptr;
+  langGroupAtom = mLangService->GetLanguageGroup(aLanguage, &rv);
+  if (NS_FAILED(rv) || !langGroupAtom) {
+    langGroupAtom = nsGkAtoms::x_western; // Assume x-western is safe...
+  }
+
+  LangGroupFontPrefs *prefs = const_cast(aPrefs);
+  if (prefs->mLangGroup) { // if initialized
+    DebugOnly count = 0;
+    for (;;) {
+      NS_ASSERTION(++count < 35, "Lang group count exceeded!!!");
+      if (prefs->mLangGroup == langGroupAtom) {
+        return prefs;
+      }
+      if (!prefs->mNext) {
+        break;
+      }
+      prefs = prefs->mNext;
+    }
+
+    // nothing cached, so go on and fetch the prefs for this lang group:
+    prefs = prefs->mNext = new LangGroupFontPrefs;
+  }
+
+  prefs->mLangGroup = langGroupAtom;
+
+  /* Fetch the font prefs to be used -- see bug 61883 for details.
+     Not all prefs are needed upfront. Some are fallback prefs intended
+     for the GFX font sub-system...
+
+  1) unit : assumed to be the same for all language groups -------------
+  font.size.unit = px | pt    XXX could be folded in the size... bug 90440
+
+  2) attributes for generic fonts --------------------------------------
+  font.default.[langGroup] = serif | sans-serif - fallback generic font
+  font.name.[generic].[langGroup] = current user' selected font on the pref dialog
+  font.name-list.[generic].[langGroup] = fontname1, fontname2, ... [factory pre-built list]
+  font.size.[generic].[langGroup] = integer - settable by the user
+  font.size-adjust.[generic].[langGroup] = "float" - settable by the user
+  font.minimum-size.[langGroup] = integer - settable by the user
+  */
+
+  nsAutoCString langGroup;
+  langGroupAtom->ToUTF8String(langGroup);
+
+  prefs->mDefaultVariableFont.size = nsPresContext::CSSPixelsToAppUnits(16);
+  prefs->mDefaultFixedFont.size = nsPresContext::CSSPixelsToAppUnits(13);
+
+  nsAutoCString pref;
+
+  // get the current applicable font-size unit
+  enum {eUnit_unknown = -1, eUnit_px, eUnit_pt};
+  int32_t unit = eUnit_px;
+
+  nsAdoptingCString cvalue =
+    Preferences::GetCString("font.size.unit");
+
+  if (!cvalue.IsEmpty()) {
+    if (cvalue.EqualsLiteral("px")) {
+      unit = eUnit_px;
+    }
+    else if (cvalue.EqualsLiteral("pt")) {
+      unit = eUnit_pt;
+    }
+    else {
+      // XXX should really send this warning to the user (Error Console?).
+      // And just default to unit = eUnit_px?
+      NS_WARNING("unexpected font-size unit -- expected: 'px' or 'pt'");
+      unit = eUnit_unknown;
+    }
+  }
+
+  // get font.minimum-size.[langGroup]
+
+  MAKE_FONT_PREF_KEY(pref, "font.minimum-size.", langGroup);
+
+  int32_t size = Preferences::GetInt(pref.get());
+  if (unit == eUnit_px) {
+    prefs->mMinimumFontSize = nsPresContext::CSSPixelsToAppUnits(size);
+  }
+  else if (unit == eUnit_pt) {
+    prefs->mMinimumFontSize = nsPresContext::CSSPointsToAppUnits(size);
+  }
+
+  nsFont* fontTypes[] = {
+    &prefs->mDefaultVariableFont,
+    &prefs->mDefaultFixedFont,
+    &prefs->mDefaultSerifFont,
+    &prefs->mDefaultSansSerifFont,
+    &prefs->mDefaultMonospaceFont,
+    &prefs->mDefaultCursiveFont,
+    &prefs->mDefaultFantasyFont
+  };
+  static_assert(MOZ_ARRAY_LENGTH(fontTypes) == eDefaultFont_COUNT,
+                "FontTypes array count is not correct");
+
+  // Get attributes specific to each generic font. We do not get the user's
+  // generic-font-name-to-specific-family-name preferences because its the
+  // generic name that should be fed into the cascade. It is up to the GFX
+  // code to look up the font prefs to convert generic names to specific
+  // family names as necessary.
+  nsAutoCString generic_dot_langGroup;
+  for (uint32_t eType = 0; eType < ArrayLength(fontTypes); ++eType) {
+    generic_dot_langGroup.Assign(kGenericFont[eType]);
+    generic_dot_langGroup.Append(langGroup);
+
+    nsFont* font = fontTypes[eType];
+
+    // set the default variable font (the other fonts are seen as 'generic' fonts
+    // in GFX and will be queried there when hunting for alternative fonts)
+    if (eType == eDefaultFont_Variable) {
+      MAKE_FONT_PREF_KEY(pref, "font.name.variable.", langGroup);
+
+      nsAdoptingString value = Preferences::GetString(pref.get());
+      if (!value.IsEmpty()) {
+        FontFamilyName defaultVariableName = FontFamilyName::Convert(value);
+        FontFamilyType defaultType = defaultVariableName.mType;
+        NS_ASSERTION(defaultType == eFamily_serif ||
+                     defaultType == eFamily_sans_serif,
+                     "default type must be serif or sans-serif");
+        prefs->mDefaultVariableFont.fontlist = FontFamilyList(defaultType);
+      }
+      else {
+        MAKE_FONT_PREF_KEY(pref, "font.default.", langGroup);
+        value = Preferences::GetString(pref.get());
+        if (!value.IsEmpty()) {
+          FontFamilyName defaultVariableName = FontFamilyName::Convert(value);
+          FontFamilyType defaultType = defaultVariableName.mType;
+          NS_ASSERTION(defaultType == eFamily_serif ||
+                       defaultType == eFamily_sans_serif,
+                       "default type must be serif or sans-serif");
+          prefs->mDefaultVariableFont.fontlist = FontFamilyList(defaultType);
+        }
+      }
+    }
+    else {
+      if (eType == eDefaultFont_Monospace) {
+        // This takes care of the confusion whereby people often expect "monospace"
+        // to have the same default font-size as "-moz-fixed" (this tentative
+        // size may be overwritten with the specific value for "monospace" when
+        // "font.size.monospace.[langGroup]" is read -- see below)
+        prefs->mDefaultMonospaceFont.size = prefs->mDefaultFixedFont.size;
+      }
+      else if (eType != eDefaultFont_Fixed) {
+        // all the other generic fonts are initialized with the size of the
+        // variable font, but their specific size can supersede later -- see below
+        font->size = prefs->mDefaultVariableFont.size;
+      }
+    }
+
+    // Bug 84398: for spec purists, a different font-size only applies to the
+    // .variable. and .fixed. fonts and the other fonts should get |font-size-adjust|.
+    // The problem is that only GfxWin has the support for |font-size-adjust|. So for
+    // parity, we enable the ability to set a different font-size on all platforms.
+
+    // get font.size.[generic].[langGroup]
+    // size=0 means 'Auto', i.e., generic fonts retain the size of the variable font
+    MAKE_FONT_PREF_KEY(pref, "font.size", generic_dot_langGroup);
+    size = Preferences::GetInt(pref.get());
+    if (size > 0) {
+      if (unit == eUnit_px) {
+        font->size = nsPresContext::CSSPixelsToAppUnits(size);
+      }
+      else if (unit == eUnit_pt) {
+        font->size = nsPresContext::CSSPointsToAppUnits(size);
+      }
+    }
+
+    // get font.size-adjust.[generic].[langGroup]
+    // XXX only applicable on GFX ports that handle |font-size-adjust|
+    MAKE_FONT_PREF_KEY(pref, "font.size-adjust", generic_dot_langGroup);
+    cvalue = Preferences::GetCString(pref.get());
+    if (!cvalue.IsEmpty()) {
+      font->sizeAdjust = (float)atof(cvalue.get());
+    }
+
+#ifdef DEBUG_rbs
+    printf("%s Family-list:%s size:%d sizeAdjust:%.2f\n",
+           generic_dot_langGroup.get(),
+           NS_ConvertUTF16toUTF8(font->name).get(), font->size,
+           font->sizeAdjust);
+#endif
+  }
+
+  return prefs;
+}
+
+const nsFont*
+StaticPresData::GetDefaultFontHelper(uint8_t aFontID, nsIAtom *aLanguage,
+                                     const LangGroupFontPrefs* aPrefs) const
+{
+  MOZ_ASSERT(aLanguage);
+  MOZ_ASSERT(aPrefs);
+
+  const nsFont *font;
+  switch (aFontID) {
+    // Special (our default variable width font and fixed width font)
+    case kPresContext_DefaultVariableFont_ID:
+      font = &aPrefs->mDefaultVariableFont;
+      break;
+    case kPresContext_DefaultFixedFont_ID:
+      font = &aPrefs->mDefaultFixedFont;
+      break;
+    // CSS
+    case kGenericFont_serif:
+      font = &aPrefs->mDefaultSerifFont;
+      break;
+    case kGenericFont_sans_serif:
+      font = &aPrefs->mDefaultSansSerifFont;
+      break;
+    case kGenericFont_monospace:
+      font = &aPrefs->mDefaultMonospaceFont;
+      break;
+    case kGenericFont_cursive:
+      font = &aPrefs->mDefaultCursiveFont;
+      break;
+    case kGenericFont_fantasy:
+      font = &aPrefs->mDefaultFantasyFont;
+      break;
+    default:
+      font = nullptr;
+      NS_ERROR("invalid arg");
+      break;
+  }
+  return font;
+}
+
+
+} // namespace mozilla
diff --git a/layout/base/StaticPresData.h b/layout/base/StaticPresData.h
new file mode 100644
index 0000000000..62e174fd26
--- /dev/null
+++ b/layout/base/StaticPresData.h
@@ -0,0 +1,160 @@
+/* -*- 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/. */
+
+#ifndef mozilla_StaticPresData_h
+#define mozilla_StaticPresData_h
+
+#include "nsAutoPtr.h"
+#include "nsCoord.h"
+#include "nsCOMPtr.h"
+#include "nsFont.h"
+#include "nsIAtom.h"
+#include "nsILanguageAtomService.h"
+
+namespace mozilla {
+
+struct LangGroupFontPrefs {
+  // Font sizes default to zero; they will be set in GetFontPreferences
+  LangGroupFontPrefs()
+    : mLangGroup(nullptr)
+    , mMinimumFontSize(0)
+    , mDefaultVariableFont(mozilla::eFamily_serif, 0)
+    , mDefaultFixedFont(mozilla::eFamily_monospace, 0)
+    , mDefaultSerifFont(mozilla::eFamily_serif, 0)
+    , mDefaultSansSerifFont(mozilla::eFamily_sans_serif, 0)
+    , mDefaultMonospaceFont(mozilla::eFamily_monospace, 0)
+    , mDefaultCursiveFont(mozilla::eFamily_cursive, 0)
+    , mDefaultFantasyFont(mozilla::eFamily_fantasy, 0)
+  {}
+
+  void Reset()
+  {
+    // Throw away any other LangGroupFontPrefs objects:
+    mNext = nullptr;
+
+    // Make GetFontPreferences reinitialize mLangGroupFontPrefs:
+    mLangGroup = nullptr;
+  }
+
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
+    size_t n = 0;
+    LangGroupFontPrefs* curr = mNext;
+    while (curr) {
+      n += aMallocSizeOf(curr);
+
+      // Measurement of the following members may be added later if DMD finds
+      // it is worthwhile:
+      // - mLangGroup
+      // - mDefault*Font
+
+      curr = curr->mNext;
+    }
+    return n;
+  }
+
+  nsCOMPtr mLangGroup;
+  nscoord mMinimumFontSize;
+  nsFont mDefaultVariableFont;
+  nsFont mDefaultFixedFont;
+  nsFont mDefaultSerifFont;
+  nsFont mDefaultSansSerifFont;
+  nsFont mDefaultMonospaceFont;
+  nsFont mDefaultCursiveFont;
+  nsFont mDefaultFantasyFont;
+  nsAutoPtr mNext;
+};
+
+/**
+ * Some functionality that has historically lived on nsPresContext does not
+ * actually need to be per-document. This singleton class serves as a host
+ * for that functionality. We delegate to it from nsPresContext where
+ * appropriate, and use it standalone in some cases as well.
+ */
+class StaticPresData
+{
+public:
+  // Initialization and shutdown of the singleton. Called exactly once.
+  static void Init();
+  static void Shutdown();
+
+  // Gets an instance of the singleton. Infallible between the calls to Init
+  // and Shutdown.
+  static StaticPresData* Get();
+
+  /**
+   * This table maps border-width enums 'thin', 'medium', 'thick'
+   * to actual nscoord values.
+   */
+  const nscoord* GetBorderWidthTable() { return mBorderWidthTable; }
+
+  /**
+   * Fetch the user's font preferences for the given aLanguage's
+   * langugage group.
+   *
+   * The original code here is pretty old, and includes an optimization
+   * whereby language-specific prefs are read per-document, and the
+   * results are stored in a linked list, which is assumed to be very short
+   * since most documents only ever use one language.
+   *
+   * Storing this per-session rather than per-document would almost certainly
+   * be fine. But just to be on the safe side, we leave the old mechanism as-is,
+   * with an additional per-session cache that new callers can use if they don't
+   * have a PresContext.
+   */
+  const LangGroupFontPrefs* GetFontPrefsForLangHelper(nsIAtom* aLanguage,
+                                                      const LangGroupFontPrefs* aPrefs) const;
+  /**
+   * Get the default font for the given language and generic font ID.
+   * aLanguage may not be nullptr.
+   *
+   * This object is read-only, you must copy the font to modify it.
+   *
+   * When aFontID is kPresContext_DefaultVariableFontID or
+   * kPresContext_DefaultFixedFontID (which equals
+   * kGenericFont_moz_fixed, which is used for the -moz-fixed generic),
+   * the nsFont returned has its name as a CSS generic family (serif or
+   * sans-serif for the former, monospace for the latter), and its size
+   * as the default font size for variable or fixed fonts for the
+   * language group.
+   *
+   * For aFontID corresponding to a CSS Generic, the nsFont returned has
+   * its name set to that generic font's name, and its size set to
+   * the user's preference for font size for that generic and the
+   * given language.
+   */
+  const nsFont* GetDefaultFontHelper(uint8_t aFontID,
+                                     nsIAtom* aLanguage,
+                                     const LangGroupFontPrefs* aPrefs) const;
+
+  /*
+   * These versions operate on the font pref cache on StaticPresData.
+   */
+
+  const nsFont* GetDefaultFont(uint8_t aFontID, nsIAtom* aLanguage) const
+  {
+    MOZ_ASSERT(aLanguage);
+    return GetDefaultFontHelper(aFontID, aLanguage, GetFontPrefsForLang(aLanguage));
+  }
+  const LangGroupFontPrefs* GetFontPrefsForLang(nsIAtom* aLanguage) const
+  {
+    MOZ_ASSERT(aLanguage);
+    return GetFontPrefsForLangHelper(aLanguage, &mStaticLangGroupFontPrefs);
+  }
+
+  void ResetCachedFontPrefs() { mStaticLangGroupFontPrefs.Reset(); }
+
+private:
+  StaticPresData();
+  ~StaticPresData() {}
+
+  nsCOMPtr mLangService;
+  nscoord mBorderWidthTable[3];
+  LangGroupFontPrefs mStaticLangGroupFontPrefs;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_StaticPresData_h
diff --git a/layout/base/crashtests/1156588.html b/layout/base/crashtests/1156588.html
new file mode 100644
index 0000000000..ed0098f79a
--- /dev/null
+++ b/layout/base/crashtests/1156588.html
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + diff --git a/layout/base/crashtests/1234622-1.html b/layout/base/crashtests/1234622-1.html new file mode 100644 index 0000000000..c715bc18d0 --- /dev/null +++ b/layout/base/crashtests/1234622-1.html @@ -0,0 +1,17 @@ + + + diff --git a/layout/base/crashtests/crashtests.list b/layout/base/crashtests/crashtests.list index 13a0e6cf18..7141d36057 100644 --- a/layout/base/crashtests/crashtests.list +++ b/layout/base/crashtests/crashtests.list @@ -293,7 +293,7 @@ load 469861-1.xhtml load 469861-2.xhtml load 470851-1.xhtml load 471594-1.xhtml -asserts-if(Android,2) load 473042.xhtml # bug 1034369 (may also cause a few assertions to be registered on the next test) +asserts-if(Android&&!asyncPan,1-2) load 473042.xhtml # bug 1034369 (may also cause a few assertions to be registered on the next test) asserts(0-5) load 474075.html # bug 847368 load 477333-1.xhtml load 477731-1.html @@ -461,7 +461,9 @@ load 1107508-1.html load 1116104.html load 1127198-1.html load 1140198.html -load 1297835.html +pref(layout.css.grid.enabled,true) load 1156588.html load 1162813.xul load 1163583.html +load 1234622-1.html load 1235467-1.html +load 1297835.html diff --git a/layout/base/moz.build b/layout/base/moz.build index f79f15e4fe..a1b42b7d98 100644 --- a/layout/base/moz.build +++ b/layout/base/moz.build @@ -109,6 +109,7 @@ EXPORTS.mozilla += [ 'RestyleManagerHandle.h', 'RestyleManagerHandleInlines.h', 'ServoRestyleManager.h', + 'StaticPresData.h', ] UNIFIED_SOURCES += [ @@ -154,6 +155,7 @@ UNIFIED_SOURCES += [ 'ScrollbarStyles.cpp', 'ServoRestyleManager.cpp', 'StackArena.cpp', + 'StaticPresData.cpp', 'TouchManager.cpp', 'ZoomConstraintsClient.cpp', ] diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 5733e0f2d3..29088c9919 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -341,6 +341,15 @@ IsFlexOrGridContainer(const nsIFrame* aFrame) t == nsGkAtoms::gridContainerFrame; } +// Returns true if aFrame has "display: -webkit-{inline-}box" +static inline bool +IsWebkitBox(const nsIFrame* aFrame) +{ + auto containerDisplay = aFrame->StyleDisplay()->mDisplay; + return containerDisplay == NS_STYLE_DISPLAY_WEBKIT_BOX || + containerDisplay == NS_STYLE_DISPLAY_WEBKIT_INLINE_BOX; +} + #if DEBUG static void AssertAnonymousFlexOrGridItemParent(const nsIFrame* aChild, @@ -2500,7 +2509,8 @@ nsCSSFrameConstructor::ConstructDocElementFrame(Element* aDocEle frameItems)); newFrame = frameItems.FirstChild(); NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames"); - } else if (display->mDisplay == NS_STYLE_DISPLAY_FLEX) { + } else if (display->mDisplay == NS_STYLE_DISPLAY_FLEX || + display->mDisplay == NS_STYLE_DISPLAY_WEBKIT_BOX) { contentFrame = NS_NewFlexContainerFrame(mPresShell, styleContext); InitAndRestoreFrame(state, aDocElement, mDocElementContainingBlock, contentFrame); @@ -2942,7 +2952,7 @@ nsCSSFrameConstructor::CreatePlaceholderFrameFor(nsIPresShell* aPresShell, nsFrameState aTypeBit) { RefPtr placeholderStyle = aPresShell->StyleSet()-> - ResolveStyleForNonElement(aParentStyle); + ResolveStyleForNonElement(aParentStyle, nsCSSAnonBoxes::mozOtherNonElement); // The placeholder frame gets a pseudo style context nsPlaceholderFrame* placeholderFrame = @@ -4665,7 +4675,8 @@ nsCSSFrameConstructor::FindDisplayData(const nsStyleDisplay* aDisplay, // viewport, we need to make sure not to add another layer of scrollbars, so // we use a different FCData struct without FCDATA_MAY_NEED_SCROLLFRAME. if (propagatedScrollToViewport && aDisplay->IsScrollableOverflow()) { - if (aDisplay->mDisplay == NS_STYLE_DISPLAY_FLEX) { + if (aDisplay->mDisplay == NS_STYLE_DISPLAY_FLEX || + aDisplay->mDisplay == NS_STYLE_DISPLAY_WEBKIT_BOX) { static const FrameConstructionData sNonScrollableFlexData = FCDATA_DECL(0, NS_NewFlexContainerFrame); return &sNonScrollableFlexData; @@ -4689,6 +4700,10 @@ nsCSSFrameConstructor::FindDisplayData(const nsStyleDisplay* aDisplay, FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame) }, { NS_STYLE_DISPLAY_INLINE_FLEX, FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame) }, + { NS_STYLE_DISPLAY_WEBKIT_BOX, + FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame) }, + { NS_STYLE_DISPLAY_WEBKIT_INLINE_BOX, + FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame) }, { NS_STYLE_DISPLAY_GRID, FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewGridContainerFrame) }, { NS_STYLE_DISPLAY_INLINE_GRID, @@ -4961,7 +4976,8 @@ nsCSSFrameConstructor::ResolveStyleContext(nsStyleContext* aParentStyleContext, NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT), "shouldn't waste time creating style contexts for " "comments and processing instructions"); - result = styleSet->ResolveStyleForNonElement(aParentStyleContext); + result = styleSet->ResolveStyleForNonElement(aParentStyleContext, + nsCSSAnonBoxes::mozText); } // ServoRestyleManager does not handle transitions yet, and when it does @@ -9806,10 +9822,13 @@ nsCSSFrameConstructor::CreateNeededAnonFlexOrGridItems( } nsIAtom* containerType = aParentFrame->GetType(); + const bool isWebkitBox = IsWebkitBox(aParentFrame); + FCItemIterator iter(aItems); do { // Advance iter past children that don't want to be wrapped - if (iter.SkipItemsThatDontNeedAnonFlexOrGridItem(aState, containerType)) { + if (iter.SkipItemsThatDontNeedAnonFlexOrGridItem(aState, containerType, + isWebkitBox)) { // Hit the end of the items without finding any remaining children that // need to be wrapped. We're finished! return; @@ -9831,8 +9850,10 @@ nsCSSFrameConstructor::CreateNeededAnonFlexOrGridItems( FCItemIterator afterWhitespaceIter(iter); bool hitEnd = afterWhitespaceIter.SkipWhitespace(aState); bool nextChildNeedsAnonItem = - !hitEnd && afterWhitespaceIter.item().NeedsAnonFlexOrGridItem(aState, - containerType); + !hitEnd && + afterWhitespaceIter.item().NeedsAnonFlexOrGridItem(aState, + containerType, + isWebkitBox); if (!nextChildNeedsAnonItem) { // There's nothing after the whitespace that we need to wrap, so we @@ -9846,7 +9867,8 @@ nsCSSFrameConstructor::CreateNeededAnonFlexOrGridItems( // we jump back to the beginning of the loop to skip over that child // (and anything else non-wrappable after it) MOZ_ASSERT(!iter.IsDone() && - !iter.item().NeedsAnonFlexOrGridItem(aState, containerType), + !iter.item().NeedsAnonFlexOrGridItem(aState, containerType, + isWebkitBox), "hitEnd and/or nextChildNeedsAnonItem lied"); continue; } @@ -9856,7 +9878,8 @@ nsCSSFrameConstructor::CreateNeededAnonFlexOrGridItems( // anonymous flex/grid item. Now we see how many children after it also want // to be wrapped in an anonymous flex/grid item. FCItemIterator endIter(iter); // iterator to find the end of the group - endIter.SkipItemsThatNeedAnonFlexOrGridItem(aState, containerType); + endIter.SkipItemsThatNeedAnonFlexOrGridItem(aState, containerType, + isWebkitBox); NS_ASSERTION(iter != endIter, "Should've had at least one wrappable child to seek past"); @@ -11081,9 +11104,9 @@ nsCSSFrameConstructor::CreateFloatingLetterFrame( // get a proper style context for it (the one passed in is for the // letter frame and will have the float property set on it; the text // frame shouldn't have that set). - RefPtr textSC; StyleSetHandle styleSet = mPresShell->StyleSet(); - textSC = styleSet->ResolveStyleForNonElement(aStyleContext); + RefPtr textSC = styleSet-> + ResolveStyleForNonElement(aStyleContext, nsCSSAnonBoxes::mozText); aTextFrame->SetStyleContextWithoutNotification(textSC); InitAndRestoreFrame(aState, aTextContent, letterFrame, aTextFrame); @@ -11101,8 +11124,8 @@ nsCSSFrameConstructor::CreateFloatingLetterFrame( // Repair the continuations style context nsStyleContext* parentStyleContext = aStyleContext->GetParent(); if (parentStyleContext) { - RefPtr newSC; - newSC = styleSet->ResolveStyleForNonElement(parentStyleContext); + RefPtr newSC = styleSet-> + ResolveStyleForNonElement(parentStyleContext, nsCSSAnonBoxes::mozText); nextTextFrame->SetStyleContext(newSC); } } @@ -11155,8 +11178,8 @@ nsCSSFrameConstructor::CreateLetterFrame(nsContainerFrame* aBlockFrame, RefPtr sc = GetFirstLetterStyle(blockContent, parentStyleContext); if (sc) { - RefPtr textSC; - textSC = mPresShell->StyleSet()->ResolveStyleForNonElement(sc); + RefPtr textSC = mPresShell->StyleSet()-> + ResolveStyleForNonElement(sc, nsCSSAnonBoxes::mozText); // Create a new text frame (the original one will be discarded) // pass a temporary stylecontext, the correct one will be set @@ -11354,8 +11377,8 @@ nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames( if (!textContent) { return NS_OK; } - RefPtr newSC; - newSC = aPresShell->StyleSet()->ResolveStyleForNonElement(parentSC); + RefPtr newSC = aPresShell->StyleSet()-> + ResolveStyleForNonElement(parentSC, nsCSSAnonBoxes::mozText); nsIFrame* newTextFrame = NS_NewTextFrame(aPresShell, newSC); newTextFrame->Init(textContent, parentFrame, nullptr); @@ -11427,8 +11450,8 @@ nsCSSFrameConstructor::RemoveFirstLetterFrames(nsIPresShell* aPresShell, if (!textContent) { break; } - RefPtr newSC; - newSC = aPresShell->StyleSet()->ResolveStyleForNonElement(parentSC); + RefPtr newSC = aPresShell->StyleSet()-> + ResolveStyleForNonElement(parentSC, nsCSSAnonBoxes::mozText); textFrame = NS_NewTextFrame(aPresShell, newSC); textFrame->Init(textContent, aFrame, nullptr); @@ -12052,8 +12075,10 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState, // Check if we're adding to-be-wrapped content right *after* an existing // anonymous flex or grid item (which would need to absorb this content). nsIAtom* containerType = aFrame->GetType(); + bool isWebkitBox = IsWebkitBox(aFrame); if (aPrevSibling && IsAnonymousFlexOrGridItem(aPrevSibling) && - iter.item().NeedsAnonFlexOrGridItem(aState, containerType)) { + iter.item().NeedsAnonFlexOrGridItem(aState, containerType, + isWebkitBox)) { RecreateFramesForContent(aFrame->GetContent(), true, REMOVE_FOR_RECONSTRUCTION, nullptr); return true; @@ -12065,7 +12090,8 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState, // Jump to the last entry in the list iter.SetToEnd(); iter.Prev(); - if (iter.item().NeedsAnonFlexOrGridItem(aState, containerType)) { + if (iter.item().NeedsAnonFlexOrGridItem(aState, containerType, + isWebkitBox)) { RecreateFramesForContent(aFrame->GetContent(), true, REMOVE_FOR_RECONSTRUCTION, nullptr); return true; @@ -12092,7 +12118,8 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState, // they're perfectly happy to go here -- they won't cause a reframe. nsIFrame* containerFrame = aFrame->GetParent(); if (!iter.SkipItemsThatNeedAnonFlexOrGridItem(aState, - containerFrame->GetType())) { + containerFrame->GetType(), + IsWebkitBox(containerFrame))) { // We hit something that _doesn't_ need an anonymous flex item! // Rebuild the flex container to bust it out. RecreateFramesForContent(containerFrame->GetContent(), true, @@ -12544,10 +12571,14 @@ Iterator::SkipItemsNotWantingParentType(ParentType aParentType) return false; } +// Note: we implement -webkit-box & -webkit-inline-box using +// nsFlexContainerFrame, but we use different rules for what gets wrapped in an +// anonymous flex item. bool nsCSSFrameConstructor::FrameConstructionItem:: NeedsAnonFlexOrGridItem(const nsFrameConstructorState& aState, - nsIAtom* aContainerType) + nsIAtom* aContainerType, + bool aIsWebkitBox) { if (mFCData->mBits & FCDATA_IS_LINE_PARTICIPANT) { // This will be an inline non-replaced box. @@ -12565,6 +12596,12 @@ nsCSSFrameConstructor::FrameConstructionItem:: return true; } + if (aIsWebkitBox && + mStyleContext->StyleDisplay()->IsInlineOutsideStyle()) { + // In a -webkit-box, all inline-level content gets wrapped in an anon item. + return true; + } + return false; } @@ -12572,10 +12609,12 @@ inline bool nsCSSFrameConstructor::FrameConstructionItemList:: Iterator::SkipItemsThatNeedAnonFlexOrGridItem( const nsFrameConstructorState& aState, - nsIAtom* aContainerType) + nsIAtom* aContainerType, + bool aIsWebkitBox) { NS_PRECONDITION(!IsDone(), "Shouldn't be done yet"); - while (item().NeedsAnonFlexOrGridItem(aState, aContainerType)) { + while (item().NeedsAnonFlexOrGridItem(aState, aContainerType, + aIsWebkitBox)) { Next(); if (IsDone()) { return true; @@ -12588,10 +12627,12 @@ inline bool nsCSSFrameConstructor::FrameConstructionItemList:: Iterator::SkipItemsThatDontNeedAnonFlexOrGridItem( const nsFrameConstructorState& aState, - nsIAtom* aContainerType) + nsIAtom* aContainerType, + bool aIsWebkitBox) { NS_PRECONDITION(!IsDone(), "Shouldn't be done yet"); - while (!(item().NeedsAnonFlexOrGridItem(aState, aContainerType))) { + while (!(item().NeedsAnonFlexOrGridItem(aState, aContainerType, + aIsWebkitBox))) { Next(); if (IsDone()) { return true; diff --git a/layout/base/nsCSSFrameConstructor.h b/layout/base/nsCSSFrameConstructor.h index 51d5c5b382..bddeeca273 100644 --- a/layout/base/nsCSSFrameConstructor.h +++ b/layout/base/nsCSSFrameConstructor.h @@ -954,13 +954,15 @@ private: // Return whether the iterator is done after doing that. // The iterator must not be done when this is called. inline bool SkipItemsThatNeedAnonFlexOrGridItem( - const nsFrameConstructorState& aState, nsIAtom* aContainerType); + const nsFrameConstructorState& aState, nsIAtom* aContainerType, + bool aIsWebkitBox); // Skip to the first frame that is a non-replaced inline or is // positioned. Return whether the iterator is done after doing that. // The iterator must not be done when this is called. inline bool SkipItemsThatDontNeedAnonFlexOrGridItem( - const nsFrameConstructorState& aState, nsIAtom* aContainerType); + const nsFrameConstructorState& aState, nsIAtom* aContainerType, + bool aIsWebkitBox); // Skip over all items that do not want a ruby parent. Return whether // the iterator is done after doing that. The iterator must not be done @@ -1096,9 +1098,13 @@ private: } // Indicates whether (when in a flex or grid container) this item needs - // to be wrapped in an anonymous block. + // to be wrapped in an anonymous block. (Note that we implement + // -webkit-box/-webkit-inline-box using our standard flexbox frame class, + // but we use different rules for what gets wrapped. The aIsWebkitBox + // parameter here tells us whether to use those different rules.) bool NeedsAnonFlexOrGridItem(const nsFrameConstructorState& aState, - nsIAtom* aContainerType); + nsIAtom* aContainerType, + bool aIsWebkitBox); // Don't call this unless the frametree really depends on the answer! // Especially so for generated content, where we don't want to reframe diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index ede9a1c9b2..9687efd1eb 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -843,8 +843,7 @@ nsCSSRendering::PaintOutline(nsPresContext* aPresContext, "shouldn't have created nsDisplayOutline item"); uint8_t outlineStyle = ourOutline->GetOutlineStyle(); - nscoord width; - ourOutline->GetOutlineWidth(width); + nscoord width = ourOutline->GetOutlineWidth(); if (width == 0 && outlineStyle != NS_STYLE_BORDER_STYLE_AUTO) { // Empty outline @@ -1247,8 +1246,7 @@ nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext, float aOpacity) { DrawTarget& aDrawTarget = *aRenderingContext.GetDrawTarget(); - const nsStyleBorder* styleBorder = aForFrame->StyleBorder(); - nsCSSShadowArray* shadows = styleBorder->mBoxShadow; + nsCSSShadowArray* shadows = aForFrame->StyleEffects()->mBoxShadow; if (!shadows) return; @@ -1484,8 +1482,7 @@ nsCSSRendering::PaintBoxShadowInner(nsPresContext* aPresContext, nsIFrame* aForFrame, const nsRect& aFrameArea) { - const nsStyleBorder* styleBorder = aForFrame->StyleBorder(); - nsCSSShadowArray* shadows = styleBorder->mBoxShadow; + nsCSSShadowArray* shadows = aForFrame->StyleEffects()->mBoxShadow; if (!shadows) return; if (aForFrame->IsThemed() && aForFrame->GetContent() && @@ -1816,7 +1813,8 @@ nsCSSRendering::GetImageLayerClip(const nsStyleImageLayers::Layer& aLayer, backgroundClip = NS_STYLE_IMAGELAYER_CLIP_PADDING; } - if (backgroundClip != NS_STYLE_IMAGELAYER_CLIP_BORDER) { + if (backgroundClip != NS_STYLE_IMAGELAYER_CLIP_BORDER && + backgroundClip != NS_STYLE_IMAGELAYER_CLIP_TEXT) { nsMargin border = aForFrame->GetUsedBorder(); if (backgroundClip == NS_STYLE_IMAGELAYER_CLIP_MOZ_ALMOST_PADDING) { // Reduce |border| by 1px (device pixels) on all sides, if @@ -1985,7 +1983,10 @@ nsCSSRendering::DetermineBackgroundColor(nsPresContext* aPresContext, aDrawBackgroundImage = true; aDrawBackgroundColor = true; - if (aFrame->HonorPrintBackgroundSettings()) { + const nsStyleVisibility* visibility = aStyleContext->StyleVisibility(); + + if (visibility->mColorAdjust != NS_STYLE_COLOR_ADJUST_EXACT && + aFrame->HonorPrintBackgroundSettings()) { aDrawBackgroundImage = aPresContext->GetBackgroundImageDraw(); aDrawBackgroundColor = aPresContext->GetBackgroundColorDraw(); } @@ -3502,6 +3503,7 @@ nsCSSRendering::PrepareImageLayer(nsPresContext* aPresContext, } } state.mImageRenderer.SetExtendMode(repeatMode); + state.mImageRenderer.SetMaskOp(aLayer.mMaskMode); state.mFillArea.IntersectRect(state.mFillArea, bgClipRect); @@ -4335,40 +4337,27 @@ nsCSSRendering::ExpandPaintingRectForDecorationLine( } void -nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame, - DrawTarget& aDrawTarget, - const Rect& aDirtyRect, - const nscolor aColor, - const gfxPoint& aPt, - const Float aICoordInFrame, - const gfxSize& aLineSize, - const gfxFloat aAscent, - const gfxFloat aOffset, - const uint8_t aDecoration, - const uint8_t aStyle, - bool aVertical, - const gfxFloat aDescentLimit) +nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame, DrawTarget& aDrawTarget, + const PaintDecorationLineParams& aParams) { - NS_ASSERTION(aStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE, "aStyle is none"); + NS_ASSERTION(aParams.style != NS_STYLE_TEXT_DECORATION_STYLE_NONE, + "aStyle is none"); - Rect rect = ToRect( - GetTextDecorationRectInternal(aPt, aLineSize, aAscent, aOffset, - aDecoration, aStyle, aVertical, - aDescentLimit)); - if (rect.IsEmpty() || !rect.Intersects(aDirtyRect)) { + Rect rect = ToRect(GetTextDecorationRectInternal(aParams.pt, aParams)); + if (rect.IsEmpty() || !rect.Intersects(aParams.dirtyRect)) { return; } - if (aDecoration != NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE && - aDecoration != NS_STYLE_TEXT_DECORATION_LINE_OVERLINE && - aDecoration != NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) { + if (aParams.decoration != NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE && + aParams.decoration != NS_STYLE_TEXT_DECORATION_LINE_OVERLINE && + aParams.decoration != NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) { NS_ERROR("Invalid decoration value!"); return; } - Float lineThickness = std::max(NS_round(aLineSize.height), 1.0); + Float lineThickness = std::max(NS_round(aParams.lineSize.height), 1.0); - ColorPattern color(ToDeviceColor(aColor)); + ColorPattern color(ToDeviceColor(aParams.color)); StrokeOptions strokeOptions(lineThickness); DrawOptions drawOptions; @@ -4376,7 +4365,7 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame, AutoPopClips autoPopClips(&aDrawTarget); - switch (aStyle) { + switch (aParams.style) { case NS_STYLE_TEXT_DECORATION_STYLE_SOLID: case NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE: break; @@ -4388,10 +4377,10 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame, strokeOptions.mDashPattern = dash; strokeOptions.mDashLength = MOZ_ARRAY_LENGTH(dash); strokeOptions.mLineCap = CapStyle::BUTT; - rect = ExpandPaintingRectForDecorationLine(aFrame, aStyle, rect, - aICoordInFrame, + rect = ExpandPaintingRectForDecorationLine(aFrame, aParams.style, + rect, aParams.icoordInFrame, dashWidth * 2, - aVertical); + aParams.vertical); // We should continue to draw the last dash even if it is not in the rect. rect.width += dashWidth; break; @@ -4409,10 +4398,10 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame, } strokeOptions.mDashPattern = dash; strokeOptions.mDashLength = MOZ_ARRAY_LENGTH(dash); - rect = ExpandPaintingRectForDecorationLine(aFrame, aStyle, rect, - aICoordInFrame, + rect = ExpandPaintingRectForDecorationLine(aFrame, aParams.style, + rect, aParams.icoordInFrame, dashWidth * 2, - aVertical); + aParams.vertical); // We should continue to draw the last dot even if it is not in the rect. rect.width += dashWidth; break; @@ -4434,18 +4423,18 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame, } // The block-direction position should be set to the middle of the line. - if (aVertical) { + if (aParams.vertical) { rect.x += lineThickness / 2; } else { rect.y += lineThickness / 2; } - switch (aStyle) { + switch (aParams.style) { case NS_STYLE_TEXT_DECORATION_STYLE_SOLID: case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED: case NS_STYLE_TEXT_DECORATION_STYLE_DASHED: { Point p1 = rect.TopLeft(); - Point p2 = aVertical ? rect.BottomLeft() : rect.TopRight(); + Point p2 = aParams.vertical ? rect.BottomLeft() : rect.TopRight(); aDrawTarget.StrokeLine(p1, p2, color, strokeOptions, drawOptions); return; } @@ -4465,16 +4454,16 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame, * +-------------------------------------------+ */ Point p1 = rect.TopLeft(); - Point p2 = aVertical ? rect.BottomLeft() : rect.TopRight(); + Point p2 = aParams.vertical ? rect.BottomLeft() : rect.TopRight(); aDrawTarget.StrokeLine(p1, p2, color, strokeOptions, drawOptions); - if (aVertical) { + if (aParams.vertical) { rect.width -= lineThickness; } else { rect.height -= lineThickness; } - p1 = aVertical ? rect.TopRight() : rect.BottomLeft(); + p1 = aParams.vertical ? rect.TopRight() : rect.BottomLeft(); p2 = rect.BottomRight(); aDrawTarget.StrokeLine(p1, p2, color, strokeOptions, drawOptions); return; @@ -4512,9 +4501,9 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame, * directions in the above description. */ - Float& rectICoord = aVertical ? rect.y : rect.x; - Float& rectISize = aVertical ? rect.height : rect.width; - const Float rectBSize = aVertical ? rect.width : rect.height; + Float& rectICoord = aParams.vertical ? rect.y : rect.x; + Float& rectISize = aParams.vertical ? rect.height : rect.width; + const Float rectBSize = aParams.vertical ? rect.width : rect.height; const Float adv = rectBSize - lineThickness; const Float flatLengthAtVertex = @@ -4522,13 +4511,14 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame, // Align the start of wavy lines to the nearest ancestor block. const Float cycleLength = 2 * (adv + flatLengthAtVertex); - rect = ExpandPaintingRectForDecorationLine(aFrame, aStyle, rect, - aICoordInFrame, cycleLength, - aVertical); + rect = ExpandPaintingRectForDecorationLine(aFrame, aParams.style, rect, + aParams.icoordInFrame, + cycleLength, aParams.vertical); // figure out if we can trim whole cycles from the left and right edges // of the line, to try and avoid creating an unnecessarily long and // complex path - const Float dirtyRectICoord = aVertical ? aDirtyRect.y : aDirtyRect.x; + const Float dirtyRectICoord = aParams.vertical ? aParams.dirtyRect.y + : aParams.dirtyRect.x; int32_t skipCycles = floor((dirtyRectICoord - rectICoord) / cycleLength); if (skipCycles > 0) { rectICoord += skipCycles * cycleLength; @@ -4537,15 +4527,15 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame, rectICoord += lineThickness / 2.0; Point pt(rect.TopLeft()); - Float& ptICoord = aVertical ? pt.y : pt.x; - Float& ptBCoord = aVertical ? pt.x : pt.y; - if (aVertical) { + Float& ptICoord = aParams.vertical ? pt.y : pt.x; + Float& ptBCoord = aParams.vertical ? pt.x : pt.y; + if (aParams.vertical) { ptBCoord += adv + lineThickness / 2.0; } Float iCoordLimit = ptICoord + rectISize + lineThickness; - const Float dirtyRectIMost = aVertical ? - aDirtyRect.YMost() : aDirtyRect.XMost(); + const Float dirtyRectIMost = aParams.vertical ? + aParams.dirtyRect.YMost() : aParams.dirtyRect.XMost(); skipCycles = floor((iCoordLimit - dirtyRectIMost) / cycleLength); if (skipCycles > 0) { iCoordLimit -= skipCycles * cycleLength; @@ -4563,7 +4553,7 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame, // In vertical mode, to go "down" relative to the text we need to // decrease the block coordinate, whereas in horizontal we increase // it. So the sense of this flag is effectively inverted. - bool goDown = aVertical ? false : true; + bool goDown = aParams.vertical ? false : true; uint32_t iter = 0; while (ptICoord < iCoordLimit) { if (++iter > 1000) { @@ -4594,78 +4584,56 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame, } } -void -nsCSSRendering::DecorationLineToPath(nsIFrame* aFrame, - gfxContext* aGfxContext, - const gfxRect& aDirtyRect, - const nscolor aColor, - const gfxPoint& aPt, - const gfxFloat aICoordInFrame, - const gfxSize& aLineSize, - const gfxFloat aAscent, - const gfxFloat aOffset, - const uint8_t aDecoration, - const uint8_t aStyle, - bool aVertical, - const gfxFloat aDescentLimit) +Rect +nsCSSRendering::DecorationLineToPath(const PaintDecorationLineParams& aParams) { - NS_ASSERTION(aStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE, "aStyle is none"); + NS_ASSERTION(aParams.style != NS_STYLE_TEXT_DECORATION_STYLE_NONE, + "aStyle is none"); - aGfxContext->NewPath(); + Rect path; // To benefit from RVO, we return this from all return points - gfxRect rect = - GetTextDecorationRectInternal(aPt, aLineSize, aAscent, aOffset, - aDecoration, aStyle, aVertical, - aDescentLimit); - if (rect.IsEmpty() || !rect.Intersects(aDirtyRect)) { - return; + Rect rect = ToRect(GetTextDecorationRectInternal(aParams.pt, aParams)); + if (rect.IsEmpty() || !rect.Intersects(aParams.dirtyRect)) { + return path; } - if (aDecoration != NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE && - aDecoration != NS_STYLE_TEXT_DECORATION_LINE_OVERLINE && - aDecoration != NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) { + if (aParams.decoration != NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE && + aParams.decoration != NS_STYLE_TEXT_DECORATION_LINE_OVERLINE && + aParams.decoration != NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) { NS_ERROR("Invalid decoration value!"); - return; + return path; } - if (aStyle != NS_STYLE_TEXT_DECORATION_STYLE_SOLID) { + if (aParams.style != NS_STYLE_TEXT_DECORATION_STYLE_SOLID) { // For the moment, we support only solid text decorations. - return; + return path; } - gfxFloat lineThickness = std::max(NS_round(aLineSize.height), 1.0); + Float lineThickness = std::max(NS_round(aParams.lineSize.height), 1.0); // The block-direction position should be set to the middle of the line. - if (aVertical) { + if (aParams.vertical) { rect.x += lineThickness / 2; - aGfxContext->Rectangle - (gfxRect(gfxPoint(rect.TopLeft() - gfxPoint(lineThickness / 2, 0.0)), - gfxSize(lineThickness, rect.Height()))); + path = Rect(rect.TopLeft() - Point(lineThickness / 2, 0.0), + Size(lineThickness, rect.Height())); } else { rect.y += lineThickness / 2; - aGfxContext->Rectangle - (gfxRect(gfxPoint(rect.TopLeft() - gfxPoint(0.0, lineThickness / 2)), - gfxSize(rect.Width(), lineThickness))); + path = Rect(rect.TopLeft() - Point(0.0, lineThickness / 2), + Size(rect.Width(), lineThickness)); } + + return path; } nsRect nsCSSRendering::GetTextDecorationRect(nsPresContext* aPresContext, - const gfxSize& aLineSize, - const gfxFloat aAscent, - const gfxFloat aOffset, - const uint8_t aDecoration, - const uint8_t aStyle, - bool aVertical, - const gfxFloat aDescentLimit) + const DecorationRectParams& aParams) { NS_ASSERTION(aPresContext, "aPresContext is null"); - NS_ASSERTION(aStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE, "aStyle is none"); + NS_ASSERTION(aParams.style != NS_STYLE_TEXT_DECORATION_STYLE_NONE, + "aStyle is none"); - gfxRect rect = - GetTextDecorationRectInternal(gfxPoint(0, 0), aLineSize, aAscent, aOffset, - aDecoration, aStyle, aVertical, - aDescentLimit); + gfxRect rect = GetTextDecorationRectInternal(Point(0, 0), aParams); // The rect values are already rounded to nearest device pixels. nsRect r; r.x = aPresContext->GfxUnitsToAppUnits(rect.X()); @@ -4676,46 +4644,40 @@ nsCSSRendering::GetTextDecorationRect(nsPresContext* aPresContext, } gfxRect -nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt, - const gfxSize& aLineSize, - const gfxFloat aAscent, - const gfxFloat aOffset, - const uint8_t aDecoration, - const uint8_t aStyle, - bool aVertical, - const gfxFloat aDescentLimit) +nsCSSRendering::GetTextDecorationRectInternal(const Point& aPt, + const DecorationRectParams& aParams) { - NS_ASSERTION(aStyle <= NS_STYLE_TEXT_DECORATION_STYLE_WAVY, + NS_ASSERTION(aParams.style <= NS_STYLE_TEXT_DECORATION_STYLE_WAVY, "Invalid aStyle value"); - if (aStyle == NS_STYLE_TEXT_DECORATION_STYLE_NONE) + if (aParams.style == NS_STYLE_TEXT_DECORATION_STYLE_NONE) return gfxRect(0, 0, 0, 0); - bool canLiftUnderline = aDescentLimit >= 0.0; + bool canLiftUnderline = aParams.descentLimit >= 0.0; - gfxFloat iCoord = aVertical ? aPt.y : aPt.x; - gfxFloat bCoord = aVertical ? aPt.x : aPt.y; + gfxFloat iCoord = aParams.vertical ? aPt.y : aPt.x; + gfxFloat bCoord = aParams.vertical ? aPt.x : aPt.y; // 'left' and 'right' are relative to the line, so for vertical writing modes // they will actually become top and bottom of the rendered line. // Similarly, aLineSize.width and .height are actually length and thickness // of the line, which runs horizontally or vertically according to aVertical. const gfxFloat left = floor(iCoord + 0.5), - right = floor(iCoord + aLineSize.width + 0.5); + right = floor(iCoord + aParams.lineSize.width + 0.5); // We compute |r| as if for a horizontal text run, and then swap vertical // and horizontal coordinates at the end if vertical was requested. gfxRect r(left, 0, right - left, 0); - gfxFloat lineThickness = NS_round(aLineSize.height); + gfxFloat lineThickness = NS_round(aParams.lineSize.height); lineThickness = std::max(lineThickness, 1.0); - gfxFloat ascent = NS_round(aAscent); - gfxFloat descentLimit = floor(aDescentLimit); + gfxFloat ascent = NS_round(aParams.ascent); + gfxFloat descentLimit = floor(aParams.descentLimit); gfxFloat suggestedMaxRectHeight = std::max(std::min(ascent, descentLimit), 1.0); r.height = lineThickness; - if (aStyle == NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE) { + if (aParams.style == NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE) { /** * We will draw double line as: * @@ -4741,7 +4703,7 @@ nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt, r.height = std::max(suggestedMaxRectHeight, lineThickness * 2.0 + 1.0); } } - } else if (aStyle == NS_STYLE_TEXT_DECORATION_STYLE_WAVY) { + } else if (aParams.style == NS_STYLE_TEXT_DECORATION_STYLE_WAVY) { /** * We will draw wavy line as: * @@ -4767,11 +4729,11 @@ nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt, } } - gfxFloat baseline = floor(bCoord + aAscent + 0.5); + gfxFloat baseline = floor(bCoord + aParams.ascent + 0.5); gfxFloat offset = 0.0; - switch (aDecoration) { + switch (aParams.decoration) { case NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE: - offset = aOffset; + offset = aParams.offset; if (canLiftUnderline) { if (descentLimit < -offset + r.Height()) { // If we can ignore the offset and the decoration line is overflowing, @@ -4785,19 +4747,19 @@ nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt, } break; case NS_STYLE_TEXT_DECORATION_LINE_OVERLINE: - offset = aOffset - lineThickness + r.Height(); + offset = aParams.offset - lineThickness + r.Height(); break; case NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH: { gfxFloat extra = floor(r.Height() / 2.0 + 0.5); extra = std::max(extra, lineThickness); - offset = aOffset - lineThickness + extra; + offset = aParams.offset - lineThickness + extra; break; } default: NS_ERROR("Invalid decoration value!"); } - if (aVertical) { + if (aParams.vertical) { r.y = baseline + floor(offset + 0.5); Swap(r.x, r.y); Swap(r.width, r.height); @@ -4824,6 +4786,7 @@ nsImageRenderer::nsImageRenderer(nsIFrame* aForFrame, , mSize(0, 0) , mFlags(aFlags) , mExtendMode(ExtendMode::CLAMP) + , mMaskOp(NS_STYLE_MASK_MODE_MATCH_SOURCE) { } @@ -5185,6 +5148,35 @@ ConvertImageRendererToDrawFlags(uint32_t aImageRendererFlags) return drawFlags; } +/* + * SVG11: A luminanceToAlpha operation is equivalent to the following matrix operation: | + * | R' | | 0 0 0 0 0 | | R | + * | G' | | 0 0 0 0 0 | | G | + * | B' | = | 0 0 0 0 0 | * | B | + * | A' | | 0.2125 0.7154 0.0721 0 0 | | A | + * | 1 | | 0 0 0 0 1 | | 1 | + */ +static void +RGBALuminanceOperation(uint8_t *aData, + int32_t aStride, + const IntSize &aSize) +{ + int32_t redFactor = 55; // 256 * 0.2125 + int32_t greenFactor = 183; // 256 * 0.7154 + int32_t blueFactor = 18; // 256 * 0.0721 + + for (int32_t y = 0; y < aSize.height; y++) { + uint32_t *pixel = (uint32_t*)(aData + aStride * y); + for (int32_t x = 0; x < aSize.width; x++) { + *pixel = (((((*pixel & 0x00FF0000) >> 16) * redFactor) + + (((*pixel & 0x0000FF00) >> 8) * greenFactor) + + ((*pixel & 0x000000FF) * blueFactor)) >> 8) << 24; + pixel++; + } + } +} + + DrawResult nsImageRenderer::Draw(nsPresContext* aPresContext, nsRenderingContext& aRenderingContext, @@ -5205,27 +5197,42 @@ nsImageRenderer::Draw(nsPresContext* aPresContext, } Filter filter = nsLayoutUtils::GetGraphicsFilterForFrame(mForFrame); + DrawResult result = DrawResult::SUCCESS; + RefPtr ctx = aRenderingContext.ThebesContext(); + IntRect tmpDTRect; + + if (ctx->CurrentOp() != CompositionOp::OP_OVER || mMaskOp == NS_STYLE_MASK_MODE_LUMINANCE) { + gfxRect clipRect = ctx->GetClipExtents(); + tmpDTRect = RoundedOut(ToRect(clipRect)); + RefPtr tempDT = ctx->GetDrawTarget()->CreateSimilarDrawTarget(tmpDTRect.Size(), SurfaceFormat::B8G8R8A8); + ctx = gfxContext::ForDrawTarget(tempDT, tmpDTRect.TopLeft()); + if (!ctx) { + gfxDevCrash(LogReason::InvalidContext) << "ImageRenderer::Draw problem " << gfx::hexa(tempDT); + return DrawResult::TEMPORARY_ERROR; + } + } switch (mType) { case eStyleImageType_Image: { CSSIntSize imageSize(nsPresContext::AppUnitsToIntCSSPixels(mSize.width), nsPresContext::AppUnitsToIntCSSPixels(mSize.height)); - return - nsLayoutUtils::DrawBackgroundImage(*aRenderingContext.ThebesContext(), + result = + nsLayoutUtils::DrawBackgroundImage(*ctx, aPresContext, mImageContainer, imageSize, filter, aDest, aFill, aRepeatSize, aAnchor, aDirtyRect, ConvertImageRendererToDrawFlags(mFlags), mExtendMode); + break; } case eStyleImageType_Gradient: { nsCSSRendering::PaintGradient(aPresContext, aRenderingContext, mGradientData, aDirtyRect, aDest, aFill, aSrc, mSize); - return DrawResult::SUCCESS; + break; } case eStyleImageType_Element: { @@ -5237,18 +5244,40 @@ nsImageRenderer::Draw(nsPresContext* aPresContext, } nsCOMPtr image(ImageOps::CreateFromDrawable(drawable)); - DrawResult result = - nsLayoutUtils::DrawImage(*aRenderingContext.ThebesContext(), + result = + nsLayoutUtils::DrawImage(*ctx, aPresContext, image, filter, aDest, aFill, aAnchor, aDirtyRect, ConvertImageRendererToDrawFlags(mFlags)); - - return result; + break; } case eStyleImageType_Null: default: - return DrawResult::SUCCESS; + break; } + + if (!tmpDTRect.IsEmpty()) { + RefPtr surf = ctx->GetDrawTarget()->Snapshot(); + if (mMaskOp == NS_STYLE_MASK_MODE_LUMINANCE) { + RefPtr maskData = surf->GetDataSurface(); + DataSourceSurface::MappedSurface map; + if (!maskData->Map(DataSourceSurface::MapType::WRITE, &map)) { + return result; + } + + RGBALuminanceOperation(map.mData, map.mStride, maskData->GetSize()); + maskData->Unmap(); + surf = maskData; + } + + DrawTarget* dt = aRenderingContext.ThebesContext()->GetDrawTarget(); + dt->DrawSurface(surf, Rect(tmpDTRect.x, tmpDTRect.y, tmpDTRect.width, tmpDTRect.height), + Rect(0, 0, tmpDTRect.width, tmpDTRect.height), + DrawSurfaceOptions(Filter::POINT), + DrawOptions(1.0f, aRenderingContext.ThebesContext()->CurrentOp())); + } + + return result; } already_AddRefed diff --git a/layout/base/nsCSSRendering.h b/layout/base/nsCSSRendering.h index 2ddb05e5b5..0520218220 100644 --- a/layout/base/nsCSSRendering.h +++ b/layout/base/nsCSSRendering.h @@ -268,6 +268,7 @@ public: bool IsReady() const { return mPrepareResult == DrawResult::SUCCESS; } DrawResult PrepareResult() const { return mPrepareResult; } void SetExtendMode(mozilla::gfx::ExtendMode aMode) { mExtendMode = aMode; } + void SetMaskOp(uint8_t aMaskOp) { mMaskOp = aMaskOp; } private: /** @@ -306,6 +307,7 @@ private: nsSize mSize; // unscaled size of the image, in app units uint32_t mFlags; mozilla::gfx::ExtendMode mExtendMode; + uint8_t mMaskOp; }; /** @@ -357,7 +359,9 @@ struct nsCSSRendering { typedef mozilla::gfx::CompositionOp CompositionOp; typedef mozilla::gfx::DrawTarget DrawTarget; typedef mozilla::gfx::Float Float; + typedef mozilla::gfx::Point Point; typedef mozilla::gfx::Rect Rect; + typedef mozilla::gfx::Size Size; typedef mozilla::gfx::RectCornerRadii RectCornerRadii; typedef mozilla::image::DrawResult DrawResult; typedef nsIFrame::Sides Sides; @@ -673,85 +677,69 @@ struct nsCSSRendering { uint8_t aEndBevelSide = 0, nscoord aEndBevelOffset = 0); + // NOTE: pt, dirtyRect, lineSize, ascent, offset in the following + // structs are non-rounded device pixels, not app units. + struct DecorationRectParams + { + // The width [length] and the height [thickness] of the decoration + // line. This is a "logical" size in textRun orientation, so that + // for a vertical textrun, width will actually be a physical height; + // and conversely, height will be a physical width. + Size lineSize; + // The ascent of the text. + Float ascent = 0.0f; + // The offset of the decoration line from the baseline of the text + // (if the value is positive, the line is lifted up). + Float offset = 0.0f; + // If descentLimit is zero or larger and the underline overflows + // from the descent space, the underline should be lifted up as far + // as possible. Note that this does not mean the underline never + // overflows from this limitation, because if the underline is + // positioned to the baseline or upper, it causes unreadability. + // Note that if this is zero or larger, the underline rect may be + // shrunken if it's possible. Therefore, this value is used for + // strikeout line and overline too. + Float descentLimit = -1.0f; + // Which line will be painted. The value can be + // NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE or + // NS_STYLE_TEXT_DECORATION_LINE_OVERLINE or + // NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH. + uint8_t decoration = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE; + // The style of the decoration line such as + // NS_STYLE_TEXT_DECORATION_STYLE_*. + uint8_t style = NS_STYLE_TEXT_DECORATION_STYLE_NONE; + bool vertical = false; + }; + struct PaintDecorationLineParams : DecorationRectParams + { + // No need to paint outside this rect. + Rect dirtyRect; + // The top/left edge of the text. + Point pt; + // The color of the decoration line. + nscolor color = NS_RGBA(0, 0, 0, 0); + // The distance between the left edge of the given frame and the + // position of the text as positioned without offset of the shadow. + Float icoordInFrame = 0.0f; + }; + /** * Function for painting the decoration lines for the text. - * NOTE: aPt, aLineSize, aAscent and aOffset are non-rounded device pixels, - * not app units. - * NOTE: aLineSize is a "logical" size in textRun orientation, so that for - * a vertical textrun, aLineSize.width (which is the decoration line - * length) will actually be a physical height; and conversely, - * aLineSize.height [thickness] will be a physical width. The alternate - * names in [brackets] in the comments here apply to the vertical case. * * input: * @param aFrame the frame which needs the decoration line * @param aGfxContext - * @param aDirtyRect no need to paint outside this rect - * @param aColor the color of the decoration line - * @param aPt the top/left edge of the text - * @param aICoordInFrame the distance between aPt.x [y] and left [top] - * edge of aFrame. If the decoration line is for - * shadow, set the distance between the left edge - * of the aFrame and the position of the text as - * positioned without offset of the shadow. - * @param aLineSize the width [length] and the height [thickness] - * of the decoration line - * @param aAscent the ascent of the text - * @param aOffset the offset of the decoration line from - * the baseline of the text (if the value is - * positive, the line is lifted up [right]) - * @param aDecoration which line will be painted. The value can be - * NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE or - * NS_STYLE_TEXT_DECORATION_LINE_OVERLINE or - * NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH. - * @param aStyle the style of the decoration line such as - * NS_STYLE_TEXT_DECORATION_STYLE_*. - * @param aDescentLimit If aDescentLimit is zero or larger and the - * underline overflows from the descent space, - * the underline should be lifted up as far as - * possible. Note that this does not mean the - * underline never overflows from this - * limitation. Because if the underline is - * positioned to the baseline or upper, it causes - * unreadability. Note that if this is zero - * or larger, the underline rect may be shrunken - * if it's possible. Therefore, this value is - * used for strikeout line and overline too. */ - static void PaintDecorationLine(nsIFrame* aFrame, - DrawTarget& aDrawTarget, - const Rect& aDirtyRect, - const nscolor aColor, - const gfxPoint& aPt, - const Float aICoordInFrame, - const gfxSize& aLineSize, - const gfxFloat aAscent, - const gfxFloat aOffset, - const uint8_t aDecoration, - const uint8_t aStyle, - bool aVertical, - const gfxFloat aDescentLimit = -1.0); + static void PaintDecorationLine(nsIFrame* aFrame, DrawTarget& aDrawTarget, + const PaintDecorationLineParams& aParams); /** - * Adds a path corresponding to the outline of the decoration line to - * the specified context. Arguments have the same meaning as for + * Returns a Rect corresponding to the outline of the decoration line for the + * given text metrics. Arguments have the same meaning as for * PaintDecorationLine. Currently this only works for solid - * decorations; for other decoration styles, an empty path is added - * to the context. + * decorations; for other decoration styles the returned Rect will be empty. */ - static void DecorationLineToPath(nsIFrame* aFrame, - gfxContext* aGfxContext, - const gfxRect& aDirtyRect, - const nscolor aColor, - const gfxPoint& aPt, - const gfxFloat aICoordInFrame, - const gfxSize& aLineSize, - const gfxFloat aAscent, - const gfxFloat aOffset, - const uint8_t aDecoration, - const uint8_t aStyle, - bool aVertical, - const gfxFloat aDescentLimit = -1.0); + static Rect DecorationLineToPath(const PaintDecorationLineParams& aParams); /** * Function for getting the decoration line rect for the text. @@ -759,41 +747,12 @@ struct nsCSSRendering { * not app units. * input: * @param aPresContext - * @param aLineSize the width and the height of the decoration - * line - * @param aAscent the ascent of the text - * @param aOffset the offset of the decoration line from - * the baseline of the text (if the value is - * positive, the line is lifted up) - * @param aDecoration which line will be painted. The value can be - * NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE or - * NS_STYLE_TEXT_DECORATION_LINE_OVERLINE or - * NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH. - * @param aStyle the style of the decoration line such as - * NS_STYLE_TEXT_DECORATION_STYLE_*. - * @param aDescentLimit If aDescentLimit is zero or larger and the - * underline overflows from the descent space, - * the underline should be lifted up as far as - * possible. Note that this does not mean the - * underline never overflows from this - * limitation. Because if the underline is - * positioned to the baseline or upper, it causes - * unreadability. Note that if this is zero - * or larger, the underline rect may be shrunken - * if it's possible. Therefore, this value is - * used for strikeout line and overline too. * output: * @return the decoration line rect for the input, * the each values are app units. */ static nsRect GetTextDecorationRect(nsPresContext* aPresContext, - const gfxSize& aLineSize, - const gfxFloat aAscent, - const gfxFloat aOffset, - const uint8_t aDecoration, - const uint8_t aStyle, - bool aVertical, - const gfxFloat aDescentLimit = -1.0); + const DecorationRectParams& aParams); static CompositionOp GetGFXBlendMode(uint8_t mBlendMode) { switch (mBlendMode) { @@ -828,14 +787,8 @@ struct nsCSSRendering { } protected: - static gfxRect GetTextDecorationRectInternal(const gfxPoint& aPt, - const gfxSize& aLineSize, - const gfxFloat aAscent, - const gfxFloat aOffset, - const uint8_t aDecoration, - const uint8_t aStyle, - bool aVertical, - const gfxFloat aDescentLimit); + static gfxRect GetTextDecorationRectInternal( + const Point& aPt, const DecorationRectParams& aParams); /** * Returns inflated rect for painting a decoration line. diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index f4bf61339a..a3db13d0be 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -491,6 +491,46 @@ AddAnimationsForProperty(nsIFrame* aFrame, nsCSSProperty aProperty, } } +static void +ClipBackgroundByText(nsIFrame* aFrame, nsRenderingContext* aContext, + const gfxRect& aFillRect) +{ + // The main function of enabling background-clip:text property value. + // When a nsDisplayBackgroundImage detects "text" bg-clip style, it will call + // this function to + // 1. Ask every text frame objects in aFrame puts glyph paths into aContext. + // 2. Then, clip aContext. + // + // Then, nsDisplayBackgroundImage paints bg-images into this clipped region, + // so we get images embedded in text shape! + + nsDisplayListBuilder builder(aFrame, nsDisplayListBuilder::GENERATE_GLYPH, false); + + builder.EnterPresShell(aFrame); + nsDisplayList list; + aFrame->BuildDisplayListForStackingContext(&builder, + nsRect(nsPoint(0, 0), aFrame->GetSize()), + &list); + builder.LeavePresShell(aFrame); + +#ifdef DEBUG + for (nsDisplayItem* i = list.GetBottom(); i; i = i->GetAbove()) { + MOZ_ASSERT(nsDisplayItem::TYPE_TEXT == i->GetType()); + } +#endif + + gfxContext* ctx = aContext->ThebesContext(); + gfxContextMatrixAutoSaveRestore save(ctx); + ctx->SetMatrix(ctx->CurrentMatrix().Translate(aFillRect.TopLeft())); + ctx->NewPath(); + + RefPtr layerManager = + list.PaintRoot(&builder, aContext, nsDisplayList::PAINT_DEFAULT); + + ctx->Clip(); + list.DeleteAll(); +} + /* static */ void nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(Layer* aLayer, nsDisplayListBuilder* aBuilder, @@ -1523,7 +1563,7 @@ TreatAsOpaque(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder) // children, which might be a large area their contents don't really cover). nsIFrame* f = aItem->Frame(); if (f->PresContext()->IsChrome() && !aItem->GetChildren() && - f->StyleDisplay()->mOpacity != 0.0) { + f->StyleEffects()->mOpacity != 0.0) { opaque = aItem->GetBounds(aBuilder, &snap); } } @@ -1924,7 +1964,7 @@ IsFrameReceivingPointerEvents(nsIFrame* aFrame) return true; } return NS_STYLE_POINTER_EVENTS_NONE != - aFrame->StyleVisibility()->GetEffectivePointerEvents(aFrame); + aFrame->StyleUserInterface()->GetEffectivePointerEvents(aFrame); } // A list of frames, and their z depth. Used for sorting @@ -2417,8 +2457,9 @@ nsDisplayBackgroundImage::AppendBackgroundItemsToTop(nsDisplayListBuilder* aBuil } const nsStyleBorder* borderStyle = aFrame->StyleBorder(); - bool hasInsetShadow = borderStyle->mBoxShadow && - borderStyle->mBoxShadow->HasShadowWithInset(true); + const nsStyleEffects* effectsStyle = aFrame->StyleEffects(); + bool hasInsetShadow = effectsStyle->mBoxShadow && + effectsStyle->mBoxShadow->HasShadowWithInset(true); bool willPaintBorder = !isThemed && !hasInsetShadow && borderStyle->HasBorder(); @@ -2871,6 +2912,7 @@ nsDisplayBackgroundImage::GetInsideClipRegion(nsDisplayItem* aItem, } else { switch (aClip) { case NS_STYLE_IMAGELAYER_CLIP_BORDER: + case NS_STYLE_IMAGELAYER_CLIP_TEXT: clipRect = nsRect(aItem->ToReferenceFrame(), frame->GetSize()); break; case NS_STYLE_IMAGELAYER_CLIP_PADDING: @@ -2910,7 +2952,8 @@ nsDisplayBackgroundImage::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, const nsStyleImageLayers::Layer& layer = mBackgroundStyle->mImage.mLayers[mLayer]; if (layer.mImage.IsOpaque() && layer.mBlendMode == NS_STYLE_BLEND_NORMAL && layer.mRepeat.mXRepeat != NS_STYLE_IMAGELAYER_REPEAT_SPACE && - layer.mRepeat.mYRepeat != NS_STYLE_IMAGELAYER_REPEAT_SPACE) { + layer.mRepeat.mYRepeat != NS_STYLE_IMAGELAYER_REPEAT_SPACE && + layer.mClip != NS_STYLE_IMAGELAYER_CLIP_TEXT) { result = GetInsideClipRegion(this, layer.mClip, mBounds); } } @@ -2984,17 +3027,30 @@ void nsDisplayBackgroundImage::PaintInternal(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx, const nsRect& aBounds, nsRect* aClipRect) { - nsPoint offset = ToReferenceFrame(); uint32_t flags = aBuilder->GetBackgroundPaintFlags(); CheckForBorderItem(this, flags); + nsRect borderBox = nsRect(ToReferenceFrame(), mFrame->GetSize()); + gfxContext* ctx = aCtx->ThebesContext(); + uint8_t clip = mBackgroundStyle->mImage.mLayers[mLayer].mClip; + + if (clip == NS_STYLE_IMAGELAYER_CLIP_TEXT) { + gfxRect bounds = nsLayoutUtils::RectToGfxRect(borderBox, mFrame->PresContext()->AppUnitsPerDevPixel()); + ctx->Save(); + ClipBackgroundByText(mFrame, aCtx, bounds); + } + image::DrawResult result = nsCSSRendering::PaintBackground(mFrame->PresContext(), *aCtx, mFrame, aBounds, - nsRect(offset, mFrame->GetSize()), + borderBox, flags, aClipRect, mLayer, CompositionOp::OP_OVER); + if (clip == NS_STYLE_IMAGELAYER_CLIP_TEXT) { + ctx->Restore(); + } + nsDisplayBackgroundGeometry::UpdateDrawResult(this, result); } @@ -3283,6 +3339,11 @@ nsDisplayBackgroundColor::Paint(nsDisplayListBuilder* aBuilder, // pixel shorter in rare cases. Disabled in favor of the old code for now. // Note that the pref layout.css.devPixelsPerPx needs to be set to 1 to // reproduce the bug. + // + // TODO: + // This new path does not include support for background-clip:text; need to + // be fixed if/when we switch to this new code path. + DrawTarget& aDrawTarget = *aCtx->GetDrawTarget(); Rect rect = NSRectToSnappedRect(borderBox, @@ -3296,6 +3357,17 @@ nsDisplayBackgroundColor::Paint(nsDisplayListBuilder* aBuilder, gfxRect bounds = nsLayoutUtils::RectToGfxRect(borderBox, mFrame->PresContext()->AppUnitsPerDevPixel()); + uint8_t clip = mBackgroundStyle->mImage.mLayers[0].mClip; + if (clip == NS_STYLE_IMAGELAYER_CLIP_TEXT) { + gfxContextAutoSaveRestore save(ctx); + + ClipBackgroundByText(mFrame, aCtx, bounds); + ctx->SetColor(mColor); + ctx->Fill(); + + return; + } + ctx->SetColor(mColor); ctx->NewPath(); ctx->Rectangle(bounds, true); @@ -3441,7 +3513,8 @@ nsDisplayLayerEventRegions::AddFrame(nsDisplayListBuilder* aBuilder, return; } - uint8_t pointerEvents = aFrame->StyleVisibility()->GetEffectivePointerEvents(aFrame); + uint8_t pointerEvents = + aFrame->StyleUserInterface()->GetEffectivePointerEvents(aFrame); if (pointerEvents == NS_STYLE_POINTER_EVENTS_NONE) { return; } @@ -4156,7 +4229,7 @@ nsDisplayOpacity::nsDisplayOpacity(nsDisplayListBuilder* aBuilder, const DisplayItemScrollClip* aScrollClip, bool aForEventsOnly) : nsDisplayWrapList(aBuilder, aFrame, aList, aScrollClip) - , mOpacity(aFrame->StyleDisplay()->mOpacity) + , mOpacity(aFrame->StyleEffects()->mOpacity) , mForEventsOnly(aForEventsOnly) , mParticipatesInPreserve3D(false) { @@ -6547,7 +6620,7 @@ nsDisplaySVGEffects::BuildLayer(nsDisplayListBuilder* aBuilder, } } - float opacity = mFrame->StyleDisplay()->mOpacity; + float opacity = mFrame->StyleEffects()->mOpacity; if (opacity == 0.0f) return nullptr; @@ -6657,9 +6730,9 @@ nsDisplaySVGEffects::PrintEffects(nsACString& aTo) nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK); bool first = true; aTo += " effects=("; - if (mFrame->StyleDisplay()->mOpacity != 1.0f) { + if (mFrame->StyleEffects()->mOpacity != 1.0f) { first = false; - aTo += nsPrintfCString("opacity(%f)", mFrame->StyleDisplay()->mOpacity); + aTo += nsPrintfCString("opacity(%f)", mFrame->StyleEffects()->mOpacity); } if (clipPathFrame) { if (!first) { diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index 8c1b0db87e..ddaff9c880 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -226,7 +226,8 @@ public: EVENT_DELIVERY, PLUGIN_GEOMETRY, FRAME_VISIBILITY, - TRANSFORM_COMPUTATION + TRANSFORM_COMPUTATION, + GENERATE_GLYPH }; nsDisplayListBuilder(nsIFrame* aReferenceFrame, Mode aMode, bool aBuildCaret); ~nsDisplayListBuilder(); @@ -268,6 +269,14 @@ public: */ bool IsForFrameVisibility() { return mMode == FRAME_VISIBILITY; } + /** + * @return true if the display list is being built for creating the glyph + * path from text items. While painting the display list, all text display + * items should only create glyph paths in target context, instead of + * drawing text into it. + */ + bool IsForGenerateGlyphPath() { return mMode == GENERATE_GLYPH; } + bool WillComputePluginGeometry() { return mWillComputePluginGeometry; } /** * @return true if "painting is suppressed" during page load and we @@ -2177,8 +2186,6 @@ private: nsDisplayItemLink mSentinel; nsDisplayItemLink* mTop; - // This is set by ComputeVisibility - nsRect mVisibleRect; // This is set to true by FrameLayerBuilder if the final visible region // is empty (i.e. everything that was visible is covered by some // opaque content in this list). diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index a89a69d044..13c7a19875 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -655,9 +655,7 @@ nsDocumentViewer::InitPresentationStuff(bool aDoInitialReflow) "Someone should have destroyed the presshell!"); // Create the style set... - StyleSetHandle styleSet; - nsresult rv = CreateStyleSet(mDocument, &styleSet); - NS_ENSURE_SUCCESS(rv, rv); + StyleSetHandle styleSet = CreateStyleSet(mDocument); // Now make the shell for the document mPresShell = mDocument->CreateShell(mPresContext, mViewManager, styleSet); @@ -726,7 +724,7 @@ nsDocumentViewer::InitPresentationStuff(bool aDoInitialReflow) return NS_ERROR_FAILURE; } - rv = selection->AddSelectionListener(mSelectionListener); + nsresult rv = selection->AddSelectionListener(mSelectionListener); if (NS_FAILED(rv)) return rv; @@ -2274,9 +2272,8 @@ StyleBackendTypeForDocument(nsIDocument* aDocument, nsIDocShell* aContainer) StyleBackendType::Gecko; } -nsresult -nsDocumentViewer::CreateStyleSet(nsIDocument* aDocument, - StyleSetHandle* aStyleSet) +StyleSetHandle +nsDocumentViewer::CreateStyleSet(nsIDocument* aDocument) { // Make sure this does the same thing as PresShell::AddSheet wrt ordering. @@ -2308,8 +2305,7 @@ nsDocumentViewer::CreateStyleSet(nsIDocument* aDocument, // should matter for SVG-as-an-image. If it does, I want to know why! // Caller will handle calling EndUpdate, per contract. - *aStyleSet = styleSet; - return NS_OK; + return styleSet; } auto cache = nsLayoutStylesheetCache::For(backendType); @@ -2463,8 +2459,7 @@ nsDocumentViewer::CreateStyleSet(nsIDocument* aDocument, } // Caller will handle calling EndUpdate, per contract. - *aStyleSet = styleSet; - return NS_OK; + return styleSet; } NS_IMETHODIMP @@ -2577,40 +2572,25 @@ nsDocumentViewer::FindContainerView() if (!containerElement) { return nullptr; } - nsCOMPtr parentPresShell; - nsCOMPtr parentDocShellItem; - docShell->GetParent(getter_AddRefs(parentDocShellItem)); - if (parentDocShellItem) { - nsCOMPtr parentDocShell = do_QueryInterface(parentDocShellItem); - parentPresShell = parentDocShell->GetPresShell(); - } - if (!parentPresShell) { - nsCOMPtr parentDoc = containerElement->GetCurrentDoc(); - if (parentDoc) { - parentPresShell = parentDoc->GetShell(); - } - } - if (!parentPresShell) { - NS_WARNING("Subdocument container has no presshell"); - } else { - nsIFrame* subdocFrame = parentPresShell->GetRealPrimaryFrameFor(containerElement); - if (subdocFrame) { - // subdocFrame might not be a subdocument frame; the frame - // constructor can treat a as an inline in some XBL - // cases. Treat that as display:none, the document is not - // displayed. - if (subdocFrame->GetType() == nsGkAtoms::subDocumentFrame) { - NS_ASSERTION(subdocFrame->GetView(), "Subdoc frames must have views"); - nsView* innerView = - static_cast(subdocFrame)->EnsureInnerView(); - containerView = innerView; - } else { - NS_WARNING("Subdocument container has non-subdocument frame"); - } + + nsIFrame* subdocFrame = nsLayoutUtils::GetRealPrimaryFrameFor(containerElement); + if (subdocFrame) { + // subdocFrame might not be a subdocument frame; the frame + // constructor can treat a as an inline in some XBL + // cases. Treat that as display:none, the document is not + // displayed. + if (subdocFrame->GetType() == nsGkAtoms::subDocumentFrame) { + NS_ASSERTION(subdocFrame->GetView(), "Subdoc frames must have views"); + nsView* innerView = + static_cast(subdocFrame)->EnsureInnerView(); + containerView = innerView; } else { - // XXX Silenced by default in bug 117528 - LAYOUT_WARNING("Subdocument container has no frame"); + NS_WARN_IF_FALSE(!subdocFrame->GetType(), + "Subdocument container has non-subdocument frame"); } + } else { + // XXX Silenced by default in bug 1175289 + LAYOUT_WARNING("Subdocument container has no frame"); } } } diff --git a/layout/base/nsIDocumentViewerPrint.h b/layout/base/nsIDocumentViewerPrint.h index 5138d16c46..e3c397f3a3 100644 --- a/layout/base/nsIDocumentViewerPrint.h +++ b/layout/base/nsIDocumentViewerPrint.h @@ -38,8 +38,7 @@ public: // The style set returned by CreateStyleSet is in the middle of an // update batch so that the caller can add sheets to it if needed. // Callers should call EndUpdate() on it when ready to use. - virtual nsresult CreateStyleSet(nsIDocument* aDocument, - mozilla::StyleSetHandle* aStyleSet) = 0; + virtual mozilla::StyleSetHandle CreateStyleSet(nsIDocument* aDocument) = 0; virtual void IncrementDestroyRefCount() = 0; virtual void DecrementDestroyRefCount() = 0; @@ -71,20 +70,19 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumentViewerPrint, /* Use this macro when declaring classes that implement this interface. */ #define NS_DECL_NSIDOCUMENTVIEWERPRINT \ - virtual void SetIsPrinting(bool aIsPrinting) override; \ - virtual bool GetIsPrinting() override; \ - virtual void SetIsPrintPreview(bool aIsPrintPreview) override; \ - virtual bool GetIsPrintPreview() override; \ - virtual nsresult CreateStyleSet(nsIDocument* aDocument, \ - mozilla::StyleSetHandle* aStyleSet) override; \ - virtual void IncrementDestroyRefCount() override; \ - virtual void DecrementDestroyRefCount() override; \ - virtual void ReturnToGalleyPresentation() override; \ - virtual void OnDonePrinting() override; \ - virtual bool IsInitializedForPrintPreview() override; \ - virtual void InitializeForPrintPreview() override; \ - virtual void SetPrintPreviewPresentation(nsViewManager* aViewManager, \ - nsPresContext* aPresContext, \ - nsIPresShell* aPresShell) override; + void SetIsPrinting(bool aIsPrinting) override; \ + bool GetIsPrinting() override; \ + void SetIsPrintPreview(bool aIsPrintPreview) override; \ + bool GetIsPrintPreview() override; \ + mozilla::StyleSetHandle CreateStyleSet(nsIDocument* aDocument) override; \ + void IncrementDestroyRefCount() override; \ + void DecrementDestroyRefCount() override; \ + void ReturnToGalleyPresentation() override; \ + void OnDonePrinting() override; \ + bool IsInitializedForPrintPreview() override; \ + void InitializeForPrintPreview() override; \ + void SetPrintPreviewPresentation(nsViewManager* aViewManager, \ + nsPresContext* aPresContext, \ + nsIPresShell* aPresShell) override; #endif /* nsIDocumentViewerPrint_h___ */ diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h index ff6ecef141..0cc8ffb571 100644 --- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -491,15 +491,6 @@ public: */ virtual nsCanvasFrame* GetCanvasFrame() const = 0; - /** - * Gets the real primary frame associated with the content object. - * - * In the case of absolutely positioned elements and floated elements, - * the real primary frame is the frame that is out of the flow and not the - * placeholder frame. - */ - virtual nsIFrame* GetRealPrimaryFrameFor(nsIContent* aContent) const = 0; - /** * Gets the placeholder frame associated with the specified frame. This is * a helper frame that forwards the request to the frame manager. diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index f6f52c88a2..56d983f650 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -142,9 +142,11 @@ using namespace mozilla::gfx; #define GRID_ENABLED_PREF_NAME "layout.css.grid.enabled" #define GRID_TEMPLATE_SUBGRID_ENABLED_PREF_NAME "layout.css.grid-template-subgrid-value.enabled" +#define WEBKIT_PREFIXES_ENABLED_PREF_NAME "layout.css.prefixes.webkit" #define DISPLAY_CONTENTS_ENABLED_PREF_NAME "layout.css.display-contents.enabled" #define TEXT_ALIGN_UNSAFE_ENABLED_PREF_NAME "layout.css.text-align-unsafe-value.enabled" #define FLOAT_LOGICAL_VALUES_ENABLED_PREF_NAME "layout.css.float-logical-values.enabled" +#define BG_CLIP_TEXT_ENABLED_PREF_NAME "layout.css.background-clip-text.enabled" #ifdef DEBUG // TODO: remove, see bug 598468. @@ -165,6 +167,7 @@ typedef nsStyleTransformMatrix::TransformReferenceBox TransformReferenceBox; /* static */ bool nsLayoutUtils::sCSSVariablesEnabled; /* static */ bool nsLayoutUtils::sInterruptibleReflowEnabled; /* static */ bool nsLayoutUtils::sSVGTransformBoxEnabled; +/* static */ bool nsLayoutUtils::sTextCombineUprightDigitsEnabled; static ViewID sScrollIdCounter = FrameMetrics::START_SCROLL_ID; @@ -222,6 +225,53 @@ GridEnabledPrefChangeCallback(const char* aPrefName, void* aClosure) } } +// When the pref "layout.css.prefixes.webkit" changes, this function is invoked +// to let us update kDisplayKTable, to selectively disable or restore the +// entries for "-webkit-box" and "-webkit-inline-box" in that table. +static void +WebkitPrefixEnabledPrefChangeCallback(const char* aPrefName, void* aClosure) +{ + MOZ_ASSERT(strncmp(aPrefName, WEBKIT_PREFIXES_ENABLED_PREF_NAME, + ArrayLength(WEBKIT_PREFIXES_ENABLED_PREF_NAME)) == 0, + "We only registered this callback for a single pref, so it " + "should only be called for that pref"); + + static int32_t sIndexOfWebkitBoxInDisplayTable; + static int32_t sIndexOfWebkitInlineBoxInDisplayTable; + static bool sAreWebkitBoxKeywordIndicesInitialized; // initialized to false + + bool isWebkitPrefixSupportEnabled = + Preferences::GetBool(WEBKIT_PREFIXES_ENABLED_PREF_NAME, false); + if (!sAreWebkitBoxKeywordIndicesInitialized) { + // First run: find the position of "-webkit-box" and "-webkit-inline-box" + // in kDisplayKTable. + sIndexOfWebkitBoxInDisplayTable = + nsCSSProps::FindIndexOfKeyword(eCSSKeyword__webkit_box, + nsCSSProps::kDisplayKTable); + MOZ_ASSERT(sIndexOfWebkitBoxInDisplayTable >= 0, + "Couldn't find -webkit-box in kDisplayKTable"); + sIndexOfWebkitInlineBoxInDisplayTable = + nsCSSProps::FindIndexOfKeyword(eCSSKeyword__webkit_inline_box, + nsCSSProps::kDisplayKTable); + MOZ_ASSERT(sIndexOfWebkitInlineBoxInDisplayTable >= 0, + "Couldn't find -webkit-inline-box in kDisplayKTable"); + sAreWebkitBoxKeywordIndicesInitialized = true; + } + + // OK -- now, stomp on or restore the "-webkit-box" entries in kDisplayKTable, + // depending on whether the webkit prefix pref is enabled vs. disabled. + if (sIndexOfWebkitBoxInDisplayTable >= 0) { + nsCSSProps::kDisplayKTable[sIndexOfWebkitBoxInDisplayTable].mKeyword = + isWebkitPrefixSupportEnabled ? + eCSSKeyword__webkit_box : eCSSKeyword_UNKNOWN; + } + if (sIndexOfWebkitInlineBoxInDisplayTable >= 0) { + nsCSSProps::kDisplayKTable[sIndexOfWebkitInlineBoxInDisplayTable].mKeyword = + isWebkitPrefixSupportEnabled ? + eCSSKeyword__webkit_inline_box : eCSSKeyword_UNKNOWN; + } +} + // When the pref "layout.css.display-contents.enabled" changes, this function is // invoked to let us update kDisplayKTable, to selectively disable or restore // the entries for "contents" in that table. @@ -344,6 +394,39 @@ FloatLogicalValuesEnabledPrefChangeCallback(const char* aPrefName, isFloatLogicalValuesEnabled ? eCSSKeyword_inline_end : eCSSKeyword_UNKNOWN; } + +// When the pref "layout.css.background-clip-text.enabled" changes, this +// function is invoked to let us update kBackgroundClipKTable, to selectively +// disable or restore the entries for "text" in that table. +static void +BackgroundClipTextEnabledPrefChangeCallback(const char* aPrefName, + void* aClosure) +{ + NS_ASSERTION(strcmp(aPrefName, BG_CLIP_TEXT_ENABLED_PREF_NAME) == 0, + "Did you misspell " BG_CLIP_TEXT_ENABLED_PREF_NAME " ?"); + + static bool sIsBGClipKeywordIndexInitialized; + static int32_t sIndexOfTextInBGClipTable; + bool isBGClipTextEnabled = + Preferences::GetBool(BG_CLIP_TEXT_ENABLED_PREF_NAME, false); + + if (!sIsBGClipKeywordIndexInitialized) { + // First run: find the position of "text" in kBackgroundClipKTable. + sIndexOfTextInBGClipTable = + nsCSSProps::FindIndexOfKeyword(eCSSKeyword_text, + nsCSSProps::kBackgroundClipKTable); + + sIsBGClipKeywordIndexInitialized = true; + } + + // OK -- now, stomp on or restore the "text" entry in kBackgroundClipKTable, + // depending on whether the pref is enabled vs. disabled. + if (sIndexOfTextInBGClipTable >= 0) { + nsCSSProps::kBackgroundClipKTable[sIndexOfTextInBGClipTable].mKeyword = + isBGClipTextEnabled ? eCSSKeyword_text : eCSSKeyword_UNKNOWN; + } +} + template static bool HasMatchingCurrentAnimations(const nsIFrame* aFrame, TestType&& aTest) @@ -966,17 +1049,19 @@ GetDisplayPortFromMarginsData(nsIContent* aContent, const ScreenMargin& margins = aMarginsData->mMargins; if (screenRect.height < maxHeightScreenPx) { int32_t budget = maxHeightScreenPx - screenRect.height; - - float top = std::min(margins.top, float(budget)); - float bottom = std::min(margins.bottom, budget - top); + // Scale the margins down to fit into the budget if necessary, maintaining + // their relative ratio. + float scale = std::min(1.0f, float(budget) / margins.TopBottom()); + float top = margins.top * scale; + float bottom = margins.bottom * scale; screenRect.y -= top; screenRect.height += top + bottom; } if (screenRect.width < maxWidthScreenPx) { int32_t budget = maxWidthScreenPx - screenRect.width; - - float left = std::min(margins.left, float(budget)); - float right = std::min(margins.right, budget - left); + float scale = std::min(1.0f, float(budget) / margins.LeftRight()); + float left = margins.left * scale; + float right = margins.right * scale; screenRect.x -= left; screenRect.width += left + right; } @@ -1008,6 +1093,16 @@ GetDisplayPortFromMarginsData(nsIContent* aContent, return result; } +static bool +ShouldDisableApzForElement(nsIContent* aContent) +{ + if (gfxPrefs::APZDisableForScrollLinkedEffects() && aContent) { + nsIDocument* doc = aContent->GetComposedDoc(); + return (doc && doc->HasScrollLinkedEffect()); + } + return false; +} + static bool GetDisplayPortImpl(nsIContent* aContent, nsRect *aResult, float aMultiplier) { @@ -1162,7 +1257,7 @@ nsLayoutUtils::SetDisplayPortMargins(nsIContent* aContent, scrollableFrame->GetDisplayPortAtLastApproximateFrameVisibilityUpdate(&oldDisplayPort); nsRect newDisplayPort; - Unused << GetDisplayPort(aContent, &newDisplayPort); + Unused << GetHighResolutionDisplayPort(aContent, &newDisplayPort); bool needVisibilityUpdate = !hadDisplayPort; // Check if the total size has changed by a large factor. @@ -1223,6 +1318,15 @@ nsLayoutUtils::HasCriticalDisplayPort(nsIContent* aContent) return GetCriticalDisplayPort(aContent, nullptr); } +bool +nsLayoutUtils::GetHighResolutionDisplayPort(nsIContent* aContent, nsRect* aResult) +{ + if (gfxPrefs::UseLowPrecisionBuffer()) { + return GetCriticalDisplayPort(aContent, aResult); + } + return GetDisplayPort(aContent, aResult); +} + void nsLayoutUtils::RemoveDisplayPort(nsIContent* aContent) { @@ -1466,6 +1570,17 @@ nsLayoutUtils::GetStyleFrame(const nsIContent* aContent) return nsLayoutUtils::GetStyleFrame(frame); } +/* static */ nsIFrame* +nsLayoutUtils::GetRealPrimaryFrameFor(const nsIContent* aContent) +{ + nsIFrame *frame = aContent->GetPrimaryFrame(); + if (!frame) { + return nullptr; + } + + return nsPlaceholderFrame::GetRealFrameFor(frame); +} + nsIFrame* nsLayoutUtils::GetFloatFromPlaceholder(nsIFrame* aFrame) { NS_ASSERTION(nsGkAtoms::placeholderFrame == aFrame->GetType(), @@ -4024,12 +4139,27 @@ nsLayoutUtils::ComputeObjectDestRect(const nsRect& aConstraintRect, already_AddRefed nsLayoutUtils::GetFontMetricsForFrame(const nsIFrame* aFrame, float aInflation) { - return GetFontMetricsForStyleContext(aFrame->StyleContext(), aInflation); + nsStyleContext* styleContext = aFrame->StyleContext(); + uint8_t variantWidth = NS_FONT_VARIANT_WIDTH_NORMAL; + if (styleContext->IsTextCombined()) { + MOZ_ASSERT(aFrame->GetType() == nsGkAtoms::textFrame); + auto textFrame = static_cast(aFrame); + auto clusters = textFrame->CountGraphemeClusters(); + if (clusters == 2) { + variantWidth = NS_FONT_VARIANT_WIDTH_HALF; + } else if (clusters == 3) { + variantWidth = NS_FONT_VARIANT_WIDTH_THIRD; + } else if (clusters == 4) { + variantWidth = NS_FONT_VARIANT_WIDTH_QUARTER; + } + } + return GetFontMetricsForStyleContext(styleContext, aInflation, variantWidth); } already_AddRefed nsLayoutUtils::GetFontMetricsForStyleContext(nsStyleContext* aStyleContext, - float aInflation) + float aInflation, + uint8_t aVariantWidth) { nsPresContext* pc = aStyleContext->PresContext(); @@ -4046,16 +4176,18 @@ nsLayoutUtils::GetFontMetricsForStyleContext(nsStyleContext* aStyleContext, params.userFontSet = pc->GetUserFontSet(); params.textPerf = pc->GetTextPerfMetrics(); - // When aInflation is 1.0, avoid making a local copy of the nsFont. + // When aInflation is 1.0 and we don't require width variant, avoid + // making a local copy of the nsFont. // This also avoids running font.size through floats when it is large, // which would be lossy. Fortunately, in such cases, aInflation is // guaranteed to be 1.0f. - if (aInflation == 1.0f) { + if (aInflation == 1.0f && aVariantWidth == NS_FONT_VARIANT_WIDTH_NORMAL) { return pc->DeviceContext()->GetMetricsFor(styleFont->mFont, params); } nsFont font = styleFont->mFont; font.size = NSToCoordRound(font.size * aInflation); + font.variantWidth = aVariantWidth; return pc->DeviceContext()->GetMetricsFor(font, params); } @@ -5539,10 +5671,10 @@ nsLayoutUtils::MinISizeFromInline(nsIFrame* aFrame, "should not be container for font size inflation"); nsIFrame::InlineMinISizeData data; - DISPLAY_MIN_WIDTH(aFrame, data.prevLines); + DISPLAY_MIN_WIDTH(aFrame, data.mPrevLines); aFrame->AddInlineMinISize(aRenderingContext, &data); data.ForceBreak(); - return data.prevLines; + return data.mPrevLines; } /* static */ nscoord @@ -5553,10 +5685,10 @@ nsLayoutUtils::PrefISizeFromInline(nsIFrame* aFrame, "should not be container for font size inflation"); nsIFrame::InlinePrefISizeData data; - DISPLAY_PREF_WIDTH(aFrame, data.prevLines); + DISPLAY_PREF_WIDTH(aFrame, data.mPrevLines); aFrame->AddInlinePrefISize(aRenderingContext, &data); data.ForceBreak(); - return data.prevLines; + return data.mPrevLines; } static nscolor @@ -6117,7 +6249,7 @@ nsLayoutUtils::GetGraphicsFilterForFrame(nsIFrame* aForFrame) sc = aForFrame->StyleContext(); } - switch (sc->StyleSVG()->mImageRendering) { + switch (sc->StyleVisibility()->mImageRendering) { case NS_STYLE_IMAGE_RENDERING_OPTIMIZESPEED: return Filter::POINT; case NS_STYLE_IMAGE_RENDERING_OPTIMIZEQUALITY: @@ -6499,7 +6631,11 @@ DrawImageInternal(gfxContext& aContext, imageRect.ToIntRect(&tmpDTRect); RefPtr tempDT = destCtx->GetDrawTarget()->CreateSimilarDrawTarget(tmpDTRect.Size(), SurfaceFormat::B8G8R8A8); - destCtx = new gfxContext(tempDT, imageRect.TopLeft()); + destCtx = gfxContext::ForDrawTarget(tempDT, imageRect.TopLeft()); + if (!destCtx) { + gfxDevCrash(LogReason::InvalidContext) << "NonOP_OVER context problem " << gfx::hexa(tempDT); + return result; + } } destCtx->SetMatrix(params.imageSpaceToDeviceSpace); @@ -6842,7 +6978,7 @@ nsLayoutUtils::HasNonZeroCornerOnSide(const nsStyleCorners& aCorners, /* static */ nsTransparencyMode nsLayoutUtils::GetFrameTransparency(nsIFrame* aBackgroundFrame, nsIFrame* aCSSRootFrame) { - if (aCSSRootFrame->StyleDisplay()->mOpacity < 1.0f) + if (aCSSRootFrame->StyleEffects()->mOpacity < 1.0f) return eTransparencyTransparent; if (HasNonZeroCorner(aCSSRootFrame->StyleBorder()->mBorderRadius)) @@ -6952,7 +7088,7 @@ nsLayoutUtils::GetTextRunFlagsForStyle(nsStyleContext* aStyleContext, if (aStyleText->mControlCharacterVisibility == NS_STYLE_CONTROL_CHARACTER_VISIBILITY_HIDDEN) { result |= gfxTextRunFactory::TEXT_HIDE_CONTROL_CHARACTERS; } - switch (aStyleContext->StyleSVG()->mTextRendering) { + switch (aStyleContext->StyleText()->mTextRendering) { case NS_STYLE_TEXT_RENDERING_OPTIMIZESPEED: result |= gfxTextRunFactory::TEXT_OPTIMIZE_SPEED; break; @@ -7577,6 +7713,24 @@ nsLayoutUtils::SizeOfTextRunsForFrames(nsIFrame* aFrame, return total; } +struct PrefCallbacks +{ + const char* name; + PrefChangedFunc func; +}; +static const PrefCallbacks kPrefCallbacks[] = { + { GRID_ENABLED_PREF_NAME, + GridEnabledPrefChangeCallback }, + { WEBKIT_PREFIXES_ENABLED_PREF_NAME, + WebkitPrefixEnabledPrefChangeCallback }, + { TEXT_ALIGN_UNSAFE_ENABLED_PREF_NAME, + TextAlignUnsafeEnabledPrefChangeCallback }, + { DISPLAY_CONTENTS_ENABLED_PREF_NAME, + DisplayContentsEnabledPrefChangeCallback }, + { FLOAT_LOGICAL_VALUES_ENABLED_PREF_NAME, + FloatLogicalValuesEnabledPrefChangeCallback }, +}; + /* static */ void nsLayoutUtils::Initialize() @@ -7603,23 +7757,16 @@ nsLayoutUtils::Initialize() "layout.interruptible-reflow.enabled"); Preferences::AddBoolVarCache(&sSVGTransformBoxEnabled, "svg.transform-box.enabled"); + Preferences::AddBoolVarCache(&sTextCombineUprightDigitsEnabled, + "layout.css.text-combine-upright-digits.enabled"); - Preferences::RegisterCallback(GridEnabledPrefChangeCallback, - GRID_ENABLED_PREF_NAME); - GridEnabledPrefChangeCallback(GRID_ENABLED_PREF_NAME, nullptr); - Preferences::RegisterCallback(TextAlignUnsafeEnabledPrefChangeCallback, - TEXT_ALIGN_UNSAFE_ENABLED_PREF_NAME); - Preferences::RegisterCallback(DisplayContentsEnabledPrefChangeCallback, - DISPLAY_CONTENTS_ENABLED_PREF_NAME); - DisplayContentsEnabledPrefChangeCallback(DISPLAY_CONTENTS_ENABLED_PREF_NAME, - nullptr); - TextAlignUnsafeEnabledPrefChangeCallback(TEXT_ALIGN_UNSAFE_ENABLED_PREF_NAME, - nullptr); - Preferences::RegisterCallback(FloatLogicalValuesEnabledPrefChangeCallback, - FLOAT_LOGICAL_VALUES_ENABLED_PREF_NAME); - FloatLogicalValuesEnabledPrefChangeCallback(FLOAT_LOGICAL_VALUES_ENABLED_PREF_NAME, + for (auto& callback : kPrefCallbacks) { + Preferences::RegisterCallbackAndCall(callback.func, callback.name); + } + Preferences::RegisterCallback(BackgroundClipTextEnabledPrefChangeCallback, + BG_CLIP_TEXT_ENABLED_PREF_NAME); + BackgroundClipTextEnabledPrefChangeCallback(BG_CLIP_TEXT_ENABLED_PREF_NAME, nullptr); - nsComputedDOMStyle::RegisterPrefChangeCallbacks(); } @@ -7632,9 +7779,13 @@ nsLayoutUtils::Shutdown() sContentMap = nullptr; } - Preferences::UnregisterCallback(GridEnabledPrefChangeCallback, - GRID_ENABLED_PREF_NAME); + for (auto& callback : kPrefCallbacks) { + Preferences::UnregisterCallback(callback.func, callback.name); + } nsComputedDOMStyle::UnregisterPrefChangeCallbacks(); + + // so the cached initial quotes array doesn't appear to be a leak + nsStyleList::Shutdown(); } /* static */ @@ -8012,7 +8163,7 @@ nsLayoutUtils::FontSizeInflationEnabled(nsPresContext *aPresContext) nsLayoutUtils::GetBoxShadowRectForFrame(nsIFrame* aFrame, const nsSize& aFrameSize) { - nsCSSShadowArray* boxShadows = aFrame->StyleBorder()->mBoxShadow; + nsCSSShadowArray* boxShadows = aFrame->StyleEffects()->mBoxShadow; if (!boxShadows) { return nsRect(); } @@ -8796,6 +8947,10 @@ nsLayoutUtils::ComputeScrollMetadata(nsIFrame* aForFrame, } } + if (ShouldDisableApzForElement(aContent)) { + metrics.SetForceDisableApz(true); + } + return metadata; } diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index b550127928..7fecd4e4ac 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -253,6 +253,12 @@ public: */ static bool HasCriticalDisplayPort(nsIContent* aContent); + /** + * If low-precision painting is turned on, delegates to GetCriticalDisplayPort. + * Otherwise, delegates to GetDisplayPort. + */ + static bool GetHighResolutionDisplayPort(nsIContent* aContent, nsRect* aResult); + /** * Remove the displayport for the given element. */ @@ -357,6 +363,15 @@ public: */ static nsIFrame* GetStyleFrame(const nsIContent* aContent); + /** + * Gets the real primary frame associated with the content object. + * + * In the case of absolutely positioned elements and floated elements, + * the real primary frame is the frame that is out of the flow and not the + * placeholder frame. + */ + static nsIFrame* GetRealPrimaryFrameFor(const nsIContent* aContent); + /** * IsGeneratedContentFor returns true if aFrame is the outermost * frame for generated content of type aPseudoElement for aContent. @@ -1240,7 +1255,8 @@ public: * @param aSizeInflation number to multiply font size by */ static already_AddRefed GetFontMetricsForStyleContext( - nsStyleContext* aStyleContext, float aSizeInflation = 1.0f); + nsStyleContext* aStyleContext, float aSizeInflation = 1.0f, + uint8_t aVariantWidth = NS_FONT_VARIANT_WIDTH_NORMAL); /** * Get the font metrics of emphasis marks corresponding to the given @@ -2402,6 +2418,10 @@ public: return sSVGTransformBoxEnabled; } + static bool TextCombineUprightDigitsEnabled() { + return sTextCombineUprightDigitsEnabled; + } + /** * See comment above "font.size.inflation.mappingIntercept" in * modules/libpref/src/init/all.js . @@ -2819,6 +2839,7 @@ private: static bool sCSSVariablesEnabled; static bool sInterruptibleReflowEnabled; static bool sSVGTransformBoxEnabled; + static bool sTextCombineUprightDigitsEnabled; /** * Helper function for LogTestDataForPaint(). diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp index 4530cc6f0c..4398eea960 100644 --- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -395,21 +395,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsPresContext) tmp->Destroy(); NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -#define MAKE_FONT_PREF_KEY(_pref, _s0, _s1) \ - _pref.Assign(_s0); \ - _pref.Append(_s1); - -static const char* const kGenericFont[] = { - ".variable.", - ".fixed.", - ".serif.", - ".sans-serif.", - ".monospace.", - ".cursive.", - ".fantasy." -}; - // whether no native theme service exists; // if this gets set to true, we'll stop asking for it. static bool sNoTheme = false; @@ -424,212 +409,6 @@ static bool sLookAndFeelChanged; // one prescontext. static bool sThemeChanged; -const nsPresContext::LangGroupFontPrefs* -nsPresContext::GetFontPrefsForLang(nsIAtom *aLanguage) const -{ - // Get language group for aLanguage: - - nsresult rv = NS_OK; - nsIAtom *langGroupAtom = nullptr; - if (!aLanguage) { - aLanguage = mLanguage; - } - if (aLanguage && mLangService) { - langGroupAtom = mLangService->GetLanguageGroup(aLanguage, &rv); - } - if (NS_FAILED(rv) || !langGroupAtom) { - langGroupAtom = nsGkAtoms::x_western; // Assume x-western is safe... - } - - // Look for cached prefs for this lang group. - // Most documents will only use one (or very few) language groups. Rather - // than have the overhead of a hash lookup, we simply look along what will - // typically be a very short (usually of length 1) linked list. There are 31 - // language groups, so in the worst case scenario we'll need to traverse 31 - // link items. - - LangGroupFontPrefs *prefs = - const_cast(&mLangGroupFontPrefs); - if (prefs->mLangGroup) { // if initialized - DebugOnly count = 0; - for (;;) { - NS_ASSERTION(++count < 35, "Lang group count exceeded!!!"); - if (prefs->mLangGroup == langGroupAtom) { - return prefs; - } - if (!prefs->mNext) { - break; - } - prefs = prefs->mNext; - } - - // nothing cached, so go on and fetch the prefs for this lang group: - prefs = prefs->mNext = new LangGroupFontPrefs; - } - - prefs->mLangGroup = langGroupAtom; - - /* Fetch the font prefs to be used -- see bug 61883 for details. - Not all prefs are needed upfront. Some are fallback prefs intended - for the GFX font sub-system... - - 1) unit : assumed to be the same for all language groups ------------- - font.size.unit = px | pt XXX could be folded in the size... bug 90440 - - 2) attributes for generic fonts -------------------------------------- - font.default.[langGroup] = serif | sans-serif - fallback generic font - font.name.[generic].[langGroup] = current user' selected font on the pref dialog - font.name-list.[generic].[langGroup] = fontname1, fontname2, ... [factory pre-built list] - font.size.[generic].[langGroup] = integer - settable by the user - font.size-adjust.[generic].[langGroup] = "float" - settable by the user - font.minimum-size.[langGroup] = integer - settable by the user - */ - - nsAutoCString langGroup; - langGroupAtom->ToUTF8String(langGroup); - - prefs->mDefaultVariableFont.size = CSSPixelsToAppUnits(16); - prefs->mDefaultFixedFont.size = CSSPixelsToAppUnits(13); - - nsAutoCString pref; - - // get the current applicable font-size unit - enum {eUnit_unknown = -1, eUnit_px, eUnit_pt}; - int32_t unit = eUnit_px; - - nsAdoptingCString cvalue = - Preferences::GetCString("font.size.unit"); - - if (!cvalue.IsEmpty()) { - if (cvalue.EqualsLiteral("px")) { - unit = eUnit_px; - } - else if (cvalue.EqualsLiteral("pt")) { - unit = eUnit_pt; - } - else { - // XXX should really send this warning to the user (Error Console?). - // And just default to unit = eUnit_px? - NS_WARNING("unexpected font-size unit -- expected: 'px' or 'pt'"); - unit = eUnit_unknown; - } - } - - // get font.minimum-size.[langGroup] - - MAKE_FONT_PREF_KEY(pref, "font.minimum-size.", langGroup); - - int32_t size = Preferences::GetInt(pref.get()); - if (unit == eUnit_px) { - prefs->mMinimumFontSize = CSSPixelsToAppUnits(size); - } - else if (unit == eUnit_pt) { - prefs->mMinimumFontSize = CSSPointsToAppUnits(size); - } - - nsFont* fontTypes[] = { - &prefs->mDefaultVariableFont, - &prefs->mDefaultFixedFont, - &prefs->mDefaultSerifFont, - &prefs->mDefaultSansSerifFont, - &prefs->mDefaultMonospaceFont, - &prefs->mDefaultCursiveFont, - &prefs->mDefaultFantasyFont - }; - static_assert(MOZ_ARRAY_LENGTH(fontTypes) == eDefaultFont_COUNT, - "FontTypes array count is not correct"); - - // Get attributes specific to each generic font. We do not get the user's - // generic-font-name-to-specific-family-name preferences because its the - // generic name that should be fed into the cascade. It is up to the GFX - // code to look up the font prefs to convert generic names to specific - // family names as necessary. - nsAutoCString generic_dot_langGroup; - for (uint32_t eType = 0; eType < ArrayLength(fontTypes); ++eType) { - generic_dot_langGroup.Assign(kGenericFont[eType]); - generic_dot_langGroup.Append(langGroup); - - nsFont* font = fontTypes[eType]; - - // set the default variable font (the other fonts are seen as 'generic' fonts - // in GFX and will be queried there when hunting for alternative fonts) - if (eType == eDefaultFont_Variable) { - MAKE_FONT_PREF_KEY(pref, "font.name.variable.", langGroup); - - nsAdoptingString value = Preferences::GetString(pref.get()); - if (!value.IsEmpty()) { - FontFamilyName defaultVariableName = FontFamilyName::Convert(value); - FontFamilyType defaultType = defaultVariableName.mType; - NS_ASSERTION(defaultType == eFamily_serif || - defaultType == eFamily_sans_serif, - "default type must be serif or sans-serif"); - prefs->mDefaultVariableFont.fontlist = FontFamilyList(defaultType); - } - else { - MAKE_FONT_PREF_KEY(pref, "font.default.", langGroup); - value = Preferences::GetString(pref.get()); - if (!value.IsEmpty()) { - FontFamilyName defaultVariableName = FontFamilyName::Convert(value); - FontFamilyType defaultType = defaultVariableName.mType; - NS_ASSERTION(defaultType == eFamily_serif || - defaultType == eFamily_sans_serif, - "default type must be serif or sans-serif"); - prefs->mDefaultVariableFont.fontlist = FontFamilyList(defaultType); - } - } - } - else { - if (eType == eDefaultFont_Monospace) { - // This takes care of the confusion whereby people often expect "monospace" - // to have the same default font-size as "-moz-fixed" (this tentative - // size may be overwritten with the specific value for "monospace" when - // "font.size.monospace.[langGroup]" is read -- see below) - prefs->mDefaultMonospaceFont.size = prefs->mDefaultFixedFont.size; - } - else if (eType != eDefaultFont_Fixed) { - // all the other generic fonts are initialized with the size of the - // variable font, but their specific size can supersede later -- see below - font->size = prefs->mDefaultVariableFont.size; - } - } - - // Bug 84398: for spec purists, a different font-size only applies to the - // .variable. and .fixed. fonts and the other fonts should get |font-size-adjust|. - // The problem is that only GfxWin has the support for |font-size-adjust|. So for - // parity, we enable the ability to set a different font-size on all platforms. - - // get font.size.[generic].[langGroup] - // size=0 means 'Auto', i.e., generic fonts retain the size of the variable font - MAKE_FONT_PREF_KEY(pref, "font.size", generic_dot_langGroup); - size = Preferences::GetInt(pref.get()); - if (size > 0) { - if (unit == eUnit_px) { - font->size = CSSPixelsToAppUnits(size); - } - else if (unit == eUnit_pt) { - font->size = CSSPointsToAppUnits(size); - } - } - - // get font.size-adjust.[generic].[langGroup] - // XXX only applicable on GFX ports that handle |font-size-adjust| - MAKE_FONT_PREF_KEY(pref, "font.size-adjust", generic_dot_langGroup); - cvalue = Preferences::GetCString(pref.get()); - if (!cvalue.IsEmpty()) { - font->sizeAdjust = (float)atof(cvalue.get()); - } - -#ifdef DEBUG_rbs - printf("%s Family-list:%s size:%d sizeAdjust:%.2f\n", - generic_dot_langGroup.get(), - NS_ConvertUTF16toUTF8(font->name).get(), font->size, - font->sizeAdjust); -#endif - } - - return prefs; -} - void nsPresContext::GetDocumentColorPreferences() { @@ -798,7 +577,8 @@ nsPresContext::GetUserPreferences() mPrefScrollbarSide = Preferences::GetInt("layout.scrollbar.side"); - ResetCachedFontPrefs(); + mLangGroupFontPrefs.Reset(); + StaticPresData::Get()->ResetCachedFontPrefs(); // * image animation const nsAdoptingCString& animatePref = @@ -1234,7 +1014,7 @@ nsPresContext::UpdateCharSet(const nsCString& aCharSet) if (mLanguage == nsGkAtoms::Unicode) { mLanguage = mLangService->GetLocaleLanguage(); } - ResetCachedFontPrefs(); + mLangGroupFontPrefs.Reset(); } switch (GET_BIDI_OPTION_TEXTTYPE(GetBidi())) { @@ -1473,44 +1253,6 @@ nsPresContext::SetImageAnimationModeExternal(uint16_t aMode) SetImageAnimationModeInternal(aMode); } -const nsFont* -nsPresContext::GetDefaultFont(uint8_t aFontID, nsIAtom *aLanguage) const -{ - const LangGroupFontPrefs *prefs = GetFontPrefsForLang(aLanguage); - - const nsFont *font; - switch (aFontID) { - // Special (our default variable width font and fixed width font) - case kPresContext_DefaultVariableFont_ID: - font = &prefs->mDefaultVariableFont; - break; - case kPresContext_DefaultFixedFont_ID: - font = &prefs->mDefaultFixedFont; - break; - // CSS - case kGenericFont_serif: - font = &prefs->mDefaultSerifFont; - break; - case kGenericFont_sans_serif: - font = &prefs->mDefaultSansSerifFont; - break; - case kGenericFont_monospace: - font = &prefs->mDefaultMonospaceFont; - break; - case kGenericFont_cursive: - font = &prefs->mDefaultCursiveFont; - break; - case kGenericFont_fantasy: - font = &prefs->mDefaultFantasyFont; - break; - default: - font = nullptr; - NS_ERROR("invalid arg"); - break; - } - return font; -} - already_AddRefed nsPresContext::GetContentLanguage() const { diff --git a/layout/base/nsPresContext.h b/layout/base/nsPresContext.h index c934e1a794..3ffaa56fe7 100644 --- a/layout/base/nsPresContext.h +++ b/layout/base/nsPresContext.h @@ -42,6 +42,7 @@ #include "Units.h" #include "mozilla/RestyleManagerHandle.h" #include "prenv.h" +#include "mozilla/StaticPresData.h" class nsAString; class nsIPrintSettings; @@ -135,7 +136,9 @@ class nsRootPresContext; class nsPresContext : public nsIObserver { public: typedef mozilla::FramePropertyTable FramePropertyTable; + typedef mozilla::LangGroupFontPrefs LangGroupFontPrefs; typedef mozilla::ScrollbarStyles ScrollbarStyles; + typedef mozilla::StaticPresData StaticPresData; NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_NSIOBSERVER @@ -365,23 +368,15 @@ public: * Get the default font for the given language and generic font ID. * If aLanguage is nullptr, the document's language is used. * - * This object is read-only, you must copy the font to modify it. - * - * When aFontID is kPresContext_DefaultVariableFontID or - * kPresContext_DefaultFixedFontID (which equals - * kGenericFont_moz_fixed, which is used for the -moz-fixed generic), - * the nsFont returned has its name as a CSS generic family (serif or - * sans-serif for the former, monospace for the latter), and its size - * as the default font size for variable or fixed fonts for the - * language group. - * - * For aFontID corresponding to a CSS Generic, the nsFont returned has - * its name set to that generic font's name, and its size set to - * the user's preference for font size for that generic and the - * given language. + * See the comment in StaticPresData::GetDefaultFont. */ const nsFont* GetDefaultFont(uint8_t aFontID, - nsIAtom *aLanguage) const; + nsIAtom *aLanguage) const + { + nsIAtom* lang = aLanguage ? aLanguage : mLanguage.get(); + return StaticPresData::Get()->GetDefaultFontHelper(aFontID, lang, + GetFontPrefsForLang(lang)); + } /** Get a cached boolean pref, by its type */ // * - initially created for bugs 31816, 20760, 22963 @@ -1121,64 +1116,14 @@ protected: void GetUserPreferences(); - // Allow nsAutoPtr dtor to access this protected struct's - // dtor: - struct LangGroupFontPrefs; - friend class nsAutoPtr; - struct LangGroupFontPrefs { - // Font sizes default to zero; they will be set in GetFontPreferences - LangGroupFontPrefs() - : mLangGroup(nullptr) - , mMinimumFontSize(0) - , mDefaultVariableFont(mozilla::eFamily_serif, 0) - , mDefaultFixedFont(mozilla::eFamily_monospace, 0) - , mDefaultSerifFont(mozilla::eFamily_serif, 0) - , mDefaultSansSerifFont(mozilla::eFamily_sans_serif, 0) - , mDefaultMonospaceFont(mozilla::eFamily_monospace, 0) - , mDefaultCursiveFont(mozilla::eFamily_cursive, 0) - , mDefaultFantasyFont(mozilla::eFamily_fantasy, 0) - {} - - size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { - size_t n = 0; - LangGroupFontPrefs *curr = mNext; - while (curr) { - n += aMallocSizeOf(curr); - - // Measurement of the following members may be added later if DMD finds - // it is worthwhile: - // - mLangGroup - // - mDefault*Font - - curr = curr->mNext; - } - return n; - } - - nsCOMPtr mLangGroup; - nscoord mMinimumFontSize; - nsFont mDefaultVariableFont; - nsFont mDefaultFixedFont; - nsFont mDefaultSerifFont; - nsFont mDefaultSansSerifFont; - nsFont mDefaultMonospaceFont; - nsFont mDefaultCursiveFont; - nsFont mDefaultFantasyFont; - nsAutoPtr mNext; - }; - /** * Fetch the user's font preferences for the given aLanguage's * langugage group. */ - const LangGroupFontPrefs* GetFontPrefsForLang(nsIAtom *aLanguage) const; - - void ResetCachedFontPrefs() { - // Throw away any other LangGroupFontPrefs objects: - mLangGroupFontPrefs.mNext = nullptr; - - // Make GetFontPreferences reinitialize mLangGroupFontPrefs: - mLangGroupFontPrefs.mLangGroup = nullptr; + const LangGroupFontPrefs* GetFontPrefsForLang(nsIAtom *aLanguage) const + { + nsIAtom* lang = aLanguage ? aLanguage : mLanguage.get(); + return StaticPresData::Get()->GetFontPrefsForLangHelper(lang, &mLangGroupFontPrefs); } void UpdateCharSet(const nsCString& aCharSet); @@ -1328,6 +1273,11 @@ protected: uint16_t mImageAnimationMode; uint16_t mImageAnimationModePref; + // Most documents will only use one (or very few) language groups. Rather + // than have the overhead of a hash lookup, we simply look along what will + // typically be a very short (usually of length 1) linked list. There are 31 + // language groups, so in the worst case scenario we'll need to traverse 31 + // link items. LangGroupFontPrefs mLangGroupFontPrefs; nscoord mBorderWidthTable[3]; @@ -1429,18 +1379,6 @@ protected: virtual ~nsPresContext(); - // these are private, use the list in nsFont.h if you want a public list - enum { - eDefaultFont_Variable, - eDefaultFont_Fixed, - eDefaultFont_Serif, - eDefaultFont_SansSerif, - eDefaultFont_Monospace, - eDefaultFont_Cursive, - eDefaultFont_Fantasy, - eDefaultFont_COUNT - }; - nscolor MakeColorPref(const nsString& aColor); void LastRelease(); diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 2b5d0a7ca8..e0736835b1 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -869,9 +869,11 @@ PresShell::Init(nsIDocument* aDocument, mPresContext = aPresContext; aPresContext->SetShell(this); - // Now we can initialize the style set. - aStyleSet->Init(aPresContext); + // Now we can initialize the style set. Make sure to set the member before + // calling Init, since various subroutines need to find the style set off + // the PresContext during initialization. mStyleSet = aStyleSet; + mStyleSet->Init(aPresContext); // Notify our prescontext that it now has a compatibility mode. Note that // this MUST happen after we set up our style set but before we create any @@ -2901,7 +2903,10 @@ PresShell::CreateReferenceRenderingContext() nsDeviceContext* devCtx = mPresContext->DeviceContext(); RefPtr rc; if (mPresContext->IsScreen()) { - rc = new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget()); + rc = gfxContext::ForDrawTarget(gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget()); + if (!rc) { + return nullptr; + } } else { // We assume the devCtx has positive width and height for this call. // However, width and height, may be outside of the reasonable range @@ -4540,18 +4545,6 @@ PresShell::StyleRuleRemoved(StyleSheetHandle aStyleSheet) RecordStyleSheetChange(aStyleSheet); } -nsIFrame* -PresShell::GetRealPrimaryFrameFor(nsIContent* aContent) const -{ - if (aContent->GetComposedDoc() != GetDocument()) { - return nullptr; - } - nsIFrame *primaryFrame = aContent->GetPrimaryFrame(); - if (!primaryFrame) - return nullptr; - return nsPlaceholderFrame::GetRealFrameFor(primaryFrame); -} - nsIFrame* PresShell::GetPlaceholderFrameFor(nsIFrame* aFrame) const { @@ -4953,11 +4946,12 @@ PresShell::PaintRangePaintInfo(const nsTArray>& aItems gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget( IntSize(pixelArea.width, pixelArea.height), SurfaceFormat::B8G8R8A8); - if (!dt) { + if (!dt || !dt->IsValid()) { return nullptr; } - RefPtr ctx = new gfxContext(dt); + RefPtr ctx = gfxContext::ForDrawTarget(dt); + MOZ_ASSERT(ctx); // already checked the draw target above if (aRegion) { // Convert aRegion from CSS pixels to dev pixels diff --git a/layout/base/nsPresShell.h b/layout/base/nsPresShell.h index 2bad5ae854..228ff266ac 100644 --- a/layout/base/nsPresShell.h +++ b/layout/base/nsPresShell.h @@ -118,7 +118,6 @@ public: virtual nsresult ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight) override; virtual nsIPageSequenceFrame* GetPageSequenceFrame() const override; virtual nsCanvasFrame* GetCanvasFrame() const override; - virtual nsIFrame* GetRealPrimaryFrameFor(nsIContent* aContent) const override; virtual nsIFrame* GetPlaceholderFrameFor(nsIFrame* aFrame) const override; virtual void FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty, diff --git a/layout/base/nsQuoteList.cpp b/layout/base/nsQuoteList.cpp index e11d72c603..0d99fdbae5 100644 --- a/layout/base/nsQuoteList.cpp +++ b/layout/base/nsQuoteList.cpp @@ -37,8 +37,9 @@ nsQuoteNode::Text() NS_ASSERTION(mType == eStyleContentType_OpenQuote || mType == eStyleContentType_CloseQuote, "should only be called when mText should be non-null"); - const nsStyleQuotes* styleQuotes = mPseudoFrame->StyleQuotes(); - int32_t quotesCount = styleQuotes->QuotesCount(); // 0 if 'quotes:none' + const nsStyleQuoteValues::QuotePairArray& quotePairs = + mPseudoFrame->StyleList()->GetQuotePairs(); + int32_t quotesCount = quotePairs.Length(); // 0 if 'quotes:none' int32_t quoteDepth = Depth(); // Reuse the last pair when the depth is greater than the number of @@ -47,15 +48,15 @@ nsQuoteNode::Text() if (quoteDepth >= quotesCount) quoteDepth = quotesCount - 1; - const nsString *result; + const nsString* result; if (quoteDepth == -1) { // close-quote from a depth of 0 or 'quotes: none' (we want a node // with the empty string so dynamic changes are easier to handle) - result = & EmptyString(); + result = &EmptyString(); } else { result = eStyleContentType_OpenQuote == mType - ? styleQuotes->OpenQuoteAt(quoteDepth) - : styleQuotes->CloseQuoteAt(quoteDepth); + ? "ePairs[quoteDepth].first + : "ePairs[quoteDepth].second; } return result; } diff --git a/layout/build/nsLayoutStatics.cpp b/layout/build/nsLayoutStatics.cpp index 5fb0287221..ad3885c88c 100644 --- a/layout/build/nsLayoutStatics.cpp +++ b/layout/build/nsLayoutStatics.cpp @@ -135,6 +135,7 @@ using namespace mozilla::system; #include "MediaDecoder.h" #include "mozilla/layers/CompositorLRU.h" #include "mozilla/dom/devicestorage/DeviceStorageStatics.h" +#include "mozilla/StaticPresData.h" #ifdef MOZ_B2G_BT #include "mozilla/dom/BluetoothUUID.h" @@ -201,6 +202,7 @@ nsLayoutStatics::Initialize() nsCellMap::Init(); + StaticPresData::Init(); nsCSSRendering::Init(); nsTextFrameTextRunCache::Init(); @@ -351,6 +353,7 @@ nsLayoutStatics::Shutdown() nsTextFrameTextRunCache::Shutdown(); nsHTMLDNSPrefetch::Shutdown(); nsCSSRendering::Shutdown(); + StaticPresData::Shutdown(); #ifdef DEBUG nsFrame::DisplayReflowShutdown(); #endif diff --git a/layout/forms/nsButtonFrameRenderer.cpp b/layout/forms/nsButtonFrameRenderer.cpp index 17e8d8dc08..35b3042ef9 100644 --- a/layout/forms/nsButtonFrameRenderer.cpp +++ b/layout/forms/nsButtonFrameRenderer.cpp @@ -259,7 +259,7 @@ nsButtonFrameRenderer::DisplayButton(nsDisplayListBuilder* aBuilder, nsDisplayList* aBackground, nsDisplayList* aForeground) { - if (mFrame->StyleBorder()->mBoxShadow) { + if (mFrame->StyleEffects()->mBoxShadow) { aBackground->AppendNewToTop(new (aBuilder) nsDisplayButtonBoxShadowOuter(aBuilder, this)); } diff --git a/layout/forms/nsComboboxControlFrame.cpp b/layout/forms/nsComboboxControlFrame.cpp index bde13e03bf..b20078f041 100644 --- a/layout/forms/nsComboboxControlFrame.cpp +++ b/layout/forms/nsComboboxControlFrame.cpp @@ -1371,7 +1371,8 @@ nsComboboxControlFrame::CreateFrameFor(nsIContent* aContent) nsStyleSet::eSkipParentDisplayBasedStyleFixup); RefPtr textStyleContext; - textStyleContext = styleSet->ResolveStyleForNonElement(mStyleContext); + textStyleContext = styleSet-> + ResolveStyleForNonElement(mStyleContext, nsCSSAnonBoxes::mozText); // Start by creating our anonymous block frame mDisplayFrame = new (shell) nsComboboxDisplayFrame(styleContext, this); diff --git a/layout/forms/nsFieldSetFrame.cpp b/layout/forms/nsFieldSetFrame.cpp index 54dffc6efd..8df87cd24c 100644 --- a/layout/forms/nsFieldSetFrame.cpp +++ b/layout/forms/nsFieldSetFrame.cpp @@ -166,7 +166,7 @@ nsFieldSetFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, // we need to paint the outline if (!(GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) && IsVisibleForPainting(aBuilder)) { - if (StyleBorder()->mBoxShadow) { + if (StyleEffects()->mBoxShadow) { aLists.BorderBackground()->AppendNewToTop(new (aBuilder) nsDisplayBoxShadowOuter(aBuilder, this)); } diff --git a/layout/forms/nsGfxButtonControlFrame.cpp b/layout/forms/nsGfxButtonControlFrame.cpp index 4e6ed3ad97..4fe2a63ec9 100644 --- a/layout/forms/nsGfxButtonControlFrame.cpp +++ b/layout/forms/nsGfxButtonControlFrame.cpp @@ -92,7 +92,7 @@ nsGfxButtonControlFrame::CreateFrameFor(nsIContent* aContent) nsPresContext* presContext = PresContext(); RefPtr textStyleContext; textStyleContext = presContext->StyleSet()-> - ResolveStyleForNonElement(mStyleContext); + ResolveStyleForNonElement(mStyleContext, nsCSSAnonBoxes::mozText); newFrame = NS_NewTextFrame(presContext->PresShell(), textStyleContext); // initialize the text frame diff --git a/layout/generic/MathMLTextRunFactory.cpp b/layout/generic/MathMLTextRunFactory.cpp index 1034d39572..193487b1bd 100644 --- a/layout/generic/MathMLTextRunFactory.cpp +++ b/layout/generic/MathMLTextRunFactory.cpp @@ -714,8 +714,8 @@ MathMLTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun, gfxTextRunFactory::Parameters innerParams = GetParametersForInner(aTextRun, &flags, aRefDrawTarget); - nsAutoPtr transformedChild; - nsAutoPtr cachedChild; + UniquePtr transformedChild; + UniquePtr cachedChild; gfxTextRun* child; if (mathVar == NS_MATHML_MATHVARIANT_BOLD && doMathvariantStyling) { diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index 9f855e494f..4a367d2718 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -722,7 +722,7 @@ nsBlockFrame::GetMinISize(nsRenderingContext *aRenderingContext) #endif if (line->IsBlock()) { data.ForceBreak(); - data.currentLine = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, + data.mCurrentLine = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, line->mFirstChild, nsLayoutUtils::MIN_ISIZE); data.ForceBreak(); } else { @@ -733,11 +733,11 @@ nsBlockFrame::GetMinISize(nsRenderingContext *aRenderingContext) // behavior for calc(10%-3px). const nsStyleCoord &indent = StyleText()->mTextIndent; if (indent.ConvertsToLength()) - data.currentLine += nsRuleNode::ComputeCoordPercentCalc(indent, 0); + data.mCurrentLine += nsRuleNode::ComputeCoordPercentCalc(indent, 0); } // XXX Bug NNNNNN Should probably handle percentage text-indent. - data.line = &line; + data.mLine = &line; data.SetLineContainer(curFrame); nsIFrame *kid = line->mFirstChild; for (int32_t i = 0, i_end = line->GetChildCount(); i != i_end; @@ -749,14 +749,14 @@ nsBlockFrame::GetMinISize(nsRenderingContext *aRenderingContext) if (gNoisyIntrinsic) { IndentBy(stdout, gNoiseIndent); printf("min: [prevLines=%d currentLine=%d]\n", - data.prevLines, data.currentLine); + data.mPrevLines, data.mCurrentLine); } #endif } } data.ForceBreak(); - mMinWidth = data.prevLines; + mMinWidth = data.mPrevLines; return mMinWidth; } @@ -810,7 +810,7 @@ nsBlockFrame::GetPrefISize(nsRenderingContext *aRenderingContext) #endif if (line->IsBlock()) { data.ForceBreak(); - data.currentLine = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, + data.mCurrentLine = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, line->mFirstChild, nsLayoutUtils::PREF_ISIZE); data.ForceBreak(); } else { @@ -821,11 +821,11 @@ nsBlockFrame::GetPrefISize(nsRenderingContext *aRenderingContext) // behavior for calc(10%-3px). const nsStyleCoord &indent = StyleText()->mTextIndent; if (indent.ConvertsToLength()) - data.currentLine += nsRuleNode::ComputeCoordPercentCalc(indent, 0); + data.mCurrentLine += nsRuleNode::ComputeCoordPercentCalc(indent, 0); } // XXX Bug NNNNNN Should probably handle percentage text-indent. - data.line = &line; + data.mLine = &line; data.SetLineContainer(curFrame); nsIFrame *kid = line->mFirstChild; for (int32_t i = 0, i_end = line->GetChildCount(); i != i_end; @@ -837,14 +837,14 @@ nsBlockFrame::GetPrefISize(nsRenderingContext *aRenderingContext) if (gNoisyIntrinsic) { IndentBy(stdout, gNoiseIndent); printf("pref: [prevLines=%d currentLine=%d]\n", - data.prevLines, data.currentLine); + data.mPrevLines, data.mCurrentLine); } #endif } } data.ForceBreak(); - mPrefWidth = data.prevLines; + mPrefWidth = data.mPrevLines; return mPrefWidth; } @@ -894,12 +894,12 @@ nsBlockFrame::GetPrefWidthTightBounds(nsRenderingContext* aRenderingContext, // behavior for calc(10%-3px). const nsStyleCoord &indent = StyleText()->mTextIndent; if (indent.ConvertsToLength()) { - data.currentLine += nsRuleNode::ComputeCoordPercentCalc(indent, 0); + data.mCurrentLine += nsRuleNode::ComputeCoordPercentCalc(indent, 0); } } // XXX Bug NNNNNN Should probably handle percentage text-indent. - data.line = &line; + data.mLine = &line; data.SetLineContainer(curFrame); nsIFrame *kid = line->mFirstChild; for (int32_t i = 0, i_end = line->GetChildCount(); i != i_end; @@ -907,8 +907,8 @@ nsBlockFrame::GetPrefWidthTightBounds(nsRenderingContext* aRenderingContext, rv = kid->GetPrefWidthTightBounds(aRenderingContext, &childX, &childXMost); NS_ENSURE_SUCCESS(rv, rv); - *aX = std::min(*aX, data.currentLine + childX); - *aXMost = std::max(*aXMost, data.currentLine + childXMost); + *aX = std::min(*aX, data.mCurrentLine + childX); + *aXMost = std::max(*aXMost, data.mCurrentLine + childXMost); kid->AddInlinePrefISize(aRenderingContext, &data); } } diff --git a/layout/generic/nsBulletFrame.cpp b/layout/generic/nsBulletFrame.cpp index a2fafbb1c5..e5c3dc32d3 100644 --- a/layout/generic/nsBulletFrame.cpp +++ b/layout/generic/nsBulletFrame.cpp @@ -681,6 +681,42 @@ nsBulletFrame::GetPrefISize(nsRenderingContext *aRenderingContext) return metrics.ISize(wm); } +// If a bullet has zero size and is "ignorable" from its styling, we behave +// as if it doesn't exist, from a line-breaking/isize-computation perspective. +// Otherwise, we use the default implementation, same as nsFrame. +static inline bool +IsIgnoreable(const nsIFrame* aFrame, nscoord aISize) +{ + if (aISize != nscoord(0)) { + return false; + } + auto listStyle = aFrame->StyleList(); + return listStyle->GetCounterStyle()->IsNone() && + !listStyle->GetListStyleImage(); +} + +/* virtual */ void +nsBulletFrame::AddInlineMinISize(nsRenderingContext* aRenderingContext, + nsIFrame::InlineMinISizeData* aData) +{ + nscoord isize = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, + this, nsLayoutUtils::MIN_ISIZE); + if (MOZ_LIKELY(!::IsIgnoreable(this, isize))) { + aData->DefaultAddInlineMinISize(this, isize); + } +} + +/* virtual */ void +nsBulletFrame::AddInlinePrefISize(nsRenderingContext* aRenderingContext, + nsIFrame::InlinePrefISizeData* aData) +{ + nscoord isize = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, + this, nsLayoutUtils::PREF_ISIZE); + if (MOZ_LIKELY(!::IsIgnoreable(this, isize))) { + aData->DefaultAddInlinePrefISize(isize); + } +} + NS_IMETHODIMP nsBulletFrame::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData) { diff --git a/layout/generic/nsBulletFrame.h b/layout/generic/nsBulletFrame.h index dba83fe26c..da7ae352d9 100644 --- a/layout/generic/nsBulletFrame.h +++ b/layout/generic/nsBulletFrame.h @@ -76,13 +76,16 @@ public: virtual nsresult GetFrameName(nsAString& aResult) const override; #endif - // nsIHTMLReflow virtual void Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aMetrics, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) override; virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override; virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override; + void AddInlineMinISize(nsRenderingContext* aRenderingContext, + nsIFrame::InlineMinISizeData* aData) override; + void AddInlinePrefISize(nsRenderingContext* aRenderingContext, + nsIFrame::InlinePrefISizeData* aData) override; // nsBulletFrame int32_t SetListItemOrdinal(int32_t aNextOrdinal, bool* aChanged, diff --git a/layout/generic/nsCanvasFrame.cpp b/layout/generic/nsCanvasFrame.cpp index 21d5a27782..1b279364dc 100644 --- a/layout/generic/nsCanvasFrame.cpp +++ b/layout/generic/nsCanvasFrame.cpp @@ -310,8 +310,9 @@ nsDisplayCanvasBackgroundImage::Paint(nsDisplayListBuilder* aBuilder, dt = destDT->CreateSimilarDrawTarget(IntSize(ceil(destRect.width), ceil(destRect.height)), SurfaceFormat::B8G8R8A8); - if (dt) { - RefPtr ctx = new gfxContext(dt); + if (dt && dt->IsValid()) { + RefPtr ctx = gfxContext::ForDrawTarget(dt); + MOZ_ASSERT(ctx); // already checked draw target above ctx->SetMatrix(ctx->CurrentMatrix().Translate(-destRect.x, -destRect.y)); nsRenderingContext context(ctx); PaintInternal(aBuilder, &context, bgClipRect, &bgClipRect); diff --git a/layout/generic/nsContainerFrame.cpp b/layout/generic/nsContainerFrame.cpp index c6874b3f35..5ee1f8387b 100644 --- a/layout/generic/nsContainerFrame.cpp +++ b/layout/generic/nsContainerFrame.cpp @@ -867,7 +867,7 @@ nsContainerFrame::DoInlineIntrinsicISize(nsRenderingContext *aRenderingContext, styleBorder->GetComputedBorderWidth(startSide) + GetCoord(styleMargin->mMargin.Get(startSide), 0); if (MOZ_LIKELY(sliceBreak)) { - aData->currentLine += startPBM; + aData->mCurrentLine += startPBM; } else { clonePBM = startPBM; } @@ -882,14 +882,14 @@ nsContainerFrame::DoInlineIntrinsicISize(nsRenderingContext *aRenderingContext, clonePBM += endPBM; } - const nsLineList_iterator* savedLine = aData->line; + const nsLineList_iterator* savedLine = aData->mLine; nsIFrame* const savedLineContainer = aData->LineContainer(); nsContainerFrame *lastInFlow; for (nsContainerFrame *nif = this; nif; nif = static_cast(nif->GetNextInFlow())) { - if (aData->currentLine == 0) { - aData->currentLine = clonePBM; + if (aData->mCurrentLine == 0) { + aData->mCurrentLine = clonePBM; } for (nsIFrame* kid : nif->mFrames) { if (aType == nsLayoutUtils::MIN_ISIZE) @@ -902,13 +902,13 @@ nsContainerFrame::DoInlineIntrinsicISize(nsRenderingContext *aRenderingContext, // After we advance to our next-in-flow, the stored line and line container // may no longer be correct. Just forget them. - aData->line = nullptr; + aData->mLine = nullptr; aData->SetLineContainer(nullptr); lastInFlow = nif; } - aData->line = savedLine; + aData->mLine = savedLine; aData->SetLineContainer(savedLineContainer); // This goes at the end no matter how things are broken and how @@ -919,7 +919,7 @@ nsContainerFrame::DoInlineIntrinsicISize(nsRenderingContext *aRenderingContext, // continuation, in which case that continuation should handle // the endSide border. if (MOZ_LIKELY(!lastInFlow->GetNextContinuation() && sliceBreak)) { - aData->currentLine += endPBM; + aData->mCurrentLine += endPBM; } } diff --git a/layout/generic/nsFirstLetterFrame.cpp b/layout/generic/nsFirstLetterFrame.cpp index e1771a92b4..65844c7c30 100644 --- a/layout/generic/nsFirstLetterFrame.cpp +++ b/layout/generic/nsFirstLetterFrame.cpp @@ -70,7 +70,8 @@ nsFirstLetterFrame::Init(nsIContent* aContent, nsStyleContext* parentStyleContext = mStyleContext->GetParent(); if (parentStyleContext) { newSC = PresContext()->StyleSet()-> - ResolveStyleForNonElement(parentStyleContext); + ResolveStyleForNonElement(parentStyleContext, + nsCSSAnonBoxes::mozOtherNonElement); SetStyleContextWithoutNotification(newSC); } } @@ -331,7 +332,8 @@ nsFirstLetterFrame::CreateContinuationForFloatingParent(nsPresContext* aPresCont nsStyleContext* parentSC = this->StyleContext()->GetParent(); if (parentSC) { RefPtr newSC; - newSC = presShell->StyleSet()->ResolveStyleForNonElement(parentSC); + newSC = presShell->StyleSet()-> + ResolveStyleForNonElement(parentSC, nsCSSAnonBoxes::mozOtherNonElement); continuation->SetStyleContext(newSC); nsLayoutUtils::MarkDescendantsDirty(continuation); } @@ -385,7 +387,8 @@ nsFirstLetterFrame::DrainOverflowFrames(nsPresContext* aPresContext) "should contain only text nodes"); nsStyleContext* parentSC = prevInFlow ? mStyleContext->GetParent() : mStyleContext; - sc = aPresContext->StyleSet()->ResolveStyleForNonElement(parentSC); + sc = aPresContext->StyleSet()-> + ResolveStyleForNonElement(parentSC, nsCSSAnonBoxes::mozText); kid->SetStyleContext(sc); nsLayoutUtils::MarkDescendantsDirty(kid); } diff --git a/layout/generic/nsFlexContainerFrame.cpp b/layout/generic/nsFlexContainerFrame.cpp index 009f75d084..a53f065d4d 100644 --- a/layout/generic/nsFlexContainerFrame.cpp +++ b/layout/generic/nsFlexContainerFrame.cpp @@ -900,7 +900,7 @@ GetFirstNonAnonBoxDescendant(nsIFrame* aFrame) // If aFrame isn't an anonymous container, then it'll do. if (!pseudoTag || // No pseudotag. !nsCSSAnonBoxes::IsAnonBox(pseudoTag) || // Pseudotag isn't anon. - pseudoTag == nsCSSAnonBoxes::mozNonElement) { // Text, not a container. + nsCSSAnonBoxes::IsNonElement(pseudoTag)) { // Text, not a container. break; } diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 12d69797a5..ed74bd1977 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -1163,9 +1163,8 @@ bool nsIFrame::HasOpacityInternal(float aThreshold) const { MOZ_ASSERT(0.0 <= aThreshold && aThreshold <= 1.0, "Invalid argument"); - const nsStyleDisplay* displayStyle = StyleDisplay(); - return StyleDisplay()->mOpacity < aThreshold || - (displayStyle->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) || + return StyleEffects()->mOpacity < aThreshold || + (StyleDisplay()->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) || (mContent && EffectCompositor::HasAnimationsForCompositor( this, eCSSProperty_opacity) && @@ -1193,9 +1192,10 @@ nsIFrame::Extend3DContext() const return false; } + const nsStyleEffects* effects = StyleEffects(); nsRect temp; return !nsFrame::ShouldApplyOverflowClipping(this, disp) && - !GetClipPropClipRect(disp, &temp, GetSize()) && + !GetClipPropClipRect(disp, effects, &temp, GetSize()) && !nsSVGIntegrationUtils::UsingEffectsForFrame(this); } @@ -1867,10 +1867,15 @@ nsFrame::DisplayBorderBackgroundOutline(nsDisplayListBuilder* aBuilder, // The visibility check belongs here since child elements have the // opportunity to override the visibility property and display even if // their parent is hidden. - if (!IsVisibleForPainting(aBuilder)) + if (!IsVisibleForPainting(aBuilder)) { return; + } - nsCSSShadowArray* shadows = StyleBorder()->mBoxShadow; + if (aBuilder->IsForGenerateGlyphPath()) { + return; + } + + nsCSSShadowArray* shadows = StyleEffects()->mBoxShadow; if (shadows && shadows->HasShadowWithInset(false)) { aLists.BorderBackground()->AppendNewToTop(new (aBuilder) nsDisplayBoxShadowOuter(aBuilder, this)); @@ -1907,17 +1912,19 @@ inline static bool IsSVGContentWithCSSClip(const nsIFrame *aFrame) } bool -nsIFrame::GetClipPropClipRect(const nsStyleDisplay* aDisp, nsRect* aRect, +nsIFrame::GetClipPropClipRect(const nsStyleDisplay* aDisp, + const nsStyleEffects* aEffects, + nsRect* aRect, const nsSize& aSize) const { NS_PRECONDITION(aRect, "Must have aRect out parameter"); - if (!(aDisp->mClipFlags & NS_STYLE_CLIP_RECT) || + if (!(aEffects->mClipFlags & NS_STYLE_CLIP_RECT) || !(aDisp->IsAbsolutelyPositioned(this) || IsSVGContentWithCSSClip(this))) { return false; } - *aRect = aDisp->mClip; + *aRect = aEffects->mClip; if (MOZ_LIKELY(StyleBorder()->mBoxDecorationBreak == NS_STYLE_BOX_DECORATION_BREAK_SLICE)) { // The clip applies to the joined boxes so it's relative the first @@ -1929,10 +1936,10 @@ nsIFrame::GetClipPropClipRect(const nsStyleDisplay* aDisp, nsRect* aRect, aRect->MoveBy(nsPoint(0, -y)); } - if (NS_STYLE_CLIP_RIGHT_AUTO & aDisp->mClipFlags) { + if (NS_STYLE_CLIP_RIGHT_AUTO & aEffects->mClipFlags) { aRect->width = aSize.width - aRect->x; } - if (NS_STYLE_CLIP_BOTTOM_AUTO & aDisp->mClipFlags) { + if (NS_STYLE_CLIP_BOTTOM_AUTO & aEffects->mClipFlags) { aRect->height = aSize.height - aRect->y; } return true; @@ -1948,10 +1955,11 @@ static bool ApplyClipPropClipping(nsDisplayListBuilder* aBuilder, const nsIFrame* aFrame, const nsStyleDisplay* aDisp, + const nsStyleEffects* aEffects, nsRect* aRect, DisplayListClipState::AutoSaveRestore& aClipState) { - if (!aFrame->GetClipPropClipRect(aDisp, aRect, aFrame->GetSize())) + if (!aFrame->GetClipPropClipRect(aDisp, aEffects, aRect, aFrame->GetSize())) return false; nsRect clipRect = *aRect + aBuilder->ToReferenceFrame(aFrame); @@ -2154,14 +2162,17 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, return; const nsStyleDisplay* disp = StyleDisplay(); + const nsStyleEffects* effects = StyleEffects(); // We can stop right away if this is a zero-opacity stacking context and // we're painting, and we're not animating opacity. Don't do this // if we're going to compute plugin geometry, since opacity-0 plugins // need to have display items built for them. - bool needEventRegions = aBuilder->IsBuildingLayerEventRegions() && - StyleVisibility()->GetEffectivePointerEvents(this) != NS_STYLE_POINTER_EVENTS_NONE; + bool needEventRegions = + aBuilder->IsBuildingLayerEventRegions() && + StyleUserInterface()->GetEffectivePointerEvents(this) != + NS_STYLE_POINTER_EVENTS_NONE; bool opacityItemForEventsOnly = false; - if (disp->mOpacity == 0.0 && aBuilder->IsForPainting() && + if (effects->mOpacity == 0.0 && aBuilder->IsForPainting() && !aBuilder->WillComputePluginGeometry() && !(disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) && !nsLayoutUtils::HasCurrentAnimationOfProperty(this, @@ -2240,7 +2251,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, } bool useOpacity = HasVisualOpacity() && !nsSVGUtils::CanOptimizeOpacity(this) && !usingSVGEffects; - bool useBlendMode = disp->mMixBlendMode != NS_STYLE_BLEND_NORMAL; + bool useBlendMode = effects->mMixBlendMode != NS_STYLE_BLEND_NORMAL; bool useStickyPosition = disp->mPosition == NS_STYLE_POSITION_STICKY && IsScrollFrameActive(aBuilder, nsLayoutUtils::GetNearestScrollableFrame(GetParent(), @@ -2281,7 +2292,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, CheckForApzAwareEventHandlers(aBuilder, this); nsRect clipPropClip; - if (ApplyClipPropClipping(aBuilder, this, disp, &clipPropClip, + if (ApplyClipPropClipping(aBuilder, this, disp, effects, &clipPropClip, nestedClipState)) { dirtyRect.IntersectRect(dirtyRect, clipPropClip); } @@ -2525,7 +2536,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, mixBlendClipState.Clear(); resultList.AppendNewToTop( new (aBuilder) nsDisplayBlendMode(aBuilder, this, &resultList, - disp->mMixBlendMode, + effects->mMixBlendMode, containerItemScrollClip)); } @@ -2558,10 +2569,16 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, if (aBuilder->IsBackgroundOnly()) return; + if (aBuilder->IsForGenerateGlyphPath()) { + if (nsGkAtoms::textFrame != aChild->GetType() && aChild->IsLeaf()) { + return; + } + } + nsIFrame* child = aChild; if (child->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE) return; - + bool isSVG = (child->GetStateBits() & NS_FRAME_SVG_LAYOUT); // true if this is a real or pseudo stacking context @@ -2669,13 +2686,14 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, // Child is composited if it's transformed, partially transparent, or has // SVG effects or a blend mode.. const nsStyleDisplay* disp = child->StyleDisplay(); + const nsStyleEffects* effects = child->StyleEffects(); const nsStylePosition* pos = child->StylePosition(); bool isVisuallyAtomic = child->HasOpacity() || child->IsTransformed() // strictly speaking, 'perspective' doesn't require visual atomicity, // but the spec says it acts like the rest of these || disp->mChildPerspective.GetUnit() == eStyleUnit_Coord - || disp->mMixBlendMode != NS_STYLE_BLEND_NORMAL + || effects->mMixBlendMode != NS_STYLE_BLEND_NORMAL || nsSVGIntegrationUtils::UsingEffectsForFrame(child) || (child->GetStateBits() & NS_FRAME_HAS_VR_CONTENT); @@ -2688,7 +2706,7 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, isVisuallyAtomic || (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT); if (isVisuallyAtomic || isPositioned || (!isSVG && disp->IsFloating(child)) || - ((disp->mClipFlags & NS_STYLE_CLIP_RECT) && + ((effects->mClipFlags & NS_STYLE_CLIP_RECT) && IsSVGContentWithCSSClip(child)) || disp->mIsolation != NS_STYLE_ISOLATION_AUTO || (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT) || @@ -2727,7 +2745,7 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, nsDisplayList list; nsDisplayList extraPositionedDescendants; if (isStackingContext) { - if (disp->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { + if (effects->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { aBuilder->SetContainsBlendMode(true); } // True stacking context. @@ -2737,7 +2755,8 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, aBuilder->DisplayCaret(child, dirty, &list); } else { nsRect clipRect; - if (ApplyClipPropClipping(aBuilder, child, disp, &clipRect, clipState)) { + if (ApplyClipPropClipping(aBuilder, child, disp, effects, &clipRect, + clipState)) { // clipRect is in builder-reference-frame coordinates, // dirty/clippedDirtyRect are in child coordinates dirty.IntersectRect(dirty, clipRect); @@ -4267,77 +4286,88 @@ nsFrame::GetPrefISize(nsRenderingContext *aRenderingContext) } /* virtual */ void -nsFrame::AddInlineMinISize(nsRenderingContext *aRenderingContext, - nsIFrame::InlineMinISizeData *aData) +nsFrame::AddInlineMinISize(nsRenderingContext* aRenderingContext, + nsIFrame::InlineMinISizeData* aData) { - NS_ASSERTION(GetParent(), "Must have a parent if we get here!"); - nsIFrame* parent = GetParent(); - bool canBreak = !CanContinueTextRun() && - !parent->StyleContext()->ShouldSuppressLineBreak() && - parent->StyleText()->WhiteSpaceCanWrap(parent); - - if (canBreak) { - aData->OptionallyBreak(); - } - aData->trailingWhitespace = 0; - aData->skipWhitespace = false; - aData->trailingTextFrame = nullptr; - aData->currentLine += nsLayoutUtils::IntrinsicForContainer(aRenderingContext, - this, nsLayoutUtils::MIN_ISIZE); - aData->atStartOfLine = false; - if (canBreak) { - aData->OptionallyBreak(); - } + nscoord isize = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, + this, nsLayoutUtils::MIN_ISIZE); + aData->DefaultAddInlineMinISize(this, isize); } /* virtual */ void -nsFrame::AddInlinePrefISize(nsRenderingContext *aRenderingContext, - nsIFrame::InlinePrefISizeData *aData) +nsFrame::AddInlinePrefISize(nsRenderingContext* aRenderingContext, + nsIFrame::InlinePrefISizeData* aData) { - aData->trailingWhitespace = 0; - aData->skipWhitespace = false; - nscoord myPref = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, - this, nsLayoutUtils::PREF_ISIZE); - aData->currentLine = NSCoordSaturatingAdd(aData->currentLine, myPref); + nscoord isize = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, + this, nsLayoutUtils::PREF_ISIZE); + aData->DefaultAddInlinePrefISize(isize); +} + +void +nsIFrame::InlineMinISizeData::DefaultAddInlineMinISize(nsIFrame* aFrame, + nscoord aISize, + bool aAllowBreak) +{ + auto parent = aFrame->GetParent(); + MOZ_ASSERT(parent, "Must have a parent if we get here!"); + const bool mayBreak = aAllowBreak && + !aFrame->CanContinueTextRun() && + !parent->StyleContext()->ShouldSuppressLineBreak() && + parent->StyleText()->WhiteSpaceCanWrap(parent); + if (mayBreak) { + OptionallyBreak(); + } + mTrailingWhitespace = 0; + mSkipWhitespace = false; + mCurrentLine += aISize; + mAtStartOfLine = false; + if (mayBreak) { + OptionallyBreak(); + } +} + +void +nsIFrame::InlinePrefISizeData::DefaultAddInlinePrefISize(nscoord aISize) +{ + mCurrentLine = NSCoordSaturatingAdd(mCurrentLine, aISize); + mTrailingWhitespace = 0; + mSkipWhitespace = false; } void nsIFrame::InlineMinISizeData::ForceBreak() { - currentLine -= trailingWhitespace; - prevLines = std::max(prevLines, currentLine); - currentLine = trailingWhitespace = 0; + mCurrentLine -= mTrailingWhitespace; + mPrevLines = std::max(mPrevLines, mCurrentLine); + mCurrentLine = mTrailingWhitespace = 0; - for (uint32_t i = 0, i_end = floats.Length(); i != i_end; ++i) { - nscoord float_min = floats[i].Width(); - if (float_min > prevLines) - prevLines = float_min; + for (uint32_t i = 0, i_end = mFloats.Length(); i != i_end; ++i) { + nscoord float_min = mFloats[i].Width(); + if (float_min > mPrevLines) + mPrevLines = float_min; } - floats.Clear(); - trailingTextFrame = nullptr; - skipWhitespace = true; + mFloats.Clear(); + mSkipWhitespace = true; } void nsIFrame::InlineMinISizeData::OptionallyBreak(nscoord aHyphenWidth) { - trailingTextFrame = nullptr; - // If we can fit more content into a smaller width by staying on this // line (because we're still at a negative offset due to negative // text-indent or negative margin), don't break. Otherwise, do the // same as ForceBreak. it doesn't really matter when we accumulate // floats. - if (currentLine + aHyphenWidth < 0 || atStartOfLine) + if (mCurrentLine + aHyphenWidth < 0 || mAtStartOfLine) return; - currentLine += aHyphenWidth; + mCurrentLine += aHyphenWidth; ForceBreak(); } void nsIFrame::InlinePrefISizeData::ForceBreak() { - if (floats.Length() != 0) { + if (mFloats.Length() != 0) { // preferred widths accumulated for floats that have already // been cleared past nscoord floats_done = 0, @@ -4346,10 +4376,10 @@ nsIFrame::InlinePrefISizeData::ForceBreak() floats_cur_left = 0, floats_cur_right = 0; - for (uint32_t i = 0, i_end = floats.Length(); i != i_end; ++i) { - const FloatInfo& floatInfo = floats[i]; - const nsStyleDisplay *floatDisp = floatInfo.Frame()->StyleDisplay(); - uint8_t breakType = floatDisp->PhysicalBreakType(lineContainerWM); + for (uint32_t i = 0, i_end = mFloats.Length(); i != i_end; ++i) { + const FloatInfo& floatInfo = mFloats[i]; + const nsStyleDisplay* floatDisp = floatInfo.Frame()->StyleDisplay(); + uint8_t breakType = floatDisp->PhysicalBreakType(mLineContainerWM); if (breakType == NS_STYLE_CLEAR_LEFT || breakType == NS_STYLE_CLEAR_RIGHT || breakType == NS_STYLE_CLEAR_BOTH) { @@ -4366,7 +4396,7 @@ nsIFrame::InlinePrefISizeData::ForceBreak() } } - uint8_t floatStyle = floatDisp->PhysicalFloats(lineContainerWM); + uint8_t floatStyle = floatDisp->PhysicalFloats(mLineContainerWM); nscoord& floats_cur = floatStyle == NS_STYLE_FLOAT_LEFT ? floats_cur_left : floats_cur_right; nscoord floatWidth = floatInfo.Width(); @@ -4381,16 +4411,16 @@ nsIFrame::InlinePrefISizeData::ForceBreak() if (floats_cur > floats_done) floats_done = floats_cur; - currentLine = NSCoordSaturatingAdd(currentLine, floats_done); + mCurrentLine = NSCoordSaturatingAdd(mCurrentLine, floats_done); - floats.Clear(); + mFloats.Clear(); } - currentLine = - NSCoordSaturatingSubtract(currentLine, trailingWhitespace, nscoord_MAX); - prevLines = std::max(prevLines, currentLine); - currentLine = trailingWhitespace = 0; - skipWhitespace = true; + mCurrentLine = + NSCoordSaturatingSubtract(mCurrentLine, mTrailingWhitespace, nscoord_MAX); + mPrevLines = std::max(mPrevLines, mCurrentLine); + mCurrentLine = mTrailingWhitespace = 0; + mSkipWhitespace = true; } static void @@ -5684,7 +5714,7 @@ ComputeEffectsRect(nsIFrame* aFrame, const nsRect& aOverflowRect, // For SVG frames, we only need to account for filters. // TODO: We could also take account of clipPath and mask to reduce the // visual overflow, but that's not essential. - if (aFrame->StyleSVGReset()->HasFilters()) { + if (aFrame->StyleEffects()->HasFilters()) { aFrame->Properties(). Set(nsIFrame::PreEffectsBBoxProperty(), new nsRect(r)); r = nsSVGUtils::GetPostFilterVisualOverflowRect(aFrame, aOverflowRect); @@ -7753,9 +7783,11 @@ UnionBorderBoxes(nsIFrame* aFrame, bool aApplyTransform, return u; } + const nsStyleEffects* effects = aFrame->StyleEffects(); nsRect clipPropClipRect; bool hasClipPropClip = - aFrame->GetClipPropClipRect(disp, &clipPropClipRect, bounds.Size()); + aFrame->GetClipPropClipRect(disp, effects, &clipPropClipRect, + bounds.Size()); // Iterate over all children except pop-ups. const nsIFrame::ChildListIDs skip(nsIFrame::kPopupList | @@ -7813,9 +7845,7 @@ ComputeAndIncludeOutlineArea(nsIFrame* aFrame, nsOverflowAreas& aOverflowAreas, return; } - nscoord width; - DebugOnly result = outline->GetOutlineWidth(width); - NS_ASSERTION(result, "GetOutlineWidth had no cached outline width"); + nscoord width = outline->GetOutlineWidth(); if (width <= 0 && outlineStyle != NS_STYLE_BORDER_STYLE_AUTO) { return; } @@ -7996,8 +8026,10 @@ nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas, ComputeEffectsRect(this, aOverflowAreas.VisualOverflow(), aNewSize); // Absolute position clipping + const nsStyleEffects* effects = StyleEffects(); nsRect clipPropClipRect; - bool hasClipPropClip = GetClipPropClipRect(disp, &clipPropClipRect, aNewSize); + bool hasClipPropClip = + GetClipPropClipRect(disp, effects, &clipPropClipRect, aNewSize); if (hasClipPropClip) { NS_FOR_FRAME_OVERFLOW_TYPES(otype) { nsRect& o = aOverflowAreas.Overflow(otype); @@ -8296,7 +8328,7 @@ nsFrame::CorrectStyleParentFrame(nsIFrame* aProspectiveParent, // Anon boxes are parented to their actual parent already, except // for non-elements. Those should not be treated as an anon box. - if (aChildPseudo && aChildPseudo != nsCSSAnonBoxes::mozNonElement && + if (aChildPseudo && !nsCSSAnonBoxes::IsNonElement(aChildPseudo) && nsCSSAnonBoxes::IsAnonBox(aChildPseudo)) { NS_ASSERTION(aChildPseudo != nsCSSAnonBoxes::mozAnonymousBlock && aChildPseudo != nsCSSAnonBoxes::mozAnonymousPositionedBlock, @@ -8554,7 +8586,7 @@ nsIFrame::VerticalAlignEnum() const return ConvertSVGDominantBaselineToVerticalAlign(dominantBaseline); } - const nsStyleCoord& verticalAlign = StyleTextReset()->mVerticalAlign; + const nsStyleCoord& verticalAlign = StyleDisplay()->mVerticalAlign; if (verticalAlign.GetUnit() == eStyleUnit_Enumerated) { return verticalAlign.GetIntValue(); } @@ -9301,11 +9333,13 @@ nsIFrame::DestroyContentArray(ContentArray* aArray) bool nsIFrame::IsPseudoStackingContextFromStyle() { - const nsStyleDisplay* disp = StyleDisplay(); // If you change this, also change the computation of pseudoStackingContext // in BuildDisplayListForChild() - return disp->mOpacity != 1.0f || - disp->IsAbsPosContainingBlock(this) || + if (StyleEffects()->mOpacity != 1.0f) { + return true; + } + const nsStyleDisplay* disp = StyleDisplay(); + return disp->IsAbsPosContainingBlock(this) || disp->IsFloating(this) || (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT); } diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index f90042b491..5d0cb21e03 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -2710,7 +2710,7 @@ ScrollFrameHelper::ScrollToImpl(nsPoint aPt, const nsRect& aRange, nsIAtom* aOri nsRect oldDisplayPort; nsIContent* content = mOuter->GetContent(); - nsLayoutUtils::GetDisplayPort(content, &oldDisplayPort); + nsLayoutUtils::GetHighResolutionDisplayPort(content, &oldDisplayPort); oldDisplayPort.MoveBy(-mScrolledFrame->GetPosition()); // Update frame position for scrolling @@ -2736,7 +2736,7 @@ ScrollFrameHelper::ScrollToImpl(nsPoint aPt, const nsRect& aRange, nsIAtom* aOri // skip the paint. nsRect displayPort; bool usingDisplayPort = - nsLayoutUtils::GetDisplayPort(content, &displayPort); + nsLayoutUtils::GetHighResolutionDisplayPort(content, &displayPort); displayPort.MoveBy(-mScrolledFrame->GetPosition()); PAINT_SKIP_LOG("New scrollpos %s usingDP %d dpEqual %d scrollableByApz %d plugins %d sle %d\n", diff --git a/layout/generic/nsHTMLReflowState.cpp b/layout/generic/nsHTMLReflowState.cpp index c7fb0b484d..b229b5df68 100644 --- a/layout/generic/nsHTMLReflowState.cpp +++ b/layout/generic/nsHTMLReflowState.cpp @@ -796,6 +796,7 @@ nsHTMLReflowState::InitFrameType(nsIAtom* aFrameType) case NS_STYLE_DISPLAY_TABLE: case NS_STYLE_DISPLAY_TABLE_CAPTION: case NS_STYLE_DISPLAY_FLEX: + case NS_STYLE_DISPLAY_WEBKIT_BOX: case NS_STYLE_DISPLAY_GRID: case NS_STYLE_DISPLAY_RUBY_TEXT_CONTAINER: frameType = NS_CSS_FRAME_TYPE_BLOCK; @@ -808,6 +809,7 @@ nsHTMLReflowState::InitFrameType(nsIAtom* aFrameType) case NS_STYLE_DISPLAY_INLINE_XUL_GRID: case NS_STYLE_DISPLAY_INLINE_STACK: case NS_STYLE_DISPLAY_INLINE_FLEX: + case NS_STYLE_DISPLAY_WEBKIT_INLINE_BOX: case NS_STYLE_DISPLAY_INLINE_GRID: case NS_STYLE_DISPLAY_RUBY: case NS_STYLE_DISPLAY_RUBY_BASE: diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index 57e4ff43b5..e30cdf4ac0 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -1682,58 +1682,58 @@ public: */ struct InlineIntrinsicISizeData { InlineIntrinsicISizeData() - : line(nullptr) - , lineContainer(nullptr) - , prevLines(0) - , currentLine(0) - , trailingWhitespace(0) - , skipWhitespace(true) + : mLine(nullptr) + , mLineContainer(nullptr) + , mPrevLines(0) + , mCurrentLine(0) + , mTrailingWhitespace(0) + , mSkipWhitespace(true) {} // The line. This may be null if the inlines are not associated with // a block or if we just don't know the line. - const nsLineList_iterator* line; + const nsLineList_iterator* mLine; // The line container. Private, to ensure we always use SetLineContainer - // to update it (so that we have a chance to store the lineContainerWM). + // to update it (so that we have a chance to store the mLineContainerWM). // // Note that nsContainerFrame::DoInlineIntrinsicISize will clear the - // |line| and |lineContainer| fields when following a next-in-flow link, + // |mLine| and |mLineContainer| fields when following a next-in-flow link, // so we must not assume these can always be dereferenced. private: - nsIFrame* lineContainer; + nsIFrame* mLineContainer; // Setter and getter for the lineContainer field: public: void SetLineContainer(nsIFrame* aLineContainer) { - lineContainer = aLineContainer; - if (lineContainer) { - lineContainerWM = lineContainer->GetWritingMode(); + mLineContainer = aLineContainer; + if (mLineContainer) { + mLineContainerWM = mLineContainer->GetWritingMode(); } } - nsIFrame* LineContainer() const { return lineContainer; } + nsIFrame* LineContainer() const { return mLineContainer; } // The maximum intrinsic width for all previous lines. - nscoord prevLines; + nscoord mPrevLines; // The maximum intrinsic width for the current line. At a line // break (mandatory for preferred width; allowed for minimum width), // the caller should call |Break()|. - nscoord currentLine; + nscoord mCurrentLine; // This contains the width of the trimmable whitespace at the end of - // |currentLine|; it is zero if there is no such whitespace. - nscoord trailingWhitespace; + // |mCurrentLine|; it is zero if there is no such whitespace. + nscoord mTrailingWhitespace; // True if initial collapsable whitespace should be skipped. This // should be true at the beginning of a block, after hard breaks // and when the last text ended with whitespace. - bool skipWhitespace; + bool mSkipWhitespace; // Writing mode of the line container (stored here so that we don't - // lose track of it if the lineContainer field is reset). - mozilla::WritingMode lineContainerWM; + // lose track of it if the mLineContainer field is reset). + mozilla::WritingMode mLineContainerWM; // Floats encountered in the lines. class FloatInfo { @@ -1749,15 +1749,19 @@ public: nscoord mWidth; }; - nsTArray floats; + nsTArray mFloats; }; struct InlineMinISizeData : public InlineIntrinsicISizeData { InlineMinISizeData() - : trailingTextFrame(nullptr) - , atStartOfLine(true) + : mAtStartOfLine(true) {} + // The default implementation for nsIFrame::AddInlineMinISize. + void DefaultAddInlineMinISize(nsIFrame* aFrame, + nscoord aISize, + bool aAllowBreak = true); + // We need to distinguish forced and optional breaks for cases where the // current line total is negative. When it is, we need to ignore // optional breaks to prevent min-width from ending up bigger than @@ -1768,19 +1772,17 @@ public: // width of the current line. void OptionallyBreak(nscoord aHyphenWidth = 0); - // The last text frame processed so far in the current line, when - // the last characters in that text frame are relevant for line - // break opportunities. - nsIFrame *trailingTextFrame; - // Whether we're currently at the start of the line. If we are, we // can't break (for example, between the text-indent and the first // word). - bool atStartOfLine; + bool mAtStartOfLine; }; struct InlinePrefISizeData : public InlineIntrinsicISizeData { void ForceBreak(); + + // The default implementation for nsIFrame::AddInlinePrefISize. + void DefaultAddInlinePrefISize(nscoord aISize); }; /** @@ -1791,7 +1793,7 @@ public: * * All *allowed* breakpoints within the frame determine what counts as * a line for the |InlineIntrinsicISizeData|. This means that - * |aData->trailingWhitespace| will always be zero (unlike for + * |aData->mTrailingWhitespace| will always be zero (unlike for * AddInlinePrefISize). * * All the comments for |GetMinISize| apply, except that this function @@ -2828,7 +2830,9 @@ public: * rect, with coordinates relative to this frame's origin. aRect must not be * null! */ - bool GetClipPropClipRect(const nsStyleDisplay* aDisp, nsRect* aRect, + bool GetClipPropClipRect(const nsStyleDisplay* aDisp, + const nsStyleEffects* aEffects, + nsRect* aRect, const nsSize& aSize) const; /** diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp index 164e46649a..f0169606cc 100644 --- a/layout/generic/nsImageFrame.cpp +++ b/layout/generic/nsImageFrame.cpp @@ -2488,29 +2488,11 @@ IsInAutoWidthTableCellForQuirk(nsIFrame *aFrame) } /* virtual */ void -nsImageFrame::AddInlineMinISize(nsRenderingContext *aRenderingContext, - nsIFrame::InlineMinISizeData *aData) +nsImageFrame::AddInlineMinISize(nsRenderingContext* aRenderingContext, + nsIFrame::InlineMinISizeData* aData) { - - NS_ASSERTION(GetParent(), "Must have a parent if we get here!"); - - nsIFrame* parent = GetParent(); - bool canBreak = - !CanContinueTextRun() && - parent->StyleText()->WhiteSpaceCanWrap(parent) && - !IsInAutoWidthTableCellForQuirk(this); - - if (canBreak) { - aData->OptionallyBreak(); - } - aData->trailingWhitespace = 0; - aData->skipWhitespace = false; - aData->trailingTextFrame = nullptr; - aData->currentLine += nsLayoutUtils::IntrinsicForContainer(aRenderingContext, - this, nsLayoutUtils::MIN_ISIZE); - aData->atStartOfLine = false; - - if (canBreak) { - aData->OptionallyBreak(); - } + nscoord isize = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, + this, nsLayoutUtils::MIN_ISIZE); + bool canBreak = !IsInAutoWidthTableCellForQuirk(this); + aData->DefaultAddInlineMinISize(this, isize, canBreak); } diff --git a/layout/generic/nsLineLayout.cpp b/layout/generic/nsLineLayout.cpp index d09824d4d9..cd07f26a21 100644 --- a/layout/generic/nsLineLayout.cpp +++ b/layout/generic/nsLineLayout.cpp @@ -2002,8 +2002,7 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd) // Get vertical-align property ("vertical-align" is the CSS name for // block-direction align) - const nsStyleCoord& verticalAlign = - frame->StyleTextReset()->mVerticalAlign; + const nsStyleCoord& verticalAlign = frame->StyleDisplay()->mVerticalAlign; uint8_t verticalAlignEnum = frame->VerticalAlignEnum(); #ifdef NOISY_BLOCKDIR_ALIGN printf(" [frame]"); diff --git a/layout/generic/nsPlaceholderFrame.cpp b/layout/generic/nsPlaceholderFrame.cpp index e510adb869..45d68119b0 100644 --- a/layout/generic/nsPlaceholderFrame.cpp +++ b/layout/generic/nsPlaceholderFrame.cpp @@ -66,9 +66,9 @@ nsPlaceholderFrame::AddInlineMinISize(nsRenderingContext* aRenderingContext, nsIFrame::InlineMinISizeData* aData) { // Override AddInlineMinWith so that *nothing* happens. In - // particular, we don't want to zero out |aData->trailingWhitespace|, + // particular, we don't want to zero out |aData->mTrailingWhitespace|, // since nsLineLayout skips placeholders when trimming trailing - // whitespace, and we don't want to set aData->skipWhitespace to + // whitespace, and we don't want to set aData->mSkipWhitespace to // false. // ...but push floats onto the list @@ -77,7 +77,7 @@ nsPlaceholderFrame::AddInlineMinISize(nsRenderingContext* aRenderingContext, nsLayoutUtils::IntrinsicForContainer(aRenderingContext, mOutOfFlowFrame, nsLayoutUtils::MIN_ISIZE); - aData->floats.AppendElement( + aData->mFloats.AppendElement( InlineIntrinsicISizeData::FloatInfo(mOutOfFlowFrame, floatWidth)); } } @@ -87,9 +87,9 @@ nsPlaceholderFrame::AddInlinePrefISize(nsRenderingContext* aRenderingContext, nsIFrame::InlinePrefISizeData* aData) { // Override AddInlinePrefWith so that *nothing* happens. In - // particular, we don't want to zero out |aData->trailingWhitespace|, + // particular, we don't want to zero out |aData->mTrailingWhitespace|, // since nsLineLayout skips placeholders when trimming trailing - // whitespace, and we don't want to set aData->skipWhitespace to + // whitespace, and we don't want to set aData->mSkipWhitespace to // false. // ...but push floats onto the list @@ -98,7 +98,7 @@ nsPlaceholderFrame::AddInlinePrefISize(nsRenderingContext* aRenderingContext, nsLayoutUtils::IntrinsicForContainer(aRenderingContext, mOutOfFlowFrame, nsLayoutUtils::PREF_ISIZE); - aData->floats.AppendElement( + aData->mFloats.AppendElement( InlineIntrinsicISizeData::FloatInfo(mOutOfFlowFrame, floatWidth)); } } diff --git a/layout/generic/nsRubyBaseContainerFrame.cpp b/layout/generic/nsRubyBaseContainerFrame.cpp index 72262b25d3..a4170d6244 100644 --- a/layout/generic/nsRubyBaseContainerFrame.cpp +++ b/layout/generic/nsRubyBaseContainerFrame.cpp @@ -148,19 +148,19 @@ CalculateColumnPrefISize(nsRenderingContext* aRenderingContext, nsIFrame::InlinePrefISizeData data; if (i == 0) { data.SetLineContainer(aBaseISizeData->LineContainer()); - data.skipWhitespace = aBaseISizeData->skipWhitespace; - data.trailingWhitespace = aBaseISizeData->trailingWhitespace; + data.mSkipWhitespace = aBaseISizeData->mSkipWhitespace; + data.mTrailingWhitespace = aBaseISizeData->mTrailingWhitespace; } else { // The line container of ruby text frames is their parent, // ruby text container frame. data.SetLineContainer(frame->GetParent()); } frame->AddInlinePrefISize(aRenderingContext, &data); - MOZ_ASSERT(data.prevLines == 0, "Shouldn't have prev lines"); - max = std::max(max, data.currentLine); + MOZ_ASSERT(data.mPrevLines == 0, "Shouldn't have prev lines"); + max = std::max(max, data.mCurrentLine); if (i == 0) { - aBaseISizeData->skipWhitespace = data.skipWhitespace; - aBaseISizeData->trailingWhitespace = data.trailingWhitespace; + aBaseISizeData->mSkipWhitespace = data.mSkipWhitespace; + aBaseISizeData->mTrailingWhitespace = data.mTrailingWhitespace; } } } @@ -182,22 +182,22 @@ nsRubyBaseContainerFrame::AddInlineMinISize( // directly if there is any span. nsIFrame::InlinePrefISizeData data; data.SetLineContainer(aData->LineContainer()); - data.skipWhitespace = aData->skipWhitespace; - data.trailingWhitespace = aData->trailingWhitespace; + data.mSkipWhitespace = aData->mSkipWhitespace; + data.mTrailingWhitespace = aData->mTrailingWhitespace; AddInlinePrefISize(aRenderingContext, &data); - aData->currentLine += data.currentLine; - if (data.currentLine > 0) { - aData->atStartOfLine = false; + aData->mCurrentLine += data.mCurrentLine; + if (data.mCurrentLine > 0) { + aData->mAtStartOfLine = false; } - aData->skipWhitespace = data.skipWhitespace; - aData->trailingWhitespace = data.trailingWhitespace; + aData->mSkipWhitespace = data.mSkipWhitespace; + aData->mTrailingWhitespace = data.mTrailingWhitespace; return; } } bool firstFrame = true; bool allowInitialLineBreak, allowLineBreak; - GetIsLineBreakAllowed(this, !aData->atStartOfLine, + GetIsLineBreakAllowed(this, !aData->mAtStartOfLine, &allowInitialLineBreak, &allowLineBreak); for (nsIFrame* frame = this; frame; frame = frame->GetNextInFlow()) { RubyColumnEnumerator enumerator( @@ -217,9 +217,9 @@ nsRubyBaseContainerFrame::AddInlineMinISize( firstFrame = false; nscoord isize = CalculateColumnPrefISize(aRenderingContext, enumerator, aData); - aData->currentLine += isize; + aData->mCurrentLine += isize; if (isize > 0) { - aData->atStartOfLine = false; + aData->mAtStartOfLine = false; } } } @@ -244,11 +244,11 @@ nsRubyBaseContainerFrame::AddInlinePrefISize( nsIFrame* frame = textContainers[i]->PrincipalChildList().FirstChild(); nsIFrame::InlinePrefISizeData data; frame->AddInlinePrefISize(aRenderingContext, &data); - MOZ_ASSERT(data.prevLines == 0, "Shouldn't have prev lines"); - sum = std::max(sum, data.currentLine); + MOZ_ASSERT(data.mPrevLines == 0, "Shouldn't have prev lines"); + sum = std::max(sum, data.mCurrentLine); } } - aData->currentLine += sum; + aData->mCurrentLine += sum; } /* virtual */ bool diff --git a/layout/generic/nsSelection.cpp b/layout/generic/nsSelection.cpp index 494284b7b1..96377b9856 100644 --- a/layout/generic/nsSelection.cpp +++ b/layout/generic/nsSelection.cpp @@ -2131,7 +2131,17 @@ nsFrameSelection::PhysicalMove(int16_t aDirection, int16_t aAmount, if (NS_SUCCEEDED(sel->GetPrimaryFrameForFocusNode(&frame, &offsetused, true))) { if (frame) { - wm = frame->GetWritingMode(); + if (!frame->StyleContext()->IsTextCombined()) { + wm = frame->GetWritingMode(); + } else { + // Using different direction for horizontal-in-vertical would + // make it hard to navigate via keyboard. Inherit the moving + // direction from its parent. + MOZ_ASSERT(frame->GetType() == nsGkAtoms::textFrame); + wm = frame->GetParent()->GetWritingMode(); + MOZ_ASSERT(wm.IsVertical(), "Text combined " + "can only appear in vertical text"); + } } } diff --git a/layout/generic/nsSimplePageSequenceFrame.cpp b/layout/generic/nsSimplePageSequenceFrame.cpp index 6f3edd2fed..ded131c78f 100644 --- a/layout/generic/nsSimplePageSequenceFrame.cpp +++ b/layout/generic/nsSimplePageSequenceFrame.cpp @@ -26,9 +26,6 @@ #include "nsServiceManagerUtils.h" #include -// DateTime Includes -#include "nsDateTimeFormatCID.h" - #define OFFSET_NOT_SET -1 // Print Options @@ -314,7 +311,7 @@ nsSimplePageSequenceFrame::Reflow(nsPresContext* aPresContext, // Create current Date/Time String if (!mDateFormatter) { - mDateFormatter = do_CreateInstance(NS_DATETIMEFORMAT_CONTRACTID); + mDateFormatter = nsIDateTimeFormat::Create(); } if (!mDateFormatter) { return; diff --git a/layout/generic/nsSubDocumentFrame.cpp b/layout/generic/nsSubDocumentFrame.cpp index 2ad0e163f5..0521cda857 100644 --- a/layout/generic/nsSubDocumentFrame.cpp +++ b/layout/generic/nsSubDocumentFrame.cpp @@ -357,7 +357,8 @@ nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, } // If we are pointer-events:none then we don't need to HitTest background - bool pointerEventsNone = StyleVisibility()->mPointerEvents == NS_STYLE_POINTER_EVENTS_NONE; + bool pointerEventsNone = + StyleUserInterface()->mPointerEvents == NS_STYLE_POINTER_EVENTS_NONE; if (!aBuilder->IsForEventDelivery() || !pointerEventsNone) { nsDisplayListCollection decorations; DisplayBorderBackgroundOutline(aBuilder, decorations); diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index 04d9bf2934..b05fdfe071 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -67,6 +67,7 @@ #include "mozilla/StyleSetHandleInlines.h" #include +#include #ifdef ACCESSIBILITY #include "nsAccessibilityService.h" #endif @@ -97,6 +98,16 @@ using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::gfx; +void +nsTextFrame::DrawPathCallbacks::NotifySelectionBackgroundNeedsFill( + const Rect& aBackgroundRect, + nscolor aColor, + DrawTarget& aDrawTarget) +{ + ColorPattern color(ToDeviceColor(aColor)); + aDrawTarget.FillRect(aBackgroundRect, color); +} + struct TabWidth { TabWidth(uint32_t aOffset, uint32_t aWidth) : mOffset(aOffset), mWidth(float(aWidth)) @@ -280,8 +291,6 @@ public: explicit nsTextPaintStyle(nsTextFrame* aFrame); void SetResolveColors(bool aResolveColors) { - NS_ASSERTION(mFrame->IsSVGText() || aResolveColors, - "must resolve colors is frame is not for SVG text"); mResolveColors = aResolveColors; } @@ -569,26 +578,25 @@ public: // Helper to create a textrun and remember it in the textframe cache, // for either 8-bit or 16-bit text strings template -gfxTextRun * +UniquePtr MakeTextRun(const T *aText, uint32_t aLength, gfxFontGroup *aFontGroup, const gfxFontGroup::Parameters* aParams, uint32_t aFlags, gfxMissingFontRecorder *aMFR) { - nsAutoPtr textRun(aFontGroup->MakeTextRun(aText, aLength, - aParams, aFlags, - aMFR)); + UniquePtr textRun = + aFontGroup->MakeTextRun(aText, aLength, aParams, aFlags, aMFR); if (!textRun) { return nullptr; } - nsresult rv = gTextRuns->AddObject(textRun); + nsresult rv = gTextRuns->AddObject(textRun.get()); if (NS_FAILED(rv)) { - gTextRuns->RemoveFromCache(textRun); + gTextRuns->RemoveFromCache(textRun.get()); return nullptr; } #ifdef NOISY_BIDI printf("Created textrun\n"); #endif - return textRun.forget(); + return textRun; } void @@ -1839,10 +1847,7 @@ CreateReferenceDrawTarget(nsTextFrame* aTextFrame) return dt.forget(); } -/** - * The returned textrun must be deleted when no longer needed. - */ -static gfxTextRun* +static UniquePtr GetHyphenTextRun(gfxTextRun* aTextRun, DrawTarget* aDrawTarget, nsTextFrame* aTextFrame) { @@ -1963,7 +1968,10 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer) lastStyleContext = f->StyleContext(); // Detect use of text-transform or font-variant anywhere in the run textStyle = f->StyleText(); - if (NS_STYLE_TEXT_TRANSFORM_NONE != textStyle->mTextTransform) { + if (NS_STYLE_TEXT_TRANSFORM_NONE != textStyle->mTextTransform || + // text-combine-upright requires converting from full-width + // characters to non-full-width correspendent in some cases. + lastStyleContext->IsTextCombined()) { anyTextTransformStyle = true; } if (textStyle->HasTextEmphasis()) { @@ -2194,9 +2202,14 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer) uint32_t offset = iter.GetSkippedOffset(); iter.AdvanceOriginal(f->GetContentLength()); uint32_t end = iter.GetSkippedOffset(); - if (sc != f->StyleContext()) { + // Text-combined frames have content-dependent transform, so we + // want to create new nsTransformedCharStyle for them anyway. + if (sc != f->StyleContext() || sc->IsTextCombined()) { sc = f->StyleContext(); charStyle = new nsTransformedCharStyle(sc); + if (sc->IsTextCombined() && f->CountGraphemeClusters() > 1) { + charStyle->mForceNonFullWidth = true; + } } uint32_t j; for (j = offset; j < end; ++j) { @@ -2209,7 +2222,7 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer) "We didn't cover all the characters in the text run!"); } - gfxTextRun* textRun; + UniquePtr textRun; gfxTextRunFactory::Parameters params = { mDrawTarget, finalUserData, &skipChars, textBreakPointsAfterTransform.Elements(), @@ -2255,18 +2268,15 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer) // the breaks may be stored in the textrun during this very call. // This is a bit annoying because it requires another loop over the frames // making up the textrun, but I don't see a way to avoid this. - SetupBreakSinksForTextRun(textRun, textPtr); + SetupBreakSinksForTextRun(textRun.get(), textPtr); if (anyTextEmphasis) { - SetupTextEmphasisForTextRun(textRun, textPtr); + SetupTextEmphasisForTextRun(textRun.get(), textPtr); } if (mSkipIncompleteTextRuns) { mSkipIncompleteTextRuns = !TextContainsLineBreakerWhiteSpace(textPtr, transformedLength, mDoubleByteText); - // Arrange for this textrun to be deleted the next time the linebreaker - // is flushed out - mTextRunsToDelete.AppendElement(textRun); // Since we're doing to destroy the user data now, avoid a dangling // pointer. Strictly speaking we don't need to do this since it should // not be used (since this textrun will not be used and will be @@ -2274,13 +2284,16 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer) // pointers around. textRun->SetUserData(nullptr); DestroyUserData(userDataToDestroy); + // Arrange for this textrun to be deleted the next time the linebreaker + // is flushed out + mTextRunsToDelete.AppendElement(textRun.release()); return nullptr; } // Actually wipe out the textruns associated with the mapped frames and associate // those frames with this text run. - AssignTextRun(textRun, fontInflation); - return textRun; + AssignTextRun(textRun.get(), fontInflation); + return textRun.release(); } // This is a cut-down version of BuildTextRunForFrames used to set up @@ -3133,6 +3146,16 @@ static void FindClusterEnd(gfxTextRun* aTextRun, int32_t aOriginalEnd, void PropertyProvider::ComputeJustification(Range aRange) { + // Horizontal-in-vertical frame is orthogonal to the line, so it + // doesn't actually include any justification opportunity inside. + // Note: although the spec says such frame should be treated as a + // U+FFFC, which indicates it is justifiable on its sides, we don't + // do that because it is difficult to implement, and doesn't make + // any difference in common use cases. + if (mFrame->StyleContext()->IsTextCombined()) { + return; + } + bool isCJ = IsChineseOrJapanese(mFrame); nsSkipCharsRunIterator run( mStart, nsSkipCharsRunIterator::LENGTH_INCLUDES_SKIPPED, aRange.Length()); @@ -3226,6 +3249,10 @@ PropertyProvider::GetSpacingInternal(Range aRange, Spacing* aSpacing, aSpacing[index].mAfter = 0.0; } + if (mFrame->StyleContext()->IsTextCombined()) { + return; + } + // Find our offset into the original+transformed string gfxSkipCharsIterator start(mStart); start.SetSkippedOffset(aRange.start); @@ -3601,7 +3628,11 @@ nsTextPaintStyle::GetTextColor() return NS_RGBA(0, 0, 0, 255); } } - return nsLayoutUtils::GetColor(mFrame, eCSSProperty_color); + + nsCSSProperty property = + mFrame->StyleText()->mWebkitTextFillColorForeground + ? eCSSProperty_color : eCSSProperty__webkit_text_fill_color; + return nsLayoutUtils::GetColor(mFrame, property); } bool @@ -4807,6 +4838,15 @@ nsDisplayText::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, } } +NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(TextCombineScaleFactorProperty, float) + +static float +GetTextCombineScaleFactor(nsTextFrame* aFrame) +{ + float factor = aFrame->Properties().Get(TextCombineScaleFactorProperty()); + return factor ? factor : 1.0f; +} + void nsDisplayText::Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) { @@ -4816,8 +4856,9 @@ nsDisplayText::Paint(nsDisplayListBuilder* aBuilder, // Add 1 pixel of dirty area around mVisibleRect to allow us to paint // antialiased pixels beyond the measured text extents. // This is temporary until we do this in the actual calculation of text extents. - LayoutDeviceRect extraVisible = LayoutDeviceRect::FromAppUnits( - mVisibleRect, mFrame->PresContext()->AppUnitsPerDevPixel()); + auto A2D = mFrame->PresContext()->AppUnitsPerDevPixel(); + LayoutDeviceRect extraVisible = + LayoutDeviceRect::FromAppUnits(mVisibleRect, A2D); extraVisible.Inflate(1); nsTextFrame* f = static_cast(mFrame); @@ -4831,14 +4872,38 @@ nsDisplayText::Paint(nsDisplayListBuilder* aBuilder, pixelVisible.Inflate(2); pixelVisible.RoundOut(); - ctx->NewPath(); - ctx->Rectangle(pixelVisible); - ctx->Clip(); + if (!aBuilder->IsForGenerateGlyphPath()) { + ctx->NewPath(); + ctx->Rectangle(pixelVisible); + ctx->Clip(); + } NS_ASSERTION(mVisIStartEdge >= 0, "illegal start edge"); NS_ASSERTION(mVisIEndEdge >= 0, "illegal end edge"); - f->PaintText(aCtx, ToReferenceFrame(), extraVisible, *this, - nullptr, nullptr, mOpacity); + + nsPoint framePt = ToReferenceFrame(); + if (f->StyleContext()->IsTextCombined()) { + float scaleFactor = GetTextCombineScaleFactor(f); + if (scaleFactor != 1.0f) { + // Setup matrix to compress text for text-combine-upright if + // necessary. This is done here because we want selection be + // compressed at the same time as text. + gfxPoint pt = nsLayoutUtils::PointToGfxPoint(framePt, A2D); + gfxMatrix mat = ctx->CurrentMatrix() + .Translate(pt).Scale(scaleFactor, 1.0).Translate(-pt); + ctx->SetMatrix(mat); + } + } + nsTextFrame::PaintTextParams params(aCtx->ThebesContext()); + params.framePt = gfxPoint(framePt.x, framePt.y); + + params.dirtyRect = extraVisible; + nsTextFrame::DrawPathCallbacks callbacks; + if (aBuilder->IsForGenerateGlyphPath()) { + params.callbacks = &callbacks; + } + + f->PaintText(params, *this, mOpacity); } void @@ -4851,12 +4916,14 @@ nsTextFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, DO_GLOBAL_REFLOW_COUNT_DSP("nsTextFrame"); + bool isTextTransparent = + NS_GET_A(StyleContext()->GetTextFillColor()) == 0; Maybe isSelected; if (((GetStateBits() & TEXT_NO_RENDERED_GLYPHS) || - (NS_GET_A(StyleColor()->mColor) == 0 && !StyleText()->HasTextShadow())) && + (isTextTransparent && !StyleText()->HasTextShadow())) && aBuilder->IsForPainting() && !IsSVGText()) { isSelected.emplace(IsSelected()); - if (!isSelected) { + if (!isSelected.value()) { TextDecorations textDecs; GetTextDecorations(PresContext(), eResolvedColors, textDecs); if (!textDecs.HasDecorationLines()) { @@ -4991,16 +5058,25 @@ nsTextFrame::GetTextDecorations( bool useOverride = false; nscolor overrideColor = NS_RGBA(0, 0, 0, 0); - // frameBStartOffset represents the offset to f's BStart from our baseline in our - // coordinate space + bool nearestBlockFound = false; + // Use writing mode of parent frame for orthogonal text frame to work. + // See comment in nsTextFrame::DrawTextRunAndDecorations. + WritingMode wm = GetParent()->GetWritingMode(); + bool vertical = wm.IsVertical(); + + nscoord ascent = GetLogicalBaseline(wm); + // physicalBlockStartOffset represents the offset from our baseline + // to f's physical block start, which is top in horizontal writing + // mode, and left in vertical writing modes, in our coordinate space. + // This physical block start is logical block start in most cases, + // but for vertical-rl, it is logical block end, and consequently in + // that case, it starts from the descent instead of ascent. + nscoord physicalBlockStartOffset = + wm.IsVerticalRL() ? GetSize().width - ascent : ascent; // baselineOffset represents the offset from our baseline to f's baseline or // the nearest block's baseline, in our coordinate space, whichever is closest // during the particular iteration - nscoord frameBStartOffset = mAscent, - baselineOffset = 0; - - bool nearestBlockFound = false; - bool vertical = GetWritingMode().IsVertical(); + nscoord baselineOffset = 0; for (nsIFrame* f = this, *fChild = nullptr; f; @@ -5043,19 +5119,22 @@ nsTextFrame::GetTextDecorations( const nscoord lineBaselineOffset = LazyGetLineBaselineOffset(fChild, fBlock); - baselineOffset = frameBStartOffset - lineBaselineOffset - + baselineOffset = physicalBlockStartOffset - lineBaselineOffset - (vertical ? fChild->GetNormalPosition().x : fChild->GetNormalPosition().y); } } else if (!nearestBlockFound) { - // use a dummy WritingMode, because nsTextFrame::GetLogicalBaseLine - // doesn't use it anyway - baselineOffset = frameBStartOffset - f->GetLogicalBaseline(WritingMode()); + // offset here is the offset from f's baseline to f's top/left + // boundary. It's descent for vertical-rl, and ascent otherwise. + nscoord offset = wm.IsVerticalRL() ? + f->GetSize().width - f->GetLogicalBaseline(wm) : + f->GetLogicalBaseline(wm); + baselineOffset = physicalBlockStartOffset - offset; } nearestBlockFound = nearestBlockFound || firstBlock; - frameBStartOffset += + physicalBlockStartOffset += vertical ? f->GetNormalPosition().x : f->GetNormalPosition().y; const uint8_t style = styleText->GetDecorationStyle(); @@ -5149,22 +5228,23 @@ GetInflationForTextDecorations(nsIFrame* aFrame, nscoord aInflationMinFontSize) struct EmphasisMarkInfo { - nsAutoPtr textRun; + UniquePtr textRun; gfxFloat advance; gfxFloat baselineOffset; }; NS_DECLARE_FRAME_PROPERTY_DELETABLE(EmphasisMarkProperty, EmphasisMarkInfo) -static gfxTextRun* -GenerateTextRunForEmphasisMarks(nsTextFrame* aFrame, nsFontMetrics* aFontMetrics, - WritingMode aWM, const nsStyleText* aStyleText) +UniquePtr +GenerateTextRunForEmphasisMarks(nsTextFrame* aFrame, + nsFontMetrics* aFontMetrics, + nsStyleContext* aStyleContext, + const nsStyleText* aStyleText) { const nsString& emphasisString = aStyleText->mTextEmphasisStyleString; RefPtr dt = CreateReferenceDrawTarget(aFrame); auto appUnitsPerDevUnit = aFrame->PresContext()->AppUnitsPerDevPixel(); - uint32_t flags = nsLayoutUtils:: - GetTextRunOrientFlagsForStyle(aFrame->StyleContext()); + uint32_t flags = nsLayoutUtils::GetTextRunOrientFlagsForStyle(aStyleContext); if (flags == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_MIXED) { // The emphasis marks should always be rendered upright per spec. flags = gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT; @@ -5196,24 +5276,31 @@ nsTextFrame::UpdateTextEmphasis(WritingMode aWM, PropertyProvider& aProvider) return nsRect(); } + nsStyleContext* styleContext = StyleContext(); + bool isTextCombined = styleContext->IsTextCombined(); + if (isTextCombined) { + styleContext = styleContext->GetParent(); + } RefPtr fm = nsLayoutUtils:: - GetFontMetricsOfEmphasisMarks(StyleContext(), GetFontSizeInflation()); + GetFontMetricsOfEmphasisMarks(styleContext, GetFontSizeInflation()); EmphasisMarkInfo* info = new EmphasisMarkInfo; info->textRun = - GenerateTextRunForEmphasisMarks(this, fm, aWM, styleText); + GenerateTextRunForEmphasisMarks(this, fm, styleContext, styleText); info->advance = info->textRun->GetAdvanceWidth(); // Calculate the baseline offset LogicalSide side = styleText->TextEmphasisSide(aWM); - nsFontMetrics* baseFontMetrics = aProvider.GetFontMetrics(); - LogicalSize frameSize = GetLogicalSize(); + LogicalSize frameSize = GetLogicalSize(aWM); // The overflow rect is inflated in the inline direction by half // advance of the emphasis mark on each side, so that even if a mark // is drawn for a zero-width character, it won't be clipped. LogicalRect overflowRect(aWM, -info->advance / 2, - /* BStart to be computed below */0, + /* BStart to be computed below */ 0, frameSize.ISize(aWM) + info->advance, fm->MaxAscent() + fm->MaxDescent()); + RefPtr baseFontMetrics = isTextCombined + ? nsLayoutUtils::GetInflatedFontMetricsForFrame(GetParent()) + : do_AddRef(aProvider.GetFontMetrics()); // When the writing mode is vertical-lr the line is inverted, and thus // the ascent and descent are swapped. nscoord absOffset = (side == eLogicalSideBStart) != aWM.IsLineInverted() ? @@ -5232,6 +5319,11 @@ nsTextFrame::UpdateTextEmphasis(WritingMode aWM, PropertyProvider& aProvider) info->baselineOffset = absOffset + endLeading; overflowRect.BStart(aWM) = frameSize.BSize(aWM) + endLeading; } + // If text combined, fix the gap between the text frame and its parent. + if (isTextCombined) { + nscoord gap = (baseFontMetrics->MaxHeight() - frameSize.BSize(aWM)) / 2; + overflowRect.BStart(aWM) += gap * (side == eLogicalSideBStart ? -1 : 1); + } Properties().Set(EmphasisMarkProperty(), info); return overflowRect.GetPhysicalRect(aWM, frameSize.GetPhysicalSize(aWM)); @@ -5246,10 +5338,9 @@ nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext, { const WritingMode wm = GetWritingMode(); bool verticalRun = mTextRun->IsVertical(); - bool useVerticalMetrics = verticalRun && mTextRun->UseCenterBaseline(); - bool inverted = wm.IsLineInverted(); if (IsFloatingFirstLetterChild()) { + bool inverted = wm.IsLineInverted(); // The underline/overline drawable area must be contained in the overflow // rect when this is in floating first letter frame at *both* modes. // In this case, aBlock is the ::first-letter frame. @@ -5267,23 +5358,25 @@ nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext, nscoord maxAscent = inverted ? fontMetrics->MaxDescent() : fontMetrics->MaxAscent(); + nsCSSRendering::DecorationRectParams params; gfxFloat appUnitsPerDevUnit = aPresContext->AppUnitsPerDevPixel(); - gfxFloat gfxWidth = + Float gfxWidth = (verticalRun ? aVisualOverflowRect->height : aVisualOverflowRect->width) / appUnitsPerDevUnit; - gfxFloat gfxAscent = gfxFloat(mAscent) / appUnitsPerDevUnit; - gfxFloat gfxMaxAscent = maxAscent / appUnitsPerDevUnit; - gfxFloat gfxUnderlineSize = underlineSize / appUnitsPerDevUnit; - gfxFloat gfxUnderlineOffset = underlineOffset / appUnitsPerDevUnit; + params.lineSize = Size(gfxWidth, underlineSize / appUnitsPerDevUnit); + params.ascent = gfxFloat(mAscent) / appUnitsPerDevUnit; + params.style = decorationStyle; + params.vertical = verticalRun; + + params.offset = underlineOffset / appUnitsPerDevUnit; + params.decoration = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE; nsRect underlineRect = - nsCSSRendering::GetTextDecorationRect(aPresContext, - gfxSize(gfxWidth, gfxUnderlineSize), gfxAscent, gfxUnderlineOffset, - NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, decorationStyle, verticalRun); + nsCSSRendering::GetTextDecorationRect(aPresContext, params); + params.offset = maxAscent / appUnitsPerDevUnit; + params.decoration = NS_STYLE_TEXT_DECORATION_LINE_OVERLINE; nsRect overlineRect = - nsCSSRendering::GetTextDecorationRect(aPresContext, - gfxSize(gfxWidth, gfxUnderlineSize), gfxAscent, gfxMaxAscent, - NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, decorationStyle, verticalRun); + nsCSSRendering::GetTextDecorationRect(aPresContext, params); aVisualOverflowRect->UnionRect(*aVisualOverflowRect, underlineRect); aVisualOverflowRect->UnionRect(*aVisualOverflowRect, overlineRect); @@ -5293,6 +5386,13 @@ nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext, // isn't realistic, we don't need to compute it now. } if (aIncludeTextDecorations) { + // Use writing mode of parent frame for orthogonal text frame to + // work. See comment in nsTextFrame::DrawTextRunAndDecorations. + WritingMode parentWM = GetParent()->GetWritingMode(); + bool verticalDec = parentWM.IsVertical(); + bool useVerticalMetrics = verticalDec != verticalRun + ? verticalDec : verticalRun && mTextRun->UseCenterBaseline(); + // Since CSS 2.1 requires that text-decoration defined on ancestors maintain // style and position, they can be drawn at virtually any y-offset, so // maxima and minima are required to reliably generate the rectangle for @@ -5303,122 +5403,85 @@ nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext, nscoord inflationMinFontSize = nsLayoutUtils::InflationMinFontSizeFor(aBlock); - const nscoord measure = verticalRun ? GetSize().height : GetSize().width; - const gfxFloat appUnitsPerDevUnit = aPresContext->AppUnitsPerDevPixel(), - gfxWidth = measure / appUnitsPerDevUnit; - gfxFloat ascent = gfxFloat(mAscent) / appUnitsPerDevUnit; - if (wm.IsVerticalRL()) { + const nscoord measure = verticalDec ? GetSize().height : GetSize().width; + const gfxFloat app = aPresContext->AppUnitsPerDevPixel(); + gfxFloat gfxWidth = measure / app; + gfxFloat ascent = gfxFloat(GetLogicalBaseline(parentWM)) / app; + nscoord frameBStart = 0; + if (parentWM.IsVerticalRL()) { + frameBStart = GetSize().width; ascent = -ascent; } + // The decoration-line offsets need to be reversed for sideways-lr mode, + // so we will multiply the values from metrics by this factor. + gfxFloat decorationOffsetDir = mTextRun->IsSidewaysLeft() ? -1.0 : 1.0; + + nsCSSRendering::DecorationRectParams params; + params.lineSize = Size(gfxWidth, 0); + params.ascent = ascent; + params.vertical = verticalDec; nscoord topOrLeft(nscoord_MAX), bottomOrRight(nscoord_MIN); + typedef gfxFont::Metrics Metrics; + auto accumulateDecorationRect = [&](const LineDecoration& dec, + gfxFloat Metrics::* lineSize, + gfxFloat Metrics::* lineOffset) { + params.style = dec.mStyle; + // If the style is solid, let's include decoration line rect of solid + // style since changing the style from none to solid/dotted/dashed + // doesn't cause reflow. + if (params.style == NS_STYLE_TEXT_DECORATION_STYLE_NONE) { + params.style = NS_STYLE_TEXT_DECORATION_STYLE_SOLID; + } + + float inflation = + GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize); + const Metrics metrics = + GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation), + useVerticalMetrics); + + params.lineSize.height = metrics.*lineSize; + params.offset = decorationOffsetDir * metrics.*lineOffset; + const nsRect decorationRect = + nsCSSRendering::GetTextDecorationRect(aPresContext, params) + + (verticalDec ? nsPoint(frameBStart - dec.mBaselineOffset, 0) + : nsPoint(0, -dec.mBaselineOffset)); + + if (verticalDec) { + topOrLeft = std::min(decorationRect.x, topOrLeft); + bottomOrRight = std::max(decorationRect.XMost(), bottomOrRight); + } else { + topOrLeft = std::min(decorationRect.y, topOrLeft); + bottomOrRight = std::max(decorationRect.YMost(), bottomOrRight); + } + }; + // Below we loop through all text decorations and compute the rectangle // containing all of them, in this frame's coordinate space - for (uint32_t i = 0; i < textDecs.mUnderlines.Length(); ++i) { - const LineDecoration& dec = textDecs.mUnderlines[i]; - uint8_t decorationStyle = dec.mStyle; - // If the style is solid, let's include decoration line rect of solid - // style since changing the style from none to solid/dotted/dashed - // doesn't cause reflow. - if (decorationStyle == NS_STYLE_TEXT_DECORATION_STYLE_NONE) { - decorationStyle = NS_STYLE_TEXT_DECORATION_STYLE_SOLID; - } - - float inflation = - GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize); - const gfxFont::Metrics metrics = - GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation), - useVerticalMetrics); - - const nsRect decorationRect = - nsCSSRendering::GetTextDecorationRect(aPresContext, - gfxSize(gfxWidth, metrics.underlineSize), - ascent, metrics.underlineOffset, - NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, decorationStyle, - verticalRun) + - nsPoint(0, -dec.mBaselineOffset); - - if (verticalRun) { - topOrLeft = std::min(decorationRect.x, topOrLeft); - bottomOrRight = std::max(decorationRect.XMost(), bottomOrRight); - } else { - topOrLeft = std::min(decorationRect.y, topOrLeft); - bottomOrRight = std::max(decorationRect.YMost(), bottomOrRight); - } + params.decoration = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE; + for (const LineDecoration& dec : textDecs.mUnderlines) { + accumulateDecorationRect(dec, &Metrics::underlineSize, + &Metrics::underlineOffset); } - for (uint32_t i = 0; i < textDecs.mOverlines.Length(); ++i) { - const LineDecoration& dec = textDecs.mOverlines[i]; - uint8_t decorationStyle = dec.mStyle; - // If the style is solid, let's include decoration line rect of solid - // style since changing the style from none to solid/dotted/dashed - // doesn't cause reflow. - if (decorationStyle == NS_STYLE_TEXT_DECORATION_STYLE_NONE) { - decorationStyle = NS_STYLE_TEXT_DECORATION_STYLE_SOLID; - } - - float inflation = - GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize); - const gfxFont::Metrics metrics = - GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation), - useVerticalMetrics); - - const nsRect decorationRect = - nsCSSRendering::GetTextDecorationRect(aPresContext, - gfxSize(gfxWidth, metrics.underlineSize), - ascent, metrics.maxAscent, - NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, decorationStyle, - verticalRun) + - nsPoint(0, -dec.mBaselineOffset); - - if (verticalRun) { - topOrLeft = std::min(decorationRect.x, topOrLeft); - bottomOrRight = std::max(decorationRect.XMost(), bottomOrRight); - } else { - topOrLeft = std::min(decorationRect.y, topOrLeft); - bottomOrRight = std::max(decorationRect.YMost(), bottomOrRight); - } + params.decoration = NS_STYLE_TEXT_DECORATION_LINE_OVERLINE; + for (const LineDecoration& dec : textDecs.mOverlines) { + accumulateDecorationRect(dec, &Metrics::underlineSize, + &Metrics::maxAscent); } - for (uint32_t i = 0; i < textDecs.mStrikes.Length(); ++i) { - const LineDecoration& dec = textDecs.mStrikes[i]; - uint8_t decorationStyle = dec.mStyle; - // If the style is solid, let's include decoration line rect of solid - // style since changing the style from none to solid/dotted/dashed - // doesn't cause reflow. - if (decorationStyle == NS_STYLE_TEXT_DECORATION_STYLE_NONE) { - decorationStyle = NS_STYLE_TEXT_DECORATION_STYLE_SOLID; - } - - float inflation = - GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize); - const gfxFont::Metrics metrics = - GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation), - useVerticalMetrics); - - const nsRect decorationRect = - nsCSSRendering::GetTextDecorationRect(aPresContext, - gfxSize(gfxWidth, metrics.strikeoutSize), - ascent, metrics.strikeoutOffset, - NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH, decorationStyle, - verticalRun) + - nsPoint(0, -dec.mBaselineOffset); - - if (verticalRun) { - topOrLeft = std::min(decorationRect.x, topOrLeft); - bottomOrRight = std::max(decorationRect.XMost(), bottomOrRight); - } else { - topOrLeft = std::min(decorationRect.y, topOrLeft); - bottomOrRight = std::max(decorationRect.YMost(), bottomOrRight); - } + params.decoration = NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH; + for (const LineDecoration& dec : textDecs.mStrikes) { + accumulateDecorationRect(dec, &Metrics::strikeoutSize, + &Metrics::strikeoutOffset); } aVisualOverflowRect->UnionRect( *aVisualOverflowRect, - verticalRun ? nsRect(topOrLeft, 0, bottomOrRight - topOrLeft, measure) + verticalDec ? nsRect(topOrLeft, 0, bottomOrRight - topOrLeft, measure) : nsRect(0, topOrLeft, measure, bottomOrRight - topOrLeft)); } aVisualOverflowRect->UnionRect(*aVisualOverflowRect, - UpdateTextEmphasis(wm, aProvider)); + UpdateTextEmphasis(parentWM, aProvider)); } // Text-shadow overflows @@ -5494,45 +5557,42 @@ nsTextFrame::ComputeSelectionUnderlineHeight( } } -void -nsTextFrame::PaintDecorationLine(gfxContext* const aCtx, - const LayoutDeviceRect& aDirtyRect, - nscolor aColor, - const nscolor* aOverrideColor, - const gfxPoint& aPt, - gfxFloat aICoordInFrame, - const gfxSize& aLineSize, - gfxFloat aAscent, - gfxFloat aOffset, - uint8_t aDecoration, - uint8_t aStyle, - DecorationType aDecorationType, - DrawPathCallbacks* aCallbacks, - bool aVertical, - gfxFloat aDescentLimit /* = -1.0 */) +enum class DecorationType { - nscolor lineColor = aOverrideColor ? *aOverrideColor : aColor; - gfxRect dirtyRect(aDirtyRect.x, aDirtyRect.y, - aDirtyRect.width, aDirtyRect.height); - if (aCallbacks) { - if (aDecorationType == eNormalDecoration) { - aCallbacks->NotifyBeforeDecorationLine(lineColor); + Normal, Selection +}; +struct nsTextFrame::PaintDecorationLineParams + : nsCSSRendering::DecorationRectParams +{ + gfxContext* context = nullptr; + LayoutDeviceRect dirtyRect; + Point pt; + const nscolor* overrideColor = nullptr; + nscolor color = NS_RGBA(0, 0, 0, 0); + gfxFloat icoordInFrame = 0.0f; + DecorationType decorationType = DecorationType::Normal; + DrawPathCallbacks* callbacks = nullptr; +}; + +void +nsTextFrame::PaintDecorationLine(const PaintDecorationLineParams& aParams) +{ + nsCSSRendering::PaintDecorationLineParams params; + static_cast(params) = aParams; + params.dirtyRect = aParams.dirtyRect.ToUnknownRect(); + params.pt = aParams.pt; + params.color = aParams.overrideColor ? *aParams.overrideColor : aParams.color; + params.icoordInFrame = Float(aParams.icoordInFrame); + if (aParams.callbacks) { + Rect path = nsCSSRendering::DecorationLineToPath(params); + if (aParams.decorationType == DecorationType::Normal) { + aParams.callbacks->PaintDecorationLine(path, params.color); } else { - aCallbacks->NotifyBeforeSelectionDecorationLine(lineColor); - } - nsCSSRendering::DecorationLineToPath(this, aCtx, dirtyRect, lineColor, - aPt, aICoordInFrame, aLineSize, aAscent, aOffset, aDecoration, aStyle, - aVertical, aDescentLimit); - if (aDecorationType == eNormalDecoration) { - aCallbacks->NotifyDecorationLinePathEmitted(); - } else { - aCallbacks->NotifySelectionDecorationLinePathEmitted(); + aParams.callbacks->PaintSelectionDecorationLine(path, params.color); } } else { - nsCSSRendering::PaintDecorationLine(this, *aCtx->GetDrawTarget(), - ToRect(dirtyRect), lineColor, - aPt, Float(aICoordInFrame), aLineSize, aAscent, aOffset, aDecoration, aStyle, - aVertical, aDescentLimit); + nsCSSRendering::PaintDecorationLine( + this, *aParams.context->GetDrawTarget(), params); } } @@ -5546,7 +5606,7 @@ nsTextFrame::DrawSelectionDecorations(gfxContext* aContext, SelectionType aType, nsTextPaintStyle& aTextPaintStyle, const TextRangeStyle &aRangeStyle, - const gfxPoint& aPt, + const Point& aPt, gfxFloat aICoordInFrame, gfxFloat aWidth, gfxFloat aAscent, @@ -5556,25 +5616,32 @@ nsTextFrame::DrawSelectionDecorations(gfxContext* aContext, gfxFloat aDecorationOffsetDir, uint8_t aDecoration) { - gfxPoint pt(aPt); - gfxSize size(aWidth, - ComputeSelectionUnderlineHeight(aTextPaintStyle.PresContext(), - aFontMetrics, aType)); - gfxFloat descentLimit = + PaintDecorationLineParams params; + params.context = aContext; + params.dirtyRect = aDirtyRect; + params.pt = aPt; + params.lineSize = Size( + aWidth, ComputeSelectionUnderlineHeight(aTextPaintStyle.PresContext(), + aFontMetrics, aType)); + params.ascent = aAscent; + gfxFloat offset = aDecoration == NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE ? + aFontMetrics.underlineOffset : aFontMetrics.maxAscent; + params.offset = offset * aDecorationOffsetDir; + params.decoration = aDecoration; + params.decorationType = DecorationType::Selection; + params.callbacks = aCallbacks; + params.vertical = aVertical; + params.descentLimit = ComputeDescentLimitForSelectionUnderline(aTextPaintStyle.PresContext(), aFontMetrics); float relativeSize; - uint8_t style; - nscolor color; int32_t index = nsTextPaintStyle::GetUnderlineStyleIndexForSelectionType(aType); bool weDefineSelectionUnderline = - aTextPaintStyle.GetSelectionUnderlineForPaint(index, &color, - &relativeSize, &style); + aTextPaintStyle.GetSelectionUnderlineForPaint(index, ¶ms.color, + &relativeSize, ¶ms.style); - gfxFloat offset = aDecoration == NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE ? - aFontMetrics.underlineOffset : aFontMetrics.maxAscent; switch (aType) { case nsISelectionController::SELECTION_IME_RAWINPUT: @@ -5594,15 +5661,15 @@ nsTextFrame::DrawSelectionDecorations(gfxContext* aContext, // +---------------------+----------------------+-------------------- // ^ ^ ^ ^ ^ // gap gap gap - pt.x += 1.0; - size.width -= 2.0; + params.pt.x += 1.0; + params.lineSize.width -= 2.0; if (aRangeStyle.IsDefined()) { // If IME defines the style, that should override our definition. if (aRangeStyle.IsLineStyleDefined()) { if (aRangeStyle.mLineStyle == TextRangeStyle::LINESTYLE_NONE) { return; } - style = aRangeStyle.mLineStyle; + params.style = aRangeStyle.mLineStyle; relativeSize = aRangeStyle.mIsBoldLine ? 2.0f : 1.0f; } else if (!weDefineSelectionUnderline) { // There is no underline style definition. @@ -5613,7 +5680,7 @@ nsTextFrame::DrawSelectionDecorations(gfxContext* aContext, if (aRangeStyle.IsUnderlineColorDefined() && (!aRangeStyle.IsForegroundColorDefined() || aRangeStyle.mUnderlineColor != aRangeStyle.mForegroundColor)) { - color = aRangeStyle.mUnderlineColor; + params.color = aRangeStyle.mUnderlineColor; } // If foreground color or background color is defined, the both colors // are computed by GetSelectionTextColors(). Then, we should use its @@ -5623,11 +5690,11 @@ nsTextFrame::DrawSelectionDecorations(gfxContext* aContext, aRangeStyle.IsBackgroundColorDefined()) { nscolor bg; GetSelectionTextColors(aType, aTextPaintStyle, aRangeStyle, - &color, &bg); + ¶ms.color, &bg); } // Otherwise, use the foreground color of the frame. else { - color = aTextPaintStyle.GetTextColor(); + params.color = aTextPaintStyle.GetTextColor(); } } else if (!weDefineSelectionUnderline) { // IME doesn't specify the selection style and we don't define selection @@ -5657,11 +5724,10 @@ nsTextFrame::DrawSelectionDecorations(gfxContext* aContext, NS_WARNING("Requested selection decorations when there aren't any"); return; } - size.height *= relativeSize; - PaintDecorationLine(aContext, aDirtyRect, color, nullptr, pt, - (aVertical ? (pt.y - aPt.y) : (pt.x - aPt.x)) + aICoordInFrame, - size, aAscent, offset * aDecorationOffsetDir, aDecoration, style, - eSelectionDecoration, aCallbacks, aVertical, descentLimit); + params.lineSize.height *= relativeSize; + params.icoordInFrame = (aVertical ? params.pt.y - aPt.y + : params.pt.x - aPt.x) + aICoordInFrame; + PaintDecorationLine(params); } /* static */ @@ -5856,10 +5922,11 @@ AddHyphenToMetrics(nsTextFrame* aTextFrame, gfxTextRun* aBaseTextRun, DrawTarget* aDrawTarget) { // Fix up metrics to include hyphen - nsAutoPtr hyphenTextRun( - GetHyphenTextRun(aBaseTextRun, aDrawTarget, aTextFrame)); - if (!hyphenTextRun.get()) + UniquePtr hyphenTextRun = + GetHyphenTextRun(aBaseTextRun, aDrawTarget, aTextFrame); + if (!hyphenTextRun) { return; + } gfxTextRun::Metrics hyphenMetrics = hyphenTextRun->MeasureText(aBoundingBoxType, aDrawTarget); @@ -5870,15 +5937,9 @@ AddHyphenToMetrics(nsTextFrame* aTextFrame, gfxTextRun* aBaseTextRun, } void -nsTextFrame::PaintOneShadow(Range aRange, +nsTextFrame::PaintOneShadow(const PaintShadowParams& aParams, nsCSSShadowItem* aShadowDetails, - PropertyProvider* aProvider, - const LayoutDeviceRect& aDirtyRect, - const gfxPoint& aFramePt, const gfxPoint& aTextBaselinePt, - gfxContext* aCtx, const nscolor& aForegroundColor, - const nsCharClipDisplayItem::ClipEdges& aClipEdges, - nscoord aLeftSideOffset, gfxRect& aBoundingBox, - uint32_t aBlurFlags) + gfxRect& aBoundingBox, uint32_t aBlurFlags) { PROFILER_LABEL("nsTextFrame", "PaintOneShadow", js::ProfileEntry::Category::GRAPHICS); @@ -5899,12 +5960,12 @@ nsTextFrame::PaintOneShadow(Range aRange, // for vertical-RL, reverse direction of x-coords of bounding box shadowGfxRect.x = -shadowGfxRect.XMost(); } - shadowGfxRect += - gfxPoint(aTextBaselinePt.x, aFramePt.y + aLeftSideOffset); + shadowGfxRect += gfxPoint(aParams.textBaselinePt.x, + aParams.framePt.y + aParams.leftSideOffset); } else { shadowGfxRect = - aBoundingBox + gfxPoint(aFramePt.x + aLeftSideOffset, - aTextBaselinePt.y); + aBoundingBox + gfxPoint(aParams.framePt.x + aParams.leftSideOffset, + aParams.textBaselinePt.y); } shadowGfxRect += shadowOffset; @@ -5916,8 +5977,8 @@ nsTextFrame::PaintOneShadow(Range aRange, nsContextBoxBlur contextBoxBlur; const auto A2D = PresContext()->AppUnitsPerDevPixel(); gfxContext* shadowContext = contextBoxBlur.Init( - shadowRect, 0, blurRadius, A2D, aCtx, - LayoutDevicePixel::ToAppUnits(aDirtyRect, A2D), nullptr, aBlurFlags); + shadowRect, 0, blurRadius, A2D, aParams.context, + LayoutDevicePixel::ToAppUnits(aParams.dirtyRect, A2D), nullptr, aBlurFlags); if (!shadowContext) return; @@ -5927,13 +5988,12 @@ nsTextFrame::PaintOneShadow(Range aRange, shadowColor = aShadowDetails->mColor; decorationOverrideColor = &shadowColor; } else { - shadowColor = aForegroundColor; + shadowColor = aParams.foregroundColor; decorationOverrideColor = nullptr; } - aCtx->Save(); - aCtx->NewPath(); - aCtx->SetColor(Color::FromABGR(shadowColor)); + aParams.context->Save(); + aParams.context->SetColor(Color::FromABGR(shadowColor)); // Draw the text onto our alpha-only surface to capture the alpha values. // Remember that the box blur context has a device offset on it, so we don't need to @@ -5942,58 +6002,57 @@ nsTextFrame::PaintOneShadow(Range aRange, nsTextPaintStyle textPaintStyle(this); DrawTextParams params(shadowContext); params.advanceWidth = &advanceWidth; - params.dirtyRect = aDirtyRect; - params.framePt = aFramePt + shadowOffset; - params.provider = aProvider; + params.dirtyRect = aParams.dirtyRect; + params.framePt = aParams.framePt + shadowOffset; + params.provider = aParams.provider; params.textStyle = &textPaintStyle; - params.textColor = aCtx == shadowContext ? shadowColor : NS_RGB(0, 0, 0); - params.clipEdges = &aClipEdges; + params.textColor = + aParams.context == shadowContext ? shadowColor : NS_RGB(0, 0, 0); + params.clipEdges = aParams.clipEdges; params.drawSoftHyphen = (GetStateBits() & TEXT_HYPHEN_BREAK) != 0; params.decorationOverrideColor = decorationOverrideColor; - DrawText(aRange, aTextBaselinePt + shadowOffset, params); + DrawText(aParams.range, aParams.textBaselinePt + shadowOffset, params); contextBoxBlur.DoPaint(); - aCtx->Restore(); + aParams.context->Restore(); } // Paints selection backgrounds and text in the correct colors. Also computes // aAllTypes, the union of all selection types that are applying to this text. bool -nsTextFrame::PaintTextWithSelectionColors(gfxContext* aCtx, - const gfxPoint& aFramePt, const gfxPoint& aTextBaselinePt, - const LayoutDeviceRect& aDirtyRect, - PropertyProvider& aProvider, - Range aContentRange, - nsTextPaintStyle& aTextPaintStyle, SelectionDetails* aDetails, - SelectionType* aAllTypes, - const nsCharClipDisplayItem::ClipEdges& aClipEdges, - nsTextFrame::DrawPathCallbacks* aCallbacks) +nsTextFrame::PaintTextWithSelectionColors( + const PaintTextSelectionParams& aParams, + SelectionDetails* aDetails, SelectionType* aAllTypes, + const nsCharClipDisplayItem::ClipEdges& aClipEdges) { + const gfxTextRun::Range& contentRange = aParams.contentRange; + // Figure out which selections control the colors to use for each character. AutoTArray prevailingSelectionsBuffer; SelectionDetails** prevailingSelections = - prevailingSelectionsBuffer.AppendElements(aContentRange.Length(), fallible); + prevailingSelectionsBuffer.AppendElements(contentRange.Length(), fallible); if (!prevailingSelections) { return false; } SelectionType allTypes = 0; - for (uint32_t i = 0; i < aContentRange.Length(); ++i) { + for (uint32_t i = 0; i < contentRange.Length(); ++i) { prevailingSelections[i] = nullptr; } SelectionDetails *sdptr = aDetails; bool anyBackgrounds = false; while (sdptr) { - int32_t start = std::max(0, sdptr->mStart - int32_t(aContentRange.start)); - int32_t end = std::min(int32_t(aContentRange.Length()), - sdptr->mEnd - int32_t(aContentRange.start)); + int32_t start = std::max(0, sdptr->mStart - int32_t(contentRange.start)); + int32_t end = std::min(int32_t(contentRange.Length()), + sdptr->mEnd - int32_t(contentRange.start)); SelectionType type = sdptr->mType; if (start < end) { allTypes |= type; // Ignore selections that don't set colors nscolor foreground, background; - if (GetSelectionTextColors(type, aTextPaintStyle, sdptr->mTextRangeStyle, + if (GetSelectionTextColors(type, *aParams.textPaintStyle, + sdptr->mTextRangeStyle, &foreground, &background)) { if (NS_GET_A(background) > 0) { anyBackgrounds = true; @@ -6018,78 +6077,87 @@ nsTextFrame::PaintTextWithSelectionColors(gfxContext* aCtx, bool vertical = mTextRun->IsVertical(); const gfxFloat startIOffset = vertical ? - aTextBaselinePt.y - aFramePt.y : aTextBaselinePt.x - aFramePt.x; + aParams.textBaselinePt.y - aParams.framePt.y : + aParams.textBaselinePt.x - aParams.framePt.x; gfxFloat iOffset, hyphenWidth; Range range; // in transformed string SelectionType type; TextRangeStyle rangeStyle; // Draw background colors if (anyBackgrounds) { - int32_t appUnitsPerDevPixel = aTextPaintStyle.PresContext()->AppUnitsPerDevPixel(); - SelectionIterator iterator(prevailingSelections, aContentRange, - aProvider, mTextRun, startIOffset); + int32_t appUnitsPerDevPixel = + aParams.textPaintStyle->PresContext()->AppUnitsPerDevPixel(); + SelectionIterator iterator(prevailingSelections, contentRange, + *aParams.provider, mTextRun, startIOffset); while (iterator.GetNextSegment(&iOffset, &range, &hyphenWidth, &type, &rangeStyle)) { nscolor foreground, background; - GetSelectionTextColors(type, aTextPaintStyle, rangeStyle, + GetSelectionTextColors(type, *aParams.textPaintStyle, rangeStyle, &foreground, &background); // Draw background color gfxFloat advance = hyphenWidth + - mTextRun->GetAdvanceWidth(range, &aProvider); + mTextRun->GetAdvanceWidth(range, aParams.provider); if (NS_GET_A(background) > 0) { nsRect bgRect; gfxFloat offs = iOffset - (mTextRun->IsInlineReversed() ? advance : 0); if (vertical) { - bgRect = nsRect(aFramePt.x, aFramePt.y + offs, + bgRect = nsRect(aParams.framePt.x, aParams.framePt.y + offs, GetSize().width, advance); } else { - bgRect = nsRect(aFramePt.x + offs, aFramePt.y, + bgRect = nsRect(aParams.framePt.x + offs, aParams.framePt.y, advance, GetSize().height); } PaintSelectionBackground( - *aCtx->GetDrawTarget(), background, aDirtyRect, + *aParams.context->GetDrawTarget(), background, aParams.dirtyRect, LayoutDeviceRect::FromAppUnits(bgRect, appUnitsPerDevPixel), - aCallbacks); + aParams.callbacks); } iterator.UpdateWithAdvance(advance); } } gfxFloat advance; - DrawTextParams params(aCtx); - params.dirtyRect = aDirtyRect; - params.framePt = aFramePt; - params.provider = &aProvider; - params.textStyle = &aTextPaintStyle; + DrawTextParams params(aParams.context); + params.dirtyRect = aParams.dirtyRect; + params.framePt = aParams.framePt; + params.provider = aParams.provider; + params.textStyle = aParams.textPaintStyle; params.clipEdges = &aClipEdges; params.advanceWidth = &advance; - params.callbacks = aCallbacks; - + params.callbacks = aParams.callbacks; + + PaintShadowParams shadowParams(aParams); + shadowParams.provider = aParams.provider; + shadowParams.clipEdges = &aClipEdges; + // Draw text const nsStyleText* textStyle = StyleText(); - SelectionIterator iterator(prevailingSelections, aContentRange, - aProvider, mTextRun, startIOffset); + SelectionIterator iterator(prevailingSelections, contentRange, + *aParams.provider, mTextRun, startIOffset); while (iterator.GetNextSegment(&iOffset, &range, &hyphenWidth, &type, &rangeStyle)) { nscolor foreground, background; - GetSelectionTextColors(type, aTextPaintStyle, rangeStyle, + GetSelectionTextColors(type, *aParams.textPaintStyle, rangeStyle, &foreground, &background); gfxPoint textBaselinePt = vertical ? - gfxPoint(aTextBaselinePt.x, aFramePt.y + iOffset) : - gfxPoint(aFramePt.x + iOffset, aTextBaselinePt.y); + gfxPoint(aParams.textBaselinePt.x, aParams.framePt.y + iOffset) : + gfxPoint(aParams.framePt.x + iOffset, aParams.textBaselinePt.y); // Determine what shadow, if any, to draw - either from textStyle // or from the ::-moz-selection pseudo-class if specified there nsCSSShadowArray* shadow = textStyle->GetTextShadow(); - GetSelectionTextShadow(this, type, aTextPaintStyle, &shadow); - if (shadow) { + GetSelectionTextShadow(this, type, *aParams.textPaintStyle, &shadow); + if (shadow && !aParams.callbacks) { nscoord startEdge = iOffset; if (mTextRun->IsInlineReversed()) { startEdge -= hyphenWidth + - mTextRun->GetAdvanceWidth(range, &aProvider); + mTextRun->GetAdvanceWidth(range, aParams.provider); } - PaintShadows(shadow, range, aDirtyRect, aFramePt, textBaselinePt, - startEdge, aProvider, foreground, aClipEdges, aCtx); + shadowParams.range = range; + shadowParams.textBaselinePt = textBaselinePt; + shadowParams.foregroundColor = foreground; + shadowParams.leftSideOffset = startEdge; + PaintShadows(shadow, shadowParams); } // Draw text segment @@ -6103,35 +6171,32 @@ nsTextFrame::PaintTextWithSelectionColors(gfxContext* aCtx, } void -nsTextFrame::PaintTextSelectionDecorations(gfxContext* aCtx, - const gfxPoint& aFramePt, - const gfxPoint& aTextBaselinePt, const LayoutDeviceRect& aDirtyRect, - PropertyProvider& aProvider, Range aContentRange, - nsTextPaintStyle& aTextPaintStyle, SelectionDetails* aDetails, - SelectionType aSelectionType, - nsTextFrame::DrawPathCallbacks* aCallbacks) +nsTextFrame::PaintTextSelectionDecorations( + const PaintTextSelectionParams& aParams, + SelectionDetails* aDetails, SelectionType aSelectionType) { // Hide text decorations if we're currently hiding @font-face fallback text - if (aProvider.GetFontGroup()->ShouldSkipDrawing()) + if (aParams.provider->GetFontGroup()->ShouldSkipDrawing()) return; // Figure out which characters will be decorated for this selection. + const gfxTextRun::Range& contentRange = aParams.contentRange; AutoTArray selectedCharsBuffer; SelectionDetails** selectedChars = - selectedCharsBuffer.AppendElements(aContentRange.Length(), fallible); + selectedCharsBuffer.AppendElements(contentRange.Length(), fallible); if (!selectedChars) { return; } - for (uint32_t i = 0; i < aContentRange.Length(); ++i) { + for (uint32_t i = 0; i < contentRange.Length(); ++i) { selectedChars[i] = nullptr; } SelectionDetails *sdptr = aDetails; while (sdptr) { if (sdptr->mType == aSelectionType) { - int32_t start = std::max(0, sdptr->mStart - int32_t(aContentRange.start)); - int32_t end = std::min(int32_t(aContentRange.Length()), - sdptr->mEnd - int32_t(aContentRange.start)); + int32_t start = std::max(0, sdptr->mStart - int32_t(contentRange.start)); + int32_t end = std::min(int32_t(contentRange.Length()), + sdptr->mEnd - int32_t(contentRange.start)); for (int32_t i = start; i < end; ++i) { selectedChars[i] = sdptr; } @@ -6139,7 +6204,7 @@ nsTextFrame::PaintTextSelectionDecorations(gfxContext* aCtx, sdptr = sdptr->mNext; } - gfxFont* firstFont = aProvider.GetFontGroup()->GetFirstValidFont(); + gfxFont* firstFont = aParams.provider->GetFontGroup()->GetFirstValidFont(); bool verticalRun = mTextRun->IsVertical(); bool rightUnderline = verticalRun && IsUnderlineRight(this); const uint8_t kDecoration = @@ -6153,22 +6218,23 @@ nsTextFrame::PaintTextSelectionDecorations(gfxContext* aCtx, // The potential adjustment from using gfxFontGroup::GetUnderlineOffset // is only valid for horizontal font metrics. decorationMetrics.underlineOffset = - aProvider.GetFontGroup()->GetUnderlineOffset(); + aParams.provider->GetFontGroup()->GetUnderlineOffset(); } - gfxFloat startIOffset = - verticalRun ? aTextBaselinePt.y - aFramePt.y : aTextBaselinePt.x - aFramePt.x; - SelectionIterator iterator(selectedChars, aContentRange, - aProvider, mTextRun, startIOffset); + gfxFloat startIOffset = verticalRun ? + aParams.textBaselinePt.y - aParams.framePt.y : + aParams.textBaselinePt.x - aParams.framePt.x; + SelectionIterator iterator(selectedChars, contentRange, + *aParams.provider, mTextRun, startIOffset); gfxFloat iOffset, hyphenWidth; Range range; - int32_t app = aTextPaintStyle.PresContext()->AppUnitsPerDevPixel(); + int32_t app = aParams.textPaintStyle->PresContext()->AppUnitsPerDevPixel(); // XXX aTextBaselinePt is in AppUnits, shouldn't it be nsFloatPoint? - gfxPoint pt; + Point pt; if (verticalRun) { - pt.x = (aTextBaselinePt.x - mAscent) / app; + pt.x = (aParams.textBaselinePt.x - mAscent) / app; } else { - pt.y = (aTextBaselinePt.y - mAscent) / app; + pt.y = (aParams.textBaselinePt.y - mAscent) / app; } gfxFloat decorationOffsetDir = mTextRun->IsSidewaysLeft() ? -1.0 : 1.0; SelectionType type; @@ -6176,37 +6242,31 @@ nsTextFrame::PaintTextSelectionDecorations(gfxContext* aCtx, while (iterator.GetNextSegment(&iOffset, &range, &hyphenWidth, &type, &selectedStyle)) { gfxFloat advance = hyphenWidth + - mTextRun->GetAdvanceWidth(range, &aProvider); + mTextRun->GetAdvanceWidth(range, aParams.provider); if (type == aSelectionType) { if (verticalRun) { - pt.y = (aFramePt.y + iOffset - + pt.y = (aParams.framePt.y + iOffset - (mTextRun->IsInlineReversed() ? advance : 0)) / app; } else { - pt.x = (aFramePt.x + iOffset - + pt.x = (aParams.framePt.x + iOffset - (mTextRun->IsInlineReversed() ? advance : 0)) / app; } gfxFloat width = Abs(advance) / app; - gfxFloat xInFrame = pt.x - (aFramePt.x / app); - DrawSelectionDecorations(aCtx, aDirtyRect, aSelectionType, - aTextPaintStyle, selectedStyle, pt, xInFrame, - width, mAscent / app, decorationMetrics, - aCallbacks, verticalRun, decorationOffsetDir, - kDecoration); + gfxFloat xInFrame = pt.x - (aParams.framePt.x / app); + DrawSelectionDecorations( + aParams.context, aParams.dirtyRect, aSelectionType, + *aParams.textPaintStyle, selectedStyle, pt, xInFrame, + width, mAscent / app, decorationMetrics, aParams.callbacks, + verticalRun, decorationOffsetDir, kDecoration); } iterator.UpdateWithAdvance(advance); } } bool -nsTextFrame::PaintTextWithSelection(gfxContext* aCtx, - const gfxPoint& aFramePt, - const gfxPoint& aTextBaselinePt, const LayoutDeviceRect& aDirtyRect, - PropertyProvider& aProvider, - Range aContentRange, - nsTextPaintStyle& aTextPaintStyle, - const nsCharClipDisplayItem::ClipEdges& aClipEdges, - gfxTextContextPaint* aContextPaint, - nsTextFrame::DrawPathCallbacks* aCallbacks) +nsTextFrame::PaintTextWithSelection( + const PaintTextSelectionParams& aParams, + const nsCharClipDisplayItem::ClipEdges& aClipEdges) { NS_ASSERTION(GetContent()->IsSelectionDescendant(), "wrong paint path"); @@ -6216,10 +6276,7 @@ nsTextFrame::PaintTextWithSelection(gfxContext* aCtx, } SelectionType allTypes; - if (!PaintTextWithSelectionColors(aCtx, aFramePt, aTextBaselinePt, aDirtyRect, - aProvider, aContentRange, - aTextPaintStyle, details, &allTypes, - aClipEdges, aCallbacks)) { + if (!PaintTextWithSelectionColors(aParams, details, &allTypes, aClipEdges)) { DestroySelectionDetails(details); return false; } @@ -6235,9 +6292,7 @@ nsTextFrame::PaintTextWithSelection(gfxContext* aCtx, // There is some selection of this type. Try to paint its decorations // (there might not be any for this type but that's OK, // PaintTextSelectionDecorations will exit early). - PaintTextSelectionDecorations(aCtx, aFramePt, aTextBaselinePt, aDirtyRect, - aProvider, aContentRange, aTextPaintStyle, - details, type, aCallbacks); + PaintTextSelectionDecorations(aParams, details, type); } } @@ -6248,7 +6303,7 @@ nsTextFrame::PaintTextWithSelection(gfxContext* aCtx, void nsTextFrame::DrawEmphasisMarks(gfxContext* aContext, WritingMode aWM, const gfxPoint& aTextBaselinePt, - Range aRange, + const gfxPoint& aFramePt, Range aRange, const nscolor* aDecorationOverrideColor, PropertyProvider* aProvider) { @@ -6257,10 +6312,22 @@ nsTextFrame::DrawEmphasisMarks(gfxContext* aContext, WritingMode aWM, return; } + bool isTextCombined = StyleContext()->IsTextCombined(); nscolor color = aDecorationOverrideColor ? *aDecorationOverrideColor : nsLayoutUtils::GetColor(this, eCSSProperty_text_emphasis_color); aContext->SetColor(Color::FromABGR(color)); - gfxPoint pt(aTextBaselinePt); + gfxPoint pt; + if (!isTextCombined) { + pt = aTextBaselinePt; + } else { + MOZ_ASSERT(aWM.IsVertical()); + pt = aFramePt; + if (aWM.IsVerticalRL()) { + pt.x += GetSize().width - GetLogicalBaseline(aWM); + } else { + pt.x += GetLogicalBaseline(aWM); + } + } if (!aWM.IsVertical()) { pt.y += info->baselineOffset; } else { @@ -6270,8 +6337,14 @@ nsTextFrame::DrawEmphasisMarks(gfxContext* aContext, WritingMode aWM, pt.x += info->baselineOffset; } } - mTextRun->DrawEmphasisMarks(aContext, info->textRun, info->advance, - pt, aRange, aProvider); + if (!isTextCombined) { + mTextRun->DrawEmphasisMarks(aContext, info->textRun.get(), info->advance, + pt, aRange, aProvider); + } else { + pt.y += (GetSize().height - info->advance) / 2; + info->textRun->Draw(Range(info->textRun.get()), pt, + gfxTextRun::DrawParams(aContext)); + } } nscolor @@ -6448,30 +6521,23 @@ nsTextFrame::MeasureCharClippedText(PropertyProvider& aProvider, void nsTextFrame::PaintShadows(nsCSSShadowArray* aShadow, - Range aRange, - const LayoutDeviceRect& aDirtyRect, - const gfxPoint& aFramePt, - const gfxPoint& aTextBaselinePt, - nscoord aLeftEdgeOffset, - PropertyProvider& aProvider, - nscolor aForegroundColor, - const nsCharClipDisplayItem::ClipEdges& aClipEdges, - gfxContext* aCtx) + const PaintShadowParams& aParams) { if (!aShadow) { return; } gfxTextRun::Metrics shadowMetrics = - mTextRun->MeasureText(aRange, gfxFont::LOOSE_INK_EXTENTS, - nullptr, &aProvider); + mTextRun->MeasureText(aParams.range, gfxFont::LOOSE_INK_EXTENTS, + nullptr, aParams.provider); if (GetWritingMode().IsLineInverted()) { Swap(shadowMetrics.mAscent, shadowMetrics.mDescent); shadowMetrics.mBoundingBox.y = -shadowMetrics.mBoundingBox.YMost(); } if (GetStateBits() & TEXT_HYPHEN_BREAK) { AddHyphenToMetrics(this, mTextRun, &shadowMetrics, - gfxFont::LOOSE_INK_EXTENTS, aCtx->GetDrawTarget()); + gfxFont::LOOSE_INK_EXTENTS, + aParams.context->GetDrawTarget()); } // Add bounds of text decorations gfxRect decorationRect(0, -shadowMetrics.mAscent, @@ -6498,25 +6564,52 @@ nsTextFrame::PaintShadows(nsCSSShadowArray* aShadow, } for (uint32_t i = aShadow->Length(); i > 0; --i) { - PaintOneShadow(aRange, aShadow->ShadowAt(i - 1), &aProvider, - aDirtyRect, aFramePt, aTextBaselinePt, aCtx, - aForegroundColor, aClipEdges, - aLeftEdgeOffset, - shadowMetrics.mBoundingBox, - blurFlags); + PaintOneShadow(aParams, aShadow->ShadowAt(i - 1), + shadowMetrics.mBoundingBox, blurFlags); } } +static bool +ShouldDrawSelection(const nsIFrame* aFrame, + const nsTextFrame::PaintTextParams& aParams) +{ + // Normal text-with-selection rendering sequence is: + // * Paint background > Paint text-selection-color > Paint text + // When we have an parent frame with background-clip-text style, rendering + // sequence changes to: + // * Paint text-selection-color > Paint background > Paint text + // + // If there is a parent frame has background-clip:text style, + // text-selection-color should be drawn with the background of that parent + // frame, so we should not draw it again while painting text frames. + // + // "aParams.callbacks != nullptr": it means we are currently painting + // background. We should paint text-selection-color. + // "aParams.callbacks == nullptr": it means we are currently painting text + // frame itself. We should not paint text-selection-color. + if (!aFrame || aParams.callbacks) { + return true; + } + + const nsStyleBackground* bg = aFrame->StyleContext()->StyleBackground(); + const nsStyleImageLayers& layers = bg->mImage; + NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, layers) { + if (layers.mLayers[i].mClip == NS_STYLE_IMAGELAYER_CLIP_TEXT) { + return false; + } + } + + return ShouldDrawSelection(aFrame->GetParent(), aParams); +} + void -nsTextFrame::PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt, - const LayoutDeviceRect& aDirtyRect, +nsTextFrame::PaintText(const PaintTextParams& aParams, const nsCharClipDisplayItem& aItem, - gfxTextContextPaint* aContextPaint, - nsTextFrame::DrawPathCallbacks* aCallbacks, float aOpacity /* = 1.0f */) { - // Don't pass in aRenderingContext here, because we need a *reference* - // context and aRenderingContext might have some transform in it + // Don't pass in the rendering context here, because we need a + // *reference* context and rendering context might have some transform + // in it // XXX get the block and line passed to us somehow! This is slow! gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated); if (!mTextRun) @@ -6530,27 +6623,28 @@ nsTextFrame::PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt, // which should include trailing spaces if present (bug 1146754). provider.InitializeForDisplay(!aItem.mIsFrameSelected.value()); - gfxContext* ctx = aRenderingContext->ThebesContext(); const bool reversed = mTextRun->IsInlineReversed(); const bool verticalRun = mTextRun->IsVertical(); WritingMode wm = GetWritingMode(); - const nscoord frameWidth = GetSize().width; - gfxPoint framePt(aPt.x, aPt.y); + const gfxFloat frameWidth = GetSize().width; + const gfxFloat frameHeight = GetSize().height; gfxPoint textBaselinePt; if (verticalRun) { if (wm.IsVerticalLR()) { - textBaselinePt.x = - nsLayoutUtils::GetSnappedBaselineX(this, ctx, aPt.x, mAscent); + textBaselinePt.x = nsLayoutUtils::GetSnappedBaselineX( + this, aParams.context, nscoord(aParams.framePt.x), mAscent); } else { - textBaselinePt.x = - nsLayoutUtils::GetSnappedBaselineX(this, ctx, aPt.x + frameWidth, - -mAscent); + textBaselinePt.x = nsLayoutUtils::GetSnappedBaselineX( + this, aParams.context, nscoord(aParams.framePt.x) + frameWidth, + -mAscent); } - textBaselinePt.y = reversed ? aPt.y + GetSize().height : aPt.y; + textBaselinePt.y = reversed ? aParams.framePt.y + frameHeight + : aParams.framePt.y; } else { textBaselinePt = - gfxPoint(reversed ? gfxFloat(aPt.x + frameWidth) : framePt.x, - nsLayoutUtils::GetSnappedBaselineY(this, ctx, aPt.y, mAscent)); + gfxPoint(reversed ? aParams.framePt.x + frameWidth : aParams.framePt.x, + nsLayoutUtils::GetSnappedBaselineY( + this, aParams.context, aParams.framePt.y, mAscent)); } Range range = ComputeTransformedRange(provider); uint32_t startOffset = range.start; @@ -6568,18 +6662,22 @@ nsTextFrame::PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt, nsCharClipDisplayItem::ClipEdges clipEdges(aItem, snappedStartEdge, snappedEndEdge); nsTextPaintStyle textPaintStyle(this); - textPaintStyle.SetResolveColors(!aCallbacks); + textPaintStyle.SetResolveColors(!aParams.callbacks); // Fork off to the (slower) paint-with-selection path if necessary. - if (aItem.mIsFrameSelected.value()) { + if (aItem.mIsFrameSelected.value() && + ShouldDrawSelection(this->GetParent(), aParams)) { MOZ_ASSERT(aOpacity == 1.0f, "We don't support opacity with selections!"); gfxSkipCharsIterator tmp(provider.GetStart()); Range contentRange( uint32_t(tmp.ConvertSkippedToOriginal(startOffset)), uint32_t(tmp.ConvertSkippedToOriginal(startOffset + maxLength))); - if (PaintTextWithSelection(ctx, framePt, textBaselinePt, aDirtyRect, - provider, contentRange, textPaintStyle, - clipEdges, aContextPaint, aCallbacks)) { + PaintTextSelectionParams params(aParams); + params.textBaselinePt = textBaselinePt; + params.provider = &provider; + params.contentRange = contentRange; + params.textPaintStyle = &textPaintStyle; + if (PaintTextWithSelection(params, clipEdges)) { return; } } @@ -6592,25 +6690,30 @@ nsTextFrame::PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt, } range = Range(startOffset, startOffset + maxLength); - if (!aCallbacks) { + if (!aParams.callbacks) { const nsStyleText* textStyle = StyleText(); - PaintShadows( - textStyle->mTextShadow, range, aDirtyRect, framePt, textBaselinePt, - snappedStartEdge, provider, foregroundColor, clipEdges, ctx); + PaintShadowParams shadowParams(aParams); + shadowParams.range = range; + shadowParams.textBaselinePt = textBaselinePt; + shadowParams.leftSideOffset = snappedStartEdge; + shadowParams.provider = &provider; + shadowParams.foregroundColor = foregroundColor; + shadowParams.clipEdges = &clipEdges; + PaintShadows(textStyle->mTextShadow, shadowParams); } gfxFloat advanceWidth; - DrawTextParams params(ctx); - params.dirtyRect = aDirtyRect; - params.framePt = framePt; + DrawTextParams params(aParams.context); + params.dirtyRect = aParams.dirtyRect; + params.framePt = aParams.framePt; params.provider = &provider; params.advanceWidth = &advanceWidth; params.textStyle = &textPaintStyle; params.textColor = foregroundColor; params.clipEdges = &clipEdges; params.drawSoftHyphen = (GetStateBits() & TEXT_HYPHEN_BREAK) != 0; - params.contextPaint = aContextPaint; - params.callbacks = aCallbacks; + params.contextPaint = aParams.contextPaint; + params.callbacks = aParams.callbacks; DrawText(range, textBaselinePt, params); } @@ -6647,8 +6750,9 @@ nsTextFrame::DrawTextRun(Range aRange, const gfxPoint& aTextBaselinePt, if (aParams.drawSoftHyphen) { // Don't use ctx as the context, because we need a reference context here, // ctx may be transformed. - nsAutoPtr hyphenTextRun(GetHyphenTextRun(mTextRun, nullptr, this)); - if (hyphenTextRun.get()) { + UniquePtr hyphenTextRun = + GetHyphenTextRun(mTextRun, nullptr, this); + if (hyphenTextRun) { // For right-to-left text runs, the soft-hyphen is positioned at the left // of the text, minus its own width gfxFloat hyphenBaselineX = aTextBaselinePt.x + @@ -6672,8 +6776,18 @@ nsTextFrame::DrawTextRunAndDecorations(Range aRange, { const gfxFloat app = aParams.textStyle->PresContext()->AppUnitsPerDevPixel(); + // Writing mode of parent frame is used because the text frame may + // be orthogonal to its parent when text-combine-upright is used or + // its parent has "display: contents", and in those cases, we want + // to draw the decoration lines according to parents' direction + // rather than ours. + const WritingMode wm = GetParent()->GetWritingMode(); + bool verticalDec = wm.IsVertical(); bool verticalRun = mTextRun->IsVertical(); - bool useVerticalMetrics = verticalRun && mTextRun->UseCenterBaseline(); + // If the text run and the decoration is orthogonal, we choose the + // metrics for decoration so that decoration line won't be broken. + bool useVerticalMetrics = verticalDec != verticalRun + ? verticalDec : verticalRun && mTextRun->UseCenterBaseline(); // XXX aFramePt is in AppUnits, shouldn't it be nsFloatPoint? nscoord x = NSToCoordRound(aParams.framePt.x); @@ -6682,30 +6796,22 @@ nsTextFrame::DrawTextRunAndDecorations(Range aRange, // 'measure' here is textrun-relative, so for a horizontal run it's the // width, while for a vertical run it's the height of the decoration const nsSize frameSize = GetSize(); - nscoord measure = verticalRun ? frameSize.height : frameSize.width; + nscoord measure = verticalDec ? frameSize.height : frameSize.width; - if (verticalRun) { + if (verticalDec) { aParams.clipEdges->Intersect(&y, &measure); } else { aParams.clipEdges->Intersect(&x, &measure); } - // decPt is the physical point where the decoration is to be drawn, - // relative to the frame; one of its coordinates will be updated below. - gfxPoint decPt(x / app, y / app); - gfxFloat& bCoord = verticalRun ? decPt.x : decPt.y; - // decSize is a textrun-relative size, so its 'width' field is actually // the run-relative measure, and 'height' will be the line thickness - gfxSize decSize(measure / app, 0); - gfxFloat ascent = gfxFloat(mAscent) / app; - + gfxFloat ascent = gfxFloat(GetLogicalBaseline(wm)) / app; // The starting edge of the frame in block direction - gfxFloat frameBStart = verticalRun ? aParams.framePt.x : aParams.framePt.y; + gfxFloat frameBStart = verticalDec ? aParams.framePt.x : aParams.framePt.y; - // In vertical-rl mode, block coordinates are measured from the right, - // so we need to adjust here. - const WritingMode wm = GetWritingMode(); + // In vertical-rl mode, block coordinates are measured from the + // right, so we need to adjust here. if (wm.IsVerticalRL()) { frameBStart += frameSize.width; ascent = -ascent; @@ -6718,80 +6824,91 @@ nsTextFrame::DrawTextRunAndDecorations(Range aRange, // so we will multiply the values from metrics by this factor. gfxFloat decorationOffsetDir = mTextRun->IsSidewaysLeft() ? -1.0 : 1.0; - // Underlines - for (uint32_t i = aDecorations.mUnderlines.Length(); i-- > 0; ) { - const LineDecoration& dec = aDecorations.mUnderlines[i]; + PaintDecorationLineParams params; + params.context = aParams.context; + params.dirtyRect = aParams.dirtyRect; + params.overrideColor = aParams.decorationOverrideColor; + params.callbacks = aParams.callbacks; + // pt is the physical point where the decoration is to be drawn, + // relative to the frame; one of its coordinates will be updated below. + params.pt = Point(x / app, y / app); + Float& bCoord = verticalDec ? params.pt.x : params.pt.y; + params.lineSize = Size(measure / app, 0); + params.ascent = ascent; + params.vertical = verticalDec; + + // The matrix of the context may have been altered for text-combine- + // upright. However, we want to draw decoration lines unscaled, thus + // we need to revert the scaling here. + gfxContextMatrixAutoSaveRestore scaledRestorer; + if (StyleContext()->IsTextCombined()) { + float scaleFactor = GetTextCombineScaleFactor(this); + if (scaleFactor != 1.0f) { + scaledRestorer.SetContext(aParams.context); + gfxMatrix unscaled = aParams.context->CurrentMatrix(); + gfxPoint pt(x / app, y / app); + unscaled.Translate(pt).Scale(1.0f / scaleFactor, 1.0f).Translate(-pt); + aParams.context->SetMatrix(unscaled); + } + } + + typedef gfxFont::Metrics Metrics; + auto paintDecorationLine = [&](const LineDecoration& dec, + gfxFloat Metrics::* lineSize, + gfxFloat Metrics::* lineOffset) { if (dec.mStyle == NS_STYLE_TEXT_DECORATION_STYLE_NONE) { - continue; + return; } float inflation = GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize); - const gfxFont::Metrics metrics = + const Metrics metrics = GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation), useVerticalMetrics); - decSize.height = metrics.underlineSize; + params.lineSize.height = metrics.*lineSize; bCoord = (frameBStart - dec.mBaselineOffset) / app; - PaintDecorationLine(aParams.context, aParams.dirtyRect, dec.mColor, - aParams.decorationOverrideColor, decPt, 0.0, decSize, ascent, - decorationOffsetDir * metrics.underlineOffset, - NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, - dec.mStyle, eNormalDecoration, aParams.callbacks, verticalRun); + params.color = dec.mColor; + params.offset = decorationOffsetDir * metrics.*lineOffset; + params.style = dec.mStyle; + PaintDecorationLine(params); + }; + + // Underlines + params.decoration = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE; + for (const LineDecoration& dec : Reversed(aDecorations.mUnderlines)) { + paintDecorationLine(dec, &Metrics::underlineSize, + &Metrics::underlineOffset); } // Overlines - for (uint32_t i = aDecorations.mOverlines.Length(); i-- > 0; ) { - const LineDecoration& dec = aDecorations.mOverlines[i]; - if (dec.mStyle == NS_STYLE_TEXT_DECORATION_STYLE_NONE) { - continue; - } - - float inflation = - GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize); - const gfxFont::Metrics metrics = - GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation), - useVerticalMetrics); - - decSize.height = metrics.underlineSize; - bCoord = (frameBStart - dec.mBaselineOffset) / app; - - PaintDecorationLine(aParams.context, aParams.dirtyRect, dec.mColor, - aParams.decorationOverrideColor, decPt, 0.0, decSize, ascent, - decorationOffsetDir * metrics.maxAscent, - NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, dec.mStyle, - eNormalDecoration, aParams.callbacks, verticalRun); + params.decoration = NS_STYLE_TEXT_DECORATION_LINE_OVERLINE; + for (const LineDecoration& dec : Reversed(aDecorations.mOverlines)) { + paintDecorationLine(dec, &Metrics::underlineSize, &Metrics::maxAscent); } - // CSS 2.1 mandates that text be painted after over/underlines, and *then* - // line-throughs - DrawTextRun(aRange, aTextBaselinePt, aParams); + { + gfxContextMatrixAutoSaveRestore unscaledRestorer; + if (scaledRestorer.HasMatrix()) { + unscaledRestorer.SetContext(aParams.context); + aParams.context->SetMatrix(scaledRestorer.Matrix()); + } + + // CSS 2.1 mandates that text be painted after over/underlines, + // and *then* line-throughs + DrawTextRun(aRange, aTextBaselinePt, aParams); + } // Emphasis marks - DrawEmphasisMarks(aParams.context, wm, aTextBaselinePt, aRange, + DrawEmphasisMarks(aParams.context, wm, + aTextBaselinePt, aParams.framePt, aRange, aParams.decorationOverrideColor, aParams.provider); // Line-throughs - for (uint32_t i = aDecorations.mStrikes.Length(); i-- > 0; ) { - const LineDecoration& dec = aDecorations.mStrikes[i]; - if (dec.mStyle == NS_STYLE_TEXT_DECORATION_STYLE_NONE) { - continue; - } - - float inflation = - GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize); - const gfxFont::Metrics metrics = - GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation), - useVerticalMetrics); - - decSize.height = metrics.strikeoutSize; - bCoord = (frameBStart - dec.mBaselineOffset) / app; - - PaintDecorationLine(aParams.context, aParams.dirtyRect, dec.mColor, - aParams.decorationOverrideColor, decPt, 0.0, decSize, ascent, - decorationOffsetDir * metrics.strikeoutOffset, - NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH, - dec.mStyle, eNormalDecoration, aParams.callbacks, verticalRun); + params.decoration = NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH; + for (const LineDecoration& dec : Reversed(aDecorations.mStrikes)) { + paintDecorationLine(dec, &Metrics::strikeoutSize, + &Metrics::strikeoutOffset); } } @@ -6912,6 +7029,9 @@ nsTextFrame::GetCharacterOffsetAtFramePointInternal(nsPoint aPoint, gfxFloat width = mTextRun->IsVertical() ? (mTextRun->IsInlineReversed() ? mRect.height - aPoint.y : aPoint.y) : (mTextRun->IsInlineReversed() ? mRect.width - aPoint.x : aPoint.x); + if (StyleContext()->IsTextCombined()) { + width /= GetTextCombineScaleFactor(this); + } gfxFloat fitWidth; Range skippedRange = ComputeTransformedRange(provider); @@ -7005,23 +7125,26 @@ nsTextFrame::CombineSelectionUnderlineRect(nsPresContext* aPresContext, const gfxFont::Metrics& metrics = firstFont->GetMetrics(useVerticalMetrics ? gfxFont::eVertical : gfxFont::eHorizontal); - gfxFloat underlineOffset = fontGroup->GetUnderlineOffset(); - gfxFloat ascent = aPresContext->AppUnitsToGfxUnits(mAscent); - gfxFloat descentLimit = + + nsCSSRendering::DecorationRectParams params; + params.ascent = aPresContext->AppUnitsToGfxUnits(mAscent); + params.offset = fontGroup->GetUnderlineOffset(); + params.descentLimit = ComputeDescentLimitForSelectionUnderline(aPresContext, metrics); + params.vertical = verticalRun; SelectionDetails *details = GetSelectionDetails(); for (SelectionDetails *sd = details; sd; sd = sd->mNext) { if (sd->mStart == sd->mEnd || !(sd->mType & SelectionTypesWithDecorations)) continue; - uint8_t style; float relativeSize; int32_t index = nsTextPaintStyle::GetUnderlineStyleIndexForSelectionType(sd->mType); if (sd->mType == nsISelectionController::SELECTION_SPELLCHECK) { if (!nsTextPaintStyle::GetSelectionUnderline(aPresContext, index, nullptr, - &relativeSize, &style)) { + &relativeSize, + ¶ms.style)) { continue; } } else { @@ -7032,24 +7155,23 @@ nsTextFrame::CombineSelectionUnderlineRect(nsPresContext* aPresContext, rangeStyle.mLineStyle == TextRangeStyle::LINESTYLE_NONE) { continue; } - style = rangeStyle.mLineStyle; + params.style = rangeStyle.mLineStyle; relativeSize = rangeStyle.mIsBoldLine ? 2.0f : 1.0f; } else if (!nsTextPaintStyle::GetSelectionUnderline(aPresContext, index, nullptr, &relativeSize, - &style)) { + ¶ms.style)) { continue; } } nsRect decorationArea; - gfxSize size(aPresContext->AppUnitsToGfxUnits(aRect.width), - ComputeSelectionUnderlineHeight(aPresContext, - metrics, sd->mType)); + + params.lineSize = Size(aPresContext->AppUnitsToGfxUnits(aRect.width), + ComputeSelectionUnderlineHeight(aPresContext, + metrics, sd->mType)); relativeSize = std::max(relativeSize, 1.0f); - size.height *= relativeSize; + params.lineSize.height *= relativeSize; decorationArea = - nsCSSRendering::GetTextDecorationRect(aPresContext, size, - ascent, underlineOffset, NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, - style, verticalRun, descentLimit); + nsCSSRendering::GetTextDecorationRect(aPresContext, params); aRect.UnionRect(aRect, decorationArea); } DestroySelectionDetails(details); @@ -7171,6 +7293,9 @@ nsTextFrame::GetPointFromOffset(int32_t inOffset, } else { outPoint->x = iSize; } + if (StyleContext()->IsTextCombined()) { + outPoint->x *= GetTextCombineScaleFactor(this); + } } return NS_OK; @@ -7770,7 +7895,7 @@ FindStartAfterSkippingWhitespace(PropertyProvider* aProvider, gfxSkipCharsIterator* aIterator, uint32_t aFlowEndInTextRun) { - if (aData->skipWhitespace) { + if (aData->mSkipWhitespace) { while (aIterator->GetSkippedOffset() < aFlowEndInTextRun && IsTrimmableSpace(aProvider->GetFragment(), aIterator->GetOriginalOffset(), aTextStyle)) { aIterator->AdvanceOriginal(1); @@ -7820,7 +7945,7 @@ nsTextFrame::AddInlineMinISizeForFlow(nsRenderingContext *aRenderingContext, uint32_t flowEndInTextRun; gfxSkipCharsIterator iter = EnsureTextRun(aTextRunType, aRenderingContext->GetDrawTarget(), - aData->LineContainer(), aData->line, &flowEndInTextRun); + aData->LineContainer(), aData->mLine, &flowEndInTextRun); gfxTextRun *textRun = GetTextRun(aTextRunType); if (!textRun) return; @@ -7852,6 +7977,16 @@ nsTextFrame::AddInlineMinISizeForFlow(nsRenderingContext *aRenderingContext, uint32_t start = FindStartAfterSkippingWhitespace(&provider, aData, textStyle, &iter, flowEndInTextRun); + // text-combine-upright frame is constantly 1em on inline-axis. + if (StyleContext()->IsTextCombined()) { + if (textRun->CanBreakLineBefore(start)) { + aData->OptionallyBreak(); + } + aData->mCurrentLine += provider.GetFontMetrics()->EmHeight(); + aData->mTrailingWhitespace = 0; + return; + } + AutoTArray hyphBuffer; bool *hyphBreakBefore = nullptr; if (hyphenating) { @@ -7886,34 +8021,34 @@ nsTextFrame::AddInlineMinISizeForFlow(nsRenderingContext *aRenderingContext, nscoord width = NSToCoordCeilClamped( textRun->GetAdvanceWidth(Range(wordStart, i), &provider)); width = std::max(0, width); - aData->currentLine = NSCoordSaturatingAdd(aData->currentLine, width); - aData->atStartOfLine = false; + aData->mCurrentLine = NSCoordSaturatingAdd(aData->mCurrentLine, width); + aData->mAtStartOfLine = false; if (collapseWhitespace) { uint32_t trimStart = GetEndOfTrimmedText(frag, textStyle, wordStart, i, &iter); if (trimStart == start) { // This is *all* trimmable whitespace, so whatever trailingWhitespace // we saw previously is still trailing... - aData->trailingWhitespace += width; + aData->mTrailingWhitespace += width; } else { // Some non-whitespace so the old trailingWhitespace is no longer trailing nscoord wsWidth = NSToCoordCeilClamped( textRun->GetAdvanceWidth(Range(trimStart, i), &provider)); - aData->trailingWhitespace = std::max(0, wsWidth); + aData->mTrailingWhitespace = std::max(0, wsWidth); } } else { - aData->trailingWhitespace = 0; + aData->mTrailingWhitespace = 0; } } if (preformattedTab) { PropertyProvider::Spacing spacing; provider.GetSpacing(Range(i, i + 1), &spacing); - aData->currentLine += nscoord(spacing.mBefore); + aData->mCurrentLine += nscoord(spacing.mBefore); gfxFloat afterTab = - AdvanceToNextTab(aData->currentLine, this, + AdvanceToNextTab(aData->mCurrentLine, this, textRun, &tabWidth); - aData->currentLine = nscoord(afterTab + spacing.mAfter); + aData->mCurrentLine = nscoord(afterTab + spacing.mAfter); wordStart = i + 1; } else if (i < flowEndInTextRun || (i == textRun->GetLength() && @@ -7932,7 +8067,7 @@ nsTextFrame::AddInlineMinISizeForFlow(nsRenderingContext *aRenderingContext, if (start < flowEndInTextRun) { // Check if we have collapsible whitespace at the end - aData->skipWhitespace = + aData->mSkipWhitespace = IsTrimmableSpace(provider.GetFragment(), iter.ConvertSkippedToOriginal(flowEndInTextRun - 1), textStyle); @@ -7972,7 +8107,7 @@ nsTextFrame::AddInlineMinISize(nsRenderingContext *aRenderingContext, aData->LineContainer() != (lc = FindLineContainer(f))) { NS_ASSERTION(f != this, "wrong InlineMinISizeData container" " for first continuation"); - aData->line = nullptr; + aData->mLine = nullptr; aData->SetLineContainer(lc); } @@ -7993,7 +8128,7 @@ nsTextFrame::AddInlinePrefISizeForFlow(nsRenderingContext *aRenderingContext, uint32_t flowEndInTextRun; gfxSkipCharsIterator iter = EnsureTextRun(aTextRunType, aRenderingContext->GetDrawTarget(), - aData->LineContainer(), aData->line, &flowEndInTextRun); + aData->LineContainer(), aData->mLine, &flowEndInTextRun); gfxTextRun *textRun = GetTextRun(aTextRunType); if (!textRun) return; @@ -8006,6 +8141,13 @@ nsTextFrame::AddInlinePrefISizeForFlow(nsRenderingContext *aRenderingContext, PropertyProvider provider(textRun, textStyle, frag, this, iter, INT32_MAX, nullptr, 0, aTextRunType); + // text-combine-upright frame is constantly 1em on inline-axis. + if (StyleContext()->IsTextCombined()) { + aData->mCurrentLine += provider.GetFontMetrics()->EmHeight(); + aData->mTrailingWhitespace = 0; + return; + } + bool collapseWhitespace = !textStyle->WhiteSpaceIsSignificant(); bool preformatNewlines = textStyle->NewlineIsSignificant(this); bool preformatTabs = textStyle->TabIsSignificant(); @@ -8039,33 +8181,33 @@ nsTextFrame::AddInlinePrefISizeForFlow(nsRenderingContext *aRenderingContext, nscoord width = NSToCoordCeilClamped( textRun->GetAdvanceWidth(Range(lineStart, i), &provider)); width = std::max(0, width); - aData->currentLine = NSCoordSaturatingAdd(aData->currentLine, width); + aData->mCurrentLine = NSCoordSaturatingAdd(aData->mCurrentLine, width); if (collapseWhitespace) { uint32_t trimStart = GetEndOfTrimmedText(frag, textStyle, lineStart, i, &iter); if (trimStart == start) { // This is *all* trimmable whitespace, so whatever trailingWhitespace // we saw previously is still trailing... - aData->trailingWhitespace += width; + aData->mTrailingWhitespace += width; } else { // Some non-whitespace so the old trailingWhitespace is no longer trailing nscoord wsWidth = NSToCoordCeilClamped( textRun->GetAdvanceWidth(Range(trimStart, i), &provider)); - aData->trailingWhitespace = std::max(0, wsWidth); + aData->mTrailingWhitespace = std::max(0, wsWidth); } } else { - aData->trailingWhitespace = 0; + aData->mTrailingWhitespace = 0; } } if (preformattedTab) { PropertyProvider::Spacing spacing; provider.GetSpacing(Range(i, i + 1), &spacing); - aData->currentLine += nscoord(spacing.mBefore); + aData->mCurrentLine += nscoord(spacing.mBefore); gfxFloat afterTab = - AdvanceToNextTab(aData->currentLine, this, + AdvanceToNextTab(aData->mCurrentLine, this, textRun, &tabWidth); - aData->currentLine = nscoord(afterTab + spacing.mAfter); + aData->mCurrentLine = nscoord(afterTab + spacing.mAfter); lineStart = i + 1; } else if (preformattedNewline) { aData->ForceBreak(); @@ -8075,7 +8217,7 @@ nsTextFrame::AddInlinePrefISizeForFlow(nsRenderingContext *aRenderingContext, // Check if we have collapsible whitespace at the end if (start < flowEndInTextRun) { - aData->skipWhitespace = + aData->mSkipWhitespace = IsTrimmableSpace(provider.GetFragment(), iter.ConvertSkippedToOriginal(flowEndInTextRun - 1), textStyle); @@ -8111,7 +8253,7 @@ nsTextFrame::AddInlinePrefISize(nsRenderingContext *aRenderingContext, aData->LineContainer() != (lc = FindLineContainer(f))) { NS_ASSERTION(f != this, "wrong InlinePrefISizeData container" " for first continuation"); - aData->line = nullptr; + aData->mLine = nullptr; aData->SetLineContainer(lc); } @@ -8743,6 +8885,11 @@ nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth, bool usedHyphenation; gfxFloat trimmedWidth = 0; gfxFloat availWidth = aAvailableWidth; + if (StyleContext()->IsTextCombined()) { + // If text-combine-upright is 'all', we would compress whatever long + // text into ~1em width, so there is no limited on the avail width. + availWidth = std::numeric_limits::infinity(); + } bool canTrimTrailingWhitespace = !textStyle->WhiteSpaceIsSignificant() || (GetStateBits() & TEXT_IS_IN_TOKEN_MATHML); gfxBreakPriority breakPriority = aLineLayout.LastOptionalBreakPriority(); @@ -8850,7 +8997,7 @@ nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth, if (!brokeText && lastBreak >= 0) { // Since everything fit and no break was forced, // record the last break opportunity - NS_ASSERTION(textMetrics.mAdvanceWidth - trimmableWidth <= aAvailableWidth, + NS_ASSERTION(textMetrics.mAdvanceWidth - trimmableWidth <= availWidth, "If the text doesn't fit, and we have a break opportunity, why didn't MeasureText use it?"); MOZ_ASSERT(lastBreak >= offset, "Strange break position"); aLineLayout.NotifyOptionalBreakPosition(this, lastBreak - offset, @@ -8898,11 +9045,31 @@ nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth, nscoord descent = std::max(NSToCoordCeil(textMetrics.mDescent), fontDescent); finalSize.BSize(wm) = aMetrics.BlockStartAscent() + descent; } + if (StyleContext()->IsTextCombined()) { + nsFontMetrics* fm = provider.GetFontMetrics(); + gfxFloat width = finalSize.ISize(wm); + gfxFloat em = fm->EmHeight(); + // Compress the characters in horizontal axis if necessary. + if (width <= em) { + Properties().Remove(TextCombineScaleFactorProperty()); + } else { + Properties().Set(TextCombineScaleFactorProperty(), em / width); + finalSize.ISize(wm) = em; + } + // Make the characters be in an 1em square. + if (finalSize.BSize(wm) != em) { + aMetrics.SetBlockStartAscent(aMetrics.BlockStartAscent() + + (em - finalSize.BSize(wm)) / 2); + finalSize.BSize(wm) = em; + } + } aMetrics.SetSize(wm, finalSize); NS_ASSERTION(aMetrics.BlockStartAscent() >= 0, "Negative ascent???"); - NS_ASSERTION(aMetrics.BSize(aMetrics.GetWritingMode()) - + NS_ASSERTION((StyleContext()->IsTextCombined() + ? aMetrics.ISize(aMetrics.GetWritingMode()) + : aMetrics.BSize(aMetrics.GetWritingMode())) - aMetrics.BlockStartAscent() >= 0, "Negative descent???"); @@ -9516,9 +9683,23 @@ nsTextFrame::IsAtEndOfLine() const } nscoord -nsTextFrame::GetLogicalBaseline(WritingMode aWritingMode ) const +nsTextFrame::GetLogicalBaseline(WritingMode aWM) const { - return mAscent; + if (!aWM.IsOrthogonalTo(GetWritingMode())) { + return mAscent; + } + + // When the text frame has a writing mode orthogonal to the desired + // writing mode, return a baseline coincides its parent frame. + nsIFrame* parent = GetParent(); + nsPoint position = GetNormalPosition(); + nscoord parentAscent = parent->GetLogicalBaseline(aWM); + if (aWM.IsVerticalRL()) { + nscoord parentDescent = parent->GetSize().width - parentAscent; + nscoord descent = parentDescent - position.x; + return GetSize().width - descent; + } + return parentAscent - (aWM.IsVertical() ? position.x : position.y); } bool @@ -9586,3 +9767,13 @@ nsTextFrame::GetJustificationAssignment() const result.mGapsAtEnd = encoded & 0xFF; return result; } + +uint32_t +nsTextFrame::CountGraphemeClusters() const +{ + const nsTextFragment* frag = GetContent()->GetText(); + MOZ_ASSERT(frag, "Text frame must have text fragment"); + nsAutoString content; + frag->AppendTo(content, GetContentOffset(), GetContentLength()); + return unicode::CountGraphemeClusters(content.Data(), content.Length()); +} diff --git a/layout/generic/nsTextFrame.h b/layout/generic/nsTextFrame.h index 8c70e09981..ccac000477 100644 --- a/layout/generic/nsTextFrame.h +++ b/layout/generic/nsTextFrame.h @@ -42,6 +42,7 @@ class nsTextFrame : public nsFrame { typedef mozilla::LayoutDeviceRect LayoutDeviceRect; typedef mozilla::TextRangeStyle TextRangeStyle; typedef mozilla::gfx::DrawTarget DrawTarget; + typedef mozilla::gfx::Point Point; typedef mozilla::gfx::Rect Rect; typedef mozilla::gfx::Size Size; typedef gfxTextRun::Range Range; @@ -214,7 +215,7 @@ public: virtual bool IsEmpty() override; virtual bool IsSelfEmpty() override { return IsEmpty(); } - virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const override; + nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const final; virtual bool HasSignificantTerminalNewline() const override; @@ -336,12 +337,12 @@ public: * Callbacks are invoked in the following order: * * NotifySelectionBackgroundNeedsFill? - * (NotifyBeforeDecorationLine NotifyDecorationLinePathEmitted)* + * PaintDecorationLine* * NotifyBeforeText * NotifyGlyphPathEmitted* * NotifyAfterText - * (NotifyBeforeDecorationLine NotifyDecorationLinePathEmitted)* - * (NotifyBeforeSelectionDecorationLine NotifySelectionDecorationLinePathEmitted)* + * PaintDecorationLine* + * PaintSelectionDecorationLine* * * The color of each part of the frame's text rendering is passed as an argument * to the NotifyBefore* callback for that part. The nscolor can take on one of @@ -365,7 +366,20 @@ public: */ virtual void NotifySelectionBackgroundNeedsFill(const Rect& aBackgroundRect, nscolor aColor, - DrawTarget& aDrawTarget) { } + DrawTarget& aDrawTarget); + + /** + * Called before (for under/over-line) or after (for line-through) the text + * is drawn to have a text decoration line drawn. + */ + virtual void PaintDecorationLine(Rect aPath, nscolor aColor) { } + + /** + * Called after selected text is drawn to have a decoration line drawn over + * the text. (All types of text decoration are drawn after the text when + * text is selected.) + */ + virtual void PaintSelectionDecorationLine(Rect aPath, nscolor aColor) { } /** * Called just before any paths have been emitted to the gfxContext @@ -379,18 +393,6 @@ public: */ virtual void NotifyAfterText() { } - /** - * Called just before a path corresponding to a text decoration line - * has been emitted to the gfxContext. - */ - virtual void NotifyBeforeDecorationLine(nscolor aColor) { } - - /** - * Called just after a path corresponding to a text decoration line - * has been emitted to the gfxContext. - */ - virtual void NotifyDecorationLinePathEmitted() { } - /** * Called just before a path corresponding to a selection decoration line * has been emitted to the gfxContext. @@ -402,6 +404,28 @@ public: * has been emitted to the gfxContext. */ virtual void NotifySelectionDecorationLinePathEmitted() { } + + virtual void NotifyGlyphPathEmitted() override {} + }; + + struct PaintTextParams + { + gfxContext* context; + gfxPoint framePt; + LayoutDeviceRect dirtyRect; + gfxTextContextPaint* contextPaint = nullptr; + DrawPathCallbacks* callbacks = nullptr; + explicit PaintTextParams(gfxContext* aContext) : context(aContext) {} + }; + + struct PaintTextSelectionParams : PaintTextParams + { + gfxPoint textBaselinePt; + PropertyProvider* provider = nullptr; + Range contentRange; + nsTextPaintStyle* textPaintStyle = nullptr; + explicit PaintTextSelectionParams(const PaintTextParams& aParams) + : PaintTextParams(aParams) {} }; struct DrawTextRunParams @@ -432,57 +456,32 @@ public: // to generate paths rather than paint the frame's text by passing a callback // object. The private DrawText() is what applies the text to a graphics // context. - void PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt, - const LayoutDeviceRect& aDirtyRect, + void PaintText(const PaintTextParams& aParams, const nsCharClipDisplayItem& aItem, - gfxTextContextPaint* aContextPaint = nullptr, - DrawPathCallbacks* aCallbacks = nullptr, float aOpacity = 1.0f); // helper: paint text frame when we're impacted by at least one selection. // Return false if the text was not painted and we should continue with // the fast path. - bool PaintTextWithSelection(gfxContext* aCtx, - const gfxPoint& aFramePt, - const gfxPoint& aTextBaselinePt, - const LayoutDeviceRect& aDirtyRect, - PropertyProvider& aProvider, - Range aRange, - nsTextPaintStyle& aTextPaintStyle, - const nsCharClipDisplayItem::ClipEdges& aClipEdges, - gfxTextContextPaint* aContextPaint, - DrawPathCallbacks* aCallbacks); + bool PaintTextWithSelection(const PaintTextSelectionParams& aParams, + const nsCharClipDisplayItem::ClipEdges& aClipEdges); // helper: paint text with foreground and background colors determined // by selection(s). Also computes a mask of all selection types applying to // our text, returned in aAllTypes. // Return false if the text was not painted and we should continue with // the fast path. - bool PaintTextWithSelectionColors(gfxContext* aCtx, - const gfxPoint& aFramePt, - const gfxPoint& aTextBaselinePt, - const LayoutDeviceRect& aDirtyRect, - PropertyProvider& aProvider, - Range aContentRange, - nsTextPaintStyle& aTextPaintStyle, + bool PaintTextWithSelectionColors(const PaintTextSelectionParams& aParams, SelectionDetails* aDetails, SelectionType* aAllTypes, - const nsCharClipDisplayItem::ClipEdges& aClipEdges, - DrawPathCallbacks* aCallbacks); + const nsCharClipDisplayItem::ClipEdges& aClipEdges); // helper: paint text decorations for text selected by aSelectionType - void PaintTextSelectionDecorations(gfxContext* aCtx, - const gfxPoint& aFramePt, - const gfxPoint& aTextBaselinePt, - const LayoutDeviceRect& aDirtyRect, - PropertyProvider& aProvider, - Range aContentRange, - nsTextPaintStyle& aTextPaintStyle, + void PaintTextSelectionDecorations(const PaintTextSelectionParams& aParams, SelectionDetails* aDetails, - SelectionType aSelectionType, - DrawPathCallbacks* aCallbacks); + SelectionType aSelectionType); void DrawEmphasisMarks(gfxContext* aContext, mozilla::WritingMode aWM, const gfxPoint& aTextBaselinePt, - Range aRange, + const gfxPoint& aFramePt, Range aRange, const nscolor* aDecorationOverrideColor, PropertyProvider* aProvider); @@ -592,6 +591,8 @@ public: void AssignJustificationGaps(const mozilla::JustificationAssignment& aAssign); mozilla::JustificationAssignment GetJustificationAssignment() const; + uint32_t CountGraphemeClusters() const; + protected: virtual ~nsTextFrame(); @@ -635,29 +636,30 @@ protected: nsRect UpdateTextEmphasis(mozilla::WritingMode aWM, PropertyProvider& aProvider); - void PaintOneShadow(Range aRange, + struct PaintShadowParams + { + gfxTextRun::Range range; + LayoutDeviceRect dirtyRect; + gfxPoint framePt; + gfxPoint textBaselinePt; + gfxContext* context; + nscolor foregroundColor = NS_RGBA(0, 0, 0, 0); + const nsCharClipDisplayItem::ClipEdges* clipEdges = nullptr; + PropertyProvider* provider = nullptr; + nscoord leftSideOffset = 0; + explicit PaintShadowParams(const PaintTextParams& aParams) + : dirtyRect(aParams.dirtyRect) + , framePt(aParams.framePt) + , context(aParams.context) {} + }; + + void PaintOneShadow(const PaintShadowParams& aParams, nsCSSShadowItem* aShadowDetails, - PropertyProvider* aProvider, - const LayoutDeviceRect& aDirtyRect, - const gfxPoint& aFramePt, - const gfxPoint& aTextBaselinePt, - gfxContext* aCtx, - const nscolor& aForegroundColor, - const nsCharClipDisplayItem::ClipEdges& aClipEdges, - nscoord aLeftSideOffset, gfxRect& aBoundingBox, uint32_t aBlurFlags); void PaintShadows(nsCSSShadowArray* aShadow, - Range aRange, - const LayoutDeviceRect& aDirtyRect, - const gfxPoint& aFramePt, - const gfxPoint& aTextBaselinePt, - nscoord aLeftEdgeOffset, - PropertyProvider& aProvider, - nscolor aForegroundColor, - const nsCharClipDisplayItem::ClipEdges& aClipEdges, - gfxContext* aCtx); + const PaintShadowParams& aParams); struct LineDecoration { nsIFrame* mFrame; @@ -756,7 +758,7 @@ protected: SelectionType aType, nsTextPaintStyle& aTextPaintStyle, const TextRangeStyle &aRangeStyle, - const gfxPoint& aPt, + const Point& aPt, gfxFloat aICoordInFrame, gfxFloat aWidth, gfxFloat aAscent, @@ -765,26 +767,9 @@ protected: bool aVertical, gfxFloat aDecorationOffsetDir, uint8_t aDecoration); - enum DecorationType - { - eNormalDecoration, - eSelectionDecoration - }; - void PaintDecorationLine(gfxContext* const aCtx, - const LayoutDeviceRect& aDirtyRect, - nscolor aColor, - const nscolor* aOverrideColor, - const gfxPoint& aPt, - gfxFloat aICoordInFrame, - const gfxSize& aLineSize, - gfxFloat aAscent, - gfxFloat aOffset, - uint8_t aDecoration, - uint8_t aStyle, - DecorationType aDecorationType, - DrawPathCallbacks* aCallbacks, - bool aVertical, - gfxFloat aDescentLimit = -1.0); + + struct PaintDecorationLineParams; + void PaintDecorationLine(const PaintDecorationLineParams& aParams); /** * ComputeDescentLimitForSelectionUnderline() computes the most far position * where we can put selection underline. diff --git a/layout/generic/nsTextRunTransformations.cpp b/layout/generic/nsTextRunTransformations.cpp index 0b2cab9e1b..8d1d5c0f82 100644 --- a/layout/generic/nsTextRunTransformations.cpp +++ b/layout/generic/nsTextRunTransformations.cpp @@ -32,7 +32,7 @@ using namespace mozilla; #define GREEK_SMALL_LETTER_FINAL_SIGMA 0x03C2 #define GREEK_SMALL_LETTER_SIGMA 0x03C3 -nsTransformedTextRun * +UniquePtr nsTransformedTextRun::Create(const gfxTextRunFactory::Parameters* aParams, nsTransformingTextRunFactory* aFactory, gfxFontGroup* aFontGroup, @@ -49,10 +49,10 @@ nsTransformedTextRun::Create(const gfxTextRunFactory::Parameters* aParams, return nullptr; } - return new (storage) nsTransformedTextRun(aParams, aFactory, aFontGroup, - aString, aLength, - aFlags, Move(aStyles), - aOwnsFactory); + return UniquePtr( + new (storage) nsTransformedTextRun(aParams, aFactory, aFontGroup, + aString, aLength, aFlags, + Move(aStyles), aOwnsFactory)); } void @@ -98,7 +98,7 @@ nsTransformedTextRun::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); } -nsTransformedTextRun* +UniquePtr nsTransformingTextRunFactory::MakeTextRun(const char16_t* aString, uint32_t aLength, const gfxTextRunFactory::Parameters* aParams, gfxFontGroup* aFontGroup, uint32_t aFlags, @@ -110,7 +110,7 @@ nsTransformingTextRunFactory::MakeTextRun(const char16_t* aString, uint32_t aLen aOwnsFactory); } -nsTransformedTextRun* +UniquePtr nsTransformingTextRunFactory::MakeTextRun(const uint8_t* aString, uint32_t aLength, const gfxTextRunFactory::Parameters* aParams, gfxFontGroup* aFontGroup, uint32_t aFlags, @@ -299,6 +299,7 @@ nsCaseTransformTextRunFactory::TransformString( nsIUGenCategory::nsUGenCategory cat; uint8_t style = aAllUppercase ? NS_STYLE_TEXT_TRANSFORM_UPPERCASE : 0; + bool forceNonFullWidth = false; const nsIAtom* lang = aLanguage; LanguageSpecificCasingBehavior languageSpecificCasing = GetCasingFor(lang); @@ -318,6 +319,7 @@ nsCaseTransformTextRunFactory::TransformString( charStyle = aTextRun->mStyles[i]; style = aAllUppercase ? NS_STYLE_TEXT_TRANSFORM_UPPERCASE : charStyle->mTextTransform; + forceNonFullWidth = charStyle->mForceNonFullWidth; nsIAtom* newLang = charStyle->mExplicitLanguage ? charStyle->mLanguage : nullptr; @@ -572,6 +574,10 @@ nsCaseTransformTextRunFactory::TransformString( break; } + if (forceNonFullWidth) { + ch = mozilla::unicode::GetFullWidthInverse(ch); + } + if (ch == uint32_t(-1)) { aDeletedCharsArray.AppendElement(true); mergeNeeded = true; @@ -637,8 +643,8 @@ nsCaseTransformTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun, GetParametersForInner(aTextRun, &flags, aRefDrawTarget); gfxFontGroup* fontGroup = aTextRun->GetFontGroup(); - nsAutoPtr transformedChild; - nsAutoPtr cachedChild; + UniquePtr transformedChild; + UniquePtr cachedChild; gfxTextRun* child; if (mInnerTransformingTextRunFactory) { diff --git a/layout/generic/nsTextRunTransformations.h b/layout/generic/nsTextRunTransformations.h index 9fc797935e..8da6a469d6 100644 --- a/layout/generic/nsTextRunTransformations.h +++ b/layout/generic/nsTextRunTransformations.h @@ -32,6 +32,7 @@ struct nsTransformedCharStyle final { uint8_t mTextTransform; uint8_t mMathVariant; bool mExplicitLanguage; + bool mForceNonFullWidth = false; private: ~nsTransformedCharStyle() {} @@ -44,16 +45,19 @@ public: virtual ~nsTransformingTextRunFactory() {} // Default 8-bit path just transforms to Unicode and takes that path - nsTransformedTextRun* MakeTextRun(const uint8_t* aString, uint32_t aLength, - const gfxFontGroup::Parameters* aParams, - gfxFontGroup* aFontGroup, uint32_t aFlags, - nsTArray>&& aStyles, - bool aOwnsFactory); - nsTransformedTextRun* MakeTextRun(const char16_t* aString, uint32_t aLength, - const gfxFontGroup::Parameters* aParams, - gfxFontGroup* aFontGroup, uint32_t aFlags, - nsTArray>&& aStyles, - bool aOwnsFactory); + mozilla::UniquePtr + MakeTextRun(const uint8_t* aString, uint32_t aLength, + const gfxFontGroup::Parameters* aParams, + gfxFontGroup* aFontGroup, uint32_t aFlags, + nsTArray>&& aStyles, + bool aOwnsFactory); + + mozilla::UniquePtr + MakeTextRun(const char16_t* aString, uint32_t aLength, + const gfxFontGroup::Parameters* aParams, + gfxFontGroup* aFontGroup, uint32_t aFlags, + nsTArray>&& aStyles, + bool aOwnsFactory); virtual void RebuildTextRun(nsTransformedTextRun* aTextRun, mozilla::gfx::DrawTarget* aRefDrawTarget, @@ -114,13 +118,14 @@ protected: class nsTransformedTextRun final : public gfxTextRun { public: - static nsTransformedTextRun *Create(const gfxTextRunFactory::Parameters* aParams, - nsTransformingTextRunFactory* aFactory, - gfxFontGroup* aFontGroup, - const char16_t* aString, uint32_t aLength, - const uint32_t aFlags, - nsTArray>&& aStyles, - bool aOwnsFactory); + static mozilla::UniquePtr + Create(const gfxTextRunFactory::Parameters* aParams, + nsTransformingTextRunFactory* aFactory, + gfxFontGroup* aFontGroup, + const char16_t* aString, uint32_t aLength, + const uint32_t aFlags, + nsTArray>&& aStyles, + bool aOwnsFactory); ~nsTransformedTextRun() { if (mOwnsFactory) { diff --git a/layout/inspector/nsFontFace.cpp b/layout/inspector/nsFontFace.cpp index 35702720f7..5b256db8e6 100644 --- a/layout/inspector/nsFontFace.cpp +++ b/layout/inspector/nsFontFace.cpp @@ -187,7 +187,7 @@ nsFontFace::GetMetadata(nsAString & aMetadata) aMetadata.Truncate(); if (mFontEntry->IsUserFont() && !mFontEntry->IsLocalUserFont()) { NS_ASSERTION(mFontEntry->mUserFontData, "missing userFontData"); - const gfxUserFontData* userFontData = mFontEntry->mUserFontData; + const gfxUserFontData* userFontData = mFontEntry->mUserFontData.get(); if (userFontData->mMetadata.Length() && userFontData->mMetaOrigLen) { nsAutoCString str; str.SetLength(userFontData->mMetaOrigLen); diff --git a/layout/inspector/tests/test_bug877690.html b/layout/inspector/tests/test_bug877690.html index f4baacf4ba..9125ae2415 100644 --- a/layout/inspector/tests/test_bug877690.html +++ b/layout/inspector/tests/test_bug877690.html @@ -90,7 +90,7 @@ function do_test() { "snow", "springgreen", "steelblue", "tan", "teal", "thistle", "tomato", "transparent", "turquoise", "violet", "wheat", "white", "whitesmoke", "yellow", "yellowgreen", "no-repeat", "repeat", "repeat-x", "repeat-y", "fixed", "scroll", "local", "center", "top", "bottom", "left", "right", - "border-box", "padding-box", "content-box", "border-box", "padding-box", "content-box", "contain", + "border-box", "padding-box", "content-box", "border-box", "padding-box", "content-box", "text", "contain", "cover", "rgb", "hsl", "rgba", "hsla", "none", "-moz-element", "-moz-image-rect", "url", "linear-gradient", "radial-gradient", "repeating-linear-gradient", "repeating-radial-gradient", "-moz-linear-gradient", "-moz-radial-gradient", "-moz-repeating-linear-gradient", "-moz-repeating-radial-gradient" ]; @@ -164,9 +164,11 @@ function do_test() { "table", "inline-table", "table-row-group", "table-header-group", "table-footer-group", "table-row", "table-column-group", "table-column", "table-cell", "table-caption", "-moz-box", "-moz-inline-box", "-moz-grid", "-moz-inline-grid", "-moz-grid-group", "-moz-grid-line", "-moz-stack", "-moz-inline-stack", - "-moz-deck", "-moz-popup", "-moz-groupbox", "flex", "inline-flex", "grid", - "inline-grid", "ruby", "ruby-base", "ruby-base-container", "ruby-text", - "ruby-text-container", "contents" ]; + "-moz-deck", "-moz-popup", "-moz-groupbox", + "flex", "inline-flex", "-webkit-box", "-webkit-inline-box", + "grid", "inline-grid", + "ruby", "ruby-base", "ruby-base-container", "ruby-text", "ruby-text-container", + "contents" ]; ok(testValues(values, expected), "property display's values."); // test property diff --git a/layout/ipc/RenderFrameParent.cpp b/layout/ipc/RenderFrameParent.cpp index 312006c1fb..15f0d338b2 100644 --- a/layout/ipc/RenderFrameParent.cpp +++ b/layout/ipc/RenderFrameParent.cpp @@ -338,8 +338,10 @@ nsDisplayRemote::nsDisplayRemote(nsDisplayListBuilder* aBuilder, , mEventRegionsOverride(EventRegionsOverride::NoOverride) { if (aBuilder->IsBuildingLayerEventRegions()) { - bool frameIsPointerEventsNone = !aFrame->PassPointerEventsToChildren() - && (aFrame->StyleVisibility()->GetEffectivePointerEvents(aFrame) == NS_STYLE_POINTER_EVENTS_NONE); + bool frameIsPointerEventsNone = + !aFrame->PassPointerEventsToChildren() && + aFrame->StyleUserInterface()->GetEffectivePointerEvents(aFrame) == + NS_STYLE_POINTER_EVENTS_NONE; if (aBuilder->IsInsidePointerEventsNoneDoc() || frameIsPointerEventsNone) { mEventRegionsOverride |= EventRegionsOverride::ForceEmptyHitRegion; } diff --git a/layout/mathml/nsMathMLChar.cpp b/layout/mathml/nsMathMLChar.cpp index a902ba50d8..5a7ad939c4 100644 --- a/layout/mathml/nsMathMLChar.cpp +++ b/layout/mathml/nsMathMLChar.cpp @@ -107,10 +107,11 @@ public: char16_t aChar, bool aVertical) = 0; - virtual gfxTextRun* MakeTextRun(DrawTarget* aDrawTarget, - int32_t aAppUnitsPerDevPixel, - gfxFontGroup* aFontGroup, - const nsGlyphCode& aGlyph) = 0; + virtual UniquePtr + MakeTextRun(DrawTarget* aDrawTarget, + int32_t aAppUnitsPerDevPixel, + gfxFontGroup* aFontGroup, + const nsGlyphCode& aGlyph) = 0; protected: nsGlyphTable() : mCharCache(0) {} // For speedy re-use, we always cache the last data used in the table. @@ -228,10 +229,11 @@ public: aChar, aVertical, 3).Exists()); } - virtual gfxTextRun* MakeTextRun(DrawTarget* aDrawTarget, - int32_t aAppUnitsPerDevPixel, - gfxFontGroup* aFontGroup, - const nsGlyphCode& aGlyph) override; + virtual UniquePtr + MakeTextRun(DrawTarget* aDrawTarget, + int32_t aAppUnitsPerDevPixel, + gfxFontGroup* aFontGroup, + const nsGlyphCode& aGlyph) override; private: // mGlyphCodeFonts[0] is the primary font associated to this table. The @@ -380,7 +382,7 @@ nsPropertiesTable::ElementAt(DrawTarget* /* aDrawTarget */, } /* virtual */ -gfxTextRun* +UniquePtr nsPropertiesTable::MakeTextRun(DrawTarget* aDrawTarget, int32_t aAppUnitsPerDevPixel, gfxFontGroup* aFontGroup, @@ -429,10 +431,11 @@ public: return mFontFamilyName; } - virtual gfxTextRun* MakeTextRun(DrawTarget* aDrawTarget, - int32_t aAppUnitsPerDevPixel, - gfxFontGroup* aFontGroup, - const nsGlyphCode& aGlyph) override; + virtual UniquePtr + MakeTextRun(DrawTarget* aDrawTarget, + int32_t aAppUnitsPerDevPixel, + gfxFontGroup* aFontGroup, + const nsGlyphCode& aGlyph) override; // This returns a new OpenTypeTable instance to give access to OpenType MATH // table or nullptr if the font does not have such table. Ownership is passed @@ -469,8 +472,7 @@ nsOpenTypeTable::UpdateCache(DrawTarget* aDrawTarget, char16_t aChar) { if (mCharCache != aChar) { - nsAutoPtr textRun; - textRun = aFontGroup-> + UniquePtr textRun = aFontGroup-> MakeTextRun(&aChar, 1, aDrawTarget, aAppUnitsPerDevPixel, 0, nullptr); const gfxTextRun::CompressedGlyph& data = textRun->GetCharacterGlyphs()[0]; if (data.IsSimpleGlyph()) { @@ -552,7 +554,7 @@ nsOpenTypeTable::HasPartsOf(DrawTarget* aDrawTarget, } /* virtual */ -gfxTextRun* +UniquePtr nsOpenTypeTable::MakeTextRun(DrawTarget* aDrawTarget, int32_t aAppUnitsPerDevPixel, gfxFontGroup* aFontGroup, @@ -564,7 +566,8 @@ nsOpenTypeTable::MakeTextRun(DrawTarget* aDrawTarget, gfxTextRunFactory::Parameters params = { aDrawTarget, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel }; - gfxTextRun* textRun = gfxTextRun::Create(¶ms, 1, aFontGroup, 0); + UniquePtr textRun = + gfxTextRun::Create(¶ms, 1, aFontGroup, 0); textRun->AddGlyphRun(aFontGroup->GetFirstValidFont(), gfxTextRange::kFontGroup, 0, false, gfxTextRunFactory::TEXT_ORIENT_HORIZONTAL); @@ -1132,10 +1135,9 @@ StretchEnumContext::TryVariants(nsGlyphTable* aGlyphTable, displayOperatorMinHeight = mathFont->GetMathConstant(gfxFontEntry::DisplayOperatorMinHeight, oneDevPixel); - nsAutoPtr textRun; - textRun = aGlyphTable->MakeTextRun(mDrawTarget, oneDevPixel, - *aFontGroup, ch); - nsBoundingMetrics bm = MeasureTextRun(mDrawTarget, textRun); + UniquePtr textRun = + aGlyphTable->MakeTextRun(mDrawTarget, oneDevPixel, *aFontGroup, ch); + nsBoundingMetrics bm = MeasureTextRun(mDrawTarget, textRun.get()); float largeopFactor = kLargeOpFactor; if (NS_STRETCH_INTEGRAL & mStretchHint) { // integrals are drawn taller @@ -1163,10 +1165,9 @@ StretchEnumContext::TryVariants(nsGlyphTable* aGlyphTable, continue; } - nsAutoPtr textRun; - textRun = aGlyphTable->MakeTextRun(mDrawTarget, oneDevPixel, - *aFontGroup, ch); - nsBoundingMetrics bm = MeasureTextRun(mDrawTarget, textRun); + UniquePtr textRun = + aGlyphTable->MakeTextRun(mDrawTarget, oneDevPixel, *aFontGroup, ch); + nsBoundingMetrics bm = MeasureTextRun(mDrawTarget, textRun.get()); if (ch.IsGlyphID()) { gfxFont* mathFont = aFontGroup->get()->GetFirstMathFont(); if (mathFont) { @@ -1213,7 +1214,7 @@ StretchEnumContext::TryVariants(nsGlyphTable* aGlyphTable, mBoundingMetrics = bm; haveBetter = true; bestSize = charSize; - mChar->mGlyphs[0] = textRun; + mChar->mGlyphs[0] = Move(textRun); mChar->mDraw = DRAW_VARIANT; } #ifdef NOISY_SEARCH @@ -1252,7 +1253,7 @@ nsMathMLChar::StretchEnumContext::TryParts(nsGlyphTable* aGlyphTable, NormalizeDefaultFont(font, mFontSizeInflation); // Compute the bounding metrics of all partial glyphs - nsAutoPtr textRun[4]; + UniquePtr textRun[4]; nsGlyphCode chdata[4]; nsBoundingMetrics bmdata[4]; nscoord sizedata[4]; @@ -1276,7 +1277,7 @@ nsMathMLChar::StretchEnumContext::TryParts(nsGlyphTable* aGlyphTable, textRun[i] = aGlyphTable->MakeTextRun(mDrawTarget, oneDevPixel, *aFontGroup, ch); - nsBoundingMetrics bm = MeasureTextRun(mDrawTarget, textRun[i]); + nsBoundingMetrics bm = MeasureTextRun(mDrawTarget, textRun[i].get()); bmdata[i] = bm; sizedata[i] = isVertical ? bm.ascent + bm.descent : bm.rightBearing - bm.leftBearing; @@ -1403,7 +1404,7 @@ nsMathMLChar::StretchEnumContext::TryParts(nsGlyphTable* aGlyphTable, // reset mChar->mDraw = DRAW_PARTS; for (int32_t i = 0; i < 4; i++) { - mChar->mGlyphs[i] = textRun[i]; + mChar->mGlyphs[i] = Move(textRun[i]); mChar->mBmData[i] = bmdata[i]; } @@ -1539,13 +1540,11 @@ nsMathMLChar::StretchInternal(nsPresContext* aPresContext, RefPtr fm = aPresContext->DeviceContext()->GetMetricsFor(font, params); uint32_t len = uint32_t(mData.Length()); - nsAutoPtr textRun; - textRun = fm->GetThebesFontGroup()-> + mGlyphs[0] = fm->GetThebesFontGroup()-> MakeTextRun(static_cast(mData.get()), len, aDrawTarget, aPresContext->AppUnitsPerDevPixel(), 0, aPresContext->MissingFontRecorder()); - aDesiredStretchSize = MeasureTextRun(aDrawTarget, textRun); - mGlyphs[0] = textRun; + aDesiredStretchSize = MeasureTextRun(aDrawTarget, mGlyphs[0].get()); bool maxWidth = (NS_STRETCH_MAXWIDTH & aStretchHint) != 0; if (!maxWidth) { @@ -2142,7 +2141,7 @@ nsMathMLChar::PaintForeground(nsPresContext* aPresContext, // draw a single glyph (base size or size variant) // XXXfredw verify if mGlyphs[0] is non-null to workaround bug 973322. if (mGlyphs[0]) { - mGlyphs[0]->Draw(Range(mGlyphs[0]), gfxPoint(0.0, mUnscaledAscent), + mGlyphs[0]->Draw(Range(mGlyphs[0].get()), gfxPoint(0.0, mUnscaledAscent), gfxTextRun::DrawParams(thebesContext)); } break; @@ -2300,7 +2299,7 @@ nsMathMLChar::PaintVertically(nsPresContext* aPresContext, } if (!clipRect.IsEmpty()) { AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect); - mGlyphs[i]->Draw(Range(mGlyphs[i]), gfxPoint(dx, dy), params); + mGlyphs[i]->Draw(Range(mGlyphs[i].get()), gfxPoint(dx, dy), params); } } } @@ -2366,7 +2365,7 @@ nsMathMLChar::PaintVertically(nsPresContext* aPresContext, clipRect.height = std::min(bm.ascent + bm.descent, fillEnd - dy); AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect); dy += bm.ascent; - mGlyphs[3]->Draw(Range(mGlyphs[3]), gfxPoint(dx, dy), params); + mGlyphs[3]->Draw(Range(mGlyphs[3].get()), gfxPoint(dx, dy), params); dy += bm.descent; } } @@ -2470,7 +2469,7 @@ nsMathMLChar::PaintHorizontally(nsPresContext* aPresContext, } if (!clipRect.IsEmpty()) { AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect); - mGlyphs[i]->Draw(Range(mGlyphs[i]), gfxPoint(dx, dy), params); + mGlyphs[i]->Draw(Range(mGlyphs[i].get()), gfxPoint(dx, dy), params); } } } @@ -2534,7 +2533,7 @@ nsMathMLChar::PaintHorizontally(nsPresContext* aPresContext, clipRect.width = std::min(bm.rightBearing - bm.leftBearing, fillEnd - dx); AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect); dx -= bm.leftBearing; - mGlyphs[3]->Draw(Range(mGlyphs[3]), gfxPoint(dx, dy), params); + mGlyphs[3]->Draw(Range(mGlyphs[3].get()), gfxPoint(dx, dy), params); dx += bm.rightBearing; } } diff --git a/layout/mathml/nsMathMLChar.h b/layout/mathml/nsMathMLChar.h index 1d3cc520e2..11599c13b5 100644 --- a/layout/mathml/nsMathMLChar.h +++ b/layout/mathml/nsMathMLChar.h @@ -207,7 +207,7 @@ private: nsStyleContext* mStyleContext; // mGlyphs/mBmData are arrays describing the glyphs used to draw the operator. // See the drawing methods below. - nsAutoPtr mGlyphs[4]; + mozilla::UniquePtr mGlyphs[4]; nsBoundingMetrics mBmData[4]; // mUnscaledAscent is the actual ascent of the char. nscoord mUnscaledAscent; diff --git a/layout/media/symbols.def.in b/layout/media/symbols.def.in index a828b4048e..cfc276b17f 100644 --- a/layout/media/symbols.def.in +++ b/layout/media/symbols.def.in @@ -560,6 +560,7 @@ _moz_cairo_win32_surface_set_can_convert_to_dib #ifdef MOZ_TREE_PIXMAN _moz_pixman_image_composite32 _moz_pixman_image_create_bits +_moz_pixman_image_set_filter _moz_pixman_image_set_transform _moz_pixman_image_unref _moz_pixman_transform_from_pixman_f_transform diff --git a/layout/printing/nsPrintEngine.cpp b/layout/printing/nsPrintEngine.cpp index be9c61b399..42ff3995d0 100644 --- a/layout/printing/nsPrintEngine.cpp +++ b/layout/printing/nsPrintEngine.cpp @@ -2122,9 +2122,7 @@ nsPrintEngine::ReflowPrintObject(nsPrintObject * aPO) rv = aPO->mViewManager->Init(mPrt->mPrintDC); NS_ENSURE_SUCCESS(rv,rv); - StyleSetHandle styleSet; - rv = mDocViewerPrint->CreateStyleSet(aPO->mDocument, &styleSet); - NS_ENSURE_SUCCESS(rv, rv); + StyleSetHandle styleSet = mDocViewerPrint->CreateStyleSet(aPO->mDocument); aPO->mPresShell = aPO->mDocument->CreateShell(aPO->mPresContext, aPO->mViewManager, styleSet); diff --git a/layout/reftests/backgrounds/background-clip-text-1-ref.html b/layout/reftests/backgrounds/background-clip-text-1-ref.html new file mode 100644 index 0000000000..ee50a86469 --- /dev/null +++ b/layout/reftests/backgrounds/background-clip-text-1-ref.html @@ -0,0 +1,19 @@ + + + + background-clip: text reference + + +
+ + + + + + + + TEXT clip + +
+ + \ No newline at end of file diff --git a/layout/reftests/backgrounds/background-clip-text-1a.html b/layout/reftests/backgrounds/background-clip-text-1a.html new file mode 100644 index 0000000000..77926604bb --- /dev/null +++ b/layout/reftests/backgrounds/background-clip-text-1a.html @@ -0,0 +1,24 @@ + + + + background-clip: text + + + +
+ TEXT clip +
+
+ + diff --git a/layout/reftests/backgrounds/background-clip-text-1b.html b/layout/reftests/backgrounds/background-clip-text-1b.html new file mode 100644 index 0000000000..e29b2c1784 --- /dev/null +++ b/layout/reftests/backgrounds/background-clip-text-1b.html @@ -0,0 +1,24 @@ + + + + background-clip: text + + + +
+

TEXT clip

+
+
+ + diff --git a/layout/reftests/backgrounds/background-clip-text-1c.html b/layout/reftests/backgrounds/background-clip-text-1c.html new file mode 100644 index 0000000000..56f707300e --- /dev/null +++ b/layout/reftests/backgrounds/background-clip-text-1c.html @@ -0,0 +1,26 @@ + + + + background-clip: text + + + +
+
+

TEXT clip

+
+
+
+ + diff --git a/layout/reftests/backgrounds/background-clip-text-1d.html b/layout/reftests/backgrounds/background-clip-text-1d.html new file mode 100644 index 0000000000..e0a1f5817c --- /dev/null +++ b/layout/reftests/backgrounds/background-clip-text-1d.html @@ -0,0 +1,23 @@ + + + + background-clip: text + + + +
+ TEXT clip +
+
+ + diff --git a/layout/reftests/backgrounds/reftest.list b/layout/reftests/backgrounds/reftest.list index da9c07b788..b857e9e3ce 100644 --- a/layout/reftests/backgrounds/reftest.list +++ b/layout/reftests/backgrounds/reftest.list @@ -164,3 +164,9 @@ fails-if(Android&&AndroidVersion==15) fuzzy-if(!Android||(Android&&AndroidVersio == background-repeat-large-area.html background-repeat-large-area-ref.html fuzzy(30,474) == background-tiling-zoom-1.html background-tiling-zoom-1-ref.html + +pref(layout.css.background-clip-text.enabled,true) fuzzy-if(winWidget,1,44) == background-clip-text-1a.html background-clip-text-1-ref.html +pref(layout.css.background-clip-text.enabled,true) fuzzy-if(winWidget,1,44) == background-clip-text-1b.html background-clip-text-1-ref.html +pref(layout.css.background-clip-text.enabled,true) fuzzy-if(winWidget,1,44) == background-clip-text-1c.html background-clip-text-1-ref.html +pref(layout.css.background-clip-text.enabled,true) fuzzy-if(winWidget,1,44) == background-clip-text-1d.html background-clip-text-1-ref.html +pref(layout.css.background-clip-text.enabled,false) != background-clip-text-1a.html background-clip-text-1-ref.html diff --git a/layout/reftests/bugs/645647-1-ref.html b/layout/reftests/bugs/645647-1-ref.html new file mode 100644 index 0000000000..c35d5520f2 --- /dev/null +++ b/layout/reftests/bugs/645647-1-ref.html @@ -0,0 +1,5 @@ + + +
Some text
+
Some text
+ diff --git a/layout/reftests/bugs/645647-1.html b/layout/reftests/bugs/645647-1.html new file mode 100644 index 0000000000..6b8e76c4ab --- /dev/null +++ b/layout/reftests/bugs/645647-1.html @@ -0,0 +1,9 @@ + + +
Some text
+
Some text
+ diff --git a/layout/reftests/bugs/645647-2-ref.html b/layout/reftests/bugs/645647-2-ref.html new file mode 100644 index 0000000000..64690fd7ee --- /dev/null +++ b/layout/reftests/bugs/645647-2-ref.html @@ -0,0 +1,27 @@ + + + + + + +
There should be no red areas.
+ + + + + diff --git a/layout/reftests/bugs/645647-2.html b/layout/reftests/bugs/645647-2.html new file mode 100644 index 0000000000..922e21161b --- /dev/null +++ b/layout/reftests/bugs/645647-2.html @@ -0,0 +1,28 @@ + + + + + + +
There should be no red areas.
+ + + + + diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index d9d932696d..66a31c5153 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1671,6 +1671,8 @@ fuzzy-if(Android&&AndroidVersion>=15,8,500) == 637852-3.html 637852-3-ref.html skip-if(B2G||Mulet) == 641770-1.html 641770-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop == 641856-1.html 641856-1-ref.html == 645491-1.html 645491-1-ref.html +== 645647-1.html 645647-1-ref.html +== 645647-2.html 645647-2-ref.html == 645768-1.html 645768-1-ref.html fails-if(layersGPUAccelerated&&cocoaWidget) fails-if(Android&&AndroidVersion<15&&AndroidVersion!=10) fuzzy-if(!layersGPUAccelerated,41,260) == 650228-1.html 650228-1-ref.html # Quartz alpha blending doesn't match GL alpha blending needs-focus == 652301-1a.html 652301-1-ref.html diff --git a/layout/reftests/font-face/reftest.list b/layout/reftests/font-face/reftest.list index 966f7e462a..7af1cfe992 100644 --- a/layout/reftests/font-face/reftest.list +++ b/layout/reftests/font-face/reftest.list @@ -7,7 +7,7 @@ HTTP(..) == download-2.html download-2-ref.html HTTP(..) != download-2.html about:blank random-if(winWidget) HTTP(..) == download-2-big.html download-2-big-otf.html # bug 470713 HTTP(..) != download-2-big-otf.html about:blank -asserts-if(Android,4-8) skip-if(Android&&AndroidVersion==17) HTTP(..) != download-3-notref.html download-3.html # bug 1019192, bug 936226 +asserts-if(Android&&!asyncPan,1-8) HTTP(..) != download-3-notref.html download-3.html # bug 1019192 asserts-if(Android,0-8) HTTP(..) == download-3-ref.html download-3.html # same bugs as above asserts-if(Android,0-8) HTTP(..) == fallback-to-system-1.html fallback-to-system-1-ref.html # just delayed assertions from above tests HTTP(..) == name-override-simple-1.html name-override-simple-1-ref.html diff --git a/layout/reftests/layers/reftest.list b/layout/reftests/layers/reftest.list index 031dac6837..0cd0607bd5 100644 --- a/layout/reftests/layers/reftest.list +++ b/layout/reftests/layers/reftest.list @@ -18,4 +18,4 @@ skip-if(!asyncPan) != pull-background-displayport-4.html about:blank # fails wit skip-if(!asyncPan) != pull-background-displayport-5.html about:blank skip-if(!asyncPan) != pull-background-displayport-6.html about:blank # fails with non-overlay scrollbars and event regions due to bug 1148515 fuzzy(2,30150) == opacity-blending.html opacity-blending-ref.html -fuzzy(1,5) == mask-layer-transform.html mask-layer-transform-ref.html +fuzzy(16,5) == mask-layer-transform.html mask-layer-transform-ref.html diff --git a/layout/reftests/w3c-css/submitted/filters/reftest.list b/layout/reftests/w3c-css/submitted/filters/reftest.list index 40c7da3e4a..36b5451867 100644 --- a/layout/reftests/w3c-css/submitted/filters/reftest.list +++ b/layout/reftests/w3c-css/submitted/filters/reftest.list @@ -1,2 +1,2 @@ -fails == filter-containing-block-dynamic-1a.html filter-containing-block-dynamic-1-ref.html # bug 1187851 -fails == filter-containing-block-dynamic-1b.html filter-containing-block-dynamic-1-ref.html # bug 1187851 +== filter-containing-block-dynamic-1a.html filter-containing-block-dynamic-1-ref.html +== filter-containing-block-dynamic-1b.html filter-containing-block-dynamic-1-ref.html diff --git a/layout/reftests/w3c-css/submitted/masking/mask-clip-1-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-clip-1-ref.html new file mode 100644 index 0000000000..0f9197e1f6 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-clip-1-ref.html @@ -0,0 +1,42 @@ + + + + + CSS mask-clip reference + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-clip-1.html b/layout/reftests/w3c-css/submitted/masking/mask-clip-1.html new file mode 100644 index 0000000000..c981536b86 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-clip-1.html @@ -0,0 +1,57 @@ + + + + + CSS Masking: mask-clip: clip mask image + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-composite-1-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-composite-1-ref.html index 4419f7b7d8..9249622cc4 100644 --- a/layout/reftests/w3c-css/submitted/masking/mask-composite-1-ref.html +++ b/layout/reftests/w3c-css/submitted/masking/mask-composite-1-ref.html @@ -17,12 +17,12 @@ div.add { left: 10px; - background-image: url(blue-100x50-transparent-100X50.svg); + background-image: url(support/blue-100x50-transparent-100x50.svg); } div.intersect { left: 230px; - background-image: url(blue-100x50-transparent-100X50.svg); + background-image: url(support/blue-100x50-transparent-100x50.svg); } diff --git a/layout/reftests/w3c-css/submitted/masking/mask-composite-1a.html b/layout/reftests/w3c-css/submitted/masking/mask-composite-1a.html index 86c1e036a2..bc640777e2 100644 --- a/layout/reftests/w3c-css/submitted/masking/mask-composite-1a.html +++ b/layout/reftests/w3c-css/submitted/masking/mask-composite-1a.html @@ -17,8 +17,8 @@ width: 100px; height: 100px; top:10px; - mask-image: url(blue-100x50-transparent-100X50.svg), - url(blue-100x50-transparent-100X50.svg); + mask-image: url(support/blue-100x50-transparent-100x50.svg), + url(support/blue-100x50-transparent-100x50.svg); } div.add { diff --git a/layout/reftests/w3c-css/submitted/masking/mask-composite-1b.html b/layout/reftests/w3c-css/submitted/masking/mask-composite-1b.html index dd3e1e5e49..e11c28f52e 100644 --- a/layout/reftests/w3c-css/submitted/masking/mask-composite-1b.html +++ b/layout/reftests/w3c-css/submitted/masking/mask-composite-1b.html @@ -17,8 +17,8 @@ width: 100px; height: 100px; top:10px; - mask-image: url(blue-100x50-transparent-100X50.png), - url(blue-100x50-transparent-100X50.png); + mask-image: url(support/blue-100x50-transparent-100x50.png), + url(support/blue-100x50-transparent-100x50.png); } div.add { diff --git a/layout/reftests/w3c-css/submitted/masking/mask-composite-2-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-composite-2-ref.html index be1fc6d99b..b9c1ca5046 100644 --- a/layout/reftests/w3c-css/submitted/masking/mask-composite-2-ref.html +++ b/layout/reftests/w3c-css/submitted/masking/mask-composite-2-ref.html @@ -22,7 +22,7 @@ div.substract { left: 120px; - background-image: url(blue-100x50-transparent-100X50.svg); + background-image: url(support/blue-100x50-transparent-100x50.svg); } div.exclude { diff --git a/layout/reftests/w3c-css/submitted/masking/mask-composite-2a.html b/layout/reftests/w3c-css/submitted/masking/mask-composite-2a.html index 9c81a43322..b624178f42 100644 --- a/layout/reftests/w3c-css/submitted/masking/mask-composite-2a.html +++ b/layout/reftests/w3c-css/submitted/masking/mask-composite-2a.html @@ -17,8 +17,8 @@ width: 100px; height: 100px; top:10px; - mask-image: url(blue-100x50-transparent-100X50.svg), - url(transparent-100x50-blue-100X50.svg); + mask-image: url(support/blue-100x50-transparent-100x50.svg), + url(support/transparent-100x50-blue-100x50.svg); } div.add { diff --git a/layout/reftests/w3c-css/submitted/masking/mask-composite-2b.html b/layout/reftests/w3c-css/submitted/masking/mask-composite-2b.html index 9092c1e7f0..2c6e8f41fd 100644 --- a/layout/reftests/w3c-css/submitted/masking/mask-composite-2b.html +++ b/layout/reftests/w3c-css/submitted/masking/mask-composite-2b.html @@ -17,8 +17,8 @@ width: 100px; height: 100px; top:10px; - mask-image: url(blue-100x50-transparent-100X50.png), - url(transparent-100x50-blue-100X50.png); + mask-image: url(support/blue-100x50-transparent-100x50.png), + url(support/transparent-100x50-blue-100x50.png); } div.add { diff --git a/layout/reftests/w3c-css/submitted/masking/mask-image-1-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-image-1-ref.html new file mode 100644 index 0000000000..2800844061 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-image-1-ref.html @@ -0,0 +1,19 @@ + + + + + CSS Masking: mask-image: mask layer image + + + + + +
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-image-1a.html b/layout/reftests/w3c-css/submitted/masking/mask-image-1a.html new file mode 100644 index 0000000000..62e00660a3 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-image-1a.html @@ -0,0 +1,26 @@ + + + + + CSS Masking: mask-image: mask layer image + + + + + + + + +
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-image-1b.html b/layout/reftests/w3c-css/submitted/masking/mask-image-1b.html new file mode 100644 index 0000000000..9bf74102f9 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-image-1b.html @@ -0,0 +1,26 @@ + + + + + CSS Masking: mask-image: mask layer image + + + + + + + + +
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-image-1c.html b/layout/reftests/w3c-css/submitted/masking/mask-image-1c.html new file mode 100644 index 0000000000..1328d06c95 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-image-1c.html @@ -0,0 +1,26 @@ + + + + + CSS Masking: mask-image: mask layer image + + + + + + + + +
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-image-2-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-image-2-ref.html new file mode 100644 index 0000000000..db949934e4 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-image-2-ref.html @@ -0,0 +1,20 @@ + + + + + CSS Masking: mask-image: mask layer image + + + + + +
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-image-2.html b/layout/reftests/w3c-css/submitted/masking/mask-image-2.html new file mode 100644 index 0000000000..a071cff3fa --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-image-2.html @@ -0,0 +1,31 @@ + + + + + CSS Masking: mask-image: mask layer image + + + + + + + + +
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-mode-a.html b/layout/reftests/w3c-css/submitted/masking/mask-mode-a.html new file mode 100644 index 0000000000..38977a0aad --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-mode-a.html @@ -0,0 +1,60 @@ + + + + + CSS Masking: mask-mode with vector image + + + + + + + + +
+
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-mode-b.html b/layout/reftests/w3c-css/submitted/masking/mask-mode-b.html new file mode 100644 index 0000000000..e5c11e9bf0 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-mode-b.html @@ -0,0 +1,61 @@ + + + + + CSS Masking: mask-mode with raster image + + + + + + + + +
+
+
+
+
+ + + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-mode-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-mode-ref.html new file mode 100644 index 0000000000..0f7dbd571f --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-mode-ref.html @@ -0,0 +1,52 @@ + + + + + CSS mask-mode reference + + + + + +
+
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-origin-1-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-origin-1-ref.html new file mode 100644 index 0000000000..df7e684d2a --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-origin-1-ref.html @@ -0,0 +1,34 @@ + + + + + CSS Masking: mask-origin: mask positioning area + + + + + +
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-origin-1.html b/layout/reftests/w3c-css/submitted/masking/mask-origin-1.html new file mode 100644 index 0000000000..3788a5591a --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-origin-1.html @@ -0,0 +1,48 @@ + + + + + CSS Masking: mask-origin: mask positioning area + + + + + + + + +
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-origin-2-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-origin-2-ref.html new file mode 100644 index 0000000000..bbb5ef2c41 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-origin-2-ref.html @@ -0,0 +1,35 @@ + + + + + CSS Masking: mask-origin: mask positioning area + + + + + +
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-origin-2.html b/layout/reftests/w3c-css/submitted/masking/mask-origin-2.html new file mode 100644 index 0000000000..90ee89b15d --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-origin-2.html @@ -0,0 +1,77 @@ + + + + + CSS Masking: mask-origin: mask positioning area + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-position-1-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-position-1-ref.html new file mode 100644 index 0000000000..e323bd3529 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-position-1-ref.html @@ -0,0 +1,33 @@ + + + + + CSS Masking: mask-position: position mask layer image + + + + + +
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-position-1a.html b/layout/reftests/w3c-css/submitted/masking/mask-position-1a.html new file mode 100644 index 0000000000..cee15ee713 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-position-1a.html @@ -0,0 +1,39 @@ + + + + + CSS Masking: mask-position: mask positioning + + + + + + + + +
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-position-1b.html b/layout/reftests/w3c-css/submitted/masking/mask-position-1b.html new file mode 100644 index 0000000000..669810e61a --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-position-1b.html @@ -0,0 +1,39 @@ + + + + + CSS Masking: mask-position: mask positioning + + + + + + + + +
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-position-1c.html b/layout/reftests/w3c-css/submitted/masking/mask-position-1c.html new file mode 100644 index 0000000000..9a1671c3d4 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-position-1c.html @@ -0,0 +1,39 @@ + + + + + CSS Masking: mask-position: mask positioning + + + + + + + + +
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-position-2-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-position-2-ref.html new file mode 100644 index 0000000000..46be4b8b08 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-position-2-ref.html @@ -0,0 +1,29 @@ + + + + + CSS Masking: mask-position: position mask layer image + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-position-2a.html b/layout/reftests/w3c-css/submitted/masking/mask-position-2a.html new file mode 100644 index 0000000000..d8427338d4 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-position-2a.html @@ -0,0 +1,34 @@ + + + + + CSS Masking: mask-position: mask positioning + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-position-2b.html b/layout/reftests/w3c-css/submitted/masking/mask-position-2b.html new file mode 100644 index 0000000000..b5f7b31051 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-position-2b.html @@ -0,0 +1,34 @@ + + + + + CSS Masking: mask-position: mask positioning + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-position-3-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-position-3-ref.html new file mode 100644 index 0000000000..dd2630356e --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-position-3-ref.html @@ -0,0 +1,29 @@ + + + + + CSS Masking: mask-position: position mask layer image + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-position-3a.html b/layout/reftests/w3c-css/submitted/masking/mask-position-3a.html new file mode 100644 index 0000000000..724a1e3ab2 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-position-3a.html @@ -0,0 +1,34 @@ + + + + + CSS Masking: mask-position: mask positioning + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-position-3b.html b/layout/reftests/w3c-css/submitted/masking/mask-position-3b.html new file mode 100644 index 0000000000..742a65ea15 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-position-3b.html @@ -0,0 +1,34 @@ + + + + + CSS Masking: mask-position: mask positioning + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-position-4-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-position-4-ref.html new file mode 100644 index 0000000000..7ddf68b28a --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-position-4-ref.html @@ -0,0 +1,29 @@ + + + + + CSS Masking: mask-position: position mask layer image + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-position-4a.html b/layout/reftests/w3c-css/submitted/masking/mask-position-4a.html new file mode 100644 index 0000000000..9d17692e83 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-position-4a.html @@ -0,0 +1,34 @@ + + + + + CSS Masking: mask-position: mask positioning + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-position-4b.html b/layout/reftests/w3c-css/submitted/masking/mask-position-4b.html new file mode 100644 index 0000000000..9b46546d31 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-position-4b.html @@ -0,0 +1,34 @@ + + + + + CSS Masking: mask-position: mask positioning + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-position-4c.html b/layout/reftests/w3c-css/submitted/masking/mask-position-4c.html new file mode 100644 index 0000000000..70959c0984 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-position-4c.html @@ -0,0 +1,34 @@ + + + + + CSS Masking: mask-position: mask positioning + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-position-4d.html b/layout/reftests/w3c-css/submitted/masking/mask-position-4d.html new file mode 100644 index 0000000000..e5dce03b4a --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-position-4d.html @@ -0,0 +1,32 @@ + + + + + CSS Masking: mask-position: mask positioning + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-position-5-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-position-5-ref.html new file mode 100644 index 0000000000..dc8c9ecb93 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-position-5-ref.html @@ -0,0 +1,38 @@ + + + + + CSS Masking: mask-position: position mask layer image + + + + + +
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-position-5.html b/layout/reftests/w3c-css/submitted/masking/mask-position-5.html new file mode 100644 index 0000000000..295402263d --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-position-5.html @@ -0,0 +1,35 @@ + + + + + CSS Masking: mask-position: mask positioning + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-position-6-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-position-6-ref.html new file mode 100644 index 0000000000..b94527eb0a --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-position-6-ref.html @@ -0,0 +1,30 @@ + + + + + CSS Masking: mask-position: position mask layer image + + + + + +
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-position-6.html b/layout/reftests/w3c-css/submitted/masking/mask-position-6.html new file mode 100644 index 0000000000..e1d9eca553 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-position-6.html @@ -0,0 +1,50 @@ + + + + + CSS Masking: mask-position: mask positioning + + + + + + + + +
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-position-7-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-position-7-ref.html new file mode 100644 index 0000000000..c3c8d85ebd --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-position-7-ref.html @@ -0,0 +1,30 @@ + + + + + CSS Masking: mask-position: position mask layer image + + + + + +
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-position-7.html b/layout/reftests/w3c-css/submitted/masking/mask-position-7.html new file mode 100644 index 0000000000..30e9a75817 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-position-7.html @@ -0,0 +1,50 @@ + + + + + CSS Masking: mask-position: mask positioning + + + + + + + + +
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-repeat-1-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-repeat-1-ref.html new file mode 100644 index 0000000000..fa386cdac1 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-repeat-1-ref.html @@ -0,0 +1,45 @@ + + + + + CSS Masking: mask-repeat: repeated mask layer image + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-repeat-1.html b/layout/reftests/w3c-css/submitted/masking/mask-repeat-1.html new file mode 100644 index 0000000000..af9225daae --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-repeat-1.html @@ -0,0 +1,47 @@ + + + + + CSS Masking: mask-repeat: repeated mask layer image + + + + + + + + +
+
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-repeat-2-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-repeat-2-ref.html new file mode 100644 index 0000000000..fa386cdac1 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-repeat-2-ref.html @@ -0,0 +1,45 @@ + + + + + CSS Masking: mask-repeat: repeated mask layer image + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-repeat-2.html b/layout/reftests/w3c-css/submitted/masking/mask-repeat-2.html new file mode 100644 index 0000000000..65eb2a43af --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-repeat-2.html @@ -0,0 +1,42 @@ + + + + + CSS Masking: mask-repeat: repeated mask layer image + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-repeat-3-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-repeat-3-ref.html new file mode 100644 index 0000000000..4d4b869ce9 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-repeat-3-ref.html @@ -0,0 +1,40 @@ + + + + + CSS Masking: mask-repeat: repeated mask layer image + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-repeat-3.html b/layout/reftests/w3c-css/submitted/masking/mask-repeat-3.html new file mode 100644 index 0000000000..540e47c69b --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-repeat-3.html @@ -0,0 +1,42 @@ + + + + + CSS Masking: mask-repeat: repeated mask layer image + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-auto-auto.html b/layout/reftests/w3c-css/submitted/masking/mask-size-auto-auto.html new file mode 100644 index 0000000000..cf69930e06 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-auto-auto.html @@ -0,0 +1,34 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-auto-length-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-size-auto-length-ref.html new file mode 100644 index 0000000000..aa21231924 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-auto-length-ref.html @@ -0,0 +1,27 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-auto-length.html b/layout/reftests/w3c-css/submitted/masking/mask-size-auto-length.html new file mode 100644 index 0000000000..10602567d4 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-auto-length.html @@ -0,0 +1,33 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-auto-percent.html b/layout/reftests/w3c-css/submitted/masking/mask-size-auto-percent.html new file mode 100644 index 0000000000..5f613ec4b5 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-auto-percent.html @@ -0,0 +1,33 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-auto-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-size-auto-ref.html new file mode 100644 index 0000000000..73e3df2703 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-auto-ref.html @@ -0,0 +1,27 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-auto.html b/layout/reftests/w3c-css/submitted/masking/mask-size-auto.html new file mode 100644 index 0000000000..19ff1369e6 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-auto.html @@ -0,0 +1,34 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-contain-clip-border-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-size-contain-clip-border-ref.html new file mode 100644 index 0000000000..afbeb72f10 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-contain-clip-border-ref.html @@ -0,0 +1,27 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-contain-clip-border.html b/layout/reftests/w3c-css/submitted/masking/mask-size-contain-clip-border.html new file mode 100644 index 0000000000..af87520d2f --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-contain-clip-border.html @@ -0,0 +1,36 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-contain-clip-padding-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-size-contain-clip-padding-ref.html new file mode 100644 index 0000000000..041e48d03d --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-contain-clip-padding-ref.html @@ -0,0 +1,35 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + +
+
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-contain-clip-padding.html b/layout/reftests/w3c-css/submitted/masking/mask-size-contain-clip-padding.html new file mode 100644 index 0000000000..dc093d94d0 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-contain-clip-padding.html @@ -0,0 +1,34 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + + + + +
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-contain-position-fifty-fifty-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-size-contain-position-fifty-fifty-ref.html new file mode 100644 index 0000000000..50ab373eb8 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-contain-position-fifty-fifty-ref.html @@ -0,0 +1,33 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + +
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-contain-position-fifty-fifty.html b/layout/reftests/w3c-css/submitted/masking/mask-size-contain-position-fifty-fifty.html new file mode 100644 index 0000000000..2be2428a7b --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-contain-position-fifty-fifty.html @@ -0,0 +1,35 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-contain-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-size-contain-ref.html new file mode 100644 index 0000000000..a7346bfcb2 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-contain-ref.html @@ -0,0 +1,27 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-contain.html b/layout/reftests/w3c-css/submitted/masking/mask-size-contain.html new file mode 100644 index 0000000000..f4b8cef793 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-contain.html @@ -0,0 +1,34 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-cover-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-size-cover-ref.html new file mode 100644 index 0000000000..961019bf16 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-cover-ref.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + +
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-cover.html b/layout/reftests/w3c-css/submitted/masking/mask-size-cover.html new file mode 100644 index 0000000000..9329ea70b7 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-cover.html @@ -0,0 +1,28 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + + + + +
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-length-auto.html b/layout/reftests/w3c-css/submitted/masking/mask-size-length-auto.html new file mode 100644 index 0000000000..2fcc74c084 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-length-auto.html @@ -0,0 +1,34 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-length-length-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-size-length-length-ref.html new file mode 100644 index 0000000000..5411da9f87 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-length-length-ref.html @@ -0,0 +1,27 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-length-length.html b/layout/reftests/w3c-css/submitted/masking/mask-size-length-length.html new file mode 100644 index 0000000000..9ef76bcd66 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-length-length.html @@ -0,0 +1,34 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-length-percent-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-size-length-percent-ref.html new file mode 100644 index 0000000000..26195541d1 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-length-percent-ref.html @@ -0,0 +1,27 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-length-percent.html b/layout/reftests/w3c-css/submitted/masking/mask-size-length-percent.html new file mode 100644 index 0000000000..592e593870 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-length-percent.html @@ -0,0 +1,34 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-length.html b/layout/reftests/w3c-css/submitted/masking/mask-size-length.html new file mode 100644 index 0000000000..76563e3329 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-length.html @@ -0,0 +1,34 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-percent-auto.html b/layout/reftests/w3c-css/submitted/masking/mask-size-percent-auto.html new file mode 100644 index 0000000000..d81cbcc02b --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-percent-auto.html @@ -0,0 +1,34 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-percent-length.html b/layout/reftests/w3c-css/submitted/masking/mask-size-percent-length.html new file mode 100644 index 0000000000..25c341b3d2 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-percent-length.html @@ -0,0 +1,34 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-percent-percent-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-size-percent-percent-ref.html new file mode 100644 index 0000000000..96a3d1bdca --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-percent-percent-ref.html @@ -0,0 +1,27 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-percent-percent-stretch-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-size-percent-percent-stretch-ref.html new file mode 100644 index 0000000000..2c023ec13c --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-percent-percent-stretch-ref.html @@ -0,0 +1,29 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-percent-percent-stretch.html b/layout/reftests/w3c-css/submitted/masking/mask-size-percent-percent-stretch.html new file mode 100644 index 0000000000..5309b379a5 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-percent-percent-stretch.html @@ -0,0 +1,34 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-percent-percent.html b/layout/reftests/w3c-css/submitted/masking/mask-size-percent-percent.html new file mode 100644 index 0000000000..d9138f75ad --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-percent-percent.html @@ -0,0 +1,34 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-size-percent.html b/layout/reftests/w3c-css/submitted/masking/mask-size-percent.html new file mode 100644 index 0000000000..4c0837b815 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-size-percent.html @@ -0,0 +1,34 @@ + + + + + CSS Masking: mask-size: mask layer size + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/reftest.list b/layout/reftests/w3c-css/submitted/masking/reftest.list index 57df96e960..aec678bc10 100644 --- a/layout/reftests/w3c-css/submitted/masking/reftest.list +++ b/layout/reftests/w3c-css/submitted/masking/reftest.list @@ -1,4 +1,67 @@ -fails == mask-composite-1a.html mask-composite-1-ref.html # bug 1251161 -fails == mask-composite-1b.html mask-composite-1-ref.html # bug 1251161 -fails fails-if(cocoaWidget) == mask-composite-2a.html mask-composite-2-ref.html # bug 1231643; bug 1251161 -fails fails-if(cocoaWidget) == mask-composite-2b.html mask-composite-2-ref.html # bug 1231643; bug 1251161 +# All mask properties test cases are meant to be failed +# until bug 1251161 is fixed, which means enabling mask shorthand. +# To enable it in compile time, refer to bug 1243734 + +# mask-composite test cases +fails == mask-composite-1a.html mask-composite-1-ref.html +fails == mask-composite-1b.html mask-composite-1-ref.html +fails == mask-composite-2a.html mask-composite-2-ref.html +fails == mask-composite-2b.html mask-composite-2-ref.html + +# mask-mode test cases +fails == mask-mode-a.html mask-mode-ref.html +fails == mask-mode-b.html mask-mode-ref.html + +# mask-image test cases +fails == mask-image-1a.html mask-image-1-ref.html +fails == mask-image-1b.html mask-image-1-ref.html +fails == mask-image-1c.html mask-image-1-ref.html +fails == mask-image-2.html mask-image-2-ref.html + +# mask-clip test cases +fails == mask-clip-1.html mask-clip-1-ref.html + +# mask-position test cases +fails == mask-position-1a.html mask-position-1-ref.html +fails == mask-position-1b.html mask-position-1-ref.html +fails == mask-position-1c.html mask-position-1-ref.html +fails == mask-position-2a.html mask-position-2-ref.html +fails == mask-position-2b.html mask-position-2-ref.html +fails == mask-position-3a.html mask-position-3-ref.html +fails == mask-position-3b.html mask-position-3-ref.html +fails == mask-position-4a.html mask-position-4-ref.html +fails == mask-position-4b.html mask-position-4-ref.html +fails == mask-position-4c.html mask-position-4-ref.html +fails == mask-position-4d.html mask-position-4-ref.html +fails == mask-position-5.html mask-position-5-ref.html +fails == mask-position-6.html mask-position-6-ref.html +fails == mask-position-7.html mask-position-7-ref.html + +# mask-repeat test cases +fails == mask-repeat-1.html mask-repeat-1-ref.html # bug 1258623 +fails == mask-repeat-2.html mask-repeat-2-ref.html # bug 1258626 +fails == mask-repeat-3.html mask-repeat-3-ref.html # bug 1258626 + +# mask-origin test cases +fails == mask-origin-1.html mask-origin-1-ref.html # bug 1258286 +fails == mask-origin-2.html mask-origin-2-ref.html # bug 1260094 + +# mask-size test cases +fails == mask-size-auto.html mask-size-auto-ref.html +fails == mask-size-auto-auto.html mask-size-auto-ref.html +fails == mask-size-auto-length.html mask-size-auto-length-ref.html +fails == mask-size-auto-percent.html mask-size-auto-length-ref.html +fails == mask-size-contain-clip-border.html mask-size-contain-clip-border-ref.html +fails == mask-size-contain-clip-padding.html mask-size-contain-clip-padding-ref.html +fails == mask-size-contain-position-fifty-fifty.html mask-size-contain-position-fifty-fifty-ref.html +fails == mask-size-contain.html mask-size-contain-ref.html +fails == mask-size-cover.html mask-size-cover-ref.html +fails == mask-size-length.html mask-size-length-length-ref.html +fails == mask-size-length-auto.html mask-size-length-length-ref.html +fails == mask-size-length-length.html mask-size-length-length-ref.html +fails == mask-size-length-percent.html mask-size-length-percent-ref.html +fails == mask-size-percent.html mask-size-percent-percent-ref.html +fails == mask-size-percent-auto.html mask-size-percent-percent-ref.html +fails == mask-size-percent-length.html mask-size-percent-percent-ref.html +fails == mask-size-percent-percent.html mask-size-percent-percent-ref.html +fails == mask-size-percent-percent-stretch.html mask-size-percent-percent-stretch-ref.html diff --git a/layout/reftests/w3c-css/submitted/masking/support/50x100-opaque-blue.svg b/layout/reftests/w3c-css/submitted/masking/support/50x100-opaque-blue.svg new file mode 100644 index 0000000000..c9a82b4939 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/support/50x100-opaque-blue.svg @@ -0,0 +1,4 @@ + + + + diff --git a/layout/reftests/w3c-css/submitted/masking/support/50x50-opaque-blue.svg b/layout/reftests/w3c-css/submitted/masking/support/50x50-opaque-blue.svg new file mode 100644 index 0000000000..a68a1fa5b5 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/support/50x50-opaque-blue.svg @@ -0,0 +1,4 @@ + + + + diff --git a/layout/reftests/w3c-css/submitted/masking/support/blue-100x100.png b/layout/reftests/w3c-css/submitted/masking/support/blue-100x100.png new file mode 100644 index 0000000000000000000000000000000000000000..3b72d5ce53c07b68fe508bb57aa61a933dbda768 GIT binary patch literal 40279 zcmeI2u}WM~6h-e-P%~ncLXjzK#UhYDFg(#ma0p=n%@pW;>;nsb-~q-+pfnKNDl^ z->sW({q}2YeZPcdN3!|K)R4%3#nQ<41(Rh)51%JP<&L*uMfo5F$oEEDjAph!_E}I5Y$y zVg$tE&=7=(5fF<*Ll7cHKr9XoL5LUuu{bmYAz}o?;?NL;h!GHrLqiZ^CvdfQzVrAl zfPMP;PS6%HcL+g<7y+?3Gz1}H1jORd5QK;k5Q{@Y5F$oEEDjAph!_E}I5Y$yVg$tE z&=7=(5fF<*Ll7cHKr9XoL5LUuu{bm&Sjewx;V9}|0OSPA@vcLNmtgQbLV|&M*CE79 zFnAsz!9cz15aJ~mJdcoIpx$)|@e&N4M@TSG?>dBd2?oz2Bp9f79YVYWgXa+v4Ai?0 zAzp&P^9TtB>RpErFTvn>gaiZiu0x2IVDLObf`NM1A;e2Ccs^6eMCamT)Vlz|7&O;q z%F!esmI@&V5hEZLhlU_TjDT1i8iEip0%CD!2tvdNh{d5H2oWP77Ker)M2vt~92$ZU zF#=+7Xb3{Y2#Cd@AqWv8AQp#)JS}AVbiP|k_pGTG`%j-pcv`*gUfp6Sfwbicgji@L zoJB|iWXlx@vCv95i;x7!mMaiqp_OnJAqkKzS0KbfE8#3c5+GZyK!}A_!dZkQK(<_g z5DTq@vj|CmY`Fp<7Fr2s5t0Deas@&xv=YuDBmuJJ3WQi_C7k`&8~O8p`oqcas%EKI J|9rE4a1Sc^F+2bO literal 0 HcmV?d00001 diff --git a/layout/reftests/w3c-css/submitted/masking/support/blue-100x100.svg b/layout/reftests/w3c-css/submitted/masking/support/blue-100x100.svg new file mode 100644 index 0000000000..38cae60fc0 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/support/blue-100x100.svg @@ -0,0 +1,4 @@ + + + + diff --git a/layout/reftests/w3c-css/submitted/masking/blue-100x50-transparent-100X50.png b/layout/reftests/w3c-css/submitted/masking/support/blue-100x50-transparent-100x50.png similarity index 100% rename from layout/reftests/w3c-css/submitted/masking/blue-100x50-transparent-100X50.png rename to layout/reftests/w3c-css/submitted/masking/support/blue-100x50-transparent-100x50.png diff --git a/layout/reftests/w3c-css/submitted/masking/transparent-100x50-blue-100X50.svg b/layout/reftests/w3c-css/submitted/masking/support/blue-100x50-transparent-100x50.svg similarity index 64% rename from layout/reftests/w3c-css/submitted/masking/transparent-100x50-blue-100X50.svg rename to layout/reftests/w3c-css/submitted/masking/support/blue-100x50-transparent-100x50.svg index f23cfe7561..459f21d5cf 100644 --- a/layout/reftests/w3c-css/submitted/masking/transparent-100x50-blue-100X50.svg +++ b/layout/reftests/w3c-css/submitted/masking/support/blue-100x50-transparent-100x50.svg @@ -1,5 +1,5 @@ - - - \ No newline at end of file + + + diff --git a/layout/reftests/w3c-css/submitted/masking/support/blue-luminance-100x100.svg b/layout/reftests/w3c-css/submitted/masking/support/blue-luminance-100x100.svg new file mode 100644 index 0000000000..5b79155b66 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/support/blue-luminance-100x100.svg @@ -0,0 +1,4 @@ + + + + diff --git a/layout/reftests/w3c-css/submitted/masking/support/green-100x100.png b/layout/reftests/w3c-css/submitted/masking/support/green-100x100.png new file mode 100644 index 0000000000000000000000000000000000000000..d65838b7f02b830e2fc05b7e20fee2e2d7ebd45d GIT binary patch literal 40279 zcmeI&v1(LN6ouh)6G-Anu&_{M3R|%ge1OP=`mP`MdZ(|MdP~_~guN``)8c&YoZVbmj2f#o@!%$NkRc>i6Xf=dLgB z9nOB0a=G-Ajp>uF>_2<`wEMqdc#eM=1(q_-;0hH0L?uEdAR!Ps0}z!6nSg{q=nOzq zB4h#*0--YiQHhWVNCdFc8sfa%ZAUegjMyF|!!fP_Hk3_w&O zWC9Wbp)&wciI53M2!zf6L?uEdAR!Ps0}z!6nSg{q=nOzqB4h#*0--YiQHhWVNC+T=NP*YHBrj7LW#!YhD3JO|1sc0@5II%_{(@sny`w-#bg6 TZ~u8QU%aZ{+?{;5vG@ERLYy$2 literal 0 HcmV?d00001 diff --git a/layout/reftests/w3c-css/submitted/masking/support/green-100x100.svg b/layout/reftests/w3c-css/submitted/masking/support/green-100x100.svg new file mode 100644 index 0000000000..87fd691a01 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/support/green-100x100.svg @@ -0,0 +1,4 @@ + + + + diff --git a/layout/reftests/w3c-css/submitted/masking/support/green-luminance-100x100.svg b/layout/reftests/w3c-css/submitted/masking/support/green-luminance-100x100.svg new file mode 100644 index 0000000000..795bd7e5c9 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/support/green-luminance-100x100.svg @@ -0,0 +1,4 @@ + + + + diff --git a/layout/reftests/w3c-css/submitted/masking/support/mask-half-transparent-100x100.svg b/layout/reftests/w3c-css/submitted/masking/support/mask-half-transparent-100x100.svg new file mode 100644 index 0000000000..78f6993552 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/support/mask-half-transparent-100x100.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/layout/reftests/w3c-css/submitted/masking/support/red-100x100.png b/layout/reftests/w3c-css/submitted/masking/support/red-100x100.png new file mode 100644 index 0000000000000000000000000000000000000000..43b8e542a924441a4028d8e3e888bdde82d831e3 GIT binary patch literal 40279 zcmeI2F-u%g5JvA)qGp3t8VOri2@*(=f1o_khfzefu@eclSxkx`NJ6>-+C zsfqS)|Mq*Y{hHWWU9QVu_7{FJ-ng^+NA`RrWr9{ZI``yMZ4W0%I~s2=YJxA!7ds2tkM#0kJqV1R-Js#NyBp zgoqIki$g;YB1S+g4h=zw7y+?3Gz1}H1jORd5QK;k5Q{@Y5F$oEEDjAph@HUk{`v2J z?*iDT|KADPBIXVu2oWP77Ker)M2vt~92$ZUF#=+7Xb3{Y2#Cd@AqWv8AQp#)AViFS zSR5LH5HSK`acBra#0ZGRp&ash)p{dKUmW!E(Io5aJ~mJdcoIpx$)| z@e&N4M@TSG?>dBd2?oz2Bp9f79YVYWgXa+v4Ai?0Azp&P^9TtB>RpErFTvn>gaiZi zu0x2IVDLObf`NM1A;e2Ccpf3aK)ve_;w2b7kC0%X-gOA^5)7Wt6f)Jh+>Lq{AQ*$@ zx=cBm1jJGy1R-Js#NyBpgoqIki$g;YB1S+g4h=zw7y+?3Gz1}H1jORd5QK;k5Q{@Y z5F$oEEDjAph!_E}I5Y$yVg$tE(2&tW_73N}rF4Ha^+Ip-iG-)s%kI@3h7w3yu0V)| zR>E0?BtW)Yfe;I=gtG`qfNZ$}Ar@K*XAzPB*>VL!EVL5NA|wH_v5MrT~a26p6kS$jr#6m0KEJ6|>TdqKeg;v7ZKd;6vuKr%X7+%#Z Lt=Gqk+n??M_?a;? literal 0 HcmV?d00001 diff --git a/layout/reftests/w3c-css/submitted/masking/support/red-100x100.svg b/layout/reftests/w3c-css/submitted/masking/support/red-100x100.svg new file mode 100644 index 0000000000..0ba285bf00 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/support/red-100x100.svg @@ -0,0 +1,4 @@ + + + + diff --git a/layout/reftests/w3c-css/submitted/masking/support/red-luminance-100x100.svg b/layout/reftests/w3c-css/submitted/masking/support/red-luminance-100x100.svg new file mode 100644 index 0000000000..05075f5210 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/support/red-luminance-100x100.svg @@ -0,0 +1,4 @@ + + + + diff --git a/layout/reftests/w3c-css/submitted/masking/transparent-100x50-blue-100X50.png b/layout/reftests/w3c-css/submitted/masking/support/transparent-100x50-blue-100x50.png similarity index 100% rename from layout/reftests/w3c-css/submitted/masking/transparent-100x50-blue-100X50.png rename to layout/reftests/w3c-css/submitted/masking/support/transparent-100x50-blue-100x50.png diff --git a/layout/reftests/w3c-css/submitted/masking/blue-100x50-transparent-100X50.svg b/layout/reftests/w3c-css/submitted/masking/support/transparent-100x50-blue-100x50.svg similarity index 64% rename from layout/reftests/w3c-css/submitted/masking/blue-100x50-transparent-100X50.svg rename to layout/reftests/w3c-css/submitted/masking/support/transparent-100x50-blue-100x50.svg index a817b4d6b5..658aed9f1e 100644 --- a/layout/reftests/w3c-css/submitted/masking/blue-100x50-transparent-100X50.svg +++ b/layout/reftests/w3c-css/submitted/masking/support/transparent-100x50-blue-100x50.svg @@ -1,5 +1,5 @@ - - - \ No newline at end of file + + + diff --git a/layout/reftests/w3c-css/submitted/reftest.list b/layout/reftests/w3c-css/submitted/reftest.list index 5740151d65..8041c4042b 100644 --- a/layout/reftests/w3c-css/submitted/reftest.list +++ b/layout/reftests/w3c-css/submitted/reftest.list @@ -78,3 +78,6 @@ include variables/reftest.list # CSS will-change Level 1 include will-change/reftest.list + +# CSS Writing Modes Level 3 +include writing-modes-3/reftest.list diff --git a/layout/reftests/w3c-css/submitted/writing-modes-3/reftest.list b/layout/reftests/w3c-css/submitted/writing-modes-3/reftest.list new file mode 100644 index 0000000000..1e014147d1 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/writing-modes-3/reftest.list @@ -0,0 +1,11 @@ +default-preferences pref(layout.css.text-combine-upright.enabled,true) + +== text-combine-upright-compression-001.html text-combine-upright-compression-001-ref.html +== text-combine-upright-compression-002.html text-combine-upright-compression-002-ref.html +== text-combine-upright-compression-003.html text-combine-upright-compression-003-ref.html +== text-combine-upright-compression-004.html text-combine-upright-compression-004-ref.html +== text-combine-upright-compression-005.html text-combine-upright-compression-005-ref.html +== text-combine-upright-compression-005a.html text-combine-upright-compression-005-ref.html +== text-combine-upright-compression-006.html text-combine-upright-compression-006-ref.html +== text-combine-upright-compression-006a.html text-combine-upright-compression-006-ref.html +== text-combine-upright-compression-007.html text-combine-upright-compression-007-ref.html diff --git a/layout/reftests/w3c-css/submitted/writing-modes-3/support/WidthTest-Regular.otf b/layout/reftests/w3c-css/submitted/writing-modes-3/support/WidthTest-Regular.otf new file mode 100644 index 0000000000000000000000000000000000000000..06b1d3c0253fde79a9ff941a40bc2cf37d31aff0 GIT binary patch literal 3248 zcmbVO3v8QL6+YL$A4%*wO) zF^js0fD@E7+g|Yvav*;X+Zgq*)r3o&fXn-zOdHW_C2TciQRqv#BQLOSc z)FU3R^0jn8yjJDi)PrZbi@o$GvH6WbJ3GXy~#}J=q8AU%yr)UaZMWFNpC!@U!mxT3w20iZCvL;l?$FF3~mCErd3UNPgoY5g@l^51j;*raq-MW@LX4G z{{(?aN9A}1Mz|4qX5_s7FgVWV|DIPJ)w80m`Ib~I5{N#5mOxZK zZ#i}HVxTF7lj~Ik4zBjv`Qe?xmRxN{$D;-`271mek57U-t$cIPa#S7rD~~$x%1*(C z*TH+rTFrmkZ~hPLb~ZClyE-@ zb)UEJjj3cOdC*&TzMcQqs0LsD7t*Pxvb9oCTsL!-8F?%Rqm@Q@|(}o`C z-rwEHG@xj&uI%*iY{^JvOL{Pu$QKLwV$4V-^*w3BDBRiAH9bA;k8wDEBA@NL!>`cl z3|Fth-Ozk!CHqEVMke=Rb$B)#&rfk`h57@=f(5yd4s7Hx{8?V2--$Z0f9_TD`JaeO zSL7eBYL|AHg{3_!1?QEO7ky^jCAXK)IBxEc+vFL?O59bxIPUv}Z2I9}9gjI*>G*lM z$x-&nU-|BPFDTs(dDa;$yN~U2c)e$@%7ko|qgTc2*Kf+LH?M2*!dahee_L*mX?e0o zy2|b4_MWn?_54u9w`rxd?pYWc}ot*>k{n=;czkfbA zfAVCm7>_+ue74QIe5P}GNX#z}Y4e@F<)P)F)ghwi0G_QQLQ$`opelUq@^GhH1>1?9I#A;r$ z2~pc%>Ym-_%zreRyF9y}TiVum_Q|EjC!MWN$B%nz=REFfbvNARAGVhtoojIn&TZ&r zv`$9dGWu(ehsl=bwprn1R7T5Dr&}(_h4O;ajaS#3T7ll>x0m`>Uq$NF)X_ou4%r3< zN5;rI6*Kro=kq`~E!^8cotrcQr_F(vTFu0CCP@KB52TsDThW0Bm>*K~sKI?p2g?-c3Dp0JYJ;ci^t;gogFZb%1Am3+Y&S2Z_o# S5_oe9ynC%&VLj@VJ@Zd=N3Nd$ literal 0 HcmV?d00001 diff --git a/layout/reftests/w3c-css/submitted/writing-modes-3/support/tcy.css b/layout/reftests/w3c-css/submitted/writing-modes-3/support/tcy.css new file mode 100644 index 0000000000..0117e215eb --- /dev/null +++ b/layout/reftests/w3c-css/submitted/writing-modes-3/support/tcy.css @@ -0,0 +1,11 @@ +.tcy { + text-combine-upright: all; +} +.fake-tcy { + display: inline-block; + writing-mode: horizontal-tb; + width: 1em; + height: 1em; + text-align: center; + line-height: 1em; +} diff --git a/layout/reftests/w3c-css/submitted/writing-modes-3/support/width-test.css b/layout/reftests/w3c-css/submitted/writing-modes-3/support/width-test.css new file mode 100644 index 0000000000..4f15976ccc --- /dev/null +++ b/layout/reftests/w3c-css/submitted/writing-modes-3/support/width-test.css @@ -0,0 +1,7 @@ +@font-face { + font-family: WidthTest; + src: url(WidthTest-Regular.otf); +} +.test { + font: 72px/1 WidthTest; +} diff --git a/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-001-ref.html b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-001-ref.html new file mode 100644 index 0000000000..b1971474e5 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-001-ref.html @@ -0,0 +1,21 @@ + + + + +CSS Reference: text-combine-upright, no compression + + + + + + +

Test passes if there are two identical zeros in the blocks below.

+
+

0

+

0

+
+ + diff --git a/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-001.html b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-001.html new file mode 100644 index 0000000000..304431bdaa --- /dev/null +++ b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-001.html @@ -0,0 +1,24 @@ + + + + +CSS Test: text-combine-upright, no compression + + + + + + + + + +

Test passes if there are two identical zeros in the blocks below.

+
+

0

+

0

+
+ + diff --git a/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-002-ref.html b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-002-ref.html new file mode 100644 index 0000000000..dd05022c07 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-002-ref.html @@ -0,0 +1,21 @@ + + + + +CSS Reference: text-combine-upright, compression of two characters + + + + + + +

Test passes if there are two identical graphs in the blocks below.

+
+

22

+

22

+
+ + diff --git a/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-002.html b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-002.html new file mode 100644 index 0000000000..2be48da2d3 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-002.html @@ -0,0 +1,24 @@ + + + + +CSS Test: text-combine-upright, compression of two characters + + + + + + + + + +

Test passes if there are two identical graphs in the blocks below.

+
+

00

+

22

+
+ + diff --git a/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-003-ref.html b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-003-ref.html new file mode 100644 index 0000000000..6c6ea736fd --- /dev/null +++ b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-003-ref.html @@ -0,0 +1,21 @@ + + + + +CSS Reference: text-combine-upright, compression of three characters + + + + + + +

Test passes if there are two identical graphs in the blocks below.

+
+

333

+

333

+
+ + diff --git a/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-003.html b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-003.html new file mode 100644 index 0000000000..5b22fb3650 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-003.html @@ -0,0 +1,24 @@ + + + + +CSS Test: text-combine-upright, compression of three characters + + + + + + + + + +

Test passes if there are two identical graphs in the blocks below.

+
+

000

+

333

+
+ + diff --git a/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-004-ref.html b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-004-ref.html new file mode 100644 index 0000000000..cb6aa00359 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-004-ref.html @@ -0,0 +1,21 @@ + + + + +CSS Reference: text-combine-upright, compression of four characters + + + + + + +

Test passes if there are two identical graphs in the blocks below.

+
+

4444

+

4444

+
+ + diff --git a/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-004.html b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-004.html new file mode 100644 index 0000000000..c6bcb02320 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-004.html @@ -0,0 +1,24 @@ + + + + +CSS Test: text-combine-upright, compression of four characters + + + + + + + + + +

Test passes if there are two identical graphs in the blocks below.

+
+

0000

+

4444

+
+ + diff --git a/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-005-ref.html b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-005-ref.html new file mode 100644 index 0000000000..a03ad409a8 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-005-ref.html @@ -0,0 +1,21 @@ + + + + +CSS Reference: text-combine-upright, no compression for single full-width character + + + + + + +

Test passes if there are two identical graphs in the blocks below.

+
+

ï¼?/span>

+

ï¼?/span>

+
+ + diff --git a/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-005.html b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-005.html new file mode 100644 index 0000000000..82e7cb531f --- /dev/null +++ b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-005.html @@ -0,0 +1,24 @@ + + + + +CSS Test: text-combine-upright, no compression for single full-width character + + + + + + + + + +

Test passes if there are two identical graphs in the blocks below.

+
+

ï¼?/span>

+

ï¼?/span>

+
+ + diff --git a/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-005a.html b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-005a.html new file mode 100644 index 0000000000..2472fffc04 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-005a.html @@ -0,0 +1,25 @@ + + + + +CSS Test: text-combine-upright, no compression for single character with full-width transform + + + + + + + + + +

Test passes if there are two identical graphs in the blocks below.

+
+

0

+

ï¼?/span>

+
+ + diff --git a/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-006-ref.html b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-006-ref.html new file mode 100644 index 0000000000..8a083f6806 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-006-ref.html @@ -0,0 +1,21 @@ + + + + +CSS Reference: text-combine-upright, compression for two full-width characters + + + + + + +

Test passes if there are two identical graphs in the blocks below.

+
+

22

+

22

+
+ + diff --git a/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-006.html b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-006.html new file mode 100644 index 0000000000..c021d62c55 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-006.html @@ -0,0 +1,24 @@ + + + + +CSS Test: text-combine-upright, compression for two full-width characters + + + + + + + + + +

Test passes if there are two identical graphs in the blocks below.

+
+

ï¼ï?

+

22

+
+ + diff --git a/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-006a.html b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-006a.html new file mode 100644 index 0000000000..bebda644ea --- /dev/null +++ b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-006a.html @@ -0,0 +1,25 @@ + + + + +CSS Test: text-combine-upright, compression for two characters with full-width transform + + + + + + + + + +

Test passes if there are two identical graphs in the blocks below.

+
+

00

+

22

+
+ + diff --git a/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-007-ref.html b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-007-ref.html new file mode 100644 index 0000000000..cc5ea6c5cb --- /dev/null +++ b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-007-ref.html @@ -0,0 +1,21 @@ + + + + +CSS Reference: text-combine-upright: all, fit any number of characters + + + + + + +

Test passes if the contents in the two blocks are identical.

+
+

一??/p> +

一??/p> +

+ + diff --git a/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-007.html b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-007.html new file mode 100644 index 0000000000..f228f885f0 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/writing-modes-3/text-combine-upright-compression-007.html @@ -0,0 +1,25 @@ + + + + +CSS Test: text-combine-upright: all, fit any number of characters + + + + + + + + + +

Test passes if the contents in the two blocks are identical.

+
+

一?‹ä?? ¼??­ä??«ä???/p> +

一??/p> +

+ + diff --git a/layout/reftests/webkit-box/reftest.list b/layout/reftests/webkit-box/reftest.list new file mode 100644 index 0000000000..5f1cac5692 --- /dev/null +++ b/layout/reftests/webkit-box/reftest.list @@ -0,0 +1,25 @@ +# This directory contains tests for "display: -webkit-box" and associated +# CSS properties. These tests require webkit prefix support to be enabled. +default-preferences pref(layout.css.prefixes.webkit,true) + +# Tests for anonymous flex item formation inside of a "-webkit-box": +# Note: some of these tests are marked as failing, because we don't match +# WebKit/Blink on them. (The reference case represents the WebKit/Blink +# rendering.) We could probably make them pass by implementing some quirks, if +# it turns out that the web depends on WebKit/Blink's behavior in these cases. +== webkit-box-anon-flex-items-1.html webkit-box-anon-flex-items-1-ref.html +fails == webkit-box-anon-flex-items-2.html webkit-box-anon-flex-items-2-ref.html +fails == webkit-box-anon-flex-items-3.html webkit-box-anon-flex-items-3-ref.html + +# Tests for "-webkit-box" & "-webkit-inline-box" as display values: +== webkit-display-values-1.html webkit-display-values-1-ref.html + +# Tests for "-webkit-box-align" (cross-axis alignment): +== webkit-box-align-horiz-1a.html webkit-box-align-horiz-1-ref.html +== webkit-box-align-horiz-1b.html webkit-box-align-horiz-1-ref.html +== webkit-box-align-vert-1.html webkit-box-align-vert-1-ref.html + +# Tests for "-webkit-box-pack" (main-axis alignment): +== webkit-box-pack-horiz-1a.html webkit-box-pack-horiz-1-ref.html +== webkit-box-pack-horiz-1b.html webkit-box-pack-horiz-1-ref.html +== webkit-box-pack-vert-1.html webkit-box-pack-vert-1-ref.html diff --git a/layout/reftests/webkit-box/webkit-box-anon-flex-items-1-ref.html b/layout/reftests/webkit-box/webkit-box-anon-flex-items-1-ref.html new file mode 100644 index 0000000000..cab3cc4cd9 --- /dev/null +++ b/layout/reftests/webkit-box/webkit-box-anon-flex-items-1-ref.html @@ -0,0 +1,101 @@ + + + + + Reference Case + + + + + +
+
+ a + ialt + + +
ib
+
it
+ z +
+
block
+
block
+
+ +
+
block
+
+ a + ialt + + +
ib
+
it
+ z +
+
block
+
+ +
+
block
+
block
+
+ a + ialt + + +
ib
+
it
+ z +
+
+ + +
+
a
+
+
tc
+
tc
+
+
b
+
+
tr
+
tc
+
+
c
+
+
tr
+
trg
+
+
d
+
t
+
e
+
+ + diff --git a/layout/reftests/webkit-box/webkit-box-anon-flex-items-1.html b/layout/reftests/webkit-box/webkit-box-anon-flex-items-1.html new file mode 100644 index 0000000000..04f3aaa229 --- /dev/null +++ b/layout/reftests/webkit-box/webkit-box-anon-flex-items-1.html @@ -0,0 +1,83 @@ + + + + + Test for contiguous inline content getting wrapped in a single block, + inside of -webkit-box. + + + + + +
+ a + ialt + + +
ib
+
it
+ z +
block
+
block
+
+ +
+
block
+ a + ialt + + +
ib
+
it
+ z +
block
+
+ +
+
block
+
block
+ a + ialt + + +
ib
+
it
+ z +
+ + +
+ a +
tc
+
tc
+ b +
tr
+
tc
+ c +
tr
+
trg
+ d +
t
+ e +
+ + diff --git a/layout/reftests/webkit-box/webkit-box-anon-flex-items-2-ref.html b/layout/reftests/webkit-box/webkit-box-anon-flex-items-2-ref.html new file mode 100644 index 0000000000..d220271d56 --- /dev/null +++ b/layout/reftests/webkit-box/webkit-box-anon-flex-items-2-ref.html @@ -0,0 +1,35 @@ + + + + + Reference Case + + + + + +
+
+ raw text + start +
+
BLOCK
+
+ end + raw text +
+
+ + diff --git a/layout/reftests/webkit-box/webkit-box-anon-flex-items-2.html b/layout/reftests/webkit-box/webkit-box-anon-flex-items-2.html new file mode 100644 index 0000000000..4336bba1cd --- /dev/null +++ b/layout/reftests/webkit-box/webkit-box-anon-flex-items-2.html @@ -0,0 +1,28 @@ + + + + + Test for how block-in-inline splits behave inside of -webkit-box. + + + + +
+ raw text + start
BLOCK
end
+ raw text +
+ + diff --git a/layout/reftests/webkit-box/webkit-box-anon-flex-items-3-ref.html b/layout/reftests/webkit-box/webkit-box-anon-flex-items-3-ref.html new file mode 100644 index 0000000000..7e098f9ecc --- /dev/null +++ b/layout/reftests/webkit-box/webkit-box-anon-flex-items-3-ref.html @@ -0,0 +1,29 @@ + + + + + Reference Case + + + + + +
+
a
+
+
b
+
c
+
+ + diff --git a/layout/reftests/webkit-box/webkit-box-anon-flex-items-3.html b/layout/reftests/webkit-box/webkit-box-anon-flex-items-3.html new file mode 100644 index 0000000000..5d71fc06e2 --- /dev/null +++ b/layout/reftests/webkit-box/webkit-box-anon-flex-items-3.html @@ -0,0 +1,23 @@ + + + + + Test for whether whitespace gets wrapped in an anonymous box, + inside of -webkit-box. + + + + + +
a
b
c
+ + diff --git a/layout/reftests/webkit-box/webkit-display-values-1-ref.html b/layout/reftests/webkit-box/webkit-display-values-1-ref.html new file mode 100644 index 0000000000..b3cb4b443d --- /dev/null +++ b/layout/reftests/webkit-box/webkit-display-values-1-ref.html @@ -0,0 +1,29 @@ + + + + + + CSS Reference + + + + +
a
+
b
+
c
+
+
d
+
e
+
f
+ + diff --git a/layout/reftests/webkit-box/webkit-display-values-1.html b/layout/reftests/webkit-box/webkit-display-values-1.html new file mode 100644 index 0000000000..f2477b361b --- /dev/null +++ b/layout/reftests/webkit-box/webkit-display-values-1.html @@ -0,0 +1,41 @@ + + + + + + CSS Test: -webkit-box & -webkit-inline-box as "display" values + + + + + +
a
+ +
b
+ +
c
+
+ + +
d
+ +
e
+ +
f
+ + diff --git a/layout/style/CounterStyleManager.cpp b/layout/style/CounterStyleManager.cpp index b803c96f28..8b92c3c17a 100644 --- a/layout/style/CounterStyleManager.cpp +++ b/layout/style/CounterStyleManager.cpp @@ -1846,19 +1846,6 @@ CounterStyle::IsDependentStyle() const } } -static int32_t -CountGraphemeClusters(const nsSubstring& aText) -{ - using mozilla::unicode::ClusterIterator; - ClusterIterator iter(aText.Data(), aText.Length()); - int32_t result = 0; - while (!iter.AtEnd()) { - ++result; - iter.Next(); - } - return result; -} - void CounterStyle::GetCounterText(CounterValue aOrdinal, WritingMode aWritingMode, @@ -1889,7 +1876,9 @@ CounterStyle::GetCounterText(CounterValue aOrdinal, GetPad(pad); // We have to calculate the difference here since suffix part of negative // sign may be appended to initialText later. - int32_t diff = pad.width - CountGraphemeClusters(initialText); + int32_t diff = pad.width - + unicode::CountGraphemeClusters(initialText.Data(), + initialText.Length()); aResult.Truncate(); if (useNegativeSign && aOrdinal < 0) { NegativeType negative; diff --git a/layout/style/Declaration.cpp b/layout/style/Declaration.cpp index 55be97f1ba..effdad23b5 100644 --- a/layout/style/Declaration.cpp +++ b/layout/style/Declaration.cpp @@ -295,12 +295,16 @@ Declaration::GetImageLayerValue( if (clip->mValue.GetIntValue() != NS_STYLE_IMAGELAYER_CLIP_BORDER || origin->mValue.GetIntValue() != NS_STYLE_IMAGELAYER_ORIGIN_PADDING) { - MOZ_ASSERT(nsCSSProps::kKeywordTableTable[ - aTable[nsStyleImageLayers::origin]] == - nsCSSProps::kImageLayerOriginKTable); - MOZ_ASSERT(nsCSSProps::kKeywordTableTable[ - aTable[nsStyleImageLayers::clip]] == - nsCSSProps::kImageLayerOriginKTable); +#ifdef DEBUG + for (size_t i = 0; nsCSSProps::kImageLayerOriginKTable[i].mValue != -1; i++) { + // For each keyword & value in kOriginKTable, ensure that + // kBackgroundKTable has a matching entry at the same position. + MOZ_ASSERT(nsCSSProps::kImageLayerOriginKTable[i].mKeyword == + nsCSSProps::kBackgroundClipKTable[i].mKeyword); + MOZ_ASSERT(nsCSSProps::kImageLayerOriginKTable[i].mValue == + nsCSSProps::kBackgroundClipKTable[i].mValue); + } +#endif static_assert(NS_STYLE_IMAGELAYER_CLIP_BORDER == NS_STYLE_IMAGELAYER_ORIGIN_BORDER && NS_STYLE_IMAGELAYER_CLIP_PADDING == diff --git a/layout/style/ServoBindingHelpers.h b/layout/style/ServoBindingHelpers.h index b720f0b973..06e2b9f0e3 100644 --- a/layout/style/ServoBindingHelpers.h +++ b/layout/style/ServoBindingHelpers.h @@ -17,10 +17,21 @@ template<> struct RefPtrTraits { static void AddRef(RawServoStyleSheet* aPtr) { - MOZ_CRASH("stylo: not implemented"); + Servo_AddRefStyleSheet(aPtr); } static void Release(RawServoStyleSheet* aPtr) { - Servo_ReleaseStylesheet(aPtr); + Servo_ReleaseStyleSheet(aPtr); + } +}; + +template<> +struct RefPtrTraits +{ + static void AddRef(ServoComputedValues* aPtr) { + Servo_AddRefComputedValues(aPtr); + } + static void Release(ServoComputedValues* aPtr) { + Servo_ReleaseComputedValues(aPtr); } }; diff --git a/layout/style/ServoBindings.cpp b/layout/style/ServoBindings.cpp index cceb53fbed..e9352b0315 100644 --- a/layout/style/ServoBindings.cpp +++ b/layout/style/ServoBindings.cpp @@ -164,12 +164,44 @@ Servo_StylesheetFromUTF8Bytes(const uint8_t* bytes, uint32_t length) } void -Servo_ReleaseStylesheet(RawServoStyleSheet* sheet) +Servo_AddRefStyleSheet(RawServoStyleSheet* sheet) +{ + MOZ_CRASH("stylo: shouldn't be calling Servo_AddRefStylesheet in a " + "non-MOZ_STYLO build"); +} + +void +Servo_ReleaseStyleSheet(RawServoStyleSheet* sheet) { MOZ_CRASH("stylo: shouldn't be calling Servo_ReleaseStylesheet in a " "non-MOZ_STYLO build"); } +void +Servo_AppendStyleSheet(RawServoStyleSheet* sheet, RawServoStyleSet* set) +{ + MOZ_CRASH("stylo: shouldn't be calling Servo_AppendStyleSheet in a " + "non-MOZ_STYLO build"); +} + +void Servo_PrependStyleSheet(RawServoStyleSheet* sheet, RawServoStyleSet* set) +{ + MOZ_CRASH("stylo: shouldn't be calling Servo_PrependStyleSheet in a " + "non-MOZ_STYLO build"); +} + +void Servo_RemoveStyleSheet(RawServoStyleSheet* sheet, RawServoStyleSet* set) +{ + MOZ_CRASH("stylo: shouldn't be calling Servo_RemoveStyleSheet in a " + "non-MOZ_STYLO build"); +} + +int Servo_StyleSheetHasRules(RawServoStyleSheet* sheet) +{ + MOZ_CRASH("stylo: shouldn't be calling Servo_StyleSheetHasRules in a " + "non-MOZ_STYLO build"); +} + RawServoStyleSet* Servo_InitStyleSet() { @@ -184,6 +216,35 @@ Servo_DropStyleSet(RawServoStyleSet* set) "non-MOZ_STYLO build"); } +ServoComputedValues* +Servo_GetComputedValues(RawGeckoElement* element) +{ + MOZ_CRASH("stylo: shouldn't be calling Servo_GetComputedValues in a " + "non-MOZ_STYLO build"); +} + +ServoComputedValues* +Servo_GetComputedValuesForAnonymousBox(ServoComputedValues* parentStyleOrNull, + nsIAtom* pseudoTag) +{ + MOZ_CRASH("stylo: shouldn't be calling Servo_GetComputedValuesForAnonymousBox in a " + "non-MOZ_STYLO build"); +} + +void +Servo_AddRefComputedValues(ServoComputedValues*) +{ + MOZ_CRASH("stylo: shouldn't be calling Servo_AddRefComputedValues in a " + "non-MOZ_STYLO build"); +} + +void +Servo_ReleaseComputedValues(ServoComputedValues*) +{ + MOZ_CRASH("stylo: shouldn't be calling Servo_ReleaseComputedValues in a " + "non-MOZ_STYLO build"); +} + void Servo_RestyleDocument(RawGeckoDocument* doc, RawServoStyleSet* set) { diff --git a/layout/style/ServoBindings.h b/layout/style/ServoBindings.h index 3c5a3d4a3b..1a21becbd6 100644 --- a/layout/style/ServoBindings.h +++ b/layout/style/ServoBindings.h @@ -7,7 +7,7 @@ #ifndef mozilla_ServoBindings_h #define mozilla_ServoBindings_h -#include +#include "stdint.h" /* * API for Servo to access Gecko data structures. This file must compile as valid @@ -17,7 +17,7 @@ * Functions beginning with Servo_ are implemented in Servo and invoked from Gecko. */ -#ifdef __cplusplus +class nsIAtom; class nsINode; typedef nsINode RawGeckoNode; namespace mozilla { namespace dom { class Element; } } @@ -26,26 +26,11 @@ typedef mozilla::dom::Element RawGeckoElement; class nsIDocument; typedef nsIDocument RawGeckoDocument; struct ServoNodeData; +struct ServoComputedValues; struct RawServoStyleSheet; struct RawServoStyleSet; -#else -struct RawGeckoNode; -typedef struct RawGeckoNode RawGeckoNode; -struct RawGeckoElement; -typedef struct RawGeckoElement RawGeckoElement; -struct RawGeckoDocument; -typedef struct RawGeckoDocument RawGeckoDocument; -struct ServoNodeData; -typedef struct ServoNodeData ServoNodeData; -struct RawServoStyleSheet; -typedef struct RawServoStyleSheet RawServoStyleSheet; -struct RawServoStyleSet; -typedef struct RawServoStyleSet RawServoStyleSet; -#endif -#ifdef __cplusplus extern "C" { -#endif // DOM Traversal. uint32_t Gecko_ChildrenCount(RawGeckoNode* node); @@ -81,15 +66,25 @@ void Servo_DropNodeData(ServoNodeData* data); // TODO: Make these return already_AddRefed and UniquePtr when the binding // generator is smart enough to handle them. RawServoStyleSheet* Servo_StylesheetFromUTF8Bytes(const uint8_t* bytes, uint32_t length); -void Servo_ReleaseStylesheet(RawServoStyleSheet* sheet); +void Servo_AddRefStyleSheet(RawServoStyleSheet* sheet); +void Servo_ReleaseStyleSheet(RawServoStyleSheet* sheet); +void Servo_AppendStyleSheet(RawServoStyleSheet* sheet, RawServoStyleSet* set); +void Servo_PrependStyleSheet(RawServoStyleSheet* sheet, RawServoStyleSet* set); +void Servo_RemoveStyleSheet(RawServoStyleSheet* sheet, RawServoStyleSet* set); +int Servo_StyleSheetHasRules(RawServoStyleSheet* sheet); RawServoStyleSet* Servo_InitStyleSet(); void Servo_DropStyleSet(RawServoStyleSet* set); +// Computed style data. +ServoComputedValues* Servo_GetComputedValues(RawGeckoElement* element); +ServoComputedValues* Servo_GetComputedValuesForAnonymousBox(ServoComputedValues* parentStyleOrNull, + nsIAtom* pseudoTag); +void Servo_AddRefComputedValues(ServoComputedValues*); +void Servo_ReleaseComputedValues(ServoComputedValues*); + // Servo API. void Servo_RestyleDocument(RawGeckoDocument* doc, RawServoStyleSet* set); -#ifdef __cplusplus } // extern "C" -#endif #endif // mozilla_ServoBindings_h diff --git a/layout/style/ServoStyleSet.cpp b/layout/style/ServoStyleSet.cpp index 9b75293505..41a078424a 100644 --- a/layout/style/ServoStyleSet.cpp +++ b/layout/style/ServoStyleSet.cpp @@ -7,13 +7,16 @@ #include "mozilla/ServoStyleSet.h" #include "nsCSSAnonBoxes.h" +#include "nsCSSPseudoElements.h" +#include "nsStyleContext.h" #include "nsStyleSet.h" using namespace mozilla; using namespace mozilla::dom; ServoStyleSet::ServoStyleSet() - : mRawSet(Servo_InitStyleSet()) + : mPresContext(nullptr) + , mRawSet(Servo_InitStyleSet()) , mBatching(0) { } @@ -21,6 +24,7 @@ ServoStyleSet::ServoStyleSet() void ServoStyleSet::Init(nsPresContext* aPresContext) { + mPresContext = aPresContext; } void @@ -69,7 +73,18 @@ already_AddRefed ServoStyleSet::ResolveStyleFor(Element* aElement, nsStyleContext* aParentContext) { - MOZ_CRASH("stylo: not implemented"); + RefPtr computedValues = dont_AddRef(Servo_GetComputedValues(aElement)); + MOZ_ASSERT(computedValues); + + // XXXbholley: nsStyleSet does visited handling here. + + // XXXbholley: Figure out the correct thing to pass here. Does this fixup + // duplicate something that servo already does? + bool skipFixup = false; + + return NS_NewStyleContext(aParentContext, mPresContext, nullptr, + CSSPseudoElementType::NotPseudo, + computedValues.forget(), skipFixup); } already_AddRefed @@ -81,7 +96,8 @@ ServoStyleSet::ResolveStyleFor(Element* aElement, } already_AddRefed -ServoStyleSet::ResolveStyleForNonElement(nsStyleContext* aParentContext) +ServoStyleSet::ResolveStyleForNonElement(nsStyleContext* aParentContext, + nsIAtom* aPseudoTag) { MOZ_CRASH("stylo: not implemented"); } @@ -103,12 +119,20 @@ ServoStyleSet::ResolveAnonymousBoxStyle(nsIAtom* aPseudoTag, { MOZ_ASSERT(nsCSSAnonBoxes::IsAnonBox(aPseudoTag)); - // FIXME(heycam): Do something with eSkipParentDisplayBasedStyleFixup, - // which is the only value of aFlags that can be passed in. MOZ_ASSERT(aFlags == 0 || aFlags == nsStyleSet::eSkipParentDisplayBasedStyleFixup); + bool skipFixup = aFlags & nsStyleSet::eSkipParentDisplayBasedStyleFixup; - MOZ_CRASH("stylo: not implemented"); + ServoComputedValues* parentStyle = + aParentContext ? aParentContext->StyleSource().AsServoComputedValues() + : nullptr; + RefPtr computedValues = + dont_AddRef(Servo_GetComputedValuesForAnonymousBox(parentStyle, aPseudoTag)); + MOZ_ASSERT(computedValues); + + return NS_NewStyleContext(aParentContext, mPresContext, nullptr, + CSSPseudoElementType::AnonBox, + computedValues.forget(), skipFixup); } // manage the set of style sheets in the style set @@ -123,6 +147,9 @@ ServoStyleSet::AppendStyleSheet(SheetType aType, mSheets[aType].RemoveElement(aSheet); mSheets[aType].AppendElement(aSheet); + // Maintain a mirrored list of sheets on the servo side. + Servo_AppendStyleSheet(aSheet->RawSheet(), mRawSet.get()); + return NS_OK; } @@ -137,6 +164,9 @@ ServoStyleSet::PrependStyleSheet(SheetType aType, mSheets[aType].RemoveElement(aSheet); mSheets[aType].InsertElementAt(0, aSheet); + // Maintain a mirrored list of sheets on the servo side. + Servo_PrependStyleSheet(aSheet->RawSheet(), mRawSet.get()); + return NS_OK; } @@ -144,7 +174,16 @@ nsresult ServoStyleSet::RemoveStyleSheet(SheetType aType, ServoStyleSheet* aSheet) { - MOZ_CRASH("stylo: not implemented"); + MOZ_ASSERT(aSheet); + MOZ_ASSERT(aSheet->IsApplicable()); + MOZ_ASSERT(nsStyleSet::IsCSSSheetType(aType)); + + mSheets[aType].RemoveElement(aSheet); + + // Maintain a mirrored list of sheets on the servo side. + Servo_RemoveStyleSheet(aSheet->RawSheet(), mRawSet.get()); + + return NS_OK; } nsresult @@ -187,7 +226,9 @@ nsresult ServoStyleSet::AddDocStyleSheet(ServoStyleSheet* aSheet, nsIDocument* aDocument) { - MOZ_CRASH("stylo: not implemented"); + // XXXbholley: Implement this. + NS_ERROR("stylo: no support for adding doc stylesheets to ServoStyleSet"); + return NS_OK; } already_AddRefed diff --git a/layout/style/ServoStyleSet.h b/layout/style/ServoStyleSet.h index 062c8ae09f..ed5ce361ce 100644 --- a/layout/style/ServoStyleSet.h +++ b/layout/style/ServoStyleSet.h @@ -63,7 +63,8 @@ public: TreeMatchContext& aTreeMatchContext); already_AddRefed - ResolveStyleForNonElement(nsStyleContext* aParentContext); + ResolveStyleForNonElement(nsStyleContext* aParentContext, + nsIAtom* aPseudoTag); already_AddRefed ResolvePseudoElementStyle(dom::Element* aParentElement, @@ -114,6 +115,7 @@ public: EventStates aStateMask); private: + nsPresContext* mPresContext; UniquePtr mRawSet; EnumeratedArray>> mSheets; diff --git a/layout/style/ServoStyleSheet.cpp b/layout/style/ServoStyleSheet.cpp index d26cfbccb9..7a0eb6a644 100644 --- a/layout/style/ServoStyleSheet.cpp +++ b/layout/style/ServoStyleSheet.cpp @@ -31,7 +31,7 @@ ServoStyleSheet::IsApplicable() const bool ServoStyleSheet::HasRules() const { - MOZ_CRASH("stylo: not implemented"); + return Servo_StyleSheetHasRules(RawSheet()); } nsIDocument* diff --git a/layout/style/ServoStyleSheet.h b/layout/style/ServoStyleSheet.h index 41c817e174..20b169a0ff 100644 --- a/layout/style/ServoStyleSheet.h +++ b/layout/style/ServoStyleSheet.h @@ -52,6 +52,8 @@ public: void List(FILE* aOut = stdout, int32_t aIndex = 0) const; #endif + RawServoStyleSheet* RawSheet() const { return mSheet; } + protected: ~ServoStyleSheet(); diff --git a/layout/style/StyleAnimationValue.cpp b/layout/style/StyleAnimationValue.cpp index 2e2a2fc970..8eee2afe25 100644 --- a/layout/style/StyleAnimationValue.cpp +++ b/layout/style/StyleAnimationValue.cpp @@ -3256,6 +3256,11 @@ StyleAnimationValue::ExtractComputedValue(nsCSSProperty aProperty, break; } + case eCSSProperty__webkit_text_fill_color: { + aComputedValue.SetColorValue(aStyleContext->GetTextFillColor()); + break; + } + case eCSSProperty_border_spacing: { const nsStyleTableBorder *styleTableBorder = static_cast(styleStruct); @@ -3390,29 +3395,29 @@ StyleAnimationValue::ExtractComputedValue(nsCSSProperty aProperty, } case eCSSProperty_clip: { - const nsStyleDisplay *display = - static_cast(styleStruct); - if (!(display->mClipFlags & NS_STYLE_CLIP_RECT)) { + const nsStyleEffects* effects = + static_cast(styleStruct); + if (!(effects->mClipFlags & NS_STYLE_CLIP_RECT)) { aComputedValue.SetAutoValue(); } else { nsCSSRect *vrect = new nsCSSRect; - const nsRect &srect = display->mClip; - if (display->mClipFlags & NS_STYLE_CLIP_TOP_AUTO) { + const nsRect &srect = effects->mClip; + if (effects->mClipFlags & NS_STYLE_CLIP_TOP_AUTO) { vrect->mTop.SetAutoValue(); } else { nscoordToCSSValue(srect.y, vrect->mTop); } - if (display->mClipFlags & NS_STYLE_CLIP_RIGHT_AUTO) { + if (effects->mClipFlags & NS_STYLE_CLIP_RIGHT_AUTO) { vrect->mRight.SetAutoValue(); } else { nscoordToCSSValue(srect.XMost(), vrect->mRight); } - if (display->mClipFlags & NS_STYLE_CLIP_BOTTOM_AUTO) { + if (effects->mClipFlags & NS_STYLE_CLIP_BOTTOM_AUTO) { vrect->mBottom.SetAutoValue(); } else { nscoordToCSSValue(srect.YMost(), vrect->mBottom); } - if (display->mClipFlags & NS_STYLE_CLIP_LEFT_AUTO) { + if (effects->mClipFlags & NS_STYLE_CLIP_LEFT_AUTO) { vrect->mLeft.SetAutoValue(); } else { nscoordToCSSValue(srect.x, vrect->mLeft); @@ -3463,9 +3468,9 @@ StyleAnimationValue::ExtractComputedValue(nsCSSProperty aProperty, } #endif case eCSSProperty_filter: { - const nsStyleSVGReset *svgReset = - static_cast(styleStruct); - const nsTArray& filters = svgReset->mFilters; + const nsStyleEffects* effects = + static_cast(styleStruct); + const nsTArray& filters = effects->mFilters; nsAutoPtr result; nsCSSValueList **resultTail = getter_Transfers(result); for (uint32_t i = 0; i < filters.Length(); ++i) { diff --git a/layout/style/StyleContextSource.h b/layout/style/StyleContextSource.h new file mode 100644 index 0000000000..22c6372e85 --- /dev/null +++ b/layout/style/StyleContextSource.h @@ -0,0 +1,138 @@ +/* -*- 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_StyleContextSource_h +#define mozilla_StyleContextSource_h + +#include "mozilla/ServoBindingHelpers.h" +#include "nsRuleNode.h" + +namespace mozilla { + +// Tagged union between Gecko Rule Nodes and Servo Computed Values. +// +// The rule node is the node in the lexicographic tree of rule nodes +// (the "rule tree") that indicates which style rules are used to +// compute the style data, and in what cascading order. The least +// specific rule matched is the one whose rule node is a child of the +// root of the rule tree, and the most specific rule matched is the +// |mRule| member of the rule node. +// +// In the Servo case, we hold an atomically-refcounted handle to a +// Servo ComputedValues struct, which is more or less the Servo equivalent +// of an nsStyleContext. + +// Underlying pointer without any strong ownership semantics. +struct NonOwningStyleContextSource +{ + MOZ_IMPLICIT NonOwningStyleContextSource(nsRuleNode* aRuleNode) + : mBits(reinterpret_cast(aRuleNode)) {} + explicit NonOwningStyleContextSource(ServoComputedValues* aComputedValues) + : mBits(reinterpret_cast(aComputedValues) | 1) {} + + bool operator==(const NonOwningStyleContextSource& aOther) const { + MOZ_ASSERT(IsServoComputedValues() == aOther.IsServoComputedValues(), + "Comparing Servo to Gecko - probably a bug"); + return mBits == aOther.mBits; + } + bool operator!=(const NonOwningStyleContextSource& aOther) const { + return !(*this == aOther); + } + + // We intentionally avoid exposing IsGeckoRuleNode() here, because that would + // encourage callers to do: + // + // if (source.IsGeckoRuleNode()) { + // // Code that we would run unconditionally if it weren't for Servo. + // } + // + // We want these branches to compile away when MOZ_STYLO is disabled, but that + // won't happen if there's an implicit null-check. + bool IsNull() const { return !mBits; } + bool IsGeckoRuleNodeOrNull() const { return !IsServoComputedValues(); } + bool IsServoComputedValues() const { +#ifdef MOZ_STYLO + return mBits & 1; +#else + return false; +#endif + } + + nsRuleNode* AsGeckoRuleNode() const { + MOZ_ASSERT(IsGeckoRuleNodeOrNull() && !IsNull()); + return reinterpret_cast(mBits); + } + + ServoComputedValues* AsServoComputedValues() const { + MOZ_ASSERT(IsServoComputedValues()); + return reinterpret_cast(mBits & ~1); + } + + bool MatchesNoRules() const { + if (IsGeckoRuleNodeOrNull()) { + return AsGeckoRuleNode()->IsRoot(); + } else { + MOZ_CRASH("stylo: not implemented"); + } + } + +private: + uintptr_t mBits; +}; + +// Higher-level struct that owns a strong reference to the source. The source +// is never null. +struct OwningStyleContextSource +{ + explicit OwningStyleContextSource(already_AddRefed aRuleNode) + : mRaw(aRuleNode.take()) { MOZ_ASSERT(!mRaw.IsNull()); }; + explicit OwningStyleContextSource(already_AddRefed aComputedValues) + : mRaw(aComputedValues.take()) { MOZ_ASSERT(!mRaw.IsNull()); } + OwningStyleContextSource(OwningStyleContextSource&& aOther) + : mRaw(aOther.mRaw) { aOther.mRaw = nullptr; } + + OwningStyleContextSource& operator=(OwningStyleContextSource&) = delete; + OwningStyleContextSource(OwningStyleContextSource&) = delete; + + ~OwningStyleContextSource() { + if (mRaw.IsNull()) { + // We must have invoked the move constructor. + } else if (IsGeckoRuleNode()) { + RefPtr releaseme = dont_AddRef(AsGeckoRuleNode()); + } else { + MOZ_ASSERT(IsServoComputedValues()); + RefPtr releaseme = + dont_AddRef(AsServoComputedValues()); + } + } + + bool operator==(const OwningStyleContextSource& aOther) const { + return mRaw == aOther.mRaw; + } + bool operator!=(const OwningStyleContextSource& aOther) const { + return !(*this == aOther); + } + bool IsNull() const { return mRaw.IsNull(); } + bool IsGeckoRuleNode() const { + MOZ_ASSERT(!mRaw.IsNull()); + return mRaw.IsGeckoRuleNodeOrNull(); + } + bool IsServoComputedValues() const { return mRaw.IsServoComputedValues(); } + + NonOwningStyleContextSource AsRaw() const { return mRaw; } + nsRuleNode* AsGeckoRuleNode() const { return mRaw.AsGeckoRuleNode(); } + ServoComputedValues* AsServoComputedValues() const { + return mRaw.AsServoComputedValues(); + } + + bool MatchesNoRules() const { return mRaw.MatchesNoRules(); } + +private: + NonOwningStyleContextSource mRaw; +}; + +} // namespace mozilla + +#endif // mozilla_StyleContextSource_h diff --git a/layout/style/StyleSetHandle.h b/layout/style/StyleSetHandle.h index 20477d2acd..f0fd9b13a8 100644 --- a/layout/style/StyleSetHandle.h +++ b/layout/style/StyleSetHandle.h @@ -119,7 +119,8 @@ public: nsStyleContext* aParentContext, TreeMatchContext& aTreeMatchContext); inline already_AddRefed - ResolveStyleForNonElement(nsStyleContext* aParentContext); + ResolveStyleForNonElement(nsStyleContext* aParentContext, + nsIAtom* aPseudoTag); inline already_AddRefed ResolvePseudoElementStyle(dom::Element* aParentElement, mozilla::CSSPseudoElementType aType, @@ -158,6 +159,9 @@ public: dom::Element* aPseudoElement, EventStates aStateMask); + inline void RootStyleContextAdded(); + inline void RootStyleContextRemoved(); + private: // Stores a pointer to an nsStyleSet or a ServoStyleSet. The least // significant bit is 0 for the former, 1 for the latter. This is diff --git a/layout/style/StyleSetHandleInlines.h b/layout/style/StyleSetHandleInlines.h index f1c8a11c1e..3ab356d563 100644 --- a/layout/style/StyleSetHandleInlines.h +++ b/layout/style/StyleSetHandleInlines.h @@ -94,9 +94,10 @@ StyleSetHandle::Ptr::ResolveStyleFor(dom::Element* aElement, } already_AddRefed -StyleSetHandle::Ptr::ResolveStyleForNonElement(nsStyleContext* aParentContext) +StyleSetHandle::Ptr::ResolveStyleForNonElement(nsStyleContext* aParentContext, + nsIAtom* aPseudoTag) { - FORWARD(ResolveStyleForNonElement, (aParentContext)); + FORWARD(ResolveStyleForNonElement, (aParentContext, aPseudoTag)); } already_AddRefed @@ -234,6 +235,26 @@ StyleSetHandle::Ptr::HasStateDependentStyle(dom::Element* aElement, aStateMask)); } +void +StyleSetHandle::Ptr::RootStyleContextAdded() +{ + if (IsGecko()) { + AsGecko()->RootStyleContextAdded(); + } else { + // Not needed. + } +} + +void +StyleSetHandle::Ptr::RootStyleContextRemoved() +{ + if (IsGecko()) { + RootStyleContextAdded(); + } else { + // Not needed. + } +} + } // namespace mozilla #undef FORWARD diff --git a/layout/style/StyleStructContext.h b/layout/style/StyleStructContext.h new file mode 100644 index 0000000000..8f8c608c40 --- /dev/null +++ b/layout/style/StyleStructContext.h @@ -0,0 +1,121 @@ +/* -*- 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_StyleStructContext_h +#define mozilla_StyleStructContext_h + +#include "CounterStyleManager.h" +#include "mozilla/LookAndFeel.h" +#include "nsPresContext.h" + +class nsDeviceContext; + +/** + * Construction context for style structs. + * + * Certain Gecko style structs have historically taken an nsPresContext + * argument in their constructor, which is used to compute various things. This + * makes Gecko style structs document-specific (which Servo style structs are + * not), and means that the initial values for style-structs cannot be shared + * across the entire runtime (as is the case in Servo). + * + * We'd like to remove this constraint so that Gecko can get the benefits of the + * Servo model, and so that Gecko aligns better with the Servo style system when + * using it. Unfortunately, this may require a fair amount of work, especially + * related to text zoom. + * + * So as an intermediate step, we define a reduced API set of "things that are + * needed when constructing style structs". This just wraps and forwards to an + * nsPresContext in the Gecko case, and returns some default not-always-correct + * values in the Servo case. We can then focus on reducing this API surface to + * zero, at which point this can be removed. + * + * We don't put the type in namespace mozilla, since we expect it to be + * temporary, and the namespacing would clutter up nsStyleStruct.h. + */ +#ifdef MOZ_STYLO +#define SERVO_DEFAULT(default_val) { if (!mPresContext) { return default_val; } } +#else +#define SERVO_DEFAULT(default_val) { MOZ_ASSERT(mPresContext); } +#endif +class StyleStructContext { +public: + MOZ_IMPLICIT StyleStructContext(nsPresContext* aPresContext) + : mPresContext(aPresContext) { MOZ_ASSERT(aPresContext); } + static StyleStructContext ServoContext() { return StyleStructContext(); } + + // XXXbholley: Need to make text zoom work with stylo. This probably means + // moving the zoom handling out of computed values and into a post- + // computation. + float TextZoom() + { + SERVO_DEFAULT(1.0); + return mPresContext->TextZoom(); + } + + const nsFont* GetDefaultFont(uint8_t aFontID) + { + // NB: The servo case only differ from the default case in terms of which + // font pref cache gets used. The distinction is probably unnecessary. + SERVO_DEFAULT(mozilla::StaticPresData::Get()-> + GetDefaultFont(aFontID, GetLanguageFromCharset())); + return mPresContext->GetDefaultFont(aFontID, GetLanguageFromCharset()); + } + + uint32_t GetBidi() + { + SERVO_DEFAULT(0); + return mPresContext->GetBidi(); + } + + int32_t AppUnitsPerDevPixel(); + + nscoord DevPixelsToAppUnits(int32_t aPixels) + { + return NSIntPixelsToAppUnits(aPixels, AppUnitsPerDevPixel()); + } + + typedef mozilla::LookAndFeel LookAndFeel; + nscolor DefaultColor() + { + SERVO_DEFAULT(LookAndFeel::GetColor(LookAndFeel::eColorID_WindowForeground, + NS_RGB(0x00, 0x00, 0x00))); + return mPresContext->DefaultColor(); + } + + mozilla::CounterStyle* BuildCounterStyle(const nsSubstring& aName) + { + SERVO_DEFAULT(nullptr); + return mPresContext->CounterStyleManager()->BuildCounterStyle(aName); + } + + nsIAtom* GetLanguageFromCharset() const + { + SERVO_DEFAULT(nsGkAtoms::x_western); + return mPresContext->GetLanguageFromCharset(); + } + + already_AddRefed GetContentLanguage() const + { + SERVO_DEFAULT(do_AddRef(nsGkAtoms::x_western)); + return mPresContext->GetContentLanguage(); + } + +private: + nsDeviceContext* DeviceContext() + { + SERVO_DEFAULT(HackilyFindSomeDeviceContext()); + return mPresContext->DeviceContext(); + } + + nsDeviceContext* HackilyFindSomeDeviceContext(); + + StyleStructContext() : mPresContext(nullptr) {} + nsPresContext* mPresContext; +}; + +#undef SERVO_DEFAULT + +#endif // mozilla_StyleStructContext_h diff --git a/layout/style/generate-stylestructlist.py b/layout/style/generate-stylestructlist.py index e9fa418ebc..346a7deaef 100755 --- a/layout/style/generate-stylestructlist.py +++ b/layout/style/generate-stylestructlist.py @@ -28,7 +28,6 @@ STYLE_STRUCTS = [("INHERITED",) + x for x in [ ("List", "nullptr", NORMAL_DEP + LENGTH_DEP), ("Text", "CheckTextCallback", NORMAL_DEP + LENGTH_DEP + COLOR_DEP), ("Visibility", "nullptr", NORMAL_DEP), - ("Quotes", "nullptr", NORMAL_DEP), ("UserInterface", "nullptr", NORMAL_DEP), ("TableBorder", "nullptr", NORMAL_DEP + LENGTH_DEP), ("SVG", "nullptr", NORMAL_DEP + LENGTH_DEP + COLOR_DEP), @@ -49,6 +48,7 @@ STYLE_STRUCTS = [("INHERITED",) + x for x in [ ("XUL", "nullptr", NORMAL_DEP), ("SVGReset", "nullptr", NORMAL_DEP + LENGTH_DEP + COLOR_DEP), ("Column", "nullptr", NORMAL_DEP + LENGTH_DEP + COLOR_DEP), + ("Effects", "nullptr", NORMAL_DEP + LENGTH_DEP + COLOR_DEP), ]] diff --git a/layout/style/moz.build b/layout/style/moz.build index c25034d8af..8e6f575ba6 100644 --- a/layout/style/moz.build +++ b/layout/style/moz.build @@ -96,6 +96,7 @@ EXPORTS.mozilla += [ 'SheetType.h', 'StyleAnimationValue.h', 'StyleBackendType.h', + 'StyleContextSource.h', 'StyleSetHandle.h', 'StyleSetHandleInlines.h', 'StyleSheet.h', @@ -103,6 +104,7 @@ EXPORTS.mozilla += [ 'StyleSheetHandleInlines.h', 'StyleSheetInfo.h', 'StyleSheetInlines.h', + 'StyleStructContext.h', ] EXPORTS.mozilla.dom += [ diff --git a/layout/style/nsCSSAnonBoxList.h b/layout/style/nsCSSAnonBoxList.h index 7f2827783b..d1b62bce44 100644 --- a/layout/style/nsCSSAnonBoxList.h +++ b/layout/style/nsCSSAnonBoxList.h @@ -19,7 +19,13 @@ // OUTPUT_CLASS=nsCSSAnonBoxes // MACRO_NAME=CSS_ANON_BOX -CSS_ANON_BOX(mozNonElement, ":-moz-non-element") +// ::-moz-text and ::-moz-other-non-element are non-elements which no +// rule will match. +CSS_ANON_BOX(mozText, ":-moz-text") +// This anonymous box has two uses: +// 1. placeholder frames, +// 2. nsFirstLetterFrames for content outside the ::first-letter. +CSS_ANON_BOX(mozOtherNonElement, ":-moz-other-non-element") CSS_ANON_BOX(mozAnonymousBlock, ":-moz-anonymous-block") CSS_ANON_BOX(mozAnonymousPositionedBlock, ":-moz-anonymous-positioned-block") diff --git a/layout/style/nsCSSAnonBoxes.h b/layout/style/nsCSSAnonBoxes.h index 2f74507db2..f631f88013 100644 --- a/layout/style/nsCSSAnonBoxes.h +++ b/layout/style/nsCSSAnonBoxes.h @@ -23,6 +23,8 @@ public: #ifdef MOZ_XUL static bool IsTreePseudoElement(nsIAtom* aPseudo); #endif + static bool IsNonElement(nsIAtom* aPseudo) + { return aPseudo == mozText || aPseudo == mozOtherNonElement; } #define CSS_ANON_BOX(_name, _value) static nsICSSAnonBoxPseudo* _name; #include "nsCSSAnonBoxList.h" diff --git a/layout/style/nsCSSKeywordList.h b/layout/style/nsCSSKeywordList.h index f9402a53a1..c110066358 100644 --- a/layout/style/nsCSSKeywordList.h +++ b/layout/style/nsCSSKeywordList.h @@ -126,6 +126,7 @@ CSS_KEY(-moz-workspace, _moz_workspace) CSS_KEY(-moz-zoom-in, _moz_zoom_in) CSS_KEY(-moz-zoom-out, _moz_zoom_out) CSS_KEY(-webkit-box, _webkit_box) +CSS_KEY(-webkit-inline-box, _webkit_inline_box) CSS_KEY(absolute, absolute) CSS_KEY(active, active) CSS_KEY(activeborder, activeborder) @@ -247,6 +248,7 @@ CSS_KEY(ease, ease) CSS_KEY(ease-in, ease_in) CSS_KEY(ease-in-out, ease_in_out) CSS_KEY(ease-out, ease_out) +CSS_KEY(economy, economy) CSS_KEY(element, element) CSS_KEY(elements, elements) CSS_KEY(ellipse, ellipse) @@ -257,6 +259,7 @@ CSS_KEY(enabled, enabled) CSS_KEY(end, end) CSS_KEY(ethiopic-numeric, ethiopic_numeric) CSS_KEY(ex, ex) +CSS_KEY(exact, exact) CSS_KEY(exclude, exclude) CSS_KEY(exclusion, exclusion) CSS_KEY(expanded, expanded) diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index d11891413a..37571b8b6f 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -35,6 +35,7 @@ #include "nsColor.h" #include "nsCSSPseudoClasses.h" #include "nsCSSPseudoElements.h" +#include "nsCSSAnonBoxes.h" #include "nsNameSpaceManager.h" #include "nsXMLNameSpaceMap.h" #include "nsError.h" @@ -5884,6 +5885,13 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask, return eSelectorParsingStatus_Error; } + if (nsCSSAnonBoxes::IsNonElement(pseudo)) { + // Non-element anonymous boxes should not match any rule. + REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown); + UngetToken(); + return eSelectorParsingStatus_Error; + } + // We currently allow :-moz-placeholder and ::-moz-placeholder. We have to // be a bit stricter regarding the pseudo-element parsing rules. if (pseudoElementType == CSSPseudoElementType::mozPlaceholder && @@ -6937,31 +6945,63 @@ CSSParserImpl::LookupKeywordPrefixAware(nsAString& aKeywordStr, nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(aKeywordStr); if (aKeywordTable == nsCSSProps::kDisplayKTable) { - if (keyword == eCSSKeyword__webkit_box && - (sWebkitPrefixedAliasesEnabled || ShouldUseUnprefixingService())) { - // Treat "display: -webkit-box" as "display: flex". In simple scenarios, - // they largely behave the same, as long as we alias the associated - // properties to modern flexbox equivalents as well. - if (mWebkitBoxUnprefixState == eHaveNotUnprefixed) { - mWebkitBoxUnprefixState = eHaveUnprefixed; + // NOTE: This code will be considerably simpler once we can do away with + // all Unprefixing Service code, in bug 1259348. But for the time being, we + // have to support two different strategies for handling -webkit-box here: + // (1) "Native support" (sWebkitPrefixedAliasesEnabled): we assume that + // -webkit-box will parse correctly (via an entry in kDisplayKTable), + // and we simply make a note that we've parsed it (so that we can we + // can give later "-moz-box" styling special handling as noted below). + // (2) "Unprefixing Service support" (ShouldUseUnprefixingService): we + // convert "-webkit-box" directly to modern "flex" (& do the same for + // any later "-moz-box" styling). + // + // Note that sWebkitPrefixedAliasesEnabled and + // ShouldUseUnprefixingService() are mutually exlusive, because the latter + // explicitly defers to the former. + if ((keyword == eCSSKeyword__webkit_box || + keyword == eCSSKeyword__webkit_inline_box)) { + const bool usingUnprefixingService = ShouldUseUnprefixingService(); + if (sWebkitPrefixedAliasesEnabled || usingUnprefixingService) { + // Make a note that we're accepting some "-webkit-{inline-}box" styling, + // so we can give special treatment to subsequent "-moz-{inline}-box". + // (See special treatment below.) + if (mWebkitBoxUnprefixState == eHaveNotUnprefixed) { + mWebkitBoxUnprefixState = eHaveUnprefixed; + } + if (usingUnprefixingService) { + // When we're using the unprefixing service, we treat + // "display:-webkit-box" as if it were "display:flex" + // (and "-webkit-inline-box" as "inline-flex"). + return (keyword == eCSSKeyword__webkit_box) ? + eCSSKeyword_flex : eCSSKeyword_inline_flex; + } } - return eCSSKeyword_flex; } - // If we've seen "display: -webkit-box" in an earlier declaration and we - // tried to unprefix it to emulate support for it, then we have to watch - // out for later "display: -moz-box" declarations; they're likely just a - // halfhearted attempt at compatibility, and they actually end up stomping - // on our emulation of the earlier -webkit-box display-value, via the CSS - // cascade. To prevent this problem, we also treat "display: -moz-box" as - // "display: flex" (but only if we unprefixed an earlier "-webkit-box"). + // If we've seen "display: -webkit-box" (or "-webkit-inline-box") in an + // earlier declaration and we honored it, then we have to watch out for + // later "display: -moz-box" (and "-moz-inline-box") declarations; they're + // likely just a halfhearted attempt at compatibility, and they actually + // end up stomping on our emulation of the earlier -webkit-box + // display-value, via the CSS cascade. To prevent this problem, we treat + // "display: -moz-box" & "-moz-inline-box" as if they were simply a + // repetition of the webkit equivalent that we already parsed. if (mWebkitBoxUnprefixState == eHaveUnprefixed && - keyword == eCSSKeyword__moz_box) { + (keyword == eCSSKeyword__moz_box || + keyword == eCSSKeyword__moz_inline_box)) { MOZ_ASSERT(sWebkitPrefixedAliasesEnabled || ShouldUseUnprefixingService(), "mDidUnprefixWebkitBoxInEarlierDecl should only be set if " "we're supporting webkit-prefixed aliases, or if we're using " "the css unprefixing service on this site"); - return eCSSKeyword_flex; + if (sWebkitPrefixedAliasesEnabled) { + return (keyword == eCSSKeyword__moz_box) ? + eCSSKeyword__webkit_box : eCSSKeyword__webkit_inline_box; + } + // (If we get here, we're using the Unprefixing Service, which means + // we're unprefixing all the way to modern flexbox display values.) + return (keyword == eCSSKeyword__moz_box) ? + eCSSKeyword_flex : eCSSKeyword_inline_flex; } } @@ -12003,13 +12043,16 @@ CSSParserImpl::ParseImageLayersItem( // The spec allows a second box value (for background-clip), // immediately following the first one (for background-origin). - // 'background-clip' and 'background-origin' use the same keyword table - MOZ_ASSERT(nsCSSProps::kKeywordTableTable[ - aTable[nsStyleImageLayers::origin]] == - nsCSSProps::kImageLayerOriginKTable); - MOZ_ASSERT(nsCSSProps::kKeywordTableTable[ - aTable[nsStyleImageLayers::clip]] == - nsCSSProps::kImageLayerOriginKTable); +#ifdef DEBUG + for (size_t i = 0; nsCSSProps::kImageLayerOriginKTable[i].mValue != -1; i++) { + // For each keyword & value in kOriginKTable, ensure that + // kBackgroundKTable has a matching entry at the same position. + MOZ_ASSERT(nsCSSProps::kImageLayerOriginKTable[i].mKeyword == + nsCSSProps::kBackgroundClipKTable[i].mKeyword); + MOZ_ASSERT(nsCSSProps::kImageLayerOriginKTable[i].mValue == + nsCSSProps::kBackgroundClipKTable[i].mValue); + } +#endif static_assert(NS_STYLE_IMAGELAYER_CLIP_BORDER == NS_STYLE_IMAGELAYER_ORIGIN_BORDER && NS_STYLE_IMAGELAYER_CLIP_PADDING == @@ -15093,6 +15136,9 @@ CSSParserImpl::ParseTextCombineUpright(nsCSSValue& aValue) // if 'digits', need to check for an explicit number [2, 3, 4] if (eCSSUnit_Enumerated == aValue.GetUnit() && aValue.GetIntValue() == NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_2) { + if (!nsLayoutUtils::TextCombineUprightDigitsEnabled()) { + return false; + } if (!GetToken(true)) { return true; } diff --git a/layout/style/nsCSSPropList.h b/layout/style/nsCSSPropList.h index 9011ab4851..c1c9773ed7 100644 --- a/layout/style/nsCSSPropList.h +++ b/layout/style/nsCSSPropList.h @@ -143,7 +143,6 @@ #define CSS_PROP_DISPLAY(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, Display, stylestructoffset_, animtype_) #define CSS_PROP_VISIBILITY(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, Visibility, stylestructoffset_, animtype_) #define CSS_PROP_CONTENT(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, Content, stylestructoffset_, animtype_) -#define CSS_PROP_QUOTES(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, Quotes, stylestructoffset_, animtype_) #define CSS_PROP_USERINTERFACE(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, UserInterface, stylestructoffset_, animtype_) #define CSS_PROP_UIRESET(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, UIReset, stylestructoffset_, animtype_) #define CSS_PROP_TABLE(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, Table, stylestructoffset_, animtype_) @@ -157,6 +156,7 @@ #define CSS_PROP_SVG(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, SVG, stylestructoffset_, animtype_) #define CSS_PROP_SVGRESET(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, SVGReset, stylestructoffset_, animtype_) #define CSS_PROP_VARIABLES(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, Variables, stylestructoffset_, animtype_) +#define CSS_PROP_EFFECTS(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, Effects, stylestructoffset_, animtype_) // And similarly for logical properties. An includer can define // CSS_PROP_LOGICAL to capture all logical properties, but otherwise they @@ -228,10 +228,6 @@ #define CSS_PROP_CONTENT(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ #define DEFINED_CSS_PROP_CONTENT #endif -#ifndef CSS_PROP_QUOTES -#define CSS_PROP_QUOTES(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ -#define DEFINED_CSS_PROP_QUOTES -#endif #ifndef CSS_PROP_USERINTERFACE #define CSS_PROP_USERINTERFACE(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ #define DEFINED_CSS_PROP_USERINTERFACE @@ -284,6 +280,10 @@ #define CSS_PROP_VARIABLES(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ #define DEFINED_CSS_PROP_VARIABLES #endif +#ifndef CSS_PROP_EFFECTS +#define CSS_PROP_EFFECTS(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ +#define DEFINED_CSS_PROP_EFFECTS +#endif #ifndef CSS_PROP_LOGICAL #define CSS_PROP_LOGICAL(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, group_, struct_, stylestructoffset_, animtype_) /* nothing */ @@ -550,7 +550,7 @@ CSS_PROP_BACKGROUND( CSS_PROPERTY_VALUE_LIST_USES_COMMAS, "", VARIANT_KEYWORD, // used by list parsing - kImageLayerOriginKTable, + kBackgroundClipKTable, CSS_PROP_NO_OFFSET, eStyleAnimType_None) CSS_PROP_BACKGROUND( @@ -1319,7 +1319,7 @@ CSS_PROP_BORDER( kBoxDecorationBreakKTable, CSS_PROP_NO_OFFSET, eStyleAnimType_None) -CSS_PROP_BORDER( +CSS_PROP_EFFECTS( box-shadow, box_shadow, BoxShadow, @@ -1331,7 +1331,7 @@ CSS_PROP_BORDER( "", 0, kBoxShadowTypeKTable, - offsetof(nsStyleBorder, mBoxShadow), + offsetof(nsStyleEffects, mBoxShadow), eStyleAnimType_Shadow) CSS_PROP_POSITION( box-sizing, @@ -1363,7 +1363,7 @@ CSS_PROP_DISPLAY( kClearKTable, CSS_PROP_NO_OFFSET, eStyleAnimType_None) -CSS_PROP_DISPLAY( +CSS_PROP_EFFECTS( clip, clip, Clip, @@ -1372,7 +1372,7 @@ CSS_PROP_DISPLAY( "", 0, nullptr, - offsetof(nsStyleDisplay, mClip), + offsetof(nsStyleEffects, mClip), eStyleAnimType_Custom) CSS_PROP_COLOR( color, @@ -1388,6 +1388,16 @@ CSS_PROP_COLOR( nullptr, offsetof(nsStyleColor, mColor), eStyleAnimType_Color) +CSS_PROP_VISIBILITY( + color-adjust, + color_adjust, + ColorAdjust, + CSS_PROPERTY_PARSE_VALUE, + "layout.css.color-adjust.enabled", + VARIANT_HK, + kColorAdjustKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) CSS_PROP_SHORTHAND( columns, columns, @@ -2600,7 +2610,7 @@ CSS_PROP_POSITION( kWidthKTable, offsetof(nsStylePosition, mMinWidth), eStyleAnimType_Coord) -CSS_PROP_DISPLAY( +CSS_PROP_EFFECTS( mix-blend-mode, mix_blend_mode, MixBlendMode, @@ -2713,7 +2723,7 @@ CSS_PROP_LOGICAL( Position, CSS_PROP_NO_OFFSET, eStyleAnimType_None) -CSS_PROP_DISPLAY( +CSS_PROP_EFFECTS( opacity, opacity, Opacity, @@ -2724,7 +2734,7 @@ CSS_PROP_DISPLAY( "", VARIANT_HN, nullptr, - offsetof(nsStyleDisplay, mOpacity), + offsetof(nsStyleEffects, mOpacity), eStyleAnimType_float) CSS_PROP_DISPLAY( -moz-orient, @@ -3023,7 +3033,7 @@ CSS_PROP_SVG( nullptr, CSS_PROP_NO_OFFSET, eStyleAnimType_None) -CSS_PROP_VISIBILITY( +CSS_PROP_USERINTERFACE( pointer-events, pointer_events, PointerEvents, @@ -3032,7 +3042,7 @@ CSS_PROP_VISIBILITY( "", VARIANT_HK, kPointerEventsKTable, - offsetof(nsStyleVisibility, mPointerEvents), + offsetof(nsStyleUserInterface, mPointerEvents), eStyleAnimType_EnumU8) CSS_PROP_DISPLAY( position, @@ -3047,7 +3057,7 @@ CSS_PROP_DISPLAY( kPositionKTable, CSS_PROP_NO_OFFSET, eStyleAnimType_None) -CSS_PROP_QUOTES( +CSS_PROP_LIST( quotes, quotes, Quotes, @@ -3320,6 +3330,19 @@ CSS_PROP_TEXT( nullptr, CSS_PROP_NO_OFFSET, eStyleAnimType_None) +CSS_PROP_TEXT( + -webkit-text-fill-color, + _webkit_text_fill_color, + WebkitTextFillColor, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED, + "layout.css.prefixes.webkit", + VARIANT_HC, + nullptr, + offsetof(nsStyleText, mWebkitTextFillColor), + eStyleAnimType_Custom) CSS_PROP_TEXT( text-indent, text_indent, @@ -3618,7 +3641,7 @@ CSS_PROP_UIRESET( // NOTE: vertical-align is only supposed to apply to :first-letter when // 'float' is 'none', but we don't worry about that since it has no // effect otherwise -CSS_PROP_TEXTRESET( +CSS_PROP_DISPLAY( vertical-align, vertical_align, VerticalAlign, @@ -3631,7 +3654,7 @@ CSS_PROP_TEXTRESET( "", VARIANT_HKLP | VARIANT_CALC, kVerticalAlignKTable, - offsetof(nsStyleTextReset, mVerticalAlign), + offsetof(nsStyleDisplay, mVerticalAlign), eStyleAnimType_Coord) CSS_PROP_VISIBILITY( visibility, @@ -3981,7 +4004,7 @@ CSS_PROP_SVG( kFillRuleKTable, offsetof(nsStyleSVG, mFillRule), eStyleAnimType_EnumU8) -CSS_PROP_SVGRESET( +CSS_PROP_EFFECTS( filter, filter, Filter, @@ -4013,7 +4036,7 @@ CSS_PROP_SVGRESET( nullptr, offsetof(nsStyleSVGReset, mFloodOpacity), eStyleAnimType_float) -CSS_PROP_SVG( +CSS_PROP_VISIBILITY( image-rendering, image_rendering, ImageRendering, @@ -4021,7 +4044,7 @@ CSS_PROP_SVG( "", VARIANT_HK, kImageRenderingKTable, - offsetof(nsStyleSVG, mImageRendering), + offsetof(nsStyleVisibility, mImageRendering), eStyleAnimType_EnumU8) CSS_PROP_SVGRESET( lighting-color, @@ -4319,7 +4342,7 @@ CSS_PROP_SVG( kTextAnchorKTable, offsetof(nsStyleSVG, mTextAnchor), eStyleAnimType_EnumU8) -CSS_PROP_SVG( +CSS_PROP_TEXT( text-rendering, text_rendering, TextRendering, @@ -4327,7 +4350,7 @@ CSS_PROP_SVG( "", VARIANT_HK, kTextRenderingKTable, - offsetof(nsStyleSVG, mTextRendering), + offsetof(nsStyleText, mTextRendering), eStyleAnimType_EnumU8) CSS_PROP_SVGRESET( vector-effect, @@ -4424,7 +4447,6 @@ CSS_PROP_FONT( #undef CSS_PROP_DISPLAY #undef CSS_PROP_VISIBILITY #undef CSS_PROP_CONTENT -#undef CSS_PROP_QUOTES #undef CSS_PROP_USERINTERFACE #undef CSS_PROP_UIRESET #undef CSS_PROP_TABLE @@ -4438,6 +4460,7 @@ CSS_PROP_FONT( #undef CSS_PROP_SVG #undef CSS_PROP_SVGRESET #undef CSS_PROP_VARIABLES +#undef CSS_PROP_EFFECTS #else /* !defined(USED_CSS_PROP) */ @@ -4481,10 +4504,6 @@ CSS_PROP_FONT( #undef CSS_PROP_CONTENT #undef DEFINED_CSS_PROP_CONTENT #endif -#ifdef DEFINED_CSS_PROP_QUOTES -#undef CSS_PROP_QUOTES -#undef DEFINED_CSS_PROP_QUOTES -#endif #ifdef DEFINED_CSS_PROP_USERINTERFACE #undef CSS_PROP_USERINTERFACE #undef DEFINED_CSS_PROP_USERINTERFACE @@ -4537,6 +4556,10 @@ CSS_PROP_FONT( #undef CSS_PROP_VARIABLES #undef DEFINED_CSS_PROP_VARIABLES #endif +#ifdef DEFINED_CSS_PROP_EFFECTS +#undef CSS_PROP_EFFECTS +#undef DEFINED_CSS_PROP_EFFECTS +#endif #endif /* !defined(USED_CSS_PROP) */ diff --git a/layout/style/nsCSSProps.cpp b/layout/style/nsCSSProps.cpp index 239e689cec..d827c69ee5 100644 --- a/layout/style/nsCSSProps.cpp +++ b/layout/style/nsCSSProps.cpp @@ -890,7 +890,9 @@ const KTableEntry nsCSSProps::kImageLayerAttachmentKTable[] = { static_assert(NS_STYLE_IMAGELAYER_CLIP_BORDER == NS_STYLE_IMAGELAYER_ORIGIN_BORDER && NS_STYLE_IMAGELAYER_CLIP_PADDING == NS_STYLE_IMAGELAYER_ORIGIN_PADDING && NS_STYLE_IMAGELAYER_CLIP_CONTENT == NS_STYLE_IMAGELAYER_ORIGIN_CONTENT, - "bg-clip and bg-origin style constants must agree"); + "Except background-clip:text, all {background,mask}-clip and " + "{background,mask}-origin style constants must agree"); + const KTableEntry nsCSSProps::kImageLayerOriginKTable[] = { { eCSSKeyword_border_box, NS_STYLE_IMAGELAYER_ORIGIN_BORDER }, { eCSSKeyword_padding_box, NS_STYLE_IMAGELAYER_ORIGIN_PADDING }, @@ -898,6 +900,23 @@ const KTableEntry nsCSSProps::kImageLayerOriginKTable[] = { { eCSSKeyword_UNKNOWN, -1 } }; +KTableEntry nsCSSProps::kBackgroundClipKTable[] = { + { eCSSKeyword_border_box, NS_STYLE_IMAGELAYER_CLIP_BORDER }, + { eCSSKeyword_padding_box, NS_STYLE_IMAGELAYER_CLIP_PADDING }, + { eCSSKeyword_content_box, NS_STYLE_IMAGELAYER_CLIP_CONTENT }, + // The next entry is controlled by the layout.css.background-clip-text.enabled + // pref. + { eCSSKeyword_text, NS_STYLE_IMAGELAYER_CLIP_TEXT }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +#if 0 +static_assert(ArrayLength(nsCSSProps::kImageLayerOriginKTable) == + ArrayLength(nsCSSProps::kBackgroundClipKTable) - 1, + "background-clip has one extra value, which is text, compared" + "to {background,mask}-origin"); +#endif + // Note: Don't change this table unless you update // ParseImageLayerPosition! @@ -1270,6 +1289,9 @@ KTableEntry nsCSSProps::kDisplayKTable[] = { // The next two entries are controlled by the layout.css.grid.enabled pref. { eCSSKeyword_grid, NS_STYLE_DISPLAY_GRID }, { eCSSKeyword_inline_grid, NS_STYLE_DISPLAY_INLINE_GRID }, + // The next two entries are controlled by the layout.css.prefixes.webkit pref. + { eCSSKeyword__webkit_box, NS_STYLE_DISPLAY_WEBKIT_BOX }, + { eCSSKeyword__webkit_inline_box, NS_STYLE_DISPLAY_WEBKIT_INLINE_BOX }, // The next entry is controlled by the layout.css.display-contents.enabled // pref. { eCSSKeyword_contents, NS_STYLE_DISPLAY_CONTENTS }, @@ -2369,6 +2391,12 @@ const KTableEntry nsCSSProps::kVectorEffectKTable[] = { { eCSSKeyword_UNKNOWN, -1 } }; +const KTableEntry nsCSSProps::kColorAdjustKTable[] = { + { eCSSKeyword_economy, NS_STYLE_COLOR_ADJUST_ECONOMY }, + { eCSSKeyword_exact, NS_STYLE_COLOR_ADJUST_EXACT }, + { eCSSKeyword_UNKNOWN, -1 } +}; + const KTableEntry nsCSSProps::kColorInterpolationKTable[] = { { eCSSKeyword_auto, NS_STYLE_COLOR_INTERPOLATION_AUTO }, { eCSSKeyword_srgb, NS_STYLE_COLOR_INTERPOLATION_SRGB }, @@ -3142,13 +3170,6 @@ enum ContentCheckCounter { ePropertyCount_for_Content }; -enum QuotesCheckCounter { - #define CSS_PROP_QUOTES ENUM_DATA_FOR_PROPERTY - #include "nsCSSPropList.h" - #undef CSS_PROP_QUOTES - ePropertyCount_for_Quotes -}; - enum TextCheckCounter { #define CSS_PROP_TEXT ENUM_DATA_FOR_PROPERTY #include "nsCSSPropList.h" @@ -3212,6 +3233,13 @@ enum VariablesCheckCounter { ePropertyCount_for_Variables }; +enum EffectsCheckCounter { + #define CSS_PROP_EFFECTS ENUM_DATA_FOR_PROPERTY + #include "nsCSSPropList.h" + #undef CSS_PROP_EFFECTS + ePropertyCount_for_Effects +}; + #undef ENUM_DATA_FOR_PROPERTY /* static */ const size_t diff --git a/layout/style/nsCSSProps.h b/layout/style/nsCSSProps.h index af096af573..f928407346 100644 --- a/layout/style/nsCSSProps.h +++ b/layout/style/nsCSSProps.h @@ -678,6 +678,9 @@ public: static const KTableEntry kImageLayerSizeKTable[]; static const KTableEntry kImageLayerCompositeKTable[]; static const KTableEntry kImageLayerModeKTable[]; + // Not const because we modify its entries when the pref + // "layout.css.background-clip.text" changes: + static KTableEntry kBackgroundClipKTable[]; static const KTableEntry kBlendModeKTable[]; static const KTableEntry kBorderCollapseKTable[]; static const KTableEntry kBorderColorKTable[]; @@ -707,6 +710,7 @@ public: static const KTableEntry kVectorEffectKTable[]; static const KTableEntry kTextAnchorKTable[]; static const KTableEntry kTextRenderingKTable[]; + static const KTableEntry kColorAdjustKTable[]; static const KTableEntry kColorInterpolationKTable[]; static const KTableEntry kColumnFillKTable[]; static const KTableEntry kBoxPropSourceKTable[]; diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index 22bc6acc27..ca22e7b49e 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -985,11 +985,21 @@ nsComputedDOMStyle::DoGetColor() return val.forget(); } +already_AddRefed +nsComputedDOMStyle::DoGetColorAdjust() +{ + RefPtr val = new nsROCSSPrimitiveValue; + val->SetIdent( + nsCSSProps::ValueToKeywordEnum(StyleVisibility()->mColorAdjust, + nsCSSProps::kColorAdjustKTable)); + return val.forget(); +} + already_AddRefed nsComputedDOMStyle::DoGetOpacity() { RefPtr val = new nsROCSSPrimitiveValue; - val->SetNumber(StyleDisplay()->mOpacity); + val->SetNumber(StyleEffects()->mOpacity); return val.forget(); } @@ -1467,9 +1477,9 @@ nsComputedDOMStyle::DoGetCounterReset() already_AddRefed nsComputedDOMStyle::DoGetQuotes() { - const nsStyleQuotes *quotes = StyleQuotes(); + const auto& quotePairs = StyleList()->GetQuotePairs(); - if (quotes->QuotesCount() == 0) { + if (quotePairs.IsEmpty()) { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(eCSSKeyword_none); return val.forget(); @@ -1477,15 +1487,15 @@ nsComputedDOMStyle::DoGetQuotes() RefPtr valueList = GetROCSSValueList(false); - for (uint32_t i = 0, i_end = quotes->QuotesCount(); i < i_end; ++i) { + for (const auto& quotePair : quotePairs) { RefPtr openVal = new nsROCSSPrimitiveValue; RefPtr closeVal = new nsROCSSPrimitiveValue; nsString s; - nsStyleUtil::AppendEscapedCSSString(*quotes->OpenQuoteAt(i), s); + nsStyleUtil::AppendEscapedCSSString(quotePair.first, s); openVal->SetString(s); s.Truncate(); - nsStyleUtil::AppendEscapedCSSString(*quotes->CloseQuoteAt(i), s); + nsStyleUtil::AppendEscapedCSSString(quotePair.second, s); closeVal->SetString(s); valueList->AppendCSSValue(openVal.forget()); @@ -1841,7 +1851,7 @@ nsComputedDOMStyle::DoGetBackgroundClip() return GetBackgroundList(&nsStyleImageLayers::Layer::mClip, &nsStyleImageLayers::mClipCount, StyleBackground()->mImage, - nsCSSProps::kImageLayerOriginKTable); + nsCSSProps::kBackgroundClipKTable); } already_AddRefed @@ -3161,15 +3171,10 @@ nsComputedDOMStyle::DoGetOutlineWidth() nscoord width; if (outline->GetOutlineStyle() == NS_STYLE_BORDER_STYLE_NONE) { - NS_ASSERTION(outline->GetOutlineWidth(width) && width == 0, - "unexpected width"); + NS_ASSERTION(outline->GetOutlineWidth() == 0, "unexpected width"); width = 0; } else { -#ifdef DEBUG - bool res = -#endif - outline->GetOutlineWidth(width); - NS_ASSERTION(res, "percent outline doesn't exist"); + width = outline->GetOutlineWidth(); } val->SetAppUnits(width); @@ -3376,7 +3381,7 @@ nsComputedDOMStyle::DoGetBoxDecorationBreak() already_AddRefed nsComputedDOMStyle::DoGetBoxShadow() { - return GetCSSShadowArray(StyleBorder()->mBoxShadow, + return GetCSSShadowArray(StyleEffects()->mBoxShadow, StyleColor()->mColor, true); } @@ -3529,7 +3534,7 @@ already_AddRefed nsComputedDOMStyle::DoGetVerticalAlign() { RefPtr val = new nsROCSSPrimitiveValue; - SetValueToCoord(val, StyleTextReset()->mVerticalAlign, false, + SetValueToCoord(val, StyleDisplay()->mVerticalAlign, false, &nsComputedDOMStyle::GetLineHeightCoord, nsCSSProps::kVerticalAlignKTable); return val.forget(); @@ -3920,12 +3925,20 @@ nsComputedDOMStyle::DoGetTextSizeAdjust() return val.forget(); } +already_AddRefed +nsComputedDOMStyle::DoGetWebkitTextFillColor() +{ + RefPtr val = new nsROCSSPrimitiveValue; + SetToRGBAColor(val, mStyleContext->GetTextFillColor()); + return val.forget(); +} + already_AddRefed nsComputedDOMStyle::DoGetPointerEvents() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( - nsCSSProps::ValueToKeywordEnum(StyleVisibility()->mPointerEvents, + nsCSSProps::ValueToKeywordEnum(StyleUserInterface()->mPointerEvents, nsCSSProps::kPointerEventsKTable)); return val.forget(); } @@ -4460,9 +4473,9 @@ nsComputedDOMStyle::DoGetClip() { RefPtr val = new nsROCSSPrimitiveValue; - const nsStyleDisplay* display = StyleDisplay(); + const nsStyleEffects* effects = StyleEffects(); - if (display->mClipFlags == NS_STYLE_CLIP_AUTO) { + if (effects->mClipFlags == NS_STYLE_CLIP_AUTO) { val->SetIdent(eCSSKeyword_auto); } else { // create the cssvalues for the sides, stick them in the rect object @@ -4472,28 +4485,28 @@ nsComputedDOMStyle::DoGetClip() nsROCSSPrimitiveValue *leftVal = new nsROCSSPrimitiveValue; nsDOMCSSRect * domRect = new nsDOMCSSRect(topVal, rightVal, bottomVal, leftVal); - if (display->mClipFlags & NS_STYLE_CLIP_TOP_AUTO) { + if (effects->mClipFlags & NS_STYLE_CLIP_TOP_AUTO) { topVal->SetIdent(eCSSKeyword_auto); } else { - topVal->SetAppUnits(display->mClip.y); + topVal->SetAppUnits(effects->mClip.y); } - if (display->mClipFlags & NS_STYLE_CLIP_RIGHT_AUTO) { + if (effects->mClipFlags & NS_STYLE_CLIP_RIGHT_AUTO) { rightVal->SetIdent(eCSSKeyword_auto); } else { - rightVal->SetAppUnits(display->mClip.width + display->mClip.x); + rightVal->SetAppUnits(effects->mClip.width + effects->mClip.x); } - if (display->mClipFlags & NS_STYLE_CLIP_BOTTOM_AUTO) { + if (effects->mClipFlags & NS_STYLE_CLIP_BOTTOM_AUTO) { bottomVal->SetIdent(eCSSKeyword_auto); } else { - bottomVal->SetAppUnits(display->mClip.height + display->mClip.y); + bottomVal->SetAppUnits(effects->mClip.height + effects->mClip.y); } - if (display->mClipFlags & NS_STYLE_CLIP_LEFT_AUTO) { + if (effects->mClipFlags & NS_STYLE_CLIP_LEFT_AUTO) { leftVal->SetIdent(eCSSKeyword_auto); } else { - leftVal->SetAppUnits(display->mClip.x); + leftVal->SetAppUnits(effects->mClip.x); } val->SetRect(domRect); } @@ -4799,7 +4812,7 @@ already_AddRefed nsComputedDOMStyle::DoGetMixBlendMode() { RefPtr val = new nsROCSSPrimitiveValue; - val->SetIdent(nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mMixBlendMode, + val->SetIdent(nsCSSProps::ValueToKeywordEnum(StyleEffects()->mMixBlendMode, nsCSSProps::kBlendModeKTable)); return val.forget(); } @@ -5719,7 +5732,7 @@ nsComputedDOMStyle::DoGetImageRendering() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( - nsCSSProps::ValueToKeywordEnum(StyleSVG()->mImageRendering, + nsCSSProps::ValueToKeywordEnum(StyleVisibility()->mImageRendering, nsCSSProps::kImageRenderingKTable)); return val.forget(); } @@ -5739,7 +5752,7 @@ nsComputedDOMStyle::DoGetTextRendering() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( - nsCSSProps::ValueToKeywordEnum(StyleSVG()->mTextRendering, + nsCSSProps::ValueToKeywordEnum(StyleText()->mTextRendering, nsCSSProps::kTextRenderingKTable)); return val.forget(); } @@ -6017,7 +6030,7 @@ nsComputedDOMStyle::CreatePrimitiveValueForStyleFilter( already_AddRefed nsComputedDOMStyle::DoGetFilter() { - const nsTArray& filters = StyleSVGReset()->mFilters; + const nsTArray& filters = StyleEffects()->mFilters; if (filters.IsEmpty()) { RefPtr value = new nsROCSSPrimitiveValue; diff --git a/layout/style/nsComputedDOMStyle.h b/layout/style/nsComputedDOMStyle.h index 0bd8e555f4..f12bfc5280 100644 --- a/layout/style/nsComputedDOMStyle.h +++ b/layout/style/nsComputedDOMStyle.h @@ -425,8 +425,10 @@ private: already_AddRefed DoGetHyphens(); already_AddRefed DoGetTabSize(); already_AddRefed DoGetTextSizeAdjust(); + already_AddRefed DoGetWebkitTextFillColor(); /* Visibility properties */ + already_AddRefed DoGetColorAdjust(); already_AddRefed DoGetOpacity(); already_AddRefed DoGetPointerEvents(); already_AddRefed DoGetVisibility(); diff --git a/layout/style/nsComputedDOMStylePropertyList.h b/layout/style/nsComputedDOMStylePropertyList.h index be5ea655ad..4c70ac8caa 100644 --- a/layout/style/nsComputedDOMStylePropertyList.h +++ b/layout/style/nsComputedDOMStylePropertyList.h @@ -102,6 +102,7 @@ COMPUTED_STYLE_PROP(caption_side, CaptionSide) COMPUTED_STYLE_PROP(clear, Clear) COMPUTED_STYLE_PROP(clip, Clip) COMPUTED_STYLE_PROP(color, Color) +COMPUTED_STYLE_PROP(color_adjust, ColorAdjust) COMPUTED_STYLE_PROP(column_count, ColumnCount) COMPUTED_STYLE_PROP(column_fill, ColumnFill) COMPUTED_STYLE_PROP(column_gap, ColumnGap) @@ -297,6 +298,12 @@ COMPUTED_STYLE_PROP(user_select, UserSelect) COMPUTED_STYLE_PROP(_moz_window_dragging, WindowDragging) COMPUTED_STYLE_PROP(_moz_window_shadow, WindowShadow) +/* ********************************** *\ + * Implementations of -webkit- styles * +\* ********************************** */ + +COMPUTED_STYLE_PROP(_webkit_text_fill_color, WebkitTextFillColor) + /* ***************************** *\ * Implementations of SVG styles * \* ***************************** */ diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index cd821607f6..5ab3ea67b2 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -193,6 +193,7 @@ nsRuleNode::EnsureBlockDisplay(uint8_t& display, case NS_STYLE_DISPLAY_TABLE : case NS_STYLE_DISPLAY_BLOCK : case NS_STYLE_DISPLAY_FLEX : + case NS_STYLE_DISPLAY_WEBKIT_BOX : case NS_STYLE_DISPLAY_GRID : // do not muck with these at all - already blocks // This is equivalent to nsStyleDisplay::IsBlockOutside. (XXX Maybe we @@ -211,6 +212,11 @@ nsRuleNode::EnsureBlockDisplay(uint8_t& display, display = NS_STYLE_DISPLAY_FLEX; break; + case NS_STYLE_DISPLAY_WEBKIT_INLINE_BOX: + // make -webkit-inline-box containers into -webkit-box containers + display = NS_STYLE_DISPLAY_WEBKIT_BOX; + break; + case NS_STYLE_DISPLAY_INLINE_GRID: // make inline grid containers into grid containers display = NS_STYLE_DISPLAY_GRID; @@ -240,6 +246,9 @@ nsRuleNode::EnsureInlineDisplay(uint8_t& display) case NS_STYLE_DISPLAY_FLEX : display = NS_STYLE_DISPLAY_INLINE_FLEX; break; + case NS_STYLE_DISPLAY_WEBKIT_BOX : + display = NS_STYLE_DISPLAY_WEBKIT_INLINE_BOX; + break; case NS_STYLE_DISPLAY_GRID : display = NS_STYLE_DISPLAY_INLINE_GRID; break; @@ -1440,50 +1449,8 @@ nsRuleNode::operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW // Overridden to prevent the global delete from being called, since the memory // came out of an nsIArena instead of the global delete operator's heap. void -nsRuleNode::DestroyInternal(nsRuleNode ***aDestroyQueueTail) +nsRuleNode::Destroy() { - nsRuleNode *destroyQueue, **destroyQueueTail; - if (aDestroyQueueTail) { - destroyQueueTail = *aDestroyQueueTail; - } else { - destroyQueue = nullptr; - destroyQueueTail = &destroyQueue; - } - - if (ChildrenAreHashed()) { - PLDHashTable *children = ChildrenHash(); - for (auto iter = children->Iter(); !iter.Done(); iter.Next()) { - auto entry = static_cast(iter.Get()); - *destroyQueueTail = entry->mRuleNode; - destroyQueueTail = &entry->mRuleNode->mNextSibling; - } - *destroyQueueTail = nullptr; // ensure null-termination - delete children; - } else if (HaveChildren()) { - *destroyQueueTail = ChildrenList(); - do { - destroyQueueTail = &(*destroyQueueTail)->mNextSibling; - } while (*destroyQueueTail); - } - mChildren.asVoid = nullptr; - - if (aDestroyQueueTail) { - // Our caller destroys the queue. - *aDestroyQueueTail = destroyQueueTail; - } else { - // We have to do destroy the queue. When we destroy each node, it - // will add its children to the queue. - while (destroyQueue) { - nsRuleNode *cur = destroyQueue; - destroyQueue = destroyQueue->mNextSibling; - if (!destroyQueue) { - NS_ASSERTION(destroyQueueTail == &cur->mNextSibling, "mangled list"); - destroyQueueTail = &destroyQueue; - } - cur->DestroyInternal(&destroyQueueTail); - } - } - // Destroy ourselves. this->~nsRuleNode(); @@ -1492,10 +1459,11 @@ nsRuleNode::DestroyInternal(nsRuleNode ***aDestroyQueueTail) mPresContext->PresShell()->FreeByObjectID(eArenaObjectID_nsRuleNode, this); } -nsRuleNode* nsRuleNode::CreateRootNode(nsPresContext* aPresContext) +already_AddRefed +nsRuleNode::CreateRootNode(nsPresContext* aPresContext) { - return new (aPresContext) - nsRuleNode(aPresContext, nullptr, nullptr, SheetType::Unknown, false); + return do_AddRef(new (aPresContext) + nsRuleNode(aPresContext, nullptr, nullptr, SheetType::Unknown, false)); } nsRuleNode::nsRuleNode(nsPresContext* aContext, nsRuleNode* aParent, @@ -1518,21 +1486,11 @@ nsRuleNode::nsRuleNode(nsPresContext* aContext, nsRuleNode* aParent, mChildren.asVoid = nullptr; MOZ_COUNT_CTOR(nsRuleNode); - if (mRule) { - mRule->AddRef(); - } - NS_ASSERTION(IsRoot() || GetLevel() == aLevel, "not enough bits"); NS_ASSERTION(IsRoot() || IsImportantRule() == aIsImportant, "yikes"); - /* If IsRoot(), then aContext->StyleSet() is typically null at this - point. In any case, we don't want to treat the root rulenode as - unused. */ - if (!IsRoot()) { - mParent->AddRef(); - MOZ_ASSERT(aContext->StyleSet()->IsGecko(), - "ServoStyleSets should not have rule nodes"); - aContext->StyleSet()->AsGecko()->RuleNodeUnused(); - } + MOZ_ASSERT(aContext->StyleSet()->IsGecko(), + "ServoStyleSets should not have rule nodes"); + aContext->StyleSet()->AsGecko()->RuleNodeUnused(this, /* aMayGC = */ false); // nsStyleSet::GetContext depends on there being only one animation // rule. @@ -1544,12 +1502,14 @@ nsRuleNode::nsRuleNode(nsPresContext* aContext, nsRuleNode* aParent, nsRuleNode::~nsRuleNode() { + MOZ_ASSERT(!HaveChildren()); MOZ_COUNT_DTOR(nsRuleNode); + if (mParent) { + mParent->RemoveChild(this); + } + if (mStyleData.mResetData || mStyleData.mInheritedData) mStyleData.Destroy(mDependentBits, mPresContext); - if (mRule) { - mRule->Release(); - } } nsRuleNode* @@ -1657,6 +1617,34 @@ nsRuleNode::ConvertChildrenToHash(int32_t aNumKids) SetChildrenHash(hash); } +void +nsRuleNode::RemoveChild(nsRuleNode* aNode) +{ + MOZ_ASSERT(HaveChildren()); + if (ChildrenAreHashed()) { + PLDHashTable* children = ChildrenHash(); + Key key = aNode->GetKey(); + MOZ_ASSERT(children->Search(&key)); + children->Remove(&key); + if (children->EntryCount() == 0) { + delete children; + mChildren.asVoid = nullptr; + } + } else { + // This linear traversal is unfortunate, but we do the same thing when + // adding nodes. The traversal is bounded by kMaxChildrenInList. + nsRuleNode** curr = &mChildren.asList; + while (*curr != aNode) { + curr = &((*curr)->mNextSibling); + MOZ_ASSERT(*curr); + } + *curr = (*curr)->mNextSibling; + + // If there was one element in the list, this sets mChildren.asList + // to 0, and HaveChildren() will return false. + } +} + inline void nsRuleNode::PropagateNoneBit(uint32_t aBit, nsRuleNode* aHighestNode) { @@ -1928,12 +1916,6 @@ static const uint32_t gContentFlags[] = { #undef CSS_PROP_CONTENT }; -static const uint32_t gQuotesFlags[] = { -#define CSS_PROP_QUOTES FLAG_DATA_FOR_PROPERTY -#include "nsCSSPropList.h" -#undef CSS_PROP_QUOTES -}; - static const uint32_t gTextFlags[] = { #define CSS_PROP_TEXT FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" @@ -1994,6 +1976,12 @@ static_assert(sizeof(gVariablesFlags) == sizeof(uint32_t), "if nsStyleVariables has properties now you can remove the dummy " "gVariablesFlags entry"); +static const uint32_t gEffectsFlags[] = { +#define CSS_PROP_EFFECTS FLAG_DATA_FOR_PROPERTY +#include "nsCSSPropList.h" +#undef CSS_PROP_EFFECTS +}; + #undef FLAG_DATA_FOR_PROPERTY static const uint32_t* gFlagsByStruct[] = { @@ -2463,7 +2451,7 @@ nsRuleNode::SetDefaultOnRoot(const nsStyleStructID aSID, nsStyleContext* aContex } case eStyleStruct_Display: { - nsStyleDisplay* disp = new (mPresContext) nsStyleDisplay(); + nsStyleDisplay* disp = new (mPresContext) nsStyleDisplay(mPresContext); aContext->SetStyle(eStyleStruct_Display, disp); return disp; } @@ -2481,7 +2469,7 @@ nsRuleNode::SetDefaultOnRoot(const nsStyleStructID aSID, nsStyleContext* aContex } case eStyleStruct_TextReset: { - nsStyleTextReset* text = new (mPresContext) nsStyleTextReset(); + nsStyleTextReset* text = new (mPresContext) nsStyleTextReset(mPresContext); aContext->SetStyle(eStyleStruct_TextReset, text); return text; } @@ -2493,13 +2481,13 @@ nsRuleNode::SetDefaultOnRoot(const nsStyleStructID aSID, nsStyleContext* aContex } case eStyleStruct_Background: { - nsStyleBackground* bg = new (mPresContext) nsStyleBackground(); + nsStyleBackground* bg = new (mPresContext) nsStyleBackground(mPresContext); aContext->SetStyle(eStyleStruct_Background, bg); return bg; } case eStyleStruct_Margin: { - nsStyleMargin* margin = new (mPresContext) nsStyleMargin(); + nsStyleMargin* margin = new (mPresContext) nsStyleMargin(mPresContext); aContext->SetStyle(eStyleStruct_Margin, margin); return margin; } @@ -2511,7 +2499,7 @@ nsRuleNode::SetDefaultOnRoot(const nsStyleStructID aSID, nsStyleContext* aContex } case eStyleStruct_Padding: { - nsStylePadding* padding = new (mPresContext) nsStylePadding(); + nsStylePadding* padding = new (mPresContext) nsStylePadding(mPresContext); aContext->SetStyle(eStyleStruct_Padding, padding); return padding; } @@ -2529,49 +2517,43 @@ nsRuleNode::SetDefaultOnRoot(const nsStyleStructID aSID, nsStyleContext* aContex } case eStyleStruct_Position: { - nsStylePosition* pos = new (mPresContext) nsStylePosition(); + nsStylePosition* pos = new (mPresContext) nsStylePosition(mPresContext); aContext->SetStyle(eStyleStruct_Position, pos); return pos; } case eStyleStruct_Table: { - nsStyleTable* table = new (mPresContext) nsStyleTable(); + nsStyleTable* table = new (mPresContext) nsStyleTable(mPresContext); aContext->SetStyle(eStyleStruct_Table, table); return table; } case eStyleStruct_TableBorder: { - nsStyleTableBorder* table = new (mPresContext) nsStyleTableBorder(); + nsStyleTableBorder* table = new (mPresContext) nsStyleTableBorder(mPresContext); aContext->SetStyle(eStyleStruct_TableBorder, table); return table; } case eStyleStruct_Content: { - nsStyleContent* content = new (mPresContext) nsStyleContent(); + nsStyleContent* content = new (mPresContext) nsStyleContent(mPresContext); aContext->SetStyle(eStyleStruct_Content, content); return content; } - case eStyleStruct_Quotes: - { - nsStyleQuotes* quotes = new (mPresContext) nsStyleQuotes(); - aContext->SetStyle(eStyleStruct_Quotes, quotes); - return quotes; - } case eStyleStruct_UserInterface: { - nsStyleUserInterface* ui = new (mPresContext) nsStyleUserInterface(); + nsStyleUserInterface* ui = new (mPresContext) nsStyleUserInterface(mPresContext); aContext->SetStyle(eStyleStruct_UserInterface, ui); return ui; } case eStyleStruct_UIReset: { - nsStyleUIReset* ui = new (mPresContext) nsStyleUIReset(); + nsStyleUIReset* ui = new (mPresContext) nsStyleUIReset(mPresContext); aContext->SetStyle(eStyleStruct_UIReset, ui); return ui; } case eStyleStruct_XUL: { - nsStyleXUL* xul = new (mPresContext) nsStyleXUL(); + nsStyleXUL* xul = new (mPresContext) nsStyleXUL(mPresContext); aContext->SetStyle(eStyleStruct_XUL, xul); return xul; } @@ -2583,22 +2565,28 @@ nsRuleNode::SetDefaultOnRoot(const nsStyleStructID aSID, nsStyleContext* aContex } case eStyleStruct_SVG: { - nsStyleSVG* svg = new (mPresContext) nsStyleSVG(); + nsStyleSVG* svg = new (mPresContext) nsStyleSVG(mPresContext); aContext->SetStyle(eStyleStruct_SVG, svg); return svg; } case eStyleStruct_SVGReset: { - nsStyleSVGReset* svgReset = new (mPresContext) nsStyleSVGReset(); + nsStyleSVGReset* svgReset = new (mPresContext) nsStyleSVGReset(mPresContext); aContext->SetStyle(eStyleStruct_SVGReset, svgReset); return svgReset; } case eStyleStruct_Variables: { - nsStyleVariables* vars = new (mPresContext) nsStyleVariables(); + nsStyleVariables* vars = new (mPresContext) nsStyleVariables(mPresContext); aContext->SetStyle(eStyleStruct_Variables, vars); return vars; } + case eStyleStruct_Effects: + { + nsStyleEffects* effects = new (mPresContext) nsStyleEffects(mPresContext); + aContext->SetStyle(eStyleStruct_Effects, effects); + return effects; + } default: /* * unhandled case: nsStyleStructID_Length. @@ -2614,13 +2602,12 @@ nsRuleNode::SetDefaultOnRoot(const nsStyleStructID aSID, nsStyleContext* aContex * Begin an nsRuleNode::Compute*Data function for an inherited struct. * * @param type_ The nsStyle* type this function computes. - * @param ctorargs_ The arguments used for the default nsStyle* constructor. * @param data_ Variable (declared here) holding the result of this * function. * @param parentdata_ Variable (declared here) holding the parent style * context's data for this struct. */ -#define COMPUTE_START_INHERITED(type_, ctorargs_, data_, parentdata_) \ +#define COMPUTE_START_INHERITED(type_, data_, parentdata_) \ NS_ASSERTION(aRuleDetail != eRuleFullInherited, \ "should not have bothered calling Compute*Data"); \ \ @@ -2640,7 +2627,7 @@ nsRuleNode::SetDefaultOnRoot(const nsStyleStructID aSID, nsStyleContext* aContex if (parentContext) { \ parentdata_ = parentContext->Style##type_(); \ } else { \ - maybeFakeParentData.emplace ctorargs_; \ + maybeFakeParentData.emplace(mPresContext); \ parentdata_ = maybeFakeParentData.ptr(); \ } \ } \ @@ -2648,7 +2635,7 @@ nsRuleNode::SetDefaultOnRoot(const nsStyleStructID aSID, nsStyleContext* aContex /* no need to copy construct an nsStyleVariables, as we will copy */ \ /* inherited variables (and call SetUncacheable()) in */ \ /* ComputeVariablesData */ \ - data_ = new (mPresContext) nsStyle##type_ ctorargs_; \ + data_ = new (mPresContext) nsStyle##type_(mPresContext); \ else if (aStartStruct) \ /* We only need to compute the delta between this computed data and */ \ /* our computed data. */ \ @@ -2662,10 +2649,10 @@ nsRuleNode::SetDefaultOnRoot(const nsStyleStructID aSID, nsStyleContext* aContex if (parentdata_) \ data_ = new (mPresContext) nsStyle##type_(*parentdata_); \ else \ - data_ = new (mPresContext) nsStyle##type_ ctorargs_; \ + data_ = new (mPresContext) nsStyle##type_(mPresContext); \ } \ else \ - data_ = new (mPresContext) nsStyle##type_ ctorargs_; \ + data_ = new (mPresContext) nsStyle##type_(mPresContext); \ } \ \ if (!parentdata_) \ @@ -2675,13 +2662,12 @@ nsRuleNode::SetDefaultOnRoot(const nsStyleStructID aSID, nsStyleContext* aContex * Begin an nsRuleNode::Compute*Data function for a reset struct. * * @param type_ The nsStyle* type this function computes. - * @param ctorargs_ The arguments used for the default nsStyle* constructor. * @param data_ Variable (declared here) holding the result of this * function. * @param parentdata_ Variable (declared here) holding the parent style * context's data for this struct. */ -#define COMPUTE_START_RESET(type_, ctorargs_, data_, parentdata_) \ +#define COMPUTE_START_RESET(type_, data_, parentdata_) \ NS_ASSERTION(aRuleDetail != eRuleFullInherited, \ "should not have bothered calling Compute*Data"); \ \ @@ -2700,7 +2686,7 @@ nsRuleNode::SetDefaultOnRoot(const nsStyleStructID aSID, nsStyleContext* aContex data_ = new (mPresContext) \ nsStyle##type_(*static_cast(aStartStruct)); \ else \ - data_ = new (mPresContext) nsStyle##type_ ctorargs_; \ + data_ = new (mPresContext) nsStyle##type_(mPresContext); \ \ /* If |conditions.Cacheable()| might be true by the time we're done, we */ \ /* can't call parentContext->Style##type_() since it could recur into */ \ @@ -2713,7 +2699,7 @@ nsRuleNode::SetDefaultOnRoot(const nsStyleStructID aSID, nsStyleContext* aContex if (parentContext) { \ parentdata_ = parentContext->Style##type_(); \ } else { \ - maybeFakeParentData.emplace ctorargs_; \ + maybeFakeParentData.emplace(mPresContext); \ parentdata_ = maybeFakeParentData.ptr(); \ } \ } \ @@ -4049,7 +4035,7 @@ nsRuleNode::ComputeFontData(void* aStartStruct, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { - COMPUTE_START_INHERITED(Font, (mPresContext), font, parentFont) + COMPUTE_START_INHERITED(Font, font, parentFont) // NOTE: The |aRuleDetail| passed in is a little bit conservative due // to the -moz-system-font property. We really don't need to consider @@ -4371,7 +4357,7 @@ nsRuleNode::ComputeTextData(void* aStartStruct, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { - COMPUTE_START_INHERITED(Text, (mPresContext), text, parentText) + COMPUTE_START_INHERITED(Text, text, parentText) // tab-size: integer, inherit SetDiscrete(*aRuleData->ValueForTabSize(), @@ -4704,6 +4690,34 @@ nsRuleNode::ComputeTextData(void* aStartStruct, MOZ_ASSERT_UNREACHABLE("Unknown value unit type"); } + // text-rendering: enum, inherit, initial + SetDiscrete(*aRuleData->ValueForTextRendering(), + text->mTextRendering, conditions, + SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, + parentText->mTextRendering, + NS_STYLE_TEXT_RENDERING_AUTO, 0, 0, 0, 0); + + // -webkit-text-fill-color: color, string, inherit, initial + const nsCSSValue* + webkitTextFillColorValue = aRuleData->ValueForWebkitTextFillColor(); + if (webkitTextFillColorValue->GetUnit() == eCSSUnit_Null) { + // We don't want to change anything in this case. + } else if (webkitTextFillColorValue->GetUnit() == eCSSUnit_Inherit || + webkitTextFillColorValue->GetUnit() == eCSSUnit_Unset) { + conditions.SetUncacheable(); + text->mWebkitTextFillColorForeground = parentText->mWebkitTextFillColorForeground; + text->mWebkitTextFillColor = parentText->mWebkitTextFillColor; + } else if ((webkitTextFillColorValue->GetUnit() == eCSSUnit_EnumColor && + webkitTextFillColorValue->GetIntValue() == NS_COLOR_CURRENTCOLOR) || + webkitTextFillColorValue->GetUnit() == eCSSUnit_Initial) { + text->mWebkitTextFillColorForeground = true; + text->mWebkitTextFillColor = mPresContext->DefaultColor(); + } else { + text->mWebkitTextFillColorForeground = false; + SetColor(*webkitTextFillColorValue, 0, mPresContext, aContext, + text->mWebkitTextFillColor, conditions); + } + // -moz-control-character-visibility: enum, inherit, initial SetDiscrete(*aRuleData->ValueForControlCharacterVisibility(), text->mControlCharacterVisibility, @@ -4723,20 +4737,7 @@ nsRuleNode::ComputeTextResetData(void* aStartStruct, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { - COMPUTE_START_RESET(TextReset, (), text, parentText) - - // vertical-align: enum, length, percent, calc, inherit - const nsCSSValue* verticalAlignValue = aRuleData->ValueForVerticalAlign(); - if (!SetCoord(*verticalAlignValue, text->mVerticalAlign, - parentText->mVerticalAlign, - SETCOORD_LPH | SETCOORD_ENUMERATED | SETCOORD_STORE_CALC, - aContext, mPresContext, conditions)) { - if (eCSSUnit_Initial == verticalAlignValue->GetUnit() || - eCSSUnit_Unset == verticalAlignValue->GetUnit()) { - text->mVerticalAlign.SetIntValue(NS_STYLE_VERTICAL_ALIGN_BASELINE, - eStyleUnit_Enumerated); - } - } + COMPUTE_START_RESET(TextReset, text, parentText) // text-decoration-line: enum (bit field), inherit, initial const nsCSSValue* decorationLineValue = @@ -4885,7 +4886,7 @@ nsRuleNode::ComputeUserInterfaceData(void* aStartStruct, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { - COMPUTE_START_INHERITED(UserInterface, (), ui, parentUI) + COMPUTE_START_INHERITED(UserInterface, ui, parentUI) // cursor: enum, url, inherit const nsCSSValue* cursorValue = aRuleData->ValueForCursor(); @@ -4969,6 +4970,13 @@ nsRuleNode::ComputeUserInterfaceData(void* aStartStruct, parentUI->mUserFocus, NS_STYLE_USER_FOCUS_NONE, 0, 0, 0, 0); + // pointer-events: enum, inherit, initial + SetDiscrete(*aRuleData->ValueForPointerEvents(), ui->mPointerEvents, + conditions, + SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, + parentUI->mPointerEvents, + NS_STYLE_POINTER_EVENTS_AUTO, 0, 0, 0, 0); + COMPUTE_END_INHERITED(UserInterface, ui) } @@ -4980,7 +4988,7 @@ nsRuleNode::ComputeUIResetData(void* aStartStruct, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { - COMPUTE_START_RESET(UIReset, (), ui, parentUI) + COMPUTE_START_RESET(UIReset, ui, parentUI) // user-select: enum, inherit, initial SetDiscrete(*aRuleData->ValueForUserSelect(), @@ -5226,7 +5234,7 @@ nsRuleNode::ComputeDisplayData(void* aStartStruct, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { - COMPUTE_START_RESET(Display, (), display, parentDisplay) + COMPUTE_START_RESET(Display, display, parentDisplay) // We may have ended up with aStartStruct's values of mDisplay and // mFloats, but those may not be correct if our style data overrides @@ -5623,11 +5631,6 @@ nsRuleNode::ComputeDisplayData(void* aStartStruct, } } - // opacity: factor, inherit, initial - SetFactor(*aRuleData->ValueForOpacity(), display->mOpacity, conditions, - parentDisplay->mOpacity, 1.0f, - SETFCT_OPACITY | SETFCT_UNSET_INITIAL); - // display: enum, inherit, initial SetDiscrete(*aRuleData->ValueForDisplay(), display->mDisplay, conditions, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, @@ -5640,13 +5643,6 @@ nsRuleNode::ComputeDisplayData(void* aStartStruct, parentDisplay->mContain, NS_STYLE_CONTAIN_NONE, 0, NS_STYLE_CONTAIN_NONE, 0, 0); - // mix-blend-mode: enum, inherit, initial - SetDiscrete(*aRuleData->ValueForMixBlendMode(), display->mMixBlendMode, - conditions, - SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, - parentDisplay->mMixBlendMode, NS_STYLE_BLEND_NORMAL, - 0, 0, 0, 0); - // scroll-behavior: enum, inherit, initial SetDiscrete(*aRuleData->ValueForScrollBehavior(), display->mScrollBehavior, conditions, @@ -5998,80 +5994,6 @@ nsRuleNode::ComputeDisplayData(void* aStartStruct, parentDisplay->mResize, NS_STYLE_RESIZE_NONE, 0, 0, 0, 0); - // clip property: length, auto, inherit - const nsCSSValue* clipValue = aRuleData->ValueForClip(); - switch (clipValue->GetUnit()) { - case eCSSUnit_Inherit: - conditions.SetUncacheable(); - display->mClipFlags = parentDisplay->mClipFlags; - display->mClip = parentDisplay->mClip; - break; - - case eCSSUnit_Initial: - case eCSSUnit_Unset: - case eCSSUnit_Auto: - display->mClipFlags = NS_STYLE_CLIP_AUTO; - display->mClip.SetRect(0,0,0,0); - break; - - case eCSSUnit_Null: - break; - - case eCSSUnit_Rect: { - const nsCSSRect& clipRect = clipValue->GetRectValue(); - - display->mClipFlags = NS_STYLE_CLIP_RECT; - - if (clipRect.mTop.GetUnit() == eCSSUnit_Auto) { - display->mClip.y = 0; - display->mClipFlags |= NS_STYLE_CLIP_TOP_AUTO; - } - else if (clipRect.mTop.IsLengthUnit()) { - display->mClip.y = CalcLength(clipRect.mTop, aContext, - mPresContext, conditions); - } - - if (clipRect.mBottom.GetUnit() == eCSSUnit_Auto) { - // Setting to NS_MAXSIZE for the 'auto' case ensures that - // the clip rect is nonempty. It is important that mClip be - // nonempty if the actual clip rect could be nonempty. - display->mClip.height = NS_MAXSIZE; - display->mClipFlags |= NS_STYLE_CLIP_BOTTOM_AUTO; - } - else if (clipRect.mBottom.IsLengthUnit()) { - display->mClip.height = CalcLength(clipRect.mBottom, aContext, - mPresContext, conditions) - - display->mClip.y; - } - - if (clipRect.mLeft.GetUnit() == eCSSUnit_Auto) { - display->mClip.x = 0; - display->mClipFlags |= NS_STYLE_CLIP_LEFT_AUTO; - } - else if (clipRect.mLeft.IsLengthUnit()) { - display->mClip.x = CalcLength(clipRect.mLeft, aContext, - mPresContext, conditions); - } - - if (clipRect.mRight.GetUnit() == eCSSUnit_Auto) { - // Setting to NS_MAXSIZE for the 'auto' case ensures that - // the clip rect is nonempty. It is important that mClip be - // nonempty if the actual clip rect could be nonempty. - display->mClip.width = NS_MAXSIZE; - display->mClipFlags |= NS_STYLE_CLIP_RIGHT_AUTO; - } - else if (clipRect.mRight.IsLengthUnit()) { - display->mClip.width = CalcLength(clipRect.mRight, aContext, - mPresContext, conditions) - - display->mClip.x; - } - break; - } - - default: - MOZ_ASSERT(false, "unrecognized clip unit"); - } - if (display->mDisplay != NS_STYLE_DISPLAY_NONE) { // CSS2 9.7 specifies display type corrections dealing with 'float' // and 'position'. Since generated content can't be floated or @@ -6262,6 +6184,19 @@ nsRuleNode::ComputeDisplayData(void* aStartStruct, MOZ_ASSERT(false, "unrecognized will-change unit"); } + // vertical-align: enum, length, percent, calc, inherit + const nsCSSValue* verticalAlignValue = aRuleData->ValueForVerticalAlign(); + if (!SetCoord(*verticalAlignValue, display->mVerticalAlign, + parentDisplay->mVerticalAlign, + SETCOORD_LPH | SETCOORD_ENUMERATED | SETCOORD_STORE_CALC, + aContext, mPresContext, conditions)) { + if (eCSSUnit_Initial == verticalAlignValue->GetUnit() || + eCSSUnit_Unset == verticalAlignValue->GetUnit()) { + display->mVerticalAlign.SetIntValue(NS_STYLE_VERTICAL_ALIGN_BASELINE, + eStyleUnit_Enumerated); + } + } + /* Convert -moz-transform-origin. */ const nsCSSValue* transformOriginValue = aRuleData->ValueForTransformOrigin(); @@ -6370,8 +6305,7 @@ nsRuleNode::ComputeVisibilityData(void* aStartStruct, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { - COMPUTE_START_INHERITED(Visibility, (mPresContext), - visibility, parentVisibility) + COMPUTE_START_INHERITED(Visibility, visibility, parentVisibility) // IMPORTANT: No properties in this struct have lengths in them. We // depend on this since CalcLengthWith can call StyleVisibility() @@ -6394,12 +6328,12 @@ nsRuleNode::ComputeVisibilityData(void* aStartStruct, parentVisibility->mVisible, NS_STYLE_VISIBILITY_VISIBLE, 0, 0, 0, 0); - // pointer-events: enum, inherit, initial - SetDiscrete(*aRuleData->ValueForPointerEvents(), visibility->mPointerEvents, - conditions, + // image-rendering: enum, inherit + SetDiscrete(*aRuleData->ValueForImageRendering(), + visibility->mImageRendering, conditions, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, - parentVisibility->mPointerEvents, - NS_STYLE_POINTER_EVENTS_AUTO, 0, 0, 0, 0); + parentVisibility->mImageRendering, + NS_STYLE_IMAGE_RENDERING_AUTO, 0, 0, 0, 0); // writing-mode: enum, inherit, initial SetDiscrete(*aRuleData->ValueForWritingMode(), visibility->mWritingMode, @@ -6453,6 +6387,12 @@ nsRuleNode::ComputeVisibilityData(void* aStartStruct, MOZ_ASSERT(orientation->GetUnit() == eCSSUnit_Null, "Should be null unit"); } + SetDiscrete(*aRuleData->ValueForColorAdjust(), visibility->mColorAdjust, + conditions, + SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, + parentVisibility->mColorAdjust, + NS_STYLE_COLOR_ADJUST_ECONOMY, 0, 0, 0, 0); + COMPUTE_END_INHERITED(Visibility, visibility) } @@ -6464,7 +6404,7 @@ nsRuleNode::ComputeColorData(void* aStartStruct, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { - COMPUTE_START_INHERITED(Color, (mPresContext), color, parentColor) + COMPUTE_START_INHERITED(Color, color, parentColor) // color: color, string, inherit // Special case for currentColor. According to CSS3, setting color to 'currentColor' @@ -6951,7 +6891,7 @@ nsRuleNode::ComputeBackgroundData(void* aStartStruct, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { - COMPUTE_START_RESET(Background, (), bg, parentBG) + COMPUTE_START_RESET(Background, bg, parentBG) // background-color: color, string, inherit const nsCSSValue* backColorValue = aRuleData->ValueForBackgroundColor(); @@ -7095,7 +7035,7 @@ nsRuleNode::ComputeMarginData(void* aStartStruct, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { - COMPUTE_START_RESET(Margin, (), margin, parentMargin) + COMPUTE_START_RESET(Margin, margin, parentMargin) // margin: length, percent, calc, inherit const nsCSSProperty* subprops = @@ -7201,7 +7141,7 @@ nsRuleNode::ComputeBorderData(void* aStartStruct, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { - COMPUTE_START_RESET(Border, (mPresContext), border, parentBorder) + COMPUTE_START_RESET(Border, border, parentBorder) // box-decoration-break: enum, inherit, initial SetDiscrete(*aRuleData->ValueForBoxDecorationBreak(), @@ -7210,33 +7150,6 @@ nsRuleNode::ComputeBorderData(void* aStartStruct, parentBorder->mBoxDecorationBreak, NS_STYLE_BOX_DECORATION_BREAK_SLICE, 0, 0, 0, 0); - // box-shadow: none, list, inherit, initial - const nsCSSValue* boxShadowValue = aRuleData->ValueForBoxShadow(); - switch (boxShadowValue->GetUnit()) { - case eCSSUnit_Null: - break; - - case eCSSUnit_Initial: - case eCSSUnit_Unset: - case eCSSUnit_None: - border->mBoxShadow = nullptr; - break; - - case eCSSUnit_Inherit: - border->mBoxShadow = parentBorder->mBoxShadow; - conditions.SetUncacheable(); - break; - - case eCSSUnit_List: - case eCSSUnit_ListDep: - border->mBoxShadow = GetShadowData(boxShadowValue->GetListValue(), - aContext, true, conditions); - break; - - default: - MOZ_ASSERT(false, "unrecognized shadow unit"); - } - // border-width, border-*-width: length, enum, inherit nsStyleCoord coord; { @@ -7544,7 +7457,7 @@ nsRuleNode::ComputePaddingData(void* aStartStruct, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { - COMPUTE_START_RESET(Padding, (), padding, parentPadding) + COMPUTE_START_RESET(Padding, padding, parentPadding) // padding: length, percent, calc, inherit const nsCSSProperty* subprops = @@ -7573,7 +7486,7 @@ nsRuleNode::ComputeOutlineData(void* aStartStruct, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { - COMPUTE_START_RESET(Outline, (mPresContext), outline, parentOutline) + COMPUTE_START_RESET(Outline, outline, parentOutline) // outline-width: length, enum, inherit const nsCSSValue* outlineWidthValue = aRuleData->ValueForOutlineWidth(); @@ -7684,7 +7597,48 @@ nsRuleNode::ComputeListData(void* aStartStruct, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { - COMPUTE_START_INHERITED(List, (mPresContext), list, parentList) + COMPUTE_START_INHERITED(List, list, parentList) + + // quotes: inherit, initial, none, [string string]+ + const nsCSSValue* quotesValue = aRuleData->ValueForQuotes(); + switch (quotesValue->GetUnit()) { + case eCSSUnit_Null: + break; + case eCSSUnit_Inherit: + case eCSSUnit_Unset: + conditions.SetUncacheable(); + list->SetQuotesInherit(parentList); + break; + case eCSSUnit_Initial: + list->SetQuotesInitial(); + break; + case eCSSUnit_None: + list->SetQuotesNone(); + break; + case eCSSUnit_PairList: + case eCSSUnit_PairListDep: { + const nsCSSValuePairList* ourQuotes = quotesValue->GetPairListValue(); + + nsStyleQuoteValues::QuotePairArray quotePairs; + quotePairs.SetLength(ListLength(ourQuotes)); + + size_t index = 0; + nsAutoString buffer; + while (ourQuotes) { + MOZ_ASSERT(ourQuotes->mXValue.GetUnit() == eCSSUnit_String && + ourQuotes->mYValue.GetUnit() == eCSSUnit_String, + "improper list contents for quotes"); + quotePairs[index].first = ourQuotes->mXValue.GetStringValue(buffer); + quotePairs[index].second = ourQuotes->mYValue.GetStringValue(buffer); + ++index; + ourQuotes = ourQuotes->mNext; + } + list->SetQuotes(Move(quotePairs)); + break; + } + default: + MOZ_ASSERT(false, "unexpected value unit"); + } // list-style-type: string, none, inherit, initial const nsCSSValue* typeValue = aRuleData->ValueForListStyleType(); @@ -8141,7 +8095,7 @@ nsRuleNode::ComputePositionData(void* aStartStruct, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { - COMPUTE_START_RESET(Position, (), pos, parentPos) + COMPUTE_START_RESET(Position, pos, parentPos) // box offsets: length, percent, calc, auto, inherit static const nsCSSProperty offsetProps[] = { @@ -8521,7 +8475,7 @@ nsRuleNode::ComputeTableData(void* aStartStruct, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { - COMPUTE_START_RESET(Table, (), table, parentTable) + COMPUTE_START_RESET(Table, table, parentTable) // table-layout: enum, inherit, initial SetDiscrete(*aRuleData->ValueForTableLayout(), @@ -8547,7 +8501,7 @@ nsRuleNode::ComputeTableBorderData(void* aStartStruct, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { - COMPUTE_START_INHERITED(TableBorder, (), table, parentTable) + COMPUTE_START_INHERITED(TableBorder, table, parentTable) // border-collapse: enum, inherit, initial SetDiscrete(*aRuleData->ValueForBorderCollapse(), table->mBorderCollapse, @@ -8608,7 +8562,7 @@ nsRuleNode::ComputeContentData(void* aStartStruct, uint32_t count; nsAutoString buffer; - COMPUTE_START_RESET(Content, (), content, parentContent) + COMPUTE_START_RESET(Content, content, parentContent) // content: [string, url, counter, attr, enum]+, normal, none, inherit const nsCSSValue* contentValue = aRuleData->ValueForContent(); @@ -8837,61 +8791,6 @@ nsRuleNode::ComputeContentData(void* aStartStruct, COMPUTE_END_RESET(Content, content) } -const void* -nsRuleNode::ComputeQuotesData(void* aStartStruct, - const nsRuleData* aRuleData, - nsStyleContext* aContext, - nsRuleNode* aHighestNode, - const RuleDetail aRuleDetail, - const RuleNodeCacheConditions aConditions) -{ - COMPUTE_START_INHERITED(Quotes, (), quotes, parentQuotes) - - // quotes: inherit, initial, none, [string string]+ - const nsCSSValue* quotesValue = aRuleData->ValueForQuotes(); - switch (quotesValue->GetUnit()) { - case eCSSUnit_Null: - break; - case eCSSUnit_Inherit: - case eCSSUnit_Unset: - conditions.SetUncacheable(); - quotes->CopyFrom(*parentQuotes); - break; - case eCSSUnit_Initial: - quotes->SetInitial(); - break; - case eCSSUnit_None: - quotes->AllocateQuotes(0); - break; - case eCSSUnit_PairList: - case eCSSUnit_PairListDep: { - const nsCSSValuePairList* ourQuotes - = quotesValue->GetPairListValue(); - nsAutoString buffer; - nsAutoString closeBuffer; - uint32_t count = ListLength(ourQuotes); - if (NS_FAILED(quotes->AllocateQuotes(count))) { - break; - } - count = 0; - while (ourQuotes) { - MOZ_ASSERT(ourQuotes->mXValue.GetUnit() == eCSSUnit_String && - ourQuotes->mYValue.GetUnit() == eCSSUnit_String, - "improper list contents for quotes"); - ourQuotes->mXValue.GetStringValue(buffer); - ourQuotes->mYValue.GetStringValue(closeBuffer); - quotes->SetQuotesAt(count++, buffer, closeBuffer); - ourQuotes = ourQuotes->mNext; - } - break; - } - default: - MOZ_ASSERT(false, "unexpected value unit"); - } - - COMPUTE_END_INHERITED(Quotes, quotes) -} - const void* nsRuleNode::ComputeXULData(void* aStartStruct, const nsRuleData* aRuleData, @@ -8900,7 +8799,7 @@ nsRuleNode::ComputeXULData(void* aStartStruct, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { - COMPUTE_START_RESET(XUL, (), xul, parentXUL) + COMPUTE_START_RESET(XUL, xul, parentXUL) // box-align: enum, inherit, initial SetDiscrete(*aRuleData->ValueForBoxAlign(), @@ -8966,7 +8865,7 @@ nsRuleNode::ComputeColumnData(void* aStartStruct, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { - COMPUTE_START_RESET(Column, (mPresContext), column, parent) + COMPUTE_START_RESET(Column, column, parent) // column-width: length, auto, inherit SetCoord(*aRuleData->ValueForColumnWidth(), @@ -9189,7 +9088,7 @@ nsRuleNode::ComputeSVGData(void* aStartStruct, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { - COMPUTE_START_INHERITED(SVG, (), svg, parentSVG) + COMPUTE_START_INHERITED(SVG, svg, parentSVG) // clip-rule: enum, inherit, initial SetDiscrete(*aRuleData->ValueForClipRule(), @@ -9232,13 +9131,6 @@ nsRuleNode::ComputeSVGData(void* aStartStruct, parentSVG->mFillRule, NS_STYLE_FILL_RULE_NONZERO, 0, 0, 0, 0); - // image-rendering: enum, inherit - SetDiscrete(*aRuleData->ValueForImageRendering(), - svg->mImageRendering, conditions, - SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, - parentSVG->mImageRendering, - NS_STYLE_IMAGE_RENDERING_AUTO, 0, 0, 0, 0); - // marker-end: url, none, inherit const nsCSSValue* markerEndValue = aRuleData->ValueForMarkerEnd(); if (eCSSUnit_URL == markerEndValue->GetUnit()) { @@ -9470,13 +9362,6 @@ nsRuleNode::ComputeSVGData(void* aStartStruct, parentSVG->mTextAnchor, NS_STYLE_TEXT_ANCHOR_START, 0, 0, 0, 0); - // text-rendering: enum, inherit, initial - SetDiscrete(*aRuleData->ValueForTextRendering(), - svg->mTextRendering, conditions, - SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, - parentSVG->mTextRendering, - NS_STYLE_TEXT_RENDERING_AUTO, 0, 0, 0, 0); - COMPUTE_END_INHERITED(SVG, svg) } @@ -9729,7 +9614,7 @@ nsRuleNode::ComputeSVGResetData(void* aStartStruct, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { - COMPUTE_START_RESET(SVGReset, (), svgReset, parentSVGReset) + COMPUTE_START_RESET(SVGReset, svgReset, parentSVGReset) // stop-color: const nsCSSValue* stopColorValue = aRuleData->ValueForStopColor(); @@ -9823,42 +9708,6 @@ nsRuleNode::ComputeSVGResetData(void* aStartStruct, parentSVGReset->mVectorEffect, NS_STYLE_VECTOR_EFFECT_NONE, 0, 0, 0, 0); - // filter: url, none, inherit - const nsCSSValue* filterValue = aRuleData->ValueForFilter(); - switch (filterValue->GetUnit()) { - case eCSSUnit_Null: - break; - case eCSSUnit_None: - case eCSSUnit_Initial: - case eCSSUnit_Unset: - svgReset->mFilters.Clear(); - break; - case eCSSUnit_Inherit: - conditions.SetUncacheable(); - svgReset->mFilters = parentSVGReset->mFilters; - break; - case eCSSUnit_List: - case eCSSUnit_ListDep: { - svgReset->mFilters.Clear(); - const nsCSSValueList* cur = filterValue->GetListValue(); - while (cur) { - nsStyleFilter styleFilter; - if (!SetStyleFilterToCSSValue(&styleFilter, cur->mValue, aContext, - mPresContext, conditions)) { - svgReset->mFilters.Clear(); - break; - } - MOZ_ASSERT(styleFilter.GetType() != NS_STYLE_FILTER_NONE, - "filter should be set"); - svgReset->mFilters.AppendElement(styleFilter); - cur = cur->mNext; - } - break; - } - default: - NS_NOTREACHED("unexpected unit"); - } - // mask-type: enum, inherit, initial SetDiscrete(*aRuleData->ValueForMaskType(), svgReset->mMaskType, @@ -10023,7 +9872,7 @@ nsRuleNode::ComputeVariablesData(void* aStartStruct, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { - COMPUTE_START_INHERITED(Variables, (), variables, parentVariables) + COMPUTE_START_INHERITED(Variables, variables, parentVariables) MOZ_ASSERT(aRuleData->mVariables, "shouldn't be in ComputeVariablesData if there were no variable " @@ -10037,6 +9886,168 @@ nsRuleNode::ComputeVariablesData(void* aStartStruct, COMPUTE_END_INHERITED(Variables, variables) } +const void* +nsRuleNode::ComputeEffectsData(void* aStartStruct, + const nsRuleData* aRuleData, + nsStyleContext* aContext, + nsRuleNode* aHighestNode, + const RuleDetail aRuleDetail, + const RuleNodeCacheConditions aConditions) +{ + COMPUTE_START_RESET(Effects, effects, parentEffects) + + // filter: url, none, inherit + const nsCSSValue* filterValue = aRuleData->ValueForFilter(); + switch (filterValue->GetUnit()) { + case eCSSUnit_Null: + break; + case eCSSUnit_None: + case eCSSUnit_Initial: + case eCSSUnit_Unset: + effects->mFilters.Clear(); + break; + case eCSSUnit_Inherit: + conditions.SetUncacheable(); + effects->mFilters = parentEffects->mFilters; + break; + case eCSSUnit_List: + case eCSSUnit_ListDep: { + effects->mFilters.Clear(); + const nsCSSValueList* cur = filterValue->GetListValue(); + while (cur) { + nsStyleFilter styleFilter; + if (!SetStyleFilterToCSSValue(&styleFilter, cur->mValue, aContext, + mPresContext, conditions)) { + effects->mFilters.Clear(); + break; + } + MOZ_ASSERT(styleFilter.GetType() != NS_STYLE_FILTER_NONE, + "filter should be set"); + effects->mFilters.AppendElement(styleFilter); + cur = cur->mNext; + } + break; + } + default: + NS_NOTREACHED("unexpected unit"); + } + + // box-shadow: none, list, inherit, initial + const nsCSSValue* boxShadowValue = aRuleData->ValueForBoxShadow(); + switch (boxShadowValue->GetUnit()) { + case eCSSUnit_Null: + break; + + case eCSSUnit_Initial: + case eCSSUnit_Unset: + case eCSSUnit_None: + effects->mBoxShadow = nullptr; + break; + + case eCSSUnit_Inherit: + effects->mBoxShadow = parentEffects->mBoxShadow; + conditions.SetUncacheable(); + break; + + case eCSSUnit_List: + case eCSSUnit_ListDep: + effects->mBoxShadow = GetShadowData(boxShadowValue->GetListValue(), + aContext, true, conditions); + break; + + default: + MOZ_ASSERT(false, "unrecognized shadow unit"); + } + + // clip property: length, auto, inherit + const nsCSSValue* clipValue = aRuleData->ValueForClip(); + switch (clipValue->GetUnit()) { + case eCSSUnit_Inherit: + conditions.SetUncacheable(); + effects->mClipFlags = parentEffects->mClipFlags; + effects->mClip = parentEffects->mClip; + break; + + case eCSSUnit_Initial: + case eCSSUnit_Unset: + case eCSSUnit_Auto: + effects->mClipFlags = NS_STYLE_CLIP_AUTO; + effects->mClip.SetRect(0,0,0,0); + break; + + case eCSSUnit_Null: + break; + + case eCSSUnit_Rect: { + const nsCSSRect& clipRect = clipValue->GetRectValue(); + + effects->mClipFlags = NS_STYLE_CLIP_RECT; + + if (clipRect.mTop.GetUnit() == eCSSUnit_Auto) { + effects->mClip.y = 0; + effects->mClipFlags |= NS_STYLE_CLIP_TOP_AUTO; + } + else if (clipRect.mTop.IsLengthUnit()) { + effects->mClip.y = CalcLength(clipRect.mTop, aContext, + mPresContext, conditions); + } + + if (clipRect.mBottom.GetUnit() == eCSSUnit_Auto) { + // Setting to NS_MAXSIZE for the 'auto' case ensures that + // the clip rect is nonempty. It is important that mClip be + // nonempty if the actual clip rect could be nonempty. + effects->mClip.height = NS_MAXSIZE; + effects->mClipFlags |= NS_STYLE_CLIP_BOTTOM_AUTO; + } + else if (clipRect.mBottom.IsLengthUnit()) { + effects->mClip.height = CalcLength(clipRect.mBottom, aContext, + mPresContext, conditions) - + effects->mClip.y; + } + + if (clipRect.mLeft.GetUnit() == eCSSUnit_Auto) { + effects->mClip.x = 0; + effects->mClipFlags |= NS_STYLE_CLIP_LEFT_AUTO; + } + else if (clipRect.mLeft.IsLengthUnit()) { + effects->mClip.x = CalcLength(clipRect.mLeft, aContext, + mPresContext, conditions); + } + + if (clipRect.mRight.GetUnit() == eCSSUnit_Auto) { + // Setting to NS_MAXSIZE for the 'auto' case ensures that + // the clip rect is nonempty. It is important that mClip be + // nonempty if the actual clip rect could be nonempty. + effects->mClip.width = NS_MAXSIZE; + effects->mClipFlags |= NS_STYLE_CLIP_RIGHT_AUTO; + } + else if (clipRect.mRight.IsLengthUnit()) { + effects->mClip.width = CalcLength(clipRect.mRight, aContext, + mPresContext, conditions) - + effects->mClip.x; + } + break; + } + + default: + MOZ_ASSERT(false, "unrecognized clip unit"); + } + + // opacity: factor, inherit, initial + SetFactor(*aRuleData->ValueForOpacity(), effects->mOpacity, conditions, + parentEffects->mOpacity, 1.0f, + SETFCT_OPACITY | SETFCT_UNSET_INITIAL); + + // mix-blend-mode: enum, inherit, initial + SetDiscrete(*aRuleData->ValueForMixBlendMode(), effects->mMixBlendMode, + conditions, + SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, + parentEffects->mMixBlendMode, NS_STYLE_BLEND_NORMAL, + 0, 0, 0, 0); + + COMPUTE_END_RESET(Effects, effects) +} + const void* nsRuleNode::GetStyleData(nsStyleStructID aSID, nsStyleContext* aContext, @@ -10075,116 +10086,6 @@ nsRuleNode::GetStyleData(nsStyleStructID aSID, return data; } -void -nsRuleNode::Mark() -{ - for (nsRuleNode *node = this; - node && !(node->mDependentBits & NS_RULE_NODE_GC_MARK); - node = node->mParent) - node->mDependentBits |= NS_RULE_NODE_GC_MARK; -} - -bool -nsRuleNode::DestroyIfNotMarked() -{ - // If we're not marked, then we have to delete ourself. - // However, we never allow the root node to GC itself, because nsStyleSet - // wants to hold onto the root node and not worry about re-creating a - // rule walker if the root node is deleted. - MOZ_ASSERT(mPresContext->StyleSet()->IsGecko(), - "ServoStyleSets should not have rule nodes"); - if (!(mDependentBits & NS_RULE_NODE_GC_MARK) && - // Skip this only if we're the *current* root and not an old one. - !(IsRoot() && mPresContext->StyleSet()->AsGecko()->GetRuleTree() == this)) { - Destroy(); - return true; - } - - // Clear our mark, for the next time around. - mDependentBits &= ~NS_RULE_NODE_GC_MARK; - return false; -} - -void -nsRuleNode::SweepChildren(nsTArray& aSweepQueue) -{ - NS_ASSERTION(!(mDependentBits & NS_RULE_NODE_GC_MARK), - "missing DestroyIfNotMarked() call"); - NS_ASSERTION(HaveChildren(), - "why call SweepChildren with no children?"); - uint32_t childrenDestroyed = 0; - nsRuleNode* survivorsWithChildren = nullptr; - if (ChildrenAreHashed()) { - PLDHashTable* children = ChildrenHash(); - uint32_t oldChildCount = children->EntryCount(); - for (auto iter = children->Iter(); !iter.Done(); iter.Next()) { - auto entry = static_cast(iter.Get()); - nsRuleNode* node = entry->mRuleNode; - if (node->DestroyIfNotMarked()) { - iter.Remove(); - } else if (node->HaveChildren()) { - // When children are hashed mNextSibling is not normally used but we - // use it here to build a list of children that needs to be swept. - nsRuleNode** headQ = &survivorsWithChildren; - node->mNextSibling = *headQ; - *headQ = node; - } - } - childrenDestroyed = oldChildCount - children->EntryCount(); - if (childrenDestroyed == oldChildCount) { - delete children; - mChildren.asVoid = nullptr; - } - } else { - for (nsRuleNode** children = ChildrenListPtr(); *children; ) { - nsRuleNode* next = (*children)->mNextSibling; - if ((*children)->DestroyIfNotMarked()) { - // This rule node was destroyed, unlink it from the list by - // making *children point to the next entry. - *children = next; - ++childrenDestroyed; - } else { - children = &(*children)->mNextSibling; - } - } - survivorsWithChildren = ChildrenList(); - } - if (survivorsWithChildren) { - aSweepQueue.AppendElement(survivorsWithChildren); - } - NS_ASSERTION(childrenDestroyed <= mRefCnt, "wrong ref count"); - mRefCnt -= childrenDestroyed; - NS_POSTCONDITION(IsRoot() || mRefCnt > 0, - "We didn't get swept, so we'd better have style contexts " - "pointing to us or to one of our descendants, which means " - "we'd better have a nonzero mRefCnt here!"); -} - -bool -nsRuleNode::Sweep() -{ - NS_ASSERTION(IsRoot(), "must start sweeping at a root"); - NS_ASSERTION(!mNextSibling, "root must not have mNextSibling"); - - if (DestroyIfNotMarked()) { - return true; - } - - AutoTArray sweepQueue; - sweepQueue.AppendElement(this); - while (!sweepQueue.IsEmpty()) { - nsTArray::index_type last = sweepQueue.Length() - 1; - nsRuleNode* ruleNode = sweepQueue[last]; - sweepQueue.RemoveElementAt(last); - for (; ruleNode; ruleNode = ruleNode->mNextSibling) { - if (ruleNode->HaveChildren()) { - ruleNode->SweepChildren(sweepQueue); - } - } - } - return false; -} - /* static */ bool nsRuleNode::HasAuthorSpecifiedRules(nsStyleContext* aStyleContext, uint32_t ruleTypeMask, diff --git a/layout/style/nsRuleNode.h b/layout/style/nsRuleNode.h index 3b439d139a..11c97ecc37 100644 --- a/layout/style/nsRuleNode.h +++ b/layout/style/nsRuleNode.h @@ -12,6 +12,7 @@ #define nsRuleNode_h___ #include "mozilla/ArenaObjectID.h" +#include "mozilla/LinkedList.h" #include "mozilla/PodOperations.h" #include "mozilla/RangedArray.h" #include "mozilla/RuleNodeCacheConditions.h" @@ -346,11 +347,11 @@ struct nsCachedStyleData * indexed by style rules (implementations of nsIStyleRule). * * The rule tree is owned by the nsStyleSet and is destroyed when the - * presentation of the document goes away. It is garbage-collected - * (using mark-and-sweep garbage collection) during the lifetime of the - * document (when dynamic changes cause the destruction of enough style - * contexts). Rule nodes are marked if they are pointed to by a style - * context or one of their descendants is. + * presentation of the document goes away. Its entries are reference- + * counted, with strong references held by child nodes, style structs + * and (for the root), the style set. Rule nodes are not immediately + * destroyed when their reference-count drops to zero, but are instead + * destroyed during a GC sweep. * * An nsStyleContext, which represents the computed style data for an * element, points to an nsRuleNode. The path from the root of the rule @@ -389,7 +390,10 @@ enum nsFontSizeType { eFontSize_CSS = 2 }; -class nsRuleNode { +// Note: This LinkedListElement is used for storing unused nodes in the +// linked list on nsStyleSet. We use mNextSibling for the singly-linked +// sibling list. +class nsRuleNode : public mozilla::LinkedListElement { public: enum RuleDetail { eRuleNone, // No props have been specified at all. @@ -415,12 +419,13 @@ public: private: nsPresContext* const mPresContext; // Our pres context. - nsRuleNode* const mParent; // A pointer to the parent node in the tree. - // This enables us to walk backwards from the - // most specific rule matched to the least - // specific rule (which is the optimal order to - // use for lookups of style properties. - nsIStyleRule* const mRule; // [STRONG] A pointer to our specific rule. + const RefPtr mParent; // A pointer to the parent node in the tree. + // This enables us to walk backwards from the + // most specific rule matched to the least + // specific rule (which is the optimal order to + // use for lookups of style properties. + + const nsCOMPtr mRule; // A pointer to our specific rule. nsRuleNode* mNextSibling; // This value should be used only by the // parent, since the parent may store @@ -456,9 +461,6 @@ private: static bool ChildrenHashMatchEntry(const PLDHashEntryHdr *aHdr, const void *aKey); - void SweepChildren(nsTArray& aSweepQueue); - bool DestroyIfNotMarked(); - static const PLDHashTableOps ChildrenHashOps; Key GetKey() const { @@ -514,6 +516,8 @@ private: } void ConvertChildrenToHash(int32_t aNumKids); + void RemoveChild(nsRuleNode* aNode); + nsCachedStyleData mStyleData; // Any data we cached on the rule node. uint32_t mDependentBits; // Used to cache the fact that we can look up @@ -537,22 +541,17 @@ private: // Compute*Data functions don't initialize from // inherited data. - // Reference count. This just counts the style contexts that reference this - // rulenode. And children the rulenode has had. When this goes to 0 or - // stops being 0, we notify the style set. - // Note, in particular, that when a child is removed mRefCnt is NOT - // decremented. This is on purpose; the notifications to the style set are - // only used to determine when it's worth running GC on the ruletree, and - // this setup makes it so we only count unused ruletree leaves for purposes - // of deciding when to GC. We could more accurately count unused rulenodes - // by releasing/addrefing our parent when our refcount transitions to or from - // 0, but it doesn't seem worth it to do that. + // Reference count. Style contexts hold strong references to their rule node, + // and rule nodes hold strong references to their parent. + // + // When the refcount drops to zero, we don't necessarily free the node. + // Instead, we notify the style set, which performs periodic sweeps. uint32_t mRefCnt; public: // Infallible overloaded new operator that allocates from a presShell arena. void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW; - void Destroy() { DestroyInternal(nullptr); } + void Destroy(); // Implemented in nsStyleSet.h, since it needs to know about nsStyleSet. inline void AddRef(); @@ -561,7 +560,6 @@ public: inline void Release(); protected: - void DestroyInternal(nsRuleNode ***aDestroyQueueTail); void PropagateDependentBit(nsStyleStructID aSID, nsRuleNode* aHighestNode, void* aStruct); void PropagateNoneBit(uint32_t aBit, nsRuleNode* aHighestNode); @@ -684,13 +682,6 @@ protected: RuleDetail aRuleDetail, const mozilla::RuleNodeCacheConditions aConditions); - const void* - ComputeQuotesData(void* aStartStruct, - const nsRuleData* aRuleData, - nsStyleContext* aContext, nsRuleNode* aHighestNode, - RuleDetail aRuleDetail, - const mozilla::RuleNodeCacheConditions aConditions); - const void* ComputeTextData(void* aStartStruct, const nsRuleData* aRuleData, @@ -755,6 +746,13 @@ protected: RuleDetail aRuleDetail, const mozilla::RuleNodeCacheConditions aConditions); + const void* + ComputeEffectsData(void* aStartStruct, + const nsRuleData* aRuleData, + nsStyleContext* aContext, nsRuleNode* aHighestNode, + RuleDetail aRuleDetail, + const mozilla::RuleNodeCacheConditions aConditions); + // helpers for |ComputeFontData| that need access to |mNoneBits|: static void SetFontSize(nsPresContext* aPresContext, const nsRuleData* aRuleData, @@ -808,7 +806,7 @@ private: public: // This is infallible; it will never return nullptr. - static nsRuleNode* CreateRootNode(nsPresContext* aPresContext); + static already_AddRefed CreateRootNode(nsPresContext* aPresContext); static void EnsureBlockDisplay(uint8_t& display, bool aConvertListItem = false); @@ -959,17 +957,6 @@ public: #undef STYLE_STRUCT_RESET #undef STYLE_STRUCT_INHERITED - /* - * Garbage collection. Mark walks up the tree, marking any unmarked - * ancestors until it reaches a marked one. Sweep recursively sweeps - * the children, destroys any that are unmarked, and clears marks, - * returning true if the node on which it was called was destroyed. - * If children are hashed, the mNextSibling field on the children is - * temporarily used internally by Sweep. - */ - void Mark(); - bool Sweep(); - static bool HasAuthorSpecifiedRules(nsStyleContext* aStyleContext, uint32_t ruleTypeMask, diff --git a/layout/style/nsStyleConsts.h b/layout/style/nsStyleConsts.h index 84cba13fc0..caa3cc16dc 100644 --- a/layout/style/nsStyleConsts.h +++ b/layout/style/nsStyleConsts.h @@ -267,10 +267,12 @@ enum class FillMode : uint32_t; #define NS_STYLE_IMAGELAYER_ATTACHMENT_LOCAL 2 // See nsStyleImageLayers -// Code depends on these constants having the same values as BG_ORIGIN_* +// Code depends on these constants having the same values as IMAGELAYER_ORIGIN_* #define NS_STYLE_IMAGELAYER_CLIP_BORDER 0 #define NS_STYLE_IMAGELAYER_CLIP_PADDING 1 #define NS_STYLE_IMAGELAYER_CLIP_CONTENT 2 +// One extra constant which does not exist in IMAGELAYER_ORIGIN_* +#define NS_STYLE_IMAGELAYER_CLIP_TEXT 3 // A magic value that we use for our "pretend that background-clip is // 'padding' when we have a solid border" optimization. This isn't @@ -467,6 +469,8 @@ enum class FillMode : uint32_t; #define NS_STYLE_DISPLAY_RUBY_TEXT 36 #define NS_STYLE_DISPLAY_RUBY_TEXT_CONTAINER 37 #define NS_STYLE_DISPLAY_CONTENTS 38 +#define NS_STYLE_DISPLAY_WEBKIT_BOX 39 +#define NS_STYLE_DISPLAY_WEBKIT_INLINE_BOX 40 // See nsStyleDisplay // If these are re-ordered, nsComputedDOMStyle::DoGetContain() and @@ -1122,6 +1126,10 @@ enum class FillMode : uint32_t; #define NS_STYLE_TEXT_RENDERING_OPTIMIZELEGIBILITY 2 #define NS_STYLE_TEXT_RENDERING_GEOMETRICPRECISION 3 +// adjust-color +#define NS_STYLE_COLOR_ADJUST_ECONOMY 0 +#define NS_STYLE_COLOR_ADJUST_EXACT 1 + // color-interpolation and color-interpolation-filters #define NS_STYLE_COLOR_INTERPOLATION_AUTO 0 #define NS_STYLE_COLOR_INTERPOLATION_SRGB 1 diff --git a/layout/style/nsStyleContext.cpp b/layout/style/nsStyleContext.cpp index e5bce1381a..88cad86e17 100644 --- a/layout/style/nsStyleContext.cpp +++ b/layout/style/nsStyleContext.cpp @@ -72,15 +72,17 @@ static bool sExpensiveStyleStructAssertionsEnabled; #endif nsStyleContext::nsStyleContext(nsStyleContext* aParent, + OwningStyleContextSource&& aSource, nsIAtom* aPseudoTag, - CSSPseudoElementType aPseudoType, - nsRuleNode* aRuleNode, - bool aSkipParentDisplayBasedStyleFixup) + CSSPseudoElementType aPseudoType) : mParent(aParent) , mChild(nullptr) , mEmptyChild(nullptr) , mPseudoTag(aPseudoTag) - , mRuleNode(aRuleNode) + , mSource(Move(aSource)) +#ifdef MOZ_STYLO + , mPresContext(nullptr) +#endif , mCachedResetData(nullptr) , mBits(((uint64_t)aPseudoType) << NS_STYLE_CONTEXT_TYPE_SHIFT) , mRefCnt(0) @@ -88,6 +90,55 @@ nsStyleContext::nsStyleContext(nsStyleContext* aParent, , mFrameRefCnt(0) , mComputingStruct(nsStyleStructID_None) #endif +{} + +nsStyleContext::nsStyleContext(nsStyleContext* aParent, + nsIAtom* aPseudoTag, + CSSPseudoElementType aPseudoType, + already_AddRefed aRuleNode, + bool aSkipParentDisplayBasedStyleFixup) + : nsStyleContext(aParent, OwningStyleContextSource(Move(aRuleNode)), + aPseudoTag, aPseudoType) +{ +#ifdef MOZ_STYLO + mPresContext = mSource.AsGeckoRuleNode()->PresContext(); +#endif + + if (aParent) { +#ifdef DEBUG + nsRuleNode *r1 = mParent->RuleNode(), *r2 = mSource.AsGeckoRuleNode(); + while (r1->GetParent()) + r1 = r1->GetParent(); + while (r2->GetParent()) + r2 = r2->GetParent(); + NS_ASSERTION(r1 == r2, "must be in the same rule tree as parent"); +#endif + } else { + PresContext()->PresShell()->StyleSet()->RootStyleContextAdded(); + } + + mSource.AsGeckoRuleNode()->SetUsedDirectly(); // before ApplyStyleFixups()! + FinishConstruction(aSkipParentDisplayBasedStyleFixup); +} + +nsStyleContext::nsStyleContext(nsStyleContext* aParent, + nsPresContext* aPresContext, + nsIAtom* aPseudoTag, + CSSPseudoElementType aPseudoType, + already_AddRefed aComputedValues, + bool aSkipParentDisplayBasedStyleFixup) + : nsStyleContext(aParent, OwningStyleContextSource(Move(aComputedValues)), + aPseudoTag, aPseudoType) +{ +#ifdef MOZ_STYLO + mPresContext = aPresContext; +#endif + + FinishConstruction(aSkipParentDisplayBasedStyleFixup); +} + +void +nsStyleContext::FinishConstruction(bool aSkipParentDisplayBasedStyleFixup) { // This check has to be done "backward", because if it were written the // more natural way it wouldn't fail even when it needed to. @@ -95,7 +146,7 @@ nsStyleContext::nsStyleContext(nsStyleContext* aParent, static_cast( CSSPseudoElementType::MAX), "pseudo element bits no longer fit in a uint64_t"); - MOZ_ASSERT(aRuleNode); + MOZ_ASSERT(!mSource.IsNull()); #ifdef DEBUG static_assert(MOZ_ARRAY_LENGTH(nsStyleContext::sDependencyTable) @@ -106,29 +157,7 @@ nsStyleContext::nsStyleContext(nsStyleContext* aParent, mNextSibling = this; mPrevSibling = this; if (mParent) { - mParent->AddRef(); mParent->AddChild(this); -#ifdef DEBUG - nsRuleNode *r1 = mParent->RuleNode(), *r2 = aRuleNode; - while (r1->GetParent()) - r1 = r1->GetParent(); - while (r2->GetParent()) - r2 = r2->GetParent(); - NS_ASSERTION(r1 == r2, "must be in the same rule tree as parent"); -#endif - } - - mRuleNode->AddRef(); - mRuleNode->SetUsedDirectly(); // before ApplyStyleFixups()! - - if (!mParent) { - // Add as a root before ApplyStyleFixups, since ApplyStyleFixups - // can trigger rule tree GC. - nsStyleSet* styleSet = - mRuleNode->PresContext()->PresShell()->StyleSet()->GetAsGecko(); - if (styleSet) { - styleSet->AddStyleContextRoot(this); - } } ApplyStyleFixups(aSkipParentDisplayBasedStyleFixup); @@ -143,14 +172,6 @@ nsStyleContext::~nsStyleContext() { NS_ASSERTION((nullptr == mChild) && (nullptr == mEmptyChild), "destructing context with children"); - nsPresContext *presContext = mRuleNode->PresContext(); - nsStyleSet* styleSet = presContext->PresShell()->StyleSet()->GetAsGecko(); - - NS_ASSERTION(!styleSet || - styleSet->GetRuleTree() == mRuleNode->RuleTree() || - styleSet->IsInRuleTreeReconstruct(), - "destroying style context from old rule tree too late"); - #ifdef DEBUG if (sExpensiveStyleStructAssertionsEnabled) { // Assert that the style structs we are about to destroy are not referenced @@ -169,15 +190,17 @@ nsStyleContext::~nsStyleContext() } #endif - mRuleNode->Release(); - - if (styleSet) { - styleSet->NotifyStyleContextDestroyed(this); - } + nsPresContext *presContext = PresContext(); + DebugOnly geckoStyleSet = presContext->PresShell()->StyleSet()->GetAsGecko(); + NS_ASSERTION(!geckoStyleSet || + geckoStyleSet->GetRuleTree() == mSource.AsGeckoRuleNode()->RuleTree() || + geckoStyleSet->IsInRuleTreeReconstruct(), + "destroying style context from old rule tree too late"); if (mParent) { mParent->RemoveChild(this); - mParent->Release(); + } else { + presContext->StyleSet()->RootStyleContextRemoved(); } // Free up our data structs. @@ -279,7 +302,7 @@ void nsStyleContext::AddChild(nsStyleContext* aChild) aChild->mNextSibling == aChild, "child already in a child list"); - nsStyleContext **listPtr = aChild->mRuleNode->IsRoot() ? &mEmptyChild : &mChild; + nsStyleContext **listPtr = aChild->mSource.MatchesNoRules() ? &mEmptyChild : &mChild; // Explicitly dereference listPtr so that compiler doesn't have to know that mNextSibling // etc. don't alias with what ever listPtr points at. nsStyleContext *list = *listPtr; @@ -299,7 +322,7 @@ void nsStyleContext::RemoveChild(nsStyleContext* aChild) { NS_PRECONDITION(nullptr != aChild && this == aChild->mParent, "bad argument"); - nsStyleContext **list = aChild->mRuleNode->IsRoot() ? &mEmptyChild : &mChild; + nsStyleContext **list = aChild->mSource.MatchesNoRules() ? &mEmptyChild : &mChild; if (aChild->mPrevSibling != aChild) { // has siblings if ((*list) == aChild) { @@ -342,51 +365,44 @@ nsStyleContext::MoveTo(nsStyleContext* aNewParent) MOZ_ASSERT(!aNewParent->IsStyleIfVisited()); MOZ_ASSERT(!mStyleIfVisited || mStyleIfVisited->mParent == mParent); - nsStyleContext* oldParent = mParent; - - if (oldParent->HasChildThatUsesResetStyle()) { + if (mParent->HasChildThatUsesResetStyle()) { aNewParent->AddStyleBit(NS_STYLE_HAS_CHILD_THAT_USES_RESET_STYLE); } - aNewParent->AddRef(); mParent->RemoveChild(this); mParent = aNewParent; mParent->AddChild(this); - oldParent->Release(); if (mStyleIfVisited) { - oldParent = mStyleIfVisited->mParent; - aNewParent->AddRef(); mStyleIfVisited->mParent->RemoveChild(mStyleIfVisited); mStyleIfVisited->mParent = aNewParent; mStyleIfVisited->mParent->AddChild(mStyleIfVisited); - oldParent->Release(); } } already_AddRefed -nsStyleContext::FindChildWithRules(const nsIAtom* aPseudoTag, - nsRuleNode* aRuleNode, - nsRuleNode* aRulesIfVisited, +nsStyleContext::FindChildWithRules(const nsIAtom* aPseudoTag, + NonOwningStyleContextSource aSource, + NonOwningStyleContextSource aSourceIfVisited, bool aRelevantLinkVisited) { uint32_t threshold = 10; // The # of siblings we're willing to examine // before just giving this whole thing up. RefPtr result; - nsStyleContext *list = aRuleNode->IsRoot() ? mEmptyChild : mChild; + nsStyleContext *list = aSource.MatchesNoRules() ? mEmptyChild : mChild; if (list) { nsStyleContext *child = list; do { - if (child->mRuleNode == aRuleNode && + if (child->mSource.AsRaw() == aSource && child->mPseudoTag == aPseudoTag && !child->IsStyleIfVisited() && child->RelevantLinkVisited() == aRelevantLinkVisited) { bool match = false; - if (aRulesIfVisited) { + if (!aSourceIfVisited.IsNull()) { match = child->GetStyleIfVisited() && - child->GetStyleIfVisited()->mRuleNode == aRulesIfVisited; + child->GetStyleIfVisited()->mSource.AsRaw() == aSourceIfVisited; } else { match = !child->GetStyleIfVisited(); } @@ -419,8 +435,10 @@ const void* nsStyleContext::StyleData(nsStyleStructID aSID) const void* cachedData = GetCachedStyleData(aSID); if (cachedData) return cachedData; // We have computed data stored on this node in the context tree. - // Our rule node will take care of it for us. - const void* newData = mRuleNode->GetStyleData(aSID, this, true); + // Our style source will take care of it for us. + const void* newData = mSource.IsGeckoRuleNode() + ? mSource.AsGeckoRuleNode()->GetStyleData(aSID, this, true) + : StyleStructFromServoComputedValues(aSID); if (!nsCachedStyleData::IsReset(aSID)) { // always cache inherited data on the style context; the rule // node set the bit in mBits for us if needed. @@ -487,12 +505,12 @@ nsStyleContext::CreateEmptyStyleData(const nsStyleStructID& aSID) void* result; nsPresContext* presContext = PresContext(); switch (aSID) { -#define UNIQUE_CASE(c_, ...) \ +#define UNIQUE_CASE(c_) \ case eStyleStruct_##c_: \ - result = new (presContext) nsStyle##c_(__VA_ARGS__); \ + result = new (presContext) nsStyle##c_(presContext); \ break; - UNIQUE_CASE(Border, presContext) + UNIQUE_CASE(Border) UNIQUE_CASE(Padding) #undef UNIQUE_CASE @@ -524,7 +542,7 @@ nsStyleContext::SetStyle(nsStyleStructID aSID, void* aStruct) void** dataSlot; if (nsCachedStyleData::IsReset(aSID)) { if (!mCachedResetData) { - mCachedResetData = new (mRuleNode->PresContext()) nsResetStyleData; + mCachedResetData = new (PresContext()) nsResetStyleData; } dataSlot = &mCachedResetData->mStyleStructs[aSID]; } else { @@ -551,7 +569,7 @@ ShouldSuppressLineBreak(const nsStyleContext* aContext, // which represents text frames, as well as ruby pseudos are excluded // because we still want to set the flag for them. if (aContext->GetPseudoType() == CSSPseudoElementType::AnonBox && - aContext->GetPseudo() != nsCSSAnonBoxes::mozNonElement && + !nsCSSAnonBoxes::IsNonElement(aContext->GetPseudo()) && !RubyUtils::IsRubyPseudo(aContext->GetPseudo())) { return false; } @@ -597,9 +615,45 @@ ShouldSuppressLineBreak(const nsStyleContext* aContext, return false; } +// Flex & grid containers blockify their children. +// "The display value of a flex item is blockified" +// https://drafts.csswg.org/css-flexbox-1/#flex-items +// "The display value of a grid item is blockified" +// https://drafts.csswg.org/css-grid/#grid-items +static bool +ShouldBlockifyChildren(const nsStyleDisplay* aStyleDisp) +{ + auto displayVal = aStyleDisp->mDisplay; + return NS_STYLE_DISPLAY_FLEX == displayVal || + NS_STYLE_DISPLAY_INLINE_FLEX == displayVal || + NS_STYLE_DISPLAY_GRID == displayVal || + NS_STYLE_DISPLAY_INLINE_GRID == displayVal; +} + void nsStyleContext::ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup) { +#define GET_UNIQUE_STYLE_DATA(name_) \ + static_cast(GetUniqueStyleData(eStyleStruct_##name_)) + + // Change writing mode of text frame for text-combine-upright. We use + // style structs of the parent to avoid triggering computation before + // we change the writing mode. + // It is safe to look at the parent's style because we are looking at + // inherited properties, and ::-moz-text never matches any rules. + if (mPseudoTag == nsCSSAnonBoxes::mozText && mParent && + mParent->StyleVisibility()->mWritingMode != + NS_STYLE_WRITING_MODE_HORIZONTAL_TB && + mParent->StyleText()->mTextCombineUpright == + NS_STYLE_TEXT_COMBINE_UPRIGHT_ALL) { + MOZ_ASSERT(!PeekStyleVisibility(), "If StyleVisibility was already " + "computed, some properties may have been computed " + "incorrectly based on the old writing mode value"); + nsStyleVisibility* mutableVis = GET_UNIQUE_STYLE_DATA(Visibility); + mutableVis->mWritingMode = NS_STYLE_WRITING_MODE_HORIZONTAL_TB; + AddStyleBit(NS_STYLE_IS_TEXT_COMBINED); + } + // See if we have any text decorations. // First see if our parent has text decorations. If our parent does, then we inherit the bit. if (mParent && mParent->HasTextDecorationLines()) { @@ -625,8 +679,7 @@ nsStyleContext::ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup) presContext->StyleSet()->ResolveStyleFor(docElement, nullptr); auto dir = rootStyle->StyleVisibility()->mDirection; if (dir != StyleVisibility()->mDirection) { - nsStyleVisibility* uniqueVisibility = - (nsStyleVisibility*)GetUniqueStyleData(eStyleStruct_Visibility); + nsStyleVisibility* uniqueVisibility = GET_UNIQUE_STYLE_DATA(Visibility); uniqueVisibility->mDirection = dir; } } @@ -644,7 +697,7 @@ nsStyleContext::ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup) text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER || text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT) { - nsStyleText* uniqueText = (nsStyleText*)GetUniqueStyleData(eStyleStruct_Text); + nsStyleText* uniqueText = GET_UNIQUE_STYLE_DATA(Text); uniqueText->mTextAlign = NS_STYLE_TEXT_ALIGN_DEFAULT; } } @@ -667,8 +720,7 @@ nsStyleContext::ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup) displayVal = NS_STYLE_DISPLAY_BLOCK; } if (displayVal != disp->mDisplay) { - nsStyleDisplay* mutable_display = - static_cast(GetUniqueStyleData(eStyleStruct_Display)); + nsStyleDisplay* mutable_display = GET_UNIQUE_STYLE_DATA(Display); disp = mutable_display; // If we're in this code, then mOriginalDisplay doesn't matter @@ -703,8 +755,8 @@ nsStyleContext::ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup) containerContext = containerContext->GetParent(); containerDisp = containerContext->StyleDisplay(); } - if (containerDisp->IsFlexOrGridDisplayType() && - GetPseudo() != nsCSSAnonBoxes::mozNonElement) { + if (ShouldBlockifyChildren(containerDisp) && + !nsCSSAnonBoxes::IsNonElement(GetPseudo())) { // NOTE: Technically, we shouldn't modify the 'display' value of // positioned elements, since they aren't flex/grid items. However, // we don't need to worry about checking for that, because if we're @@ -718,8 +770,7 @@ 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 = - static_cast(GetUniqueStyleData(eStyleStruct_Display)); + nsStyleDisplay* mutable_display = GET_UNIQUE_STYLE_DATA(Display); disp = mutable_display; mutable_display->mDisplay = displayVal; } @@ -738,8 +789,7 @@ nsStyleContext::ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup) uint8_t displayVal = disp->mDisplay; nsRuleNode::EnsureInlineDisplay(displayVal); if (displayVal != disp->mDisplay) { - nsStyleDisplay* mutable_display = - static_cast(GetUniqueStyleData(eStyleStruct_Display)); + nsStyleDisplay* mutable_display = GET_UNIQUE_STYLE_DATA(Display); disp = mutable_display; mutable_display->mDisplay = displayVal; } @@ -763,8 +813,7 @@ nsStyleContext::ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup) unicodeBidi = NS_STYLE_UNICODE_BIDI_ISOLATE_OVERRIDE; } if (unicodeBidi != textReset->mUnicodeBidi) { - auto mutableTextReset = static_cast( - GetUniqueStyleData(eStyleStruct_TextReset)); + nsStyleTextReset* mutableTextReset = GET_UNIQUE_STYLE_DATA(TextReset); mutableTextReset->mUnicodeBidi = unicodeBidi; } } @@ -778,7 +827,7 @@ nsStyleContext::ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup) * ...etc. */ if (disp->mDisplay == NS_STYLE_DISPLAY_INLINE && - mPseudoTag != nsCSSAnonBoxes::mozNonElement && + !nsCSSAnonBoxes::IsNonElement(mPseudoTag) && mParent) { auto cbContext = mParent; while (cbContext->StyleDisplay()->mDisplay == NS_STYLE_DISPLAY_CONTENTS) { @@ -789,8 +838,7 @@ nsStyleContext::ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup) // and text-orientation) here; just the writing-mode property is enough. if (StyleVisibility()->mWritingMode != cbContext->StyleVisibility()->mWritingMode) { - nsStyleDisplay* mutable_display = - static_cast(GetUniqueStyleData(eStyleStruct_Display)); + nsStyleDisplay* mutable_display = GET_UNIQUE_STYLE_DATA(Display); disp = mutable_display; mutable_display->mOriginalDisplay = mutable_display->mDisplay = NS_STYLE_DISPLAY_INLINE_BLOCK; @@ -799,6 +847,7 @@ nsStyleContext::ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup) // Compute User Interface style, to trigger loads of cursors StyleUserInterface(); +#undef GET_UNIQUE_STYLE_DATA } nsChangeHint @@ -830,12 +879,12 @@ nsStyleContext::CalcStyleDifference(nsStyleContext* aOther, // we could later get a small change in one of those structs that we // don't want to miss. - // If our rule nodes are the same, then any differences in style data + // If our sources are the same, then any differences in style data // are already accounted for by differences on ancestors. We know // this because CalcStyleDifference is always called on two style // contexts that point to the same element, so we know that our // position in the style context tree is the same and our position in - // the rule node tree is also the same. + // the rule node tree (if applicable) is also the same. // However, if there were noninherited style change hints on the // parent, we might produce these same noninherited hints on this // style context's frame due to 'inherit' values, so we do need to @@ -843,13 +892,13 @@ nsStyleContext::CalcStyleDifference(nsStyleContext* aOther, // (Things like 'em' units are handled by the change hint produced // by font-size changing, so we don't need to worry about them like // we worry about 'inherit' values.) - bool compare = mRuleNode != aOther->mRuleNode; + bool compare = mSource != aOther->mSource; DebugOnly structsFound = 0; // If we had any change in variable values, then we'll need to examine // all of the other style structs too, even if the new style context has - // the same rule node as the old one. + // the same source as the old one. const nsStyleVariables* thisVariables = PeekStyleVariables(); if (thisVariables) { structsFound |= NS_STYLE_INHERIT_BIT(Variables); @@ -927,7 +976,6 @@ nsStyleContext::CalcStyleDifference(nsStyleContext* aOther, DO_STRUCT_DIFFERENCE(UIReset); DO_STRUCT_DIFFERENCE(Text); DO_STRUCT_DIFFERENCE(List); - DO_STRUCT_DIFFERENCE(Quotes); DO_STRUCT_DIFFERENCE(SVGReset); DO_STRUCT_DIFFERENCE(SVG); #undef EXTRA_DIFF_ARGS @@ -940,6 +988,7 @@ nsStyleContext::CalcStyleDifference(nsStyleContext* aOther, DO_STRUCT_DIFFERENCE(Padding); DO_STRUCT_DIFFERENCE(Border); DO_STRUCT_DIFFERENCE(TextReset); + DO_STRUCT_DIFFERENCE(Effects); DO_STRUCT_DIFFERENCE(Background); DO_STRUCT_DIFFERENCE(Color); #undef EXTRA_DIFF_ARGS @@ -1076,7 +1125,10 @@ nsStyleContext::CalcStyleDifference(nsStyleContext* aOther, const nsStyleText* otherVisText = otherVis->StyleText(); if (thisVisText->mTextEmphasisColorForeground != otherVisText->mTextEmphasisColorForeground || - thisVisText->mTextEmphasisColor != otherVisText->mTextEmphasisColor) { + thisVisText->mTextEmphasisColor != otherVisText->mTextEmphasisColor || + thisVisText->mWebkitTextFillColorForeground != + otherVisText->mWebkitTextFillColorForeground || + thisVisText->mWebkitTextFillColor != otherVisText->mWebkitTextFillColor) { change = true; } } @@ -1118,30 +1170,6 @@ nsStyleContext::CalcStyleDifference(nsStyleContext* aOther, return NS_SubtractHint(hint, nsChangeHint_NeutralChange); } -void -nsStyleContext::Mark() -{ - // Mark our rule node. - mRuleNode->Mark(); - - // Mark our children (i.e., tell them to mark their rule nodes, etc.). - if (mChild) { - nsStyleContext* child = mChild; - do { - child->Mark(); - child = child->mNextSibling; - } while (mChild != child); - } - - if (mEmptyChild) { - nsStyleContext* child = mEmptyChild; - do { - child->Mark(); - child = child->mNextSibling; - } while (mEmptyChild != child); - } -} - #ifdef DEBUG void nsStyleContext::List(FILE* out, int32_t aIndent, bool aListDescendants) { @@ -1160,10 +1188,12 @@ void nsStyleContext::List(FILE* out, int32_t aIndent, bool aListDescendants) str.Append(' '); } - if (mRuleNode) { + if (mSource.IsServoComputedValues()) { + fprintf_stderr(out, "%s{ServoComputedValues}\n", str.get()); + } else if (mSource.IsGeckoRuleNode()) { fprintf_stderr(out, "%s{\n", str.get()); str.Truncate(); - nsRuleNode* ruleNode = mRuleNode; + nsRuleNode* ruleNode = mSource.AsGeckoRuleNode(); while (ruleNode) { nsIStyleRule *styleRule = ruleNode->GetRule(); if (styleRule) { @@ -1214,8 +1244,8 @@ nsStyleContext::operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_N void nsStyleContext::Destroy() { - // Get the pres context from our rule node. - RefPtr presContext = mRuleNode->PresContext(); + // Get the pres context. + RefPtr presContext = PresContext(); // Call our destructor. this->~nsStyleContext(); @@ -1233,17 +1263,33 @@ NS_NewStyleContext(nsStyleContext* aParentContext, nsRuleNode* aRuleNode, bool aSkipParentDisplayBasedStyleFixup) { + RefPtr node = aRuleNode; RefPtr context = new (aRuleNode->PresContext()) - nsStyleContext(aParentContext, aPseudoTag, aPseudoType, aRuleNode, + nsStyleContext(aParentContext, aPseudoTag, aPseudoType, node.forget(), aSkipParentDisplayBasedStyleFixup); return context.forget(); } +already_AddRefed +NS_NewStyleContext(nsStyleContext* aParentContext, + nsPresContext* aPresContext, + nsIAtom* aPseudoTag, + CSSPseudoElementType aPseudoType, + already_AddRefed aComputedValues, + bool aSkipParentDisplayBasedStyleFixup) +{ + RefPtr context = + new (aPresContext) + nsStyleContext(aParentContext, aPresContext, aPseudoTag, aPseudoType, + Move(aComputedValues), aSkipParentDisplayBasedStyleFixup); + return context.forget(); +} + nsIPresShell* nsStyleContext::Arena() { - return mRuleNode->PresContext()->PresShell(); + return PresContext()->PresShell(); } static inline void @@ -1298,6 +1344,7 @@ nsStyleContext::GetVisitedDependentColor(nsCSSProperty aProperty) aProperty == eCSSProperty_column_rule_color || aProperty == eCSSProperty_text_decoration_color || aProperty == eCSSProperty_text_emphasis_color || + aProperty == eCSSProperty__webkit_text_fill_color || aProperty == eCSSProperty_fill || aProperty == eCSSProperty_stroke, "we need to add to nsStyleContext::CalcStyleDifference"); @@ -1415,11 +1462,10 @@ nsStyleContext::SwapStyleData(nsStyleContext* aNewContext, uint32_t aStructs) continue; } if (!mCachedResetData) { - mCachedResetData = new (mRuleNode->PresContext()) nsResetStyleData; + mCachedResetData = new (PresContext()) nsResetStyleData; } if (!aNewContext->mCachedResetData) { - aNewContext->mCachedResetData = - new (mRuleNode->PresContext()) nsResetStyleData; + aNewContext->mCachedResetData = new (PresContext()) nsResetStyleData; } void*& thisData = mCachedResetData->mStyleStructs[i]; void*& otherData = aNewContext->mCachedResetData->mStyleStructs[i]; @@ -1581,7 +1627,7 @@ nsStyleContext::LogStyleContextTree(bool aFirst, uint32_t aStructs) nsCString parent; if (aFirst) { - parent.AppendPrintf("parent=%p ", mParent); + parent.AppendPrintf("parent=%p ", mParent.get()); } LOG_RESTYLE("%p(%d) %s%s%s%s", diff --git a/layout/style/nsStyleContext.h b/layout/style/nsStyleContext.h index 7ef1368c46..48eb76a9dc 100644 --- a/layout/style/nsStyleContext.h +++ b/layout/style/nsStyleContext.h @@ -8,9 +8,10 @@ #ifndef _nsStyleContext_h_ #define _nsStyleContext_h_ -#include "mozilla/RestyleLogging.h" #include "mozilla/Assertions.h" -#include "nsRuleNode.h" +#include "mozilla/RestyleLogging.h" +#include "mozilla/StyleContextSource.h" +#include "nsStyleSet.h" class nsIAtom; class nsPresContext; @@ -37,8 +38,6 @@ enum class CSSPseudoElementType : uint8_t; * 1. the |nsIFrame|s that are using the style context and * 2. any *child* style contexts (this might be the reverse of * expectation, but it makes sense in this case) - * Style contexts participate in the mark phase of rule node garbage - * collection. */ class nsStyleContext final @@ -70,7 +69,16 @@ public: */ nsStyleContext(nsStyleContext* aParent, nsIAtom* aPseudoTag, mozilla::CSSPseudoElementType aPseudoType, - nsRuleNode* aRuleNode, + already_AddRefed aRuleNode, + bool aSkipParentDisplayBasedStyleFixup); + + // Version of the above that takes a ServoComputedValues instead of a Gecko + // nsRuleNode. + nsStyleContext(nsStyleContext* aParent, + nsPresContext* aPresContext, + nsIAtom* aPseudoTag, + mozilla::CSSPseudoElementType aPseudoType, + already_AddRefed aComputedValues, bool aSkipParentDisplayBasedStyleFixup); void* operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW; @@ -135,7 +143,13 @@ public: return mRefCnt == 1; } - nsPresContext* PresContext() const { return mRuleNode->PresContext(); } + nsPresContext* PresContext() const { +#ifdef MOZ_STYLO + return mPresContext; +#else + return mSource.AsGeckoRuleNode()->PresContext(); +#endif + } nsStyleContext* GetParent() const { return mParent; } @@ -148,15 +162,26 @@ public: // Find, if it already exists *and is easily findable* (i.e., near the // start of the child list), a style context whose: // * GetPseudo() matches aPseudoTag - // * RuleNode() matches aRules - // * !GetStyleIfVisited() == !aRulesIfVisited, and, if they're - // non-null, GetStyleIfVisited()->RuleNode() == aRulesIfVisited + // * mSource matches aSource + // * !!GetStyleIfVisited() == !!aSourceIfVisited, and, if they're + // non-null, GetStyleIfVisited()->mSource == aSourceIfVisited // * RelevantLinkVisited() == aRelevantLinkVisited already_AddRefed - FindChildWithRules(const nsIAtom* aPseudoTag, nsRuleNode* aRules, - nsRuleNode* aRulesIfVisited, + FindChildWithRules(const nsIAtom* aPseudoTag, + mozilla::NonOwningStyleContextSource aSource, + mozilla::NonOwningStyleContextSource aSourceIfVisited, bool aRelevantLinkVisited); + /** + * Get the color that should be used to fill text: either + * the current foreground color, or a separately-specified text fill color. + */ + nscolor GetTextFillColor() { + const nsStyleText* textStyle = StyleText(); + return textStyle->mWebkitTextFillColorForeground + ? StyleColor()->mColor : textStyle->mWebkitTextFillColor; + } + // Does this style context or any of its ancestors have text // decoration lines? // Differs from nsStyleTextReset::HasTextDecorationLines, which tests @@ -178,6 +203,11 @@ public: bool IsInDisplayNoneSubtree() const { return !!(mBits & NS_STYLE_IN_DISPLAY_NONE_SUBTREE); } + // Is this horizontal-in-vertical (tate-chu-yoko) text? This flag is + // only set on style contexts whose pseudo is nsCSSAnonBoxes::mozText. + bool IsTextCombined() const + { return !!(mBits & NS_STYLE_IS_TEXT_COMBINED); } + // Does this style context represent the style for a pseudo-element or // inherit data from such a style context? Whether this returns true // is equivalent to whether it or any of its ancestors returns @@ -275,14 +305,12 @@ public: return mBits & nsCachedStyleData::GetBitForSID(aSID); } - nsRuleNode* RuleNode() { return mRuleNode; } - void AddStyleBit(const uint64_t& aBit) { mBits |= aBit; } + nsRuleNode* RuleNode() { + MOZ_RELEASE_ASSERT(mSource.IsGeckoRuleNode()); + return mSource.AsGeckoRuleNode(); + } - /* - * Mark this style context's rule node (and its ancestors) to prevent - * it from being garbage collected. - */ - void Mark(); + void AddStyleBit(const uint64_t& aBit) { mBits |= aBit; } /* * Get the style data for a style struct. This is the most important @@ -462,10 +490,21 @@ public: return cachedData; } + mozilla::NonOwningStyleContextSource StyleSource() const { return mSource.AsRaw(); } + private: // Private destructor, to discourage deletion outside of Release(): ~nsStyleContext(); + // Delegated Helper constructor. + nsStyleContext(nsStyleContext* aParent, + mozilla::OwningStyleContextSource&& aSource, + nsIAtom* aPseudoTag, + mozilla::CSSPseudoElementType aPseudoType); + + // Helper post-contruct hook. + void FinishConstruction(bool aSkipParentDisplayBasedStyleFixup); + void AddChild(nsStyleContext* aChild); void RemoveChild(nsStyleContext* aChild); @@ -474,6 +513,10 @@ private: void ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup); + const void* StyleStructFromServoComputedValues(nsStyleStructID aSID) { + MOZ_CRASH("stylo: not implemented"); + } + #ifdef DEBUG struct AutoCheckDependency { @@ -520,7 +563,7 @@ private: /* Have the rulenode deal */ \ AUTO_CHECK_DEPENDENCY(eStyleStruct_##name_); \ const nsStyle##name_ * newData = \ - mRuleNode->GetStyle##name_(this, mBits); \ + mSource.AsGeckoRuleNode()->GetStyle##name_(this, mBits); \ /* always cache inherited data on the style context; the rule */ \ /* node set the bit in mBits for us if needed. */ \ mCachedInheritedData.mStyleStructs[eStyleStruct_##name_] = \ @@ -539,7 +582,7 @@ private: } \ /* Have the rulenode deal */ \ AUTO_CHECK_DEPENDENCY(eStyleStruct_##name_); \ - return mRuleNode->GetStyle##name_(this); \ + return mSource.AsGeckoRuleNode()->GetStyle##name_(this); \ } #include "nsStyleStructList.h" #undef STYLE_STRUCT_RESET @@ -562,7 +605,7 @@ private: bool ShouldLogRestyle() { return true; } #endif - nsStyleContext* mParent; // STRONG + RefPtr mParent; // Children are kept in two circularly-linked lists. The list anchor // is not part of the list (null for empty), and we point to the first @@ -584,20 +627,23 @@ private: // the relevant atom. nsCOMPtr mPseudoTag; - // The rule node is the node in the lexicographic tree of rule nodes - // (the "rule tree") that indicates which style rules are used to - // compute the style data, and in what cascading order. The least - // specific rule matched is the one whose rule node is a child of the - // root of the rule tree, and the most specific rule matched is the - // |mRule| member of |mRuleNode|. - nsRuleNode* const mRuleNode; + // The source for our style data, either a Gecko nsRuleNode or a Servo + // ComputedValues struct. This never changes after construction, except + // when it's released and nulled out during teardown. + const mozilla::OwningStyleContextSource mSource; + +#ifdef MOZ_STYLO + // In Gecko, we can get this off the rule node. We make this conditional + // on stylo builds to avoid the memory bloat on release. + nsPresContext* mPresContext; +#endif // mCachedInheritedData and mCachedResetData point to both structs that // are owned by this style context and structs that are owned by one of // this style context's ancestors (which are indirectly owned since this // style context owns a reference to its parent). If the bit in |mBits| // is set for a struct, that means that the pointer for that struct is - // owned by an ancestor or by mRuleNode rather than by this style context. + // owned by an ancestor or by the rule node rather than by this style context. // Since style contexts typically have some inherited data but only sometimes // have reset data, we always allocate the mCachedInheritedData, but only // sometimes allocate the mCachedResetData. @@ -606,7 +652,7 @@ private: // mBits stores a number of things: // - It records (using the style struct bits) which structs are - // inherited from the parent context or owned by mRuleNode (i.e., + // inherited from the parent context or owned by the rule node (i.e., // not owned by the style context). // - It also stores the additional bits listed at the top of // nsStyleStruct.h. @@ -637,4 +683,13 @@ NS_NewStyleContext(nsStyleContext* aParentContext, mozilla::CSSPseudoElementType aPseudoType, nsRuleNode* aRuleNode, bool aSkipParentDisplayBasedStyleFixup); + +already_AddRefed +NS_NewStyleContext(nsStyleContext* aParentContext, + nsPresContext* aPresContext, + nsIAtom* aPseudoTag, + mozilla::CSSPseudoElementType aPseudoType, + already_AddRefed aComputedValues, + bool aSkipParentDisplayBasedStyleFixup); + #endif diff --git a/layout/style/nsStyleSet.cpp b/layout/style/nsStyleSet.cpp index f5394211d7..72bdca426f 100644 --- a/layout/style/nsStyleSet.cpp +++ b/layout/style/nsStyleSet.cpp @@ -187,11 +187,16 @@ nsStyleSet::nsStyleSet() : mRuleTree(nullptr), mBatching(0), mInShutdown(false), + mInGC(false), mAuthorStyleDisabled(false), mInReconstruct(false), mInitFontFeatureValuesLookup(true), mNeedsRestyleAfterEnsureUniqueInner(false), mDirty(0), + mRootStyleContextCount(0), +#ifdef DEBUG + mOldRootNode(nullptr), +#endif mUnusedRuleNodeCount(0) { } @@ -246,9 +251,6 @@ nsStyleSet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const } n += mScopedDocSheetRuleProcessors.ShallowSizeOfExcludingThis(aMallocSizeOf); - n += mRoots.ShallowSizeOfExcludingThis(aMallocSizeOf); - n += mOldRuleTrees.ShallowSizeOfExcludingThis(aMallocSizeOf); - return n; } @@ -277,26 +279,21 @@ nsStyleSet::BeginReconstruct() { NS_ASSERTION(!mInReconstruct, "Unmatched begin/end?"); NS_ASSERTION(mRuleTree, "Reconstructing before first construction?"); + mInReconstruct = true; // Clear any ArenaRefPtr-managed style contexts, as we don't want them // held on to after the rule tree has been reconstructed. PresContext()->PresShell()->ClearArenaRefPtrs(eArenaObjectID_nsStyleContext); - // Create a new rule tree root - nsRuleNode* newTree = nsRuleNode::CreateRootNode(mRuleTree->PresContext()); +#ifdef DEBUG + MOZ_ASSERT(!mOldRootNode); + mOldRootNode = mRuleTree; +#endif - // Save the old rule tree so we can destroy it later - if (!mOldRuleTrees.AppendElement(mRuleTree)) { - newTree->Destroy(); - return NS_ERROR_OUT_OF_MEMORY; - } - - // We need to keep mRoots so that the rule tree GC will only free the - // rule trees that really aren't referenced anymore (which should be - // all of them, if there are no bugs in reresolution code). - - mInReconstruct = true; - mRuleTree = newTree; + // Create a new rule tree root, dropping the reference to our old rule tree. + // After reconstruction, we will re-enable GC, and allow everything to be + // collected. + mRuleTree = nsRuleNode::CreateRootNode(mRuleTree->PresContext()); return NS_OK; } @@ -306,22 +303,6 @@ nsStyleSet::EndReconstruct() { NS_ASSERTION(mInReconstruct, "Unmatched begin/end?"); mInReconstruct = false; -#ifdef DEBUG - for (int32_t i = mRoots.Length() - 1; i >= 0; --i) { - // Since nsStyleContext's mParent and mRuleNode are immutable, and - // style contexts own their parents, and nsStyleContext asserts in - // its constructor that the style context and its parent are in the - // same rule tree, we don't need to check any of the children of - // mRoots; we only need to check the rule nodes of mRoots - // themselves. - - NS_ASSERTION(mRoots[i]->RuleNode()->RuleTree() == mRuleTree, - "style context has old rule node"); - } -#endif - // This *should* destroy the only element of mOldRuleTrees, but in - // case of some bugs (which would trigger the above assertions), it - // won't. GCRuleTrees(); } @@ -1798,12 +1779,12 @@ nsStyleSet::ResolveStyleWithoutAnimation(dom::Element* aTarget, } already_AddRefed -nsStyleSet::ResolveStyleForNonElement(nsStyleContext* aParentContext) +nsStyleSet::ResolveStyleForNonElement(nsStyleContext* aParentContext, + nsIAtom* aPseudoTag) { - return GetContext(aParentContext, mRuleTree, nullptr, - nsCSSAnonBoxes::mozNonElement, - CSSPseudoElementType::AnonBox, nullptr, - eNoFlags); + MOZ_ASSERT(nsCSSAnonBoxes::IsNonElement(aPseudoTag)); + return GetContext(aParentContext, mRuleTree, nullptr, aPseudoTag, + CSSPseudoElementType::AnonBox, nullptr, eNoFlags); } void @@ -2197,87 +2178,42 @@ void nsStyleSet::BeginShutdown() { mInShutdown = 1; - mRoots.Clear(); // no longer valid, since we won't keep it up to date } void nsStyleSet::Shutdown() { - mRuleTree->Destroy(); mRuleTree = nullptr; - - // We can have old rule trees either because: - // (1) we failed the assertions in EndReconstruct, or - // (2) we're shutting down within a reconstruct (see bug 462392) - for (uint32_t i = mOldRuleTrees.Length(); i > 0; ) { - --i; - mOldRuleTrees[i]->Destroy(); - } - mOldRuleTrees.Clear(); + GCRuleTrees(); + MOZ_ASSERT(mUnusedRuleNodeList.isEmpty()); + MOZ_ASSERT(mUnusedRuleNodeCount == 0); } -static const uint32_t kGCInterval = 300; - -// Notification that a style context with a null parent has been created. -void -nsStyleSet::AddStyleContextRoot(nsStyleContext* aStyleContext) -{ - // aStyleContext has not been fully initialized, but its parent and - // rule node are correct. - MOZ_ASSERT(!aStyleContext->GetParent()); - mRoots.AppendElement(aStyleContext); -} - -void -nsStyleSet::NotifyStyleContextDestroyed(nsStyleContext* aStyleContext) -{ - if (mInShutdown) - return; - - // Remove style contexts from mRoots even if mOldRuleTree is non-null. This - // could be a style context from the new ruletree! - if (!aStyleContext->GetParent()) { - mRoots.RemoveElement(aStyleContext); - } - - if (mInReconstruct) - return; - - if (mUnusedRuleNodeCount >= kGCInterval) { - GCRuleTrees(); - } -} void nsStyleSet::GCRuleTrees() { - mUnusedRuleNodeCount = 0; + MOZ_ASSERT(!mInReconstruct); + MOZ_ASSERT(!mInGC); + mInGC = true; - // Mark the style context tree by marking all style contexts which - // have no parent, which will mark all descendants. This will reach - // style contexts in the undisplayed map and "additional style - // contexts" since they are descendants of the roots. - for (int32_t i = mRoots.Length() - 1; i >= 0; --i) { - mRoots[i]->Mark(); - } - - // Sweep the rule tree. + while (!mUnusedRuleNodeList.isEmpty()) { + nsRuleNode* node = mUnusedRuleNodeList.popFirst(); #ifdef DEBUG - bool deleted = -#endif - mRuleTree->Sweep(); - NS_ASSERTION(!deleted, "Root node must not be gc'd"); - - // Sweep the old rule trees. - for (uint32_t i = mOldRuleTrees.Length(); i > 0; ) { - --i; - if (mOldRuleTrees[i]->Sweep()) { - // It was deleted, as it should be. - mOldRuleTrees.RemoveElementAt(i); - } else { - NS_NOTREACHED("old rule tree still referenced"); + if (node == mOldRootNode) { + // Flag that we've GCed the old root, if any. + mOldRootNode = nullptr; } +#endif + node->Destroy(); } + +#ifdef DEBUG + NS_ASSERTION(!mOldRootNode, "Should have GCed old root node"); + mOldRootNode = nullptr; +#endif + mUnusedRuleNodeCount = 0; + mInGC = false; } already_AddRefed diff --git a/layout/style/nsStyleSet.h b/layout/style/nsStyleSet.h index 4818c433d5..a6d3894c71 100644 --- a/layout/style/nsStyleSet.h +++ b/layout/style/nsStyleSet.h @@ -15,6 +15,7 @@ #include "mozilla/Attributes.h" #include "mozilla/CSSStyleSheet.h" #include "mozilla/EnumeratedArray.h" +#include "mozilla/LinkedList.h" #include "mozilla/MemoryReporting.h" #include "mozilla/SheetType.h" @@ -165,11 +166,16 @@ class nsStyleSet final // such as text nodes, placeholder frames, and the nsFirstLetterFrame // for everything after the first letter. // - // Perhaps this should go away and we shouldn't even create style - // contexts for such content nodes. However, not doing any rule - // matching for them is a first step. + // aPseudoTag can be either mozText or mozOtherNonElement. + // + // Perhaps mozOtherNonElement should go away and we shouldn't even + // create style contexts for such content nodes. However, not doing + // any rule matching for them is a first step. + // When text-combine-upright is not present, we may also want to avoid + // resolving style contexts for text frames as well. already_AddRefed - ResolveStyleForNonElement(nsStyleContext* aParentContext); + ResolveStyleForNonElement(nsStyleContext* aParentContext, + nsIAtom* aPseudoTag); // Get a style context for a pseudo-element. aParentElement must be // non-null. aPseudoID is the CSSPseudoElementType for the @@ -261,14 +267,6 @@ class nsStyleSet final // Free all of the data associated with this style set. void Shutdown(); - // Notification that a style context with a null parent has been created. - // The argument is a style context that has not been fully initialized, - // but its parent and rule node are correct. - void AddStyleContextRoot(nsStyleContext* aStyleContext); - - // Notification that a style context is being destroyed. - void NotifyStyleContextDestroyed(nsStyleContext* aStyleContext); - // Get a new style context that lives in a different parent // The new context will be the same as the old if the new parent is the // same as the old parent. @@ -363,6 +361,14 @@ class nsStyleSet final return mInReconstruct; } + void RootStyleContextAdded() { + ++mRootStyleContextCount; + } + void RootStyleContextRemoved() { + MOZ_ASSERT(mRootStyleContextCount > 0); + --mRootStyleContextCount; + } + // Return whether the rule tree has cached data such that we need to // do dynamic change handling for changes that change the results of // media queries or require rebuilding all style data. @@ -370,18 +376,25 @@ class nsStyleSet final // they have cached rule cascades; getting the rule cascades again in // order to do rule matching will get the correct rule cascade. bool HasCachedStyleData() const { - return (mRuleTree && mRuleTree->TreeHasCachedData()) || !mRoots.IsEmpty(); + return (mRuleTree && mRuleTree->TreeHasCachedData()) || mRootStyleContextCount > 0; } // Notify the style set that a rulenode is no longer in use, or was // just created and is not in use yet. - void RuleNodeUnused() { + static const uint32_t kGCInterval = 300; + void RuleNodeUnused(nsRuleNode* aNode, bool aMayGC) { ++mUnusedRuleNodeCount; + mUnusedRuleNodeList.insertBack(aNode); + if (aMayGC && mUnusedRuleNodeCount >= kGCInterval && !mInGC && !mInReconstruct) { + GCRuleTrees(); + } } // Notify the style set that a rulenode that wasn't in use now is - void RuleNodeInUse() { + void RuleNodeInUse(nsRuleNode* aNode) { + MOZ_ASSERT(mUnusedRuleNodeCount > 0); --mUnusedRuleNodeCount; + aNode->removeFrom(mUnusedRuleNodeList); } // Returns true if a restyle of the document is needed due to cloning @@ -411,7 +424,11 @@ private: nsStyleSet(const nsStyleSet& aCopy) = delete; nsStyleSet& operator=(const nsStyleSet& aCopy) = delete; - // Run mark-and-sweep GC on mRuleTree and mOldRuleTrees, based on mRoots. + // Free all the rules with reference-count zero. This continues iterating + // over the free list until it is empty, which allows immediate collection + // of nodes whose reference-count drops to zero during the destruction of + // a child node. This allows the collection of entire trees at once, since + // children hold their parents alive. void GCRuleTrees(); nsresult DirtyRuleProcessors(mozilla::SheetType aType); @@ -498,21 +515,36 @@ private: RefPtr mBindingManager; - nsRuleNode* mRuleTree; // This is the root of our rule tree. It is a - // lexicographic tree of matched rules that style - // contexts use to look up properties. + RefPtr mRuleTree; // This is the root of our rule tree. It is a + // lexicographic tree of matched rules that style + // contexts use to look up properties. uint16_t mBatching; unsigned mInShutdown : 1; + unsigned mInGC : 1; unsigned mAuthorStyleDisabled: 1; unsigned mInReconstruct : 1; unsigned mInitFontFeatureValuesLookup : 1; unsigned mNeedsRestyleAfterEnsureUniqueInner : 1; unsigned mDirty : int(mozilla::SheetType::Count); // one bit per sheet type - uint32_t mUnusedRuleNodeCount; // used to batch rule node GC - nsTArray mRoots; // style contexts with no parent + uint32_t mRootStyleContextCount; + +#ifdef DEBUG + // In debug builds, we stash a weak pointer here to the old root during + // reconstruction. During GC, we check for this pointer, and null it out + // when we encounter it. This allows us to assert that the old root (and + // thus all of its subtree) was GCed after reconstruction, which implies + // that there are no style contexts holding on to old rule nodes. + nsRuleNode* mOldRootNode; +#endif + + // Track our rule nodes with zero refcount. When this hits a threshold, we + // sweep and free. Keeping unused rule nodes around for a bit allows us to + // reuse them in many cases. + mozilla::LinkedList mUnusedRuleNodeList; + uint32_t mUnusedRuleNodeCount; // Empty style rules to force things that restrict which properties // apply into different branches of the rule tree. @@ -526,11 +558,6 @@ private: // elements to disable the effect of text zooming. RefPtr mDisableTextZoomStyleRule; - // Old rule trees, which should only be non-empty between - // BeginReconstruct and EndReconstruct, but in case of bugs that cause - // style contexts to exist too long, may last longer. - nsTArray mOldRuleTrees; - // whether font feature values lookup object needs initialization RefPtr mFontFeatureValuesLookup; }; @@ -539,20 +566,20 @@ private: inline void nsRuleNode::AddRef() { - if (mRefCnt++ == 0 && !IsRoot()) { + if (mRefCnt++ == 0) { MOZ_ASSERT(mPresContext->StyleSet()->IsGecko(), "ServoStyleSets should not have rule nodes"); - mPresContext->StyleSet()->AsGecko()->RuleNodeInUse(); + mPresContext->StyleSet()->AsGecko()->RuleNodeInUse(this); } } inline void nsRuleNode::Release() { - if (--mRefCnt == 0 && !IsRoot()) { + if (--mRefCnt == 0) { MOZ_ASSERT(mPresContext->StyleSet()->IsGecko(), "ServoStyleSets should not have rule nodes"); - mPresContext->StyleSet()->AsGecko()->RuleNodeUnused(); + mPresContext->StyleSet()->AsGecko()->RuleNodeUnused(this, /* aMayGC = */ true); } } #endif diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index 23077ad954..f92c981577 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -15,6 +15,7 @@ #include "nsThemeConstants.h" #include "nsString.h" #include "nsPresContext.h" +#include "nsIAppShellService.h" #include "nsIWidget.h" #include "nsCRTGlue.h" #include "nsCSSParser.h" @@ -92,15 +93,31 @@ static int safe_strcmp(const char16_t* a, const char16_t* b) return NS_strcmp(a, b); } +int32_t +StyleStructContext::AppUnitsPerDevPixel() +{ + return DeviceContext()->AppUnitsPerDevPixel(); +} + +nsDeviceContext* +StyleStructContext::HackilyFindSomeDeviceContext() +{ + nsCOMPtr appShell(do_GetService("@mozilla.org/appshell/appShellService;1")); + MOZ_ASSERT(appShell); + nsCOMPtr win; + appShell->GetHiddenDOMWindow(getter_AddRefs(win)); + return nsLayoutUtils::GetDeviceContextForScreenInfo(static_cast(win.get())); +} + static bool AreShadowArraysEqual(nsCSSShadowArray* lhs, nsCSSShadowArray* rhs); // -------------------- // nsStyleFont // -nsStyleFont::nsStyleFont(const nsFont& aFont, nsPresContext *aPresContext) +nsStyleFont::nsStyleFont(const nsFont& aFont, StyleStructContext aContext) : mFont(aFont) - , mSize(nsStyleFont::ZoomText(aPresContext, mFont.size)) + , mSize(nsStyleFont::ZoomText(aContext, mFont.size)) , mGenericID(kGenericFont_NONE) , mScriptLevel(0) , mMathVariant(NS_MATHML_MATHVARIANT_NONE) @@ -109,10 +126,10 @@ nsStyleFont::nsStyleFont(const nsFont& aFont, nsPresContext *aPresContext) , mExplicitLanguage(false) , mAllowZoom(true) , mScriptUnconstrainedSize(mSize) - , mScriptMinSize(aPresContext->CSSTwipsToAppUnits( + , mScriptMinSize(nsPresContext::CSSTwipsToAppUnits( NS_POINTS_TO_TWIPS(NS_MATHML_DEFAULT_SCRIPT_MIN_SIZE_PT))) , mScriptSizeMultiplier(NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER) - , mLanguage(GetLanguage(aPresContext)) + , mLanguage(GetLanguage(aContext)) { MOZ_COUNT_CTOR(nsStyleFont); mFont.size = mSize; @@ -136,9 +153,9 @@ nsStyleFont::nsStyleFont(const nsStyleFont& aSrc) MOZ_COUNT_CTOR(nsStyleFont); } -nsStyleFont::nsStyleFont(nsPresContext* aPresContext) - : nsStyleFont(*(aPresContext->GetDefaultFont( - kPresContext_DefaultVariableFont_ID, nullptr)), aPresContext) +nsStyleFont::nsStyleFont(StyleStructContext aContext) + : nsStyleFont(*aContext.GetDefaultFont(kPresContext_DefaultVariableFont_ID), + aContext) { } @@ -196,11 +213,11 @@ nsChangeHint nsStyleFont::CalcDifference(const nsStyleFont& aOther) const } /* static */ nscoord -nsStyleFont::ZoomText(nsPresContext *aPresContext, nscoord aSize) +nsStyleFont::ZoomText(StyleStructContext aContext, nscoord aSize) { // aSize can be negative (e.g.: calc(-1px)) so we can't assert that here. // The caller is expected deal with that. - return NSCoordSaturatingMultiply(aSize, aPresContext->TextZoom()); + return NSToCoordTruncClamped(float(aSize) * aContext.TextZoom()); } /* static */ nscoord @@ -208,20 +225,20 @@ nsStyleFont::UnZoomText(nsPresContext *aPresContext, nscoord aSize) { // aSize can be negative (e.g.: calc(-1px)) so we can't assert that here. // The caller is expected deal with that. - return NSCoordSaturatingMultiply(aSize, 1.0 / aPresContext->TextZoom()); + return NSToCoordTruncClamped(float(aSize) / aPresContext->TextZoom()); } /* static */ already_AddRefed -nsStyleFont::GetLanguage(nsPresContext* aPresContext) +nsStyleFont::GetLanguage(StyleStructContext aContext) { - RefPtr language = aPresContext->GetContentLanguage(); + RefPtr language = aContext.GetContentLanguage(); if (!language) { // we didn't find a (usable) Content-Language, so we fall back // to whatever the presContext guessed from the charset // NOTE this should not be used elsewhere, because we want websites // to use UTF-8 with proper language tag, instead of relying on // deriving language from charset. See bug 1040668 comment 67. - language = aPresContext->GetLanguageFromCharset(); + language = aContext.GetLanguageFromCharset(); } return language.forget(); } @@ -252,7 +269,7 @@ static nscoord CalcCoord(const nsStyleCoord& aCoord, return nsRuleNode::ComputeCoordPercentCalc(aCoord, 0); } -nsStyleMargin::nsStyleMargin() +nsStyleMargin::nsStyleMargin(StyleStructContext aContext) : mHasCachedMargin(false) , mCachedMargin(0, 0, 0, 0) { @@ -303,7 +320,7 @@ nsChangeHint nsStyleMargin::CalcDifference(const nsStyleMargin& aOther) const nsChangeHint_ClearAncestorIntrinsics; } -nsStylePadding::nsStylePadding() +nsStylePadding::nsStylePadding(StyleStructContext aContext) : mHasCachedPadding(false) , mCachedPadding(0, 0, 0, 0) { @@ -356,9 +373,8 @@ nsChangeHint nsStylePadding::CalcDifference(const nsStylePadding& aOther) const nsChangeHint_ClearDescendantIntrinsics); } -nsStyleBorder::nsStyleBorder(nsPresContext* aPresContext) +nsStyleBorder::nsStyleBorder(StyleStructContext aContext) : mBorderColors(nullptr), - mBoxShadow(nullptr), mBorderImageFill(NS_STYLE_BORDER_IMAGE_SLICE_NOFILL), mBorderImageRepeatH(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH), mBorderImageRepeatV(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH), @@ -373,7 +389,7 @@ nsStyleBorder::nsStyleBorder(nsPresContext* aPresContext) } nscoord medium = - (aPresContext->GetBorderWidthTable())[NS_STYLE_BORDER_WIDTH_MEDIUM]; + (StaticPresData::Get()->GetBorderWidthTable())[NS_STYLE_BORDER_WIDTH_MEDIUM]; NS_FOR_CSS_SIDES(side) { mBorderImageSlice.Set(side, nsStyleCoord(1.0f, eStyleUnit_Percent)); mBorderImageWidth.Set(side, nsStyleCoord(1.0f, eStyleUnit_Factor)); @@ -384,7 +400,7 @@ nsStyleBorder::nsStyleBorder(nsPresContext* aPresContext) mBorderColor[side] = NS_RGB(0, 0, 0); } - mTwipsPerPixel = aPresContext->DevPixelsToAppUnits(1); + mTwipsPerPixel = aContext.DevPixelsToAppUnits(1); } nsBorderColors::~nsBorderColors() @@ -405,7 +421,6 @@ nsBorderColors::Clone(bool aDeep) const nsStyleBorder::nsStyleBorder(const nsStyleBorder& aSrc) : mBorderColors(nullptr), - mBoxShadow(aSrc.mBoxShadow), mBorderRadius(aSrc.mBorderRadius), mBorderImageSource(aSrc.mBorderImageSource), mBorderImageSlice(aSrc.mBorderImageSlice), @@ -492,18 +507,6 @@ nsChangeHint nsStyleBorder::CalcDifference(const nsStyleBorder& aOther) const mBoxDecorationBreak != aOther.mBoxDecorationBreak) return NS_STYLE_HINT_REFLOW; - nsChangeHint boxShadowHint = nsChangeHint(0); - if (!AreShadowArraysEqual(mBoxShadow, aOther.mBoxShadow)) { - // Update overflow regions & trigger DLBI to be sure it's noticed: - NS_UpdateHint(boxShadowHint, nsChangeHint_UpdateOverflow); - NS_UpdateHint(boxShadowHint, nsChangeHint_SchedulePaint); - // Also request a repaint, since it's possible that only the color - // of the shadow is changing (and UpdateOverflow/SchedulePaint won't - // repaint for that, since they won't know what needs invalidating.) - NS_UpdateHint(boxShadowHint, nsChangeHint_RepaintFrame); - // Don't return yet; we may also need nsChangeHint_BorderStyleNoneChange. - } - NS_FOR_CSS_SIDES(ix) { // See the explanation in nsChangeHint.h of // nsChangeHint_BorderStyleNoneChange . @@ -511,19 +514,11 @@ nsChangeHint nsStyleBorder::CalcDifference(const nsStyleBorder& aOther) const // assume a repaint hint for some other change rather than bother // tracking this result through the rest of the function. if (HasVisibleStyle(ix) != aOther.HasVisibleStyle(ix)) { - return NS_CombineHint(boxShadowHint, - nsChangeHint_RepaintFrame | - nsChangeHint_BorderStyleNoneChange); + return nsChangeHint_RepaintFrame | + nsChangeHint_BorderStyleNoneChange; } } - if (boxShadowHint) { - // NOTE: This hint (UpdateOverflow + SchedulePaint + RepaintFrame) is - // expected to subsume all hints returned after this point. (Hence, we're - // OK to return early.) - return boxShadowHint; - } - // Note that mBorderStyle stores not only the border style but also // color-related flags. Given that we've already done an mComputedBorder // comparison, border-style differences can only lead to a repaint hint. So @@ -570,7 +565,7 @@ nsChangeHint nsStyleBorder::CalcDifference(const nsStyleBorder& aOther) const return NS_STYLE_HINT_NONE; } -nsStyleOutline::nsStyleOutline(nsPresContext* aPresContext) +nsStyleOutline::nsStyleOutline(StyleStructContext aContext) { MOZ_COUNT_CTOR(nsStyleOutline); // spacing values not inherited @@ -585,8 +580,7 @@ nsStyleOutline::nsStyleOutline(nsPresContext* aPresContext) mOutlineStyle = NS_STYLE_BORDER_STYLE_NONE; mOutlineColor = NS_RGB(0, 0, 0); - mHasCachedOutline = false; - mTwipsPerPixel = aPresContext->DevPixelsToAppUnits(1); + mTwipsPerPixel = aContext.DevPixelsToAppUnits(1); SetOutlineInitialColor(); } @@ -597,7 +591,6 @@ nsStyleOutline::nsStyleOutline(const nsStyleOutline& aSrc) , mOutlineOffset(aSrc.mOutlineOffset) , mCachedOutlineWidth(aSrc.mCachedOutlineWidth) , mOutlineColor(aSrc.mOutlineColor) - , mHasCachedOutline(aSrc.mHasCachedOutline) , mOutlineStyle(aSrc.mOutlineStyle) , mTwipsPerPixel(aSrc.mTwipsPerPixel) { @@ -609,17 +602,14 @@ nsStyleOutline::RecalcData(nsPresContext* aContext) { if (NS_STYLE_BORDER_STYLE_NONE == GetOutlineStyle()) { mCachedOutlineWidth = 0; - mHasCachedOutline = true; - } else if (IsFixedUnit(mOutlineWidth, true)) { + } else { + MOZ_ASSERT(IsFixedUnit(mOutlineWidth, true)); // Clamp negative calc() to 0. mCachedOutlineWidth = std::max(CalcCoord(mOutlineWidth, aContext->GetBorderWidthTable(), 3), 0); mCachedOutlineWidth = NS_ROUND_BORDER_TO_PIXELS(mCachedOutlineWidth, mTwipsPerPixel); - mHasCachedOutline = true; } - else - mHasCachedOutline = false; } nsChangeHint nsStyleOutline::CalcDifference(const nsStyleOutline& aOther) const @@ -646,9 +636,7 @@ nsChangeHint nsStyleOutline::CalcDifference(const nsStyleOutline& aOther) const if (mOutlineWidth != aOther.mOutlineWidth || mOutlineOffset != aOther.mOutlineOffset || mTwipsPerPixel != aOther.mTwipsPerPixel || - mHasCachedOutline != aOther.mHasCachedOutline || - (mHasCachedOutline && - (mCachedOutlineWidth != aOther.mCachedOutlineWidth))) { + mCachedOutlineWidth != aOther.mCachedOutlineWidth) { return nsChangeHint_NeutralChange; } @@ -658,13 +646,13 @@ nsChangeHint nsStyleOutline::CalcDifference(const nsStyleOutline& aOther) const // -------------------- // nsStyleList // -nsStyleList::nsStyleList(nsPresContext* aPresContext) +nsStyleList::nsStyleList(StyleStructContext aContext) : mListStylePosition(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE), mListStyleType(NS_LITERAL_STRING("disc")), - mCounterStyle(aPresContext->CounterStyleManager()-> - BuildCounterStyle(mListStyleType)) + mCounterStyle(aContext.BuildCounterStyle(mListStyleType)) { MOZ_COUNT_CTOR(nsStyleList); + SetQuotesInitial(); } nsStyleList::~nsStyleList() @@ -676,14 +664,74 @@ nsStyleList::nsStyleList(const nsStyleList& aSource) : mListStylePosition(aSource.mListStylePosition), mListStyleType(aSource.mListStyleType), mCounterStyle(aSource.mCounterStyle), + mQuotes(aSource.mQuotes), mImageRegion(aSource.mImageRegion) { SetListStyleImage(aSource.GetListStyleImage()); MOZ_COUNT_CTOR(nsStyleList); } -nsChangeHint nsStyleList::CalcDifference(const nsStyleList& aOther) const +void +nsStyleList::SetQuotesInherit(const nsStyleList* aOther) { + mQuotes = aOther->mQuotes; +} + +void +nsStyleList::SetQuotesInitial() +{ + if (!sInitialQuotes) { + // The initial value for quotes is the en-US typographic convention: + // outermost are LEFT and RIGHT DOUBLE QUOTATION MARK, alternating + // with LEFT and RIGHT SINGLE QUOTATION MARK. + static const char16_t initialQuotes[8] = { + 0x201C, 0, 0x201D, 0, 0x2018, 0, 0x2019, 0 + }; + + sInitialQuotes = new nsStyleQuoteValues; + sInitialQuotes->mQuotePairs.AppendElement( + std::make_pair(nsDependentString(&initialQuotes[0], 1), + nsDependentString(&initialQuotes[2], 1))); + sInitialQuotes->mQuotePairs.AppendElement( + std::make_pair(nsDependentString(&initialQuotes[4], 1), + nsDependentString(&initialQuotes[6], 1))); + } + + mQuotes = sInitialQuotes; +} + +void +nsStyleList::SetQuotesNone() +{ + if (!sNoneQuotes) { + sNoneQuotes = new nsStyleQuoteValues; + } + mQuotes = sNoneQuotes; +} + +void +nsStyleList::SetQuotes(nsStyleQuoteValues::QuotePairArray&& aValues) +{ + mQuotes = new nsStyleQuoteValues; + mQuotes->mQuotePairs = Move(aValues); +} + +const nsStyleQuoteValues::QuotePairArray& +nsStyleList::GetQuotePairs() const +{ + return mQuotes->mQuotePairs; +} + +nsChangeHint +nsStyleList::CalcDifference(const nsStyleList& aOther) const +{ + // If the quotes implementation is ever going to change we might not need + // a framechange here and a reflow should be sufficient. See bug 35768. + if (mQuotes != aOther.mQuotes && + (mQuotes || aOther.mQuotes) && + GetQuotePairs() != aOther.GetQuotePairs()) { + return NS_STYLE_HINT_FRAMECHANGE; + } if (mListStylePosition != aOther.mListStylePosition) return NS_STYLE_HINT_FRAMECHANGE; if (EqualImages(mListStyleImage, aOther.mListStyleImage) && @@ -700,10 +748,17 @@ nsChangeHint nsStyleList::CalcDifference(const nsStyleList& aOther) const return NS_STYLE_HINT_REFLOW; } +StaticRefPtr +nsStyleList::sInitialQuotes; + +StaticRefPtr +nsStyleList::sNoneQuotes; + + // -------------------- // nsStyleXUL // -nsStyleXUL::nsStyleXUL() +nsStyleXUL::nsStyleXUL(StyleStructContext aContext) { MOZ_COUNT_CTOR(nsStyleXUL); mBoxAlign = NS_STYLE_BOX_ALIGN_STRETCH; @@ -752,7 +807,7 @@ nsChangeHint nsStyleXUL::CalcDifference(const nsStyleXUL& aOther) const // /* static */ const uint32_t nsStyleColumn::kMaxColumnCount = 1000; -nsStyleColumn::nsStyleColumn(nsPresContext* aPresContext) +nsStyleColumn::nsStyleColumn(StyleStructContext aContext) { MOZ_COUNT_CTOR(nsStyleColumn); mColumnCount = NS_STYLE_COLUMN_COUNT_AUTO; @@ -760,12 +815,13 @@ nsStyleColumn::nsStyleColumn(nsPresContext* aPresContext) mColumnGap.SetNormalValue(); mColumnFill = NS_STYLE_COLUMN_FILL_BALANCE; - mColumnRuleWidth = (aPresContext->GetBorderWidthTable())[NS_STYLE_BORDER_WIDTH_MEDIUM]; + mColumnRuleWidth = + (StaticPresData::Get()->GetBorderWidthTable())[NS_STYLE_BORDER_WIDTH_MEDIUM]; mColumnRuleStyle = NS_STYLE_BORDER_STYLE_NONE; mColumnRuleColor = NS_RGB(0, 0, 0); mColumnRuleColorIsForeground = true; - mTwipsPerPixel = aPresContext->AppUnitsPerDevPixel(); + mTwipsPerPixel = aContext.AppUnitsPerDevPixel(); } nsStyleColumn::~nsStyleColumn() @@ -821,7 +877,7 @@ nsChangeHint nsStyleColumn::CalcDifference(const nsStyleColumn& aOther) const // -------------------- // nsStyleSVG // -nsStyleSVG::nsStyleSVG() +nsStyleSVG::nsStyleSVG(StyleStructContext aContext) { MOZ_COUNT_CTOR(nsStyleSVG); mFill.mType = eStyleSVGPaintType_Color; @@ -844,13 +900,11 @@ nsStyleSVG::nsStyleSVG() mColorInterpolation = NS_STYLE_COLOR_INTERPOLATION_SRGB; mColorInterpolationFilters = NS_STYLE_COLOR_INTERPOLATION_LINEARRGB; mFillRule = NS_STYLE_FILL_RULE_NONZERO; - mImageRendering = NS_STYLE_IMAGE_RENDERING_AUTO; mPaintOrder = NS_STYLE_PAINT_ORDER_NORMAL; mShapeRendering = NS_STYLE_SHAPE_RENDERING_AUTO; mStrokeLinecap = NS_STYLE_STROKE_LINECAP_BUTT; mStrokeLinejoin = NS_STYLE_STROKE_LINEJOIN_MITER; mTextAnchor = NS_STYLE_TEXT_ANCHOR_START; - mTextRendering = NS_STYLE_TEXT_RENDERING_AUTO; mFillOpacitySource = eStyleSVGOpacitySource_Normal; mStrokeOpacitySource = eStyleSVGOpacitySource_Normal; mStrokeDasharrayFromObject = false; @@ -898,13 +952,11 @@ nsStyleSVG::nsStyleSVG(const nsStyleSVG& aSource) mColorInterpolation = aSource.mColorInterpolation; mColorInterpolationFilters = aSource.mColorInterpolationFilters; mFillRule = aSource.mFillRule; - mImageRendering = aSource.mImageRendering; mPaintOrder = aSource.mPaintOrder; mShapeRendering = aSource.mShapeRendering; mStrokeLinecap = aSource.mStrokeLinecap; mStrokeLinejoin = aSource.mStrokeLinejoin; mTextAnchor = aSource.mTextAnchor; - mTextRendering = aSource.mTextRendering; mFillOpacitySource = aSource.mFillOpacitySource; mStrokeOpacitySource = aSource.mStrokeOpacitySource; mStrokeDasharrayFromObject = aSource.mStrokeDasharrayFromObject; @@ -966,14 +1018,12 @@ nsChangeHint nsStyleSVG::CalcDifference(const nsStyleSVG& aOther) const // we need a reflow here. No intrinsic sizes need to change, so // nsChangeHint_NeedReflow is sufficient. // Note that stroke-dashoffset does not affect nsSVGPathGeometryFrame::mRect. - // text-anchor and text-rendering changes also require a reflow since they - // change frames' rects. + // text-anchor changes also require a reflow since it changes frames' rects. if (mStrokeWidth != aOther.mStrokeWidth || mStrokeMiterlimit != aOther.mStrokeMiterlimit || mStrokeLinecap != aOther.mStrokeLinecap || mStrokeLinejoin != aOther.mStrokeLinejoin || - mTextAnchor != aOther.mTextAnchor || - mTextRendering != aOther.mTextRendering) { + mTextAnchor != aOther.mTextAnchor) { NS_UpdateHint(hint, nsChangeHint_NeedReflow); NS_UpdateHint(hint, nsChangeHint_NeedDirtyReflow); // XXX remove me: bug 876085 NS_UpdateHint(hint, nsChangeHint_RepaintFrame); @@ -989,7 +1039,6 @@ nsChangeHint nsStyleSVG::CalcDifference(const nsStyleSVG& aOther) const mColorInterpolation != aOther.mColorInterpolation || mColorInterpolationFilters != aOther.mColorInterpolationFilters || mFillRule != aOther.mFillRule || - mImageRendering != aOther.mImageRendering || mPaintOrder != aOther.mPaintOrder || mShapeRendering != aOther.mShapeRendering || mStrokeDasharrayLength != aOther.mStrokeDasharrayLength || @@ -1240,7 +1289,7 @@ nsStyleFilter::SetDropShadow(nsCSSShadowArray* aDropShadow) // -------------------- // nsStyleSVGReset // -nsStyleSVGReset::nsStyleSVGReset() +nsStyleSVGReset::nsStyleSVGReset(StyleStructContext aContext) { MOZ_COUNT_CTOR(nsStyleSVGReset); mStopColor = NS_RGB(0,0,0); @@ -1266,7 +1315,6 @@ nsStyleSVGReset::nsStyleSVGReset(const nsStyleSVGReset& aSource) mFloodColor = aSource.mFloodColor; mLightingColor = aSource.mLightingColor; mClipPath = aSource.mClipPath; - mFilters = aSource.mFilters; mStopOpacity = aSource.mStopOpacity; mFloodOpacity = aSource.mFloodOpacity; mDominantBaseline = aSource.mDominantBaseline; @@ -1286,14 +1334,11 @@ nsChangeHint nsStyleSVGReset::CalcDifference(const nsStyleSVGReset& aOther) cons { nsChangeHint hint = nsChangeHint(0); - if (mClipPath != aOther.mClipPath || - mFilters != aOther.mFilters) { + if (mClipPath != aOther.mClipPath) { NS_UpdateHint(hint, nsChangeHint_UpdateEffects); NS_UpdateHint(hint, nsChangeHint_RepaintFrame); - // We only actually need to update the overflow area for filter - // changes. However, clip-path changes require that we update - // the PreEffectsBBoxProperty, which is done during overflow - // computation. + // clip-path changes require that we update the PreEffectsBBoxProperty, + // which is done during overflow computation. NS_UpdateHint(hint, nsChangeHint_UpdateOverflow); } @@ -1373,7 +1418,7 @@ bool nsStyleSVGPaint::operator==(const nsStyleSVGPaint& aOther) const // -------------------- // nsStylePosition // -nsStylePosition::nsStylePosition(void) +nsStylePosition::nsStylePosition(StyleStructContext aContext) { MOZ_COUNT_CTOR(nsStylePosition); @@ -1702,7 +1747,7 @@ nsStylePosition::ComputedJustifySelf(nsStyleContext* aParent) const // nsStyleTable // -nsStyleTable::nsStyleTable() +nsStyleTable::nsStyleTable(StyleStructContext aContext) { MOZ_COUNT_CTOR(nsStyleTable); // values not inherited @@ -1733,7 +1778,7 @@ nsChangeHint nsStyleTable::CalcDifference(const nsStyleTable& aOther) const // ----------------------- // nsStyleTableBorder -nsStyleTableBorder::nsStyleTableBorder() +nsStyleTableBorder::nsStyleTableBorder(StyleStructContext aContext) { MOZ_COUNT_CTOR(nsStyleTableBorder); mBorderCollapse = NS_STYLE_BORDER_SEPARATE; @@ -1784,10 +1829,10 @@ nsChangeHint nsStyleTableBorder::CalcDifference(const nsStyleTableBorder& aOther // nsStyleColor // -nsStyleColor::nsStyleColor(nsPresContext* aPresContext) +nsStyleColor::nsStyleColor(StyleStructContext aContext) { MOZ_COUNT_CTOR(nsStyleColor); - mColor = aPresContext->DefaultColor(); + mColor = aContext.DefaultColor(); } nsStyleColor::nsStyleColor(const nsStyleColor& aSource) @@ -2572,7 +2617,7 @@ nsStyleImageLayers::Layer::CalcDifference(const nsStyleImageLayers::Layer& aOthe // nsStyleBackground // -nsStyleBackground::nsStyleBackground() +nsStyleBackground::nsStyleBackground(StyleStructContext aContext) : mBackgroundColor(NS_RGBA(0, 0, 0, 0)) { MOZ_COUNT_CTOR(nsStyleBackground); @@ -2765,7 +2810,7 @@ mozilla::StyleAnimation::operator==(const mozilla::StyleAnimation& aOther) const mIterationCount == aOther.mIterationCount; } -nsStyleDisplay::nsStyleDisplay() +nsStyleDisplay::nsStyleDisplay(StyleStructContext aContext) : mWillChangeBitField(0) { MOZ_COUNT_CTOR(nsStyleDisplay); @@ -2784,9 +2829,6 @@ nsStyleDisplay::nsStyleDisplay() mOverflowY = NS_STYLE_OVERFLOW_VISIBLE; mOverflowClipBox = NS_STYLE_OVERFLOW_CLIP_BOX_PADDING_BOX; mResize = NS_STYLE_RESIZE_NONE; - mClipFlags = NS_STYLE_CLIP_AUTO; - mClip.SetRect(0,0,0,0); - mOpacity = 1.0f; mSpecifiedTransform = nullptr; mTransformOrigin[0].SetPercentValue(0.5f); // Transform is centered on origin mTransformOrigin[1].SetPercentValue(0.5f); @@ -2794,11 +2836,11 @@ nsStyleDisplay::nsStyleDisplay() mPerspectiveOrigin[0].SetPercentValue(0.5f); mPerspectiveOrigin[1].SetPercentValue(0.5f); mChildPerspective.SetNoneValue(); + mVerticalAlign.SetIntValue(NS_STYLE_VERTICAL_ALIGN_BASELINE, eStyleUnit_Enumerated); mBackfaceVisibility = NS_STYLE_BACKFACE_VISIBILITY_VISIBLE; mTransformStyle = NS_STYLE_TRANSFORM_STYLE_FLAT; mTransformBox = NS_STYLE_TRANSFORM_BOX_BORDER_BOX; mOrient = NS_STYLE_ORIENT_INLINE; - mMixBlendMode = NS_STYLE_BLEND_NORMAL; mIsolation = NS_STYLE_ISOLATION_AUTO; mTouchAction = NS_STYLE_TOUCH_ACTION_AUTO; mTopLayer = NS_STYLE_TOP_LAYER_NONE; @@ -2835,8 +2877,6 @@ nsStyleDisplay::nsStyleDisplay() nsStyleDisplay::nsStyleDisplay(const nsStyleDisplay& aSource) : mBinding(aSource.mBinding) - , mClip(aSource.mClip) - , mOpacity(aSource.mOpacity) , mDisplay(aSource.mDisplay) , mOriginalDisplay(aSource.mOriginalDisplay) , mContain(aSource.mContain) @@ -2852,9 +2892,7 @@ nsStyleDisplay::nsStyleDisplay(const nsStyleDisplay& aSource) , mOverflowY(aSource.mOverflowY) , mOverflowClipBox(aSource.mOverflowClipBox) , mResize(aSource.mResize) - , mClipFlags(aSource.mClipFlags) , mOrient(aSource.mOrient) - , mMixBlendMode(aSource.mMixBlendMode) , mIsolation(aSource.mIsolation) , mTopLayer(aSource.mTopLayer) , mWillChangeBitField(aSource.mWillChangeBitField) @@ -2872,6 +2910,7 @@ nsStyleDisplay::nsStyleDisplay(const nsStyleDisplay& aSource) , mTransformBox(aSource.mTransformBox) , mSpecifiedTransform(aSource.mSpecifiedTransform) , mChildPerspective(aSource.mChildPerspective) + , mVerticalAlign(aSource.mVerticalAlign) , mTransitions(aSource.mTransitions) , mTransitionTimingFunctionCount(aSource.mTransitionTimingFunctionCount) , mTransitionDurationCount(aSource.mTransitionDurationCount) @@ -2951,6 +2990,12 @@ nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const nsChangeHint_NeedDirtyReflow))); } + if (mVerticalAlign != aOther.mVerticalAlign) { + // XXX Can this just be AllReflowHints + RepaintFrame, and be included in + // the block below? + NS_UpdateHint(hint, NS_STYLE_HINT_REFLOW); + } + // XXX the following is conservative, for now: changing float breaking shouldn't // necessarily require a repaint, reflow should suffice. if (mBreakType != aOther.mBreakType @@ -2959,35 +3004,11 @@ nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const || mBreakAfter != aOther.mBreakAfter || mAppearance != aOther.mAppearance || mOrient != aOther.mOrient - || mOverflowClipBox != aOther.mOverflowClipBox - || mClipFlags != aOther.mClipFlags) + || mOverflowClipBox != aOther.mOverflowClipBox) NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_AllReflowHints, nsChangeHint_RepaintFrame)); - if (!mClip.IsEqualInterior(aOther.mClip)) { - // If the clip has changed, we just need to update overflow areas. DLBI - // will handle the invalidation. - NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_UpdateOverflow, - nsChangeHint_SchedulePaint)); - } - - if (mOpacity != aOther.mOpacity) { - // If we're going from the optimized >=0.99 opacity value to 1.0 or back, then - // repaint the frame because DLBI will not catch the invalidation. Otherwise, - // just update the opacity layer. - if ((mOpacity >= 0.99f && mOpacity < 1.0f && aOther.mOpacity == 1.0f) || - (aOther.mOpacity >= 0.99f && aOther.mOpacity < 1.0f && mOpacity == 1.0f)) { - NS_UpdateHint(hint, nsChangeHint_RepaintFrame); - } else { - NS_UpdateHint(hint, nsChangeHint_UpdateOpacityLayer); - if ((mOpacity == 1.0f) != (aOther.mOpacity == 1.0f)) { - NS_UpdateHint(hint, nsChangeHint_UpdateUsesOpacity); - } - } - } - - if (mMixBlendMode != aOther.mMixBlendMode - || mIsolation != aOther.mIsolation) { + if (mIsolation != aOther.mIsolation) { NS_UpdateHint(hint, nsChangeHint_RepaintFrame); } @@ -3099,8 +3120,7 @@ nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const // properties, since some data did change in the style struct. if (!hint && - (!mClip.IsEqualEdges(aOther.mClip) || - mOriginalDisplay != aOther.mOriginalDisplay || + (mOriginalDisplay != aOther.mOriginalDisplay || mOriginalFloats != aOther.mOriginalFloats || mTransitions != aOther.mTransitions || mTransitionTimingFunctionCount != @@ -3128,19 +3148,20 @@ nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const // nsStyleVisibility // -nsStyleVisibility::nsStyleVisibility(nsPresContext* aPresContext) +nsStyleVisibility::nsStyleVisibility(StyleStructContext aContext) { MOZ_COUNT_CTOR(nsStyleVisibility); - uint32_t bidiOptions = aPresContext->GetBidi(); + uint32_t bidiOptions = aContext.GetBidi(); if (GET_BIDI_OPTION_DIRECTION(bidiOptions) == IBMBIDI_TEXTDIRECTION_RTL) mDirection = NS_STYLE_DIRECTION_RTL; else mDirection = NS_STYLE_DIRECTION_LTR; mVisible = NS_STYLE_VISIBILITY_VISIBLE; - mPointerEvents = NS_STYLE_POINTER_EVENTS_AUTO; + mImageRendering = NS_STYLE_IMAGE_RENDERING_AUTO; mWritingMode = NS_STYLE_WRITING_MODE_HORIZONTAL_TB; mTextOrientation = NS_STYLE_TEXT_ORIENTATION_MIXED; + mColorAdjust = NS_STYLE_COLOR_ADJUST_ECONOMY; } nsStyleVisibility::nsStyleVisibility(const nsStyleVisibility& aSource) @@ -3149,9 +3170,10 @@ nsStyleVisibility::nsStyleVisibility(const nsStyleVisibility& aSource) mImageOrientation = aSource.mImageOrientation; mDirection = aSource.mDirection; mVisible = aSource.mVisible; - mPointerEvents = aSource.mPointerEvents; + mImageRendering = aSource.mImageRendering; mWritingMode = aSource.mWritingMode; mTextOrientation = aSource.mTextOrientation; + mColorAdjust = aSource.mColorAdjust; } nsChangeHint nsStyleVisibility::CalcDifference(const nsStyleVisibility& aOther) const @@ -3179,12 +3201,12 @@ nsChangeHint nsStyleVisibility::CalcDifference(const nsStyleVisibility& aOther) if (mTextOrientation != aOther.mTextOrientation) { NS_UpdateHint(hint, NS_STYLE_HINT_REFLOW); } - if (mPointerEvents != aOther.mPointerEvents) { - // nsSVGPathGeometryFrame's mRect depends on stroke _and_ on the value - // of pointer-events. See nsSVGPathGeometryFrame::ReflowSVG's use of - // GetHitTestFlags. (Only a reflow, no visual change.) - NS_UpdateHint(hint, nsChangeHint_NeedReflow); - NS_UpdateHint(hint, nsChangeHint_NeedDirtyReflow); // XXX remove me: bug 876085 + if (mImageRendering != aOther.mImageRendering) { + hint |= nsChangeHint_RepaintFrame; + } + if (mColorAdjust != aOther.mColorAdjust) { + // color-adjust only affects media where dynamic changes can't happen. + NS_UpdateHint(hint, nsChangeHint_NeutralChange); } } return hint; @@ -3296,7 +3318,7 @@ nsStyleContentData::UntrackImage(nsPresContext* aContext) // nsStyleContent // -nsStyleContent::nsStyleContent(void) +nsStyleContent::nsStyleContent(StyleStructContext aContext) : mMarkerOffset(), mContents(nullptr), mIncrements(nullptr), @@ -3438,88 +3460,14 @@ nsresult nsStyleContent::AllocateContents(uint32_t aCount) return NS_OK; } -// --------------------- -// nsStyleQuotes -// - -nsStyleQuotes::nsStyleQuotes(void) - : mQuotesCount(0), - mQuotes(nullptr) -{ - MOZ_COUNT_CTOR(nsStyleQuotes); - SetInitial(); -} - -nsStyleQuotes::~nsStyleQuotes(void) -{ - MOZ_COUNT_DTOR(nsStyleQuotes); - DELETE_ARRAY_IF(mQuotes); -} - -nsStyleQuotes::nsStyleQuotes(const nsStyleQuotes& aSource) - : mQuotesCount(0), - mQuotes(nullptr) -{ - MOZ_COUNT_CTOR(nsStyleQuotes); - CopyFrom(aSource); -} - -void -nsStyleQuotes::SetInitial() -{ - // The initial value for quotes is the en-US typographic convention: - // outermost are LEFT and RIGHT DOUBLE QUOTATION MARK, alternating - // with LEFT and RIGHT SINGLE QUOTATION MARK. - static const char16_t initialQuotes[8] = { - 0x201C, 0, 0x201D, 0, 0x2018, 0, 0x2019, 0 - }; - - if (NS_SUCCEEDED(AllocateQuotes(2))) { - SetQuotesAt(0, - nsDependentString(&initialQuotes[0], 1), - nsDependentString(&initialQuotes[2], 1)); - SetQuotesAt(1, - nsDependentString(&initialQuotes[4], 1), - nsDependentString(&initialQuotes[6], 1)); - } -} - -void -nsStyleQuotes::CopyFrom(const nsStyleQuotes& aSource) -{ - if (NS_SUCCEEDED(AllocateQuotes(aSource.QuotesCount()))) { - uint32_t count = (mQuotesCount * 2); - for (uint32_t index = 0; index < count; index += 2) { - aSource.GetQuotesAt(index, mQuotes[index], mQuotes[index + 1]); - } - } -} - -nsChangeHint nsStyleQuotes::CalcDifference(const nsStyleQuotes& aOther) const -{ - // If the quotes implementation is ever going to change we might not need - // a framechange here and a reflow should be sufficient. See bug 35768. - if (mQuotesCount == aOther.mQuotesCount) { - uint32_t ix = (mQuotesCount * 2); - while (0 < ix--) { - if (mQuotes[ix] != aOther.mQuotes[ix]) { - return NS_STYLE_HINT_FRAMECHANGE; - } - } - - return NS_STYLE_HINT_NONE; - } - return NS_STYLE_HINT_FRAMECHANGE; -} // -------------------- // nsStyleTextReset // -nsStyleTextReset::nsStyleTextReset(void) +nsStyleTextReset::nsStyleTextReset(StyleStructContext aContext) { MOZ_COUNT_CTOR(nsStyleTextReset); - mVerticalAlign.SetIntValue(NS_STYLE_VERTICAL_ALIGN_BASELINE, eStyleUnit_Enumerated); mTextDecorationLine = NS_STYLE_TEXT_DECORATION_LINE_NONE; mTextDecorationColor = NS_RGB(0,0,0); mTextDecorationStyle = @@ -3538,38 +3486,39 @@ nsStyleTextReset::~nsStyleTextReset(void) MOZ_COUNT_DTOR(nsStyleTextReset); } -nsChangeHint nsStyleTextReset::CalcDifference(const nsStyleTextReset& aOther) const +nsChangeHint +nsStyleTextReset::CalcDifference(const nsStyleTextReset& aOther) const { - if (mVerticalAlign == aOther.mVerticalAlign - && mUnicodeBidi == aOther.mUnicodeBidi) { - uint8_t lineStyle = GetDecorationStyle(); - uint8_t otherLineStyle = aOther.GetDecorationStyle(); - if (mTextDecorationLine != aOther.mTextDecorationLine || - lineStyle != otherLineStyle) { - // Changes to our text-decoration line can impact our overflow area & - // also our descendants' overflow areas (particularly for text-frame - // descendants). So, we update those areas & trigger a repaint. - nsChangeHint hint = nsChangeHint_RepaintFrame; - NS_UpdateHint(hint, nsChangeHint_UpdateSubtreeOverflow); - NS_UpdateHint(hint, nsChangeHint_SchedulePaint); - return hint; - } - - // Repaint for decoration color changes - nscolor decColor, otherDecColor; - bool isFG, otherIsFG; - GetDecorationColor(decColor, isFG); - aOther.GetDecorationColor(otherDecColor, otherIsFG); - if (isFG != otherIsFG || (!isFG && decColor != otherDecColor)) { - return nsChangeHint_RepaintFrame; - } - - if (mTextOverflow != aOther.mTextOverflow) { - return nsChangeHint_RepaintFrame; - } - return NS_STYLE_HINT_NONE; + if (mUnicodeBidi != aOther.mUnicodeBidi) { + return NS_STYLE_HINT_REFLOW; } - return NS_STYLE_HINT_REFLOW; + + uint8_t lineStyle = GetDecorationStyle(); + uint8_t otherLineStyle = aOther.GetDecorationStyle(); + if (mTextDecorationLine != aOther.mTextDecorationLine || + lineStyle != otherLineStyle) { + // Changes to our text-decoration line can impact our overflow area & + // also our descendants' overflow areas (particularly for text-frame + // descendants). So, we update those areas & trigger a repaint. + return nsChangeHint_RepaintFrame | + nsChangeHint_UpdateSubtreeOverflow | + nsChangeHint_SchedulePaint; + } + + // Repaint for decoration color changes + nscolor decColor, otherDecColor; + bool isFG, otherIsFG; + GetDecorationColor(decColor, isFG); + aOther.GetDecorationColor(otherDecColor, otherIsFG); + if (isFG != otherIsFG || (!isFG && decColor != otherDecColor)) { + return nsChangeHint_RepaintFrame; + } + + if (mTextOverflow != aOther.mTextOverflow) { + return nsChangeHint_RepaintFrame; + } + + return NS_STYLE_HINT_NONE; } // Returns true if the given shadow-arrays are equal. @@ -3594,7 +3543,7 @@ AreShadowArraysEqual(nsCSSShadowArray* lhs, // nsStyleText // -nsStyleText::nsStyleText(nsPresContext* aPresContext) +nsStyleText::nsStyleText(StyleStructContext aContext) { MOZ_COUNT_CTOR(nsStyleText); mTextAlign = NS_STYLE_TEXT_ALIGN_DEFAULT; @@ -3602,6 +3551,7 @@ nsStyleText::nsStyleText(nsPresContext* aPresContext) mTextAlignTrue = false; mTextAlignLastTrue = false; mTextEmphasisColorForeground = true; + mWebkitTextFillColorForeground = true; mTextTransform = NS_STYLE_TEXT_TRANSFORM_NONE; mWhiteSpace = NS_STYLE_WHITESPACE_NORMAL; mWordBreak = NS_STYLE_WORDBREAK_NORMAL; @@ -3612,12 +3562,14 @@ nsStyleText::nsStyleText(nsPresContext* aPresContext) mTextSizeAdjust = NS_STYLE_TEXT_SIZE_ADJUST_AUTO; mTextCombineUpright = NS_STYLE_TEXT_COMBINE_UPRIGHT_NONE; mTextEmphasisStyle = NS_STYLE_TEXT_EMPHASIS_STYLE_NONE; - nsCOMPtr language = aPresContext->GetContentLanguage(); + mTextRendering = NS_STYLE_TEXT_RENDERING_AUTO; + nsCOMPtr language = aContext.GetContentLanguage(); mTextEmphasisPosition = language && nsStyleUtil::MatchesLanguagePrefix(language, MOZ_UTF16("zh")) ? NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT_ZH : NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT; - mTextEmphasisColor = aPresContext->DefaultColor(); + mTextEmphasisColor = aContext.DefaultColor(); + mWebkitTextFillColor = aContext.DefaultColor(); mControlCharacterVisibility = nsCSSParser::ControlCharVisibilityDefault(); mWordSpacing.SetCoordValue(0); @@ -3635,6 +3587,7 @@ nsStyleText::nsStyleText(const nsStyleText& aSource) mTextAlignTrue(false), mTextAlignLastTrue(false), mTextEmphasisColorForeground(aSource.mTextEmphasisColorForeground), + mWebkitTextFillColorForeground(aSource.mWebkitTextFillColorForeground), mTextTransform(aSource.mTextTransform), mWhiteSpace(aSource.mWhiteSpace), mWordBreak(aSource.mWordBreak), @@ -3647,8 +3600,10 @@ nsStyleText::nsStyleText(const nsStyleText& aSource) mControlCharacterVisibility(aSource.mControlCharacterVisibility), mTextEmphasisPosition(aSource.mTextEmphasisPosition), mTextEmphasisStyle(aSource.mTextEmphasisStyle), + mTextRendering(aSource.mTextRendering), mTabSize(aSource.mTabSize), mTextEmphasisColor(aSource.mTextEmphasisColor), + mWebkitTextFillColor(aSource.mWebkitTextFillColor), mWordSpacing(aSource.mWordSpacing), mLetterSpacing(aSource.mLetterSpacing), mLineHeight(aSource.mLineHeight), @@ -3704,12 +3659,25 @@ nsChangeHint nsStyleText::CalcDifference(const nsStyleText& aOther) const nsChangeHint_RepaintFrame; } + nsChangeHint hint = nsChangeHint(0); + + // text-rendering changes require a reflow since they change SVG + // frames' rects. + if (mTextRendering != aOther.mTextRendering) { + hint |= nsChangeHint_NeedReflow | + nsChangeHint_NeedDirtyReflow | // XXX remove me: bug 876085 + nsChangeHint_RepaintFrame; + } + if (!AreShadowArraysEqual(mTextShadow, aOther.mTextShadow) || mTextEmphasisStyle != aOther.mTextEmphasisStyle || mTextEmphasisStyleString != aOther.mTextEmphasisStyleString) { - return nsChangeHint_UpdateSubtreeOverflow | - nsChangeHint_SchedulePaint | - nsChangeHint_RepaintFrame; + hint |= nsChangeHint_UpdateSubtreeOverflow | + nsChangeHint_SchedulePaint | + nsChangeHint_RepaintFrame; + + // We don't add any other hints below. + return hint; } MOZ_ASSERT(!mTextEmphasisColorForeground || @@ -3718,9 +3686,15 @@ nsChangeHint nsStyleText::CalcDifference(const nsStyleText& aOther) const "If the text-emphasis-color are both foreground color, " "mTextEmphasisColor should also be identical"); if (mTextEmphasisColorForeground != aOther.mTextEmphasisColorForeground || - mTextEmphasisColor != aOther.mTextEmphasisColor) { - return nsChangeHint_SchedulePaint | - nsChangeHint_RepaintFrame; + mTextEmphasisColor != aOther.mTextEmphasisColor || + mWebkitTextFillColorForeground != aOther.mWebkitTextFillColorForeground || + mWebkitTextFillColor != aOther.mWebkitTextFillColor) { + NS_UpdateHint(hint, nsChangeHint_SchedulePaint); + NS_UpdateHint(hint, nsChangeHint_RepaintFrame); + } + + if (hint) { + return hint; } if (mTextEmphasisPosition != aOther.mTextEmphasisPosition) { @@ -3785,12 +3759,13 @@ nsCursorImage::operator=(const nsCursorImage& aOther) return *this; } -nsStyleUserInterface::nsStyleUserInterface(void) +nsStyleUserInterface::nsStyleUserInterface(StyleStructContext aContext) { MOZ_COUNT_CTOR(nsStyleUserInterface); mUserInput = NS_STYLE_USER_INPUT_AUTO; mUserModify = NS_STYLE_USER_MODIFY_READ_ONLY; mUserFocus = NS_STYLE_USER_FOCUS_NONE; + mPointerEvents = NS_STYLE_POINTER_EVENTS_AUTO; mCursor = NS_STYLE_CURSOR_AUTO; // fix for bugzilla bug 51113 @@ -3802,6 +3777,7 @@ nsStyleUserInterface::nsStyleUserInterface(const nsStyleUserInterface& aSource) mUserInput(aSource.mUserInput), mUserModify(aSource.mUserModify), mUserFocus(aSource.mUserFocus), + mPointerEvents(aSource.mPointerEvents), mCursor(aSource.mCursor) { MOZ_COUNT_CTOR(nsStyleUserInterface); @@ -3825,6 +3801,14 @@ nsChangeHint nsStyleUserInterface::CalcDifference(const nsStyleUserInterface& aO if (mCursorArrayLength > 0 || aOther.mCursorArrayLength > 0) NS_UpdateHint(hint, nsChangeHint_UpdateCursor); + if (mPointerEvents != aOther.mPointerEvents) { + // nsSVGPathGeometryFrame's mRect depends on stroke _and_ on the value + // of pointer-events. See nsSVGPathGeometryFrame::ReflowSVG's use of + // GetHitTestFlags. (Only a reflow, no visual change.) + hint |= nsChangeHint_NeedReflow | + nsChangeHint_NeedDirtyReflow; // XXX remove me: bug 876085 + } + if (mUserModify != aOther.mUserModify) NS_UpdateHint(hint, NS_STYLE_HINT_VISUAL); @@ -3863,7 +3847,7 @@ nsStyleUserInterface::CopyCursorArrayFrom(const nsStyleUserInterface& aSource) // nsStyleUIReset // -nsStyleUIReset::nsStyleUIReset(void) +nsStyleUIReset::nsStyleUIReset(StyleStructContext aContext) { MOZ_COUNT_CTOR(nsStyleUIReset); mUserSelect = NS_STYLE_USER_SELECT_AUTO; @@ -3913,7 +3897,7 @@ nsChangeHint nsStyleUIReset::CalcDifference(const nsStyleUIReset& aOther) const // nsStyleVariables // -nsStyleVariables::nsStyleVariables() +nsStyleVariables::nsStyleVariables(StyleStructContext aContext) { MOZ_COUNT_CTOR(nsStyleVariables); } @@ -3934,3 +3918,98 @@ nsStyleVariables::CalcDifference(const nsStyleVariables& aOther) const { return nsChangeHint(0); } + +//----------------------- +// nsStyleEffects +// + +nsStyleEffects::nsStyleEffects(StyleStructContext aContext) + : mBoxShadow(nullptr) + , mClip(0, 0, 0, 0) + , mOpacity(1.0f) + , mClipFlags(NS_STYLE_CLIP_AUTO) + , mMixBlendMode(NS_STYLE_BLEND_NORMAL) +{ + MOZ_COUNT_CTOR(nsStyleEffects); +} + +nsStyleEffects::nsStyleEffects(const nsStyleEffects& aSource) + : mFilters(aSource.mFilters) + , mBoxShadow(aSource.mBoxShadow) + , mClip(aSource.mClip) + , mOpacity(aSource.mOpacity) + , mClipFlags(aSource.mClipFlags) + , mMixBlendMode(aSource.mMixBlendMode) +{ + MOZ_COUNT_CTOR(nsStyleEffects); +} + +nsStyleEffects::~nsStyleEffects() +{ + MOZ_COUNT_DTOR(nsStyleEffects); +} + +nsChangeHint +nsStyleEffects::CalcDifference(const nsStyleEffects& aOther) const +{ + nsChangeHint hint = nsChangeHint(0); + + if (!AreShadowArraysEqual(mBoxShadow, aOther.mBoxShadow)) { + // Update overflow regions & trigger DLBI to be sure it's noticed. + // Also request a repaint, since it's possible that only the color + // of the shadow is changing (and UpdateOverflow/SchedulePaint won't + // repaint for that, since they won't know what needs invalidating.) + hint |= nsChangeHint_UpdateOverflow | + nsChangeHint_SchedulePaint | + nsChangeHint_RepaintFrame; + } + + if (mClipFlags != aOther.mClipFlags) { + hint |= nsChangeHint_AllReflowHints | + nsChangeHint_RepaintFrame; + } + + if (!mClip.IsEqualInterior(aOther.mClip)) { + // If the clip has changed, we just need to update overflow areas. DLBI + // will handle the invalidation. + hint |= nsChangeHint_UpdateOverflow | + nsChangeHint_SchedulePaint; + } + + if (mOpacity != aOther.mOpacity) { + // If we're going from the optimized >=0.99 opacity value to 1.0 or back, then + // repaint the frame because DLBI will not catch the invalidation. Otherwise, + // just update the opacity layer. + if ((mOpacity >= 0.99f && mOpacity < 1.0f && aOther.mOpacity == 1.0f) || + (aOther.mOpacity >= 0.99f && aOther.mOpacity < 1.0f && mOpacity == 1.0f)) { + hint |= nsChangeHint_RepaintFrame; + } else { + hint |= nsChangeHint_UpdateOpacityLayer; + if ((mOpacity == 1.0f) != (aOther.mOpacity == 1.0f)) { + hint |= nsChangeHint_UpdateUsesOpacity; + } + } + } + + if (HasFilters() != aOther.HasFilters()) { + // A change from/to being a containing block for position:fixed. + hint |= nsChangeHint_UpdateContainingBlock; + } + + if (mFilters != aOther.mFilters) { + hint |= nsChangeHint_UpdateEffects | + nsChangeHint_RepaintFrame | + nsChangeHint_UpdateOverflow; + } + + if (mMixBlendMode != aOther.mMixBlendMode) { + hint |= nsChangeHint_RepaintFrame; + } + + if (!hint && + !mClip.IsEqualEdges(aOther.mClip)) { + hint |= nsChangeHint_NeutralChange; + } + + return hint; +} diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index 1327f173da..2df14dbc65 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -16,6 +16,8 @@ #include "mozilla/Attributes.h" #include "mozilla/CSSVariableValues.h" #include "mozilla/SheetType.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/StyleStructContext.h" #include "nsColor.h" #include "nsCoord.h" #include "nsMargin.h" @@ -31,6 +33,8 @@ #include "imgRequestProxy.h" #include "Orientation.h" #include "CounterStyleManager.h" +#include // offsetof() +#include class nsIFrame; class nsIURI; @@ -78,12 +82,14 @@ struct nsStyleVisibility; #define NS_STYLE_INELIGIBLE_FOR_SHARING 0x200000000 // See nsStyleContext::HasChildThatUsesResetStyle #define NS_STYLE_HAS_CHILD_THAT_USES_RESET_STYLE 0x400000000 +// See nsStyleContext::IsTextCombined +#define NS_STYLE_IS_TEXT_COMBINED 0x800000000 // See nsStyleContext::GetPseudoEnum -#define NS_STYLE_CONTEXT_TYPE_SHIFT 35 +#define NS_STYLE_CONTEXT_TYPE_SHIFT 36 // Additional bits for nsRuleNode's mDependentBits: #define NS_RULE_NODE_IS_ANIMATION_RULE 0x01000000 -#define NS_RULE_NODE_GC_MARK 0x02000000 +// Free bit 0x02000000 #define NS_RULE_NODE_USED_DIRECTLY 0x04000000 #define NS_RULE_NODE_IS_IMPORTANT 0x08000000 #define NS_RULE_NODE_LEVEL_MASK 0xf0000000 @@ -96,15 +102,61 @@ static_assert(int(mozilla::SheetType::Count) - 1 <= (NS_RULE_NODE_LEVEL_MASK >> NS_RULE_NODE_LEVEL_SHIFT), "NS_RULE_NODE_LEVEL_MASK cannot fit SheetType"); -static_assert(NS_RULE_NODE_IS_ANIMATION_RULE == (1 << nsStyleStructID_Length), - "NS_RULE_NODE_IS_ANIMATION_RULE must not overlap the style struct bits."); -// The lifetime of these objects is managed by the presshell's arena. +static_assert(NS_STYLE_INHERIT_MASK == (1 << nsStyleStructID_Length) - 1, + "NS_STYLE_INHERIT_MASK is not correct"); +static_assert((NS_RULE_NODE_IS_ANIMATION_RULE & NS_STYLE_INHERIT_MASK) == 0, + "NS_RULE_NODE_IS_ANIMATION_RULE must not overlap the style struct bits."); + +/** + * These *_Simple types are used to map Gecko types to layout-equivalent but + * simpler Rust types, to aid Rust binding generation. + * + * If something in this types or the assertions below needs to change, ask + * bholley, heycam or emilio before! + * + *
+ */ +struct nsPoint_Simple { + nscoord x, y; +}; + +static_assert(sizeof(nsPoint_Simple) == sizeof(nsPoint), "Wrong nsPoint_Simple size"); +static_assert(offsetof(nsPoint_Simple, x) == offsetof(nsPoint, x), "Wrong nsPoint_Simple member alignment"); +static_assert(offsetof(nsPoint_Simple, y) == offsetof(nsPoint, y), "Wrong nsPoint_Simple member alignment"); + +/** + *
+ */ +struct nsMargin_Simple { + nscoord top, right, bottom, left; +}; + +static_assert(sizeof(nsMargin_Simple) == sizeof(nsMargin), "Wrong nsMargin_Simple size"); +static_assert(offsetof(nsMargin_Simple, top) == offsetof(nsMargin, top), "Wrong nsMargin_Simple member alignment"); +static_assert(offsetof(nsMargin_Simple, right) == offsetof(nsMargin, right), "Wrong nsMargin_Simple member alignment"); +static_assert(offsetof(nsMargin_Simple, bottom) == offsetof(nsMargin, bottom), "Wrong nsMargin_Simple member alignment"); +static_assert(offsetof(nsMargin_Simple, left) == offsetof(nsMargin, left), "Wrong nsMargin_Simple member alignment"); + +/** + *
+ */ +struct nsRect_Simple { + nscoord x, y, width, height; +}; + +static_assert(sizeof(nsRect_Simple) == sizeof(nsRect), "Wrong nsRect_Simple size"); +static_assert(offsetof(nsRect_Simple, x) == offsetof(nsRect, x), "Wrong nsRect_Simple member alignment"); +static_assert(offsetof(nsRect_Simple, y) == offsetof(nsRect, y), "Wrong nsRect_Simple member alignment"); +static_assert(offsetof(nsRect_Simple, width) == offsetof(nsRect, width), "Wrong nsRect_Simple member alignment"); +static_assert(offsetof(nsRect_Simple, height) == offsetof(nsRect, height), "Wrong nsRect_Simple member alignment"); + +// The lifetime of these objects is managed by the presshell's arena. struct nsStyleFont { - nsStyleFont(const nsFont& aFont, nsPresContext *aPresContext); + nsStyleFont(const nsFont& aFont, StyleStructContext aContext); nsStyleFont(const nsStyleFont& aStyleFont); - explicit nsStyleFont(nsPresContext *aPresContext); + explicit nsStyleFont(StyleStructContext aContext); ~nsStyleFont(void) { MOZ_COUNT_DTOR(nsStyleFont); } @@ -127,15 +179,16 @@ struct nsStyleFont * aSize is allowed to be negative, but the caller is expected to deal with * negative results. The result is clamped to nscoord_MIN .. nscoord_MAX. */ - static nscoord ZoomText(nsPresContext* aPresContext, nscoord aSize); + static nscoord ZoomText(StyleStructContext aContext, nscoord aSize); /** * Return aSize divided by the current text zoom factor (in aPresContext). * aSize is allowed to be negative, but the caller is expected to deal with * negative results. The result is clamped to nscoord_MIN .. nscoord_MAX. */ static nscoord UnZoomText(nsPresContext* aPresContext, nscoord aSize); - static already_AddRefed GetLanguage(nsPresContext* aPresContext); + static already_AddRefed GetLanguage(StyleStructContext aPresContext); + void* operator new(size_t sz, nsStyleFont* aSelf) CPP_THROW_NEW { return aSelf; } void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> AllocateByObjectID(mozilla::eArenaObjectID_nsStyleFont, sz); @@ -370,7 +423,7 @@ private: struct nsStyleColor { - explicit nsStyleColor(nsPresContext* aPresContext); + explicit nsStyleColor(StyleStructContext aContext); nsStyleColor(const nsStyleColor& aOther); ~nsStyleColor(void) { MOZ_COUNT_DTOR(nsStyleColor); @@ -386,6 +439,7 @@ struct nsStyleColor return nsChangeHint(0); } + void* operator new(size_t sz, nsStyleColor* aSelf) CPP_THROW_NEW { return aSelf; } void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> AllocateByObjectID(mozilla::eArenaObjectID_nsStyleColor, sz); @@ -671,10 +725,11 @@ struct nsStyleImageLayers { }; struct nsStyleBackground { - nsStyleBackground(); + explicit nsStyleBackground(StyleStructContext aContext); nsStyleBackground(const nsStyleBackground& aOther); ~nsStyleBackground(); + void* operator new(size_t sz, nsStyleBackground* aSelf) CPP_THROW_NEW { return aSelf; } void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> AllocateByObjectID(mozilla::eArenaObjectID_nsStyleBackground, sz); @@ -724,12 +779,13 @@ struct nsStyleBackground { struct nsStyleMargin { - nsStyleMargin(void); + explicit nsStyleMargin(StyleStructContext aContext); nsStyleMargin(const nsStyleMargin& aMargin); ~nsStyleMargin(void) { MOZ_COUNT_DTOR(nsStyleMargin); } + void* operator new(size_t sz, nsStyleMargin* aSelf) CPP_THROW_NEW { return aSelf; } void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> AllocateByObjectID(mozilla::eArenaObjectID_nsStyleMargin, sz); @@ -769,12 +825,13 @@ protected: struct nsStylePadding { - nsStylePadding(void); + explicit nsStylePadding(StyleStructContext aContext); nsStylePadding(const nsStylePadding& aPadding); ~nsStylePadding(void) { MOZ_COUNT_DTOR(nsStylePadding); } + void* operator new(size_t sz, nsStylePadding* aSelf) CPP_THROW_NEW { return aSelf; } void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> AllocateByObjectID(mozilla::eArenaObjectID_nsStylePadding, sz); @@ -971,10 +1028,11 @@ static bool IsVisibleBorderStyle(uint8_t aStyle) struct nsStyleBorder { - explicit nsStyleBorder(nsPresContext* aContext); + explicit nsStyleBorder(StyleStructContext aContext); nsStyleBorder(const nsStyleBorder& aBorder); ~nsStyleBorder(); + void* operator new(size_t sz, nsStyleBorder* aSelf) CPP_THROW_NEW { return aSelf; } void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> AllocateByObjectID(mozilla::eArenaObjectID_nsStyleBorder, sz); @@ -1149,7 +1207,6 @@ struct nsStyleBorder public: nsBorderColors** mBorderColors; // [reset] composite (stripe) colors - RefPtr mBoxShadow; // [reset] nullptr for 'none' public: nsStyleCorners mBorderRadius; // [reset] coord, percent @@ -1198,12 +1255,13 @@ private: struct nsStyleOutline { - explicit nsStyleOutline(nsPresContext* aPresContext); + explicit nsStyleOutline(StyleStructContext aContext); nsStyleOutline(const nsStyleOutline& aOutline); ~nsStyleOutline(void) { MOZ_COUNT_DTOR(nsStyleOutline); } + void* operator new(size_t sz, nsStyleOutline* aSelf) CPP_THROW_NEW { return aSelf; } void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> AllocateByObjectID(mozilla::eArenaObjectID_nsStyleOutline, sz); @@ -1235,13 +1293,9 @@ struct nsStyleOutline nsStyleCoord mOutlineWidth; // [reset] coord, enum (see nsStyleConsts.h) nscoord mOutlineOffset; // [reset] - bool GetOutlineWidth(nscoord& aWidth) const + nscoord GetOutlineWidth() const { - if (mHasCachedOutline) { - aWidth = mCachedOutlineWidth; - return true; - } - return false; + return mCachedOutlineWidth; } uint8_t GetOutlineStyle(void) const @@ -1288,19 +1342,35 @@ protected: nscolor mOutlineColor; // [reset] - bool mHasCachedOutline; uint8_t mOutlineStyle; // [reset] See nsStyleConsts.h nscoord mTwipsPerPixel; }; +/** + * An object that allows sharing of arrays that store 'quotes' property + * values. This is particularly important for inheritance, where we want + * to share the same 'quotes' value with a parent style context. + */ +class nsStyleQuoteValues +{ +public: + typedef nsTArray> QuotePairArray; + NS_INLINE_DECL_REFCOUNTING(nsStyleQuoteValues); + QuotePairArray mQuotePairs; + +private: + ~nsStyleQuoteValues() {} +}; + struct nsStyleList { - explicit nsStyleList(nsPresContext* aPresContext); + explicit nsStyleList(StyleStructContext aContext); nsStyleList(const nsStyleList& aStyleList); ~nsStyleList(void); + void* operator new(size_t sz, nsStyleList* aSelf) CPP_THROW_NEW { return aSelf; } void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> AllocateByObjectID(mozilla::eArenaObjectID_nsStyleList, sz); @@ -1324,6 +1394,11 @@ struct nsStyleList nsChangeHint_ClearAncestorIntrinsics; } + static void Shutdown() { + sInitialQuotes = nullptr; + sNoneQuotes = nullptr; + } + imgRequestProxy* GetListStyleImage() const { return mListStyleImage; } void SetListStyleImage(imgRequestProxy* aReq) { @@ -1352,14 +1427,27 @@ struct nsStyleList CounterStyleManager()->BuildCounterStyle(aType)); } + const nsStyleQuoteValues::QuotePairArray& GetQuotePairs() const; + + void SetQuotesInherit(const nsStyleList* aOther); + void SetQuotesInitial(); + void SetQuotesNone(); + void SetQuotes(nsStyleQuoteValues::QuotePairArray&& aValues); + uint8_t mListStylePosition; // [inherited] private: nsString mListStyleType; // [inherited] RefPtr mCounterStyle; // [inherited] RefPtr mListStyleImage; // [inherited] + RefPtr mQuotes; // [inherited] nsStyleList& operator=(const nsStyleList& aOther) = delete; public: nsRect mImageRegion; // [inherited] the rect to use within an image + +private: + // nsStyleQuoteValues objects representing two common values, for sharing. + static mozilla::StaticRefPtr sInitialQuotes; + static mozilla::StaticRefPtr sNoneQuotes; }; struct nsStyleGridLine @@ -1484,10 +1572,11 @@ struct nsStyleTextOverflow struct nsStyleTextReset { - nsStyleTextReset(void); + explicit nsStyleTextReset(StyleStructContext aContext); nsStyleTextReset(const nsStyleTextReset& aOther); ~nsStyleTextReset(void); + void* operator new(size_t sz, nsStyleTextReset* aSelf) CPP_THROW_NEW { return aSelf; } void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> AllocateByObjectID(mozilla::eArenaObjectID_nsStyleTextReset, sz); @@ -1556,7 +1645,6 @@ struct nsStyleTextReset nsChangeHint_ClearAncestorIntrinsics; } - nsStyleCoord mVerticalAlign; // [reset] coord, percent, calc, enum (see nsStyleConsts.h) nsStyleTextOverflow mTextOverflow; // [reset] enum, string uint8_t mTextDecorationLine; // [reset] see nsStyleConsts.h @@ -1569,10 +1657,11 @@ protected: struct nsStyleText { - explicit nsStyleText(nsPresContext* aPresContext); + explicit nsStyleText(StyleStructContext aContext); nsStyleText(const nsStyleText& aOther); ~nsStyleText(void); + void* operator new(size_t sz, nsStyleText* aSelf) CPP_THROW_NEW { return aSelf; } void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> AllocateByObjectID(mozilla::eArenaObjectID_nsStyleText, sz); @@ -1602,6 +1691,7 @@ struct nsStyleText bool mTextAlignTrue : 1; // [inherited] see nsStyleConsts.h bool mTextAlignLastTrue : 1; // [inherited] see nsStyleConsts.h bool mTextEmphasisColorForeground : 1;// [inherited] whether text-emphasis-color is currentColor + bool mWebkitTextFillColorForeground : 1; // [inherited] whether -webkit-text-fill-color is currentColor uint8_t mTextTransform; // [inherited] see nsStyleConsts.h uint8_t mWhiteSpace; // [inherited] see nsStyleConsts.h uint8_t mWordBreak; // [inherited] see nsStyleConsts.h @@ -1614,8 +1704,10 @@ struct nsStyleText uint8_t mControlCharacterVisibility; // [inherited] see nsStyleConsts.h uint8_t mTextEmphasisPosition; // [inherited] see nsStyleConsts.h uint8_t mTextEmphasisStyle; // [inherited] see nsStyleConsts.h + uint8_t mTextRendering; // [inherited] see nsStyleConsts.h int32_t mTabSize; // [inherited] see nsStyleConsts.h nscolor mTextEmphasisColor; // [inherited] + nscolor mWebkitTextFillColor; // [inherited] nsStyleCoord mWordSpacing; // [inherited] coord, percent, calc nsStyleCoord mLetterSpacing; // [inherited] coord, normal @@ -1775,12 +1867,13 @@ protected: struct nsStyleVisibility { - explicit nsStyleVisibility(nsPresContext* aPresContext); + explicit nsStyleVisibility(StyleStructContext aContext); nsStyleVisibility(const nsStyleVisibility& aVisibility); ~nsStyleVisibility() { MOZ_COUNT_DTOR(nsStyleVisibility); } + void* operator new(size_t sz, nsStyleVisibility* aSelf) CPP_THROW_NEW { return aSelf; } void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> AllocateByObjectID(mozilla::eArenaObjectID_nsStyleVisibility, sz); @@ -1793,7 +1886,8 @@ struct nsStyleVisibility nsChangeHint CalcDifference(const nsStyleVisibility& aOther) const; static nsChangeHint MaxDifference() { - return NS_STYLE_HINT_FRAMECHANGE; + return NS_STYLE_HINT_FRAMECHANGE | + nsChangeHint_NeutralChange; } static nsChangeHint DifferenceAlwaysHandledForDescendants() { // CalcDifference never returns the reflow hints that are sometimes @@ -1806,9 +1900,10 @@ struct nsStyleVisibility nsStyleImageOrientation mImageOrientation; // [inherited] uint8_t mDirection; // [inherited] see nsStyleConsts.h NS_STYLE_DIRECTION_* uint8_t mVisible; // [inherited] - uint8_t mPointerEvents; // [inherited] see nsStyleConsts.h + uint8_t mImageRendering; // [inherited] see nsStyleConsts.h uint8_t mWritingMode; // [inherited] see nsStyleConsts.h uint8_t mTextOrientation; // [inherited] see nsStyleConsts.h + uint8_t mColorAdjust; // [inherited] see nsStyleConsts.h bool IsVisible() const { return (mVisible == NS_STYLE_VISIBILITY_VISIBLE); @@ -1818,8 +1913,6 @@ struct nsStyleVisibility return ((mVisible == NS_STYLE_VISIBILITY_VISIBLE) || (mVisible == NS_STYLE_VISIBILITY_COLLAPSE)); } - - inline uint8_t GetEffectivePointerEvents(nsIFrame* aFrame) const; }; struct nsTimingFunction @@ -2055,12 +2148,13 @@ private: struct nsStyleDisplay { - nsStyleDisplay(); + explicit nsStyleDisplay(StyleStructContext aContext); nsStyleDisplay(const nsStyleDisplay& aOther); ~nsStyleDisplay() { MOZ_COUNT_DTOR(nsStyleDisplay); } + void* operator new(size_t sz, nsStyleDisplay* aSelf) CPP_THROW_NEW { return aSelf; } void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> AllocateByObjectID(mozilla::eArenaObjectID_nsStyleDisplay, sz); @@ -2075,8 +2169,6 @@ struct nsStyleDisplay static nsChangeHint MaxDifference() { // 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 | @@ -2098,8 +2190,6 @@ struct nsStyleDisplay // We guarantee that if mBinding is non-null, so are mBinding->GetURI() and // mBinding->mOriginPrincipal. RefPtr mBinding; // [reset] - nsRect mClip; // [reset] offsets from upper-left border edge - float mOpacity; // [reset] uint8_t mDisplay; // [reset] see nsStyleConsts.h NS_STYLE_DISPLAY_* uint8_t mOriginalDisplay; // [reset] saved mDisplay for position:absolute/fixed // and float:left/right; otherwise equal @@ -2118,9 +2208,7 @@ struct nsStyleDisplay uint8_t mOverflowY; // [reset] see nsStyleConsts.h uint8_t mOverflowClipBox; // [reset] see nsStyleConsts.h uint8_t mResize; // [reset] see nsStyleConsts.h - uint8_t mClipFlags; // [reset] see nsStyleConsts.h uint8_t mOrient; // [reset] see nsStyleConsts.h - uint8_t mMixBlendMode; // [reset] see nsStyleConsts.h uint8_t mIsolation; // [reset] see nsStyleConsts.h uint8_t mTopLayer; // [reset] see nsStyleConsts.h uint8_t mWillChangeBitField; // [reset] see nsStyleConsts.h. Stores a @@ -2152,6 +2240,8 @@ struct nsStyleDisplay nsStyleCoord mChildPerspective; // [reset] none, coord nsStyleCoord mPerspectiveOrigin[2]; // [reset] percent, coord, calc + nsStyleCoord mVerticalAlign; // [reset] coord, percent, calc, enum (see nsStyleConsts.h) + AutoTArray mTransitions; // [reset] // The number of elements in mTransitions that are not from repeating // a list due to another property being longer. @@ -2185,6 +2275,7 @@ struct nsStyleDisplay bool IsBlockOutsideStyle() const { return NS_STYLE_DISPLAY_BLOCK == mDisplay || NS_STYLE_DISPLAY_FLEX == mDisplay || + NS_STYLE_DISPLAY_WEBKIT_BOX == mDisplay || NS_STYLE_DISPLAY_GRID == mDisplay || NS_STYLE_DISPLAY_LIST_ITEM == mDisplay || NS_STYLE_DISPLAY_TABLE == mDisplay; @@ -2196,6 +2287,7 @@ struct nsStyleDisplay NS_STYLE_DISPLAY_INLINE_TABLE == aDisplay || NS_STYLE_DISPLAY_INLINE_BOX == aDisplay || NS_STYLE_DISPLAY_INLINE_FLEX == aDisplay || + NS_STYLE_DISPLAY_WEBKIT_INLINE_BOX == aDisplay || NS_STYLE_DISPLAY_INLINE_GRID == aDisplay || NS_STYLE_DISPLAY_INLINE_XUL_GRID == aDisplay || NS_STYLE_DISPLAY_INLINE_STACK == aDisplay || @@ -2256,13 +2348,6 @@ struct nsStyleDisplay return IsRubyDisplayType(mDisplay); } - bool IsFlexOrGridDisplayType() const { - return NS_STYLE_DISPLAY_FLEX == mDisplay || - NS_STYLE_DISPLAY_INLINE_FLEX == mDisplay || - NS_STYLE_DISPLAY_GRID == mDisplay || - NS_STYLE_DISPLAY_INLINE_GRID == mDisplay; - } - bool IsOutOfFlowStyle() const { return (IsAbsolutelyPositionedStyle() || IsFloatingStyle()); } @@ -2335,10 +2420,11 @@ struct nsStyleDisplay struct nsStyleTable { - nsStyleTable(void); + explicit nsStyleTable(StyleStructContext aContext); nsStyleTable(const nsStyleTable& aOther); ~nsStyleTable(void); + void* operator new(size_t sz, nsStyleTable* aSelf) CPP_THROW_NEW { return aSelf; } void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> AllocateByObjectID(mozilla::eArenaObjectID_nsStyleTable, sz); @@ -2445,10 +2531,11 @@ struct nsStyleGridTemplate struct nsStylePosition { - nsStylePosition(void); + explicit nsStylePosition(StyleStructContext aContext); nsStylePosition(const nsStylePosition& aOther); ~nsStylePosition(void); + void* operator new(size_t sz, nsStylePosition* aSelf) CPP_THROW_NEW { return aSelf; } void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> AllocateByObjectID(mozilla::eArenaObjectID_nsStylePosition, sz); @@ -2638,10 +2725,11 @@ private: struct nsStyleTableBorder { - nsStyleTableBorder(); + explicit nsStyleTableBorder(StyleStructContext aContext); nsStyleTableBorder(const nsStyleTableBorder& aOther); ~nsStyleTableBorder(void); + void* operator new(size_t sz, nsStyleTableBorder* aSelf) CPP_THROW_NEW { return aSelf; } void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> AllocateByObjectID(mozilla::eArenaObjectID_nsStyleTableBorder, sz); @@ -2735,95 +2823,13 @@ struct nsStyleCounterData #define DELETE_ARRAY_IF(array) if (array) { delete[] array; array = nullptr; } -struct nsStyleQuotes -{ - nsStyleQuotes(); - nsStyleQuotes(const nsStyleQuotes& aQuotes); - ~nsStyleQuotes(); - - void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { - return aContext->PresShell()-> - AllocateByObjectID(mozilla::eArenaObjectID_nsStyleQuotes, sz); - } - void Destroy(nsPresContext* aContext) { - this->~nsStyleQuotes(); - aContext->PresShell()-> - FreeByObjectID(mozilla::eArenaObjectID_nsStyleQuotes, this); - } - - void SetInitial(); - void CopyFrom(const nsStyleQuotes& aSource); - - nsChangeHint CalcDifference(const nsStyleQuotes& aOther) const; - static nsChangeHint MaxDifference() { - return NS_STYLE_HINT_FRAMECHANGE; - } - static nsChangeHint DifferenceAlwaysHandledForDescendants() { - // CalcDifference never returns the reflow hints that are sometimes - // handled for descendants as hints not handled for descendants. - return nsChangeHint_NeedReflow | - nsChangeHint_ReflowChangesSizeOrPosition | - nsChangeHint_ClearAncestorIntrinsics; - } - - uint32_t QuotesCount(void) const { return mQuotesCount; } // [inherited] - - const nsString* OpenQuoteAt(uint32_t aIndex) const - { - NS_ASSERTION(aIndex < mQuotesCount, "out of range"); - return mQuotes + (aIndex * 2); - } - const nsString* CloseQuoteAt(uint32_t aIndex) const - { - NS_ASSERTION(aIndex < mQuotesCount, "out of range"); - return mQuotes + (aIndex * 2 + 1); - } - nsresult GetQuotesAt(uint32_t aIndex, nsString& aOpen, nsString& aClose) const { - if (aIndex < mQuotesCount) { - aIndex *= 2; - aOpen = mQuotes[aIndex]; - aClose = mQuotes[++aIndex]; - return NS_OK; - } - return NS_ERROR_ILLEGAL_VALUE; - } - - nsresult AllocateQuotes(uint32_t aCount) { - if (aCount != mQuotesCount) { - DELETE_ARRAY_IF(mQuotes); - if (aCount) { - mQuotes = new nsString[aCount * 2]; - if (! mQuotes) { - mQuotesCount = 0; - return NS_ERROR_OUT_OF_MEMORY; - } - } - mQuotesCount = aCount; - } - return NS_OK; - } - - nsresult SetQuotesAt(uint32_t aIndex, const nsString& aOpen, const nsString& aClose) { - if (aIndex < mQuotesCount) { - aIndex *= 2; - mQuotes[aIndex] = aOpen; - mQuotes[++aIndex] = aClose; - return NS_OK; - } - return NS_ERROR_ILLEGAL_VALUE; - } - -protected: - uint32_t mQuotesCount; - nsString* mQuotes; -}; - struct nsStyleContent { - nsStyleContent(void); + explicit nsStyleContent(StyleStructContext aContext); nsStyleContent(const nsStyleContent& aContent); ~nsStyleContent(void); + void* operator new(size_t sz, nsStyleContent* aSelf) CPP_THROW_NEW { return aSelf; } void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> AllocateByObjectID(mozilla::eArenaObjectID_nsStyleContent, sz); @@ -2930,10 +2936,11 @@ protected: struct nsStyleUIReset { - nsStyleUIReset(void); + explicit nsStyleUIReset(StyleStructContext aContext); nsStyleUIReset(const nsStyleUIReset& aOther); ~nsStyleUIReset(void); + void* operator new(size_t sz, nsStyleUIReset* aSelf) CPP_THROW_NEW { return aSelf; } void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> AllocateByObjectID(mozilla::eArenaObjectID_nsStyleUIReset, sz); @@ -2995,10 +3002,11 @@ private: struct nsStyleUserInterface { - nsStyleUserInterface(void); + explicit nsStyleUserInterface(StyleStructContext aContext); nsStyleUserInterface(const nsStyleUserInterface& aOther); ~nsStyleUserInterface(void); + void* operator new(size_t sz, nsStyleUserInterface* aSelf) CPP_THROW_NEW { return aSelf; } void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> AllocateByObjectID(mozilla::eArenaObjectID_nsStyleUserInterface, sz); @@ -3026,6 +3034,7 @@ struct nsStyleUserInterface uint8_t mUserInput; // [inherited] uint8_t mUserModify; // [inherited] (modify-content) uint8_t mUserFocus; // [inherited] (auto-select) + uint8_t mPointerEvents; // [inherited] see nsStyleConsts.h uint8_t mCursor; // [inherited] See nsStyleConsts.h @@ -3038,14 +3047,17 @@ struct nsStyleUserInterface // Does not free mCursorArray; the caller is responsible for calling // |delete [] mCursorArray| first if it is needed. void CopyCursorArrayFrom(const nsStyleUserInterface& aSource); + + inline uint8_t GetEffectivePointerEvents(nsIFrame* aFrame) const; }; struct nsStyleXUL { - nsStyleXUL(); + explicit nsStyleXUL(StyleStructContext aContext); nsStyleXUL(const nsStyleXUL& aSource); ~nsStyleXUL(); + void* operator new(size_t sz, nsStyleXUL* aSelf) CPP_THROW_NEW { return aSelf; } void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> AllocateByObjectID(mozilla::eArenaObjectID_nsStyleXUL, sz); @@ -3079,10 +3091,11 @@ struct nsStyleXUL struct nsStyleColumn { - explicit nsStyleColumn(nsPresContext* aPresContext); + explicit nsStyleColumn(StyleStructContext aContext); nsStyleColumn(const nsStyleColumn& aSource); ~nsStyleColumn(); + void* operator new(size_t sz, nsStyleColumn* aSelf) CPP_THROW_NEW { return aSelf; } void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> AllocateByObjectID(mozilla::eArenaObjectID_nsStyleColumn, sz); @@ -3173,10 +3186,11 @@ struct nsStyleSVGPaint struct nsStyleSVG { - nsStyleSVG(); + explicit nsStyleSVG(StyleStructContext aContext); nsStyleSVG(const nsStyleSVG& aSource); ~nsStyleSVG(); + void* operator new(size_t sz, nsStyleSVG* aSelf) CPP_THROW_NEW { return aSelf; } void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> AllocateByObjectID(mozilla::eArenaObjectID_nsStyleSVG, sz); @@ -3219,13 +3233,11 @@ struct nsStyleSVG uint8_t mColorInterpolation; // [inherited] see nsStyleConsts.h uint8_t mColorInterpolationFilters; // [inherited] see nsStyleConsts.h uint8_t mFillRule; // [inherited] see nsStyleConsts.h - uint8_t mImageRendering; // [inherited] see nsStyleConsts.h uint8_t mPaintOrder; // [inherited] see nsStyleConsts.h uint8_t mShapeRendering; // [inherited] see nsStyleConsts.h uint8_t mStrokeLinecap; // [inherited] see nsStyleConsts.h uint8_t mStrokeLinejoin; // [inherited] see nsStyleConsts.h uint8_t mTextAnchor; // [inherited] see nsStyleConsts.h - uint8_t mTextRendering; // [inherited] see nsStyleConsts.h // In SVG glyphs, whether we inherit fill or stroke opacity from the outer // text object. @@ -3459,10 +3471,11 @@ struct nsTArray_CopyChooser struct nsStyleSVGReset { - nsStyleSVGReset(); + explicit nsStyleSVGReset(StyleStructContext aContext); nsStyleSVGReset(const nsStyleSVGReset& aSource); ~nsStyleSVGReset(); + void* operator new(size_t sz, nsStyleSVGReset* aSelf) CPP_THROW_NEW { return aSelf; } void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> AllocateByObjectID(mozilla::eArenaObjectID_nsStyleSVGReset, sz); @@ -3484,10 +3497,6 @@ struct nsStyleSVGReset nsChangeHint_ClearAncestorIntrinsics; } - bool HasFilters() const { - return !mFilters.IsEmpty(); - } - bool HasClipPath() const { return mClipPath.GetType() != NS_STYLE_CLIP_PATH_NONE; } @@ -3498,7 +3507,6 @@ struct nsStyleSVGReset nsStyleImageLayers mMask; nsStyleClipPath mClipPath; // [reset] - nsTArray mFilters; // [reset] nscolor mStopColor; // [reset] nscolor mFloodColor; // [reset] nscolor mLightingColor; // [reset] @@ -3513,10 +3521,11 @@ struct nsStyleSVGReset struct nsStyleVariables { - nsStyleVariables(); + explicit nsStyleVariables(StyleStructContext aContext); nsStyleVariables(const nsStyleVariables& aSource); ~nsStyleVariables(); + void* operator new(size_t sz, nsStyleVariables* aSelf) CPP_THROW_NEW { return aSelf; } void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> AllocateByObjectID(mozilla::eArenaObjectID_nsStyleVariables, sz); @@ -3540,4 +3549,53 @@ struct nsStyleVariables mozilla::CSSVariableValues mVariables; }; +struct nsStyleEffects +{ + explicit nsStyleEffects(StyleStructContext aContext); + nsStyleEffects(const nsStyleEffects& aSource); + ~nsStyleEffects(); + + void* operator new(size_t sz, nsStyleEffects* aSelf) CPP_THROW_NEW { return aSelf; } + void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { + return aContext->PresShell()-> + AllocateByObjectID(mozilla::eArenaObjectID_nsStyleEffects, sz); + } + void Destroy(nsPresContext* aContext) { + this->~nsStyleEffects(); + aContext->PresShell()-> + FreeByObjectID(mozilla::eArenaObjectID_nsStyleEffects, this); + } + + nsChangeHint CalcDifference(const nsStyleEffects& aOther) const; + static nsChangeHint MaxDifference() { + return nsChangeHint_AllReflowHints | + nsChangeHint_UpdateOverflow | + nsChangeHint_SchedulePaint | + nsChangeHint_RepaintFrame | + nsChangeHint_UpdateOpacityLayer | + nsChangeHint_UpdateUsesOpacity | + nsChangeHint_UpdateContainingBlock | + nsChangeHint_UpdateEffects | + nsChangeHint_NeutralChange; + } + static nsChangeHint DifferenceAlwaysHandledForDescendants() { + // CalcDifference never returns the reflow hints that are sometimes + // handled for descendants as hints not handled for descendants. + return nsChangeHint_NeedReflow | + nsChangeHint_ReflowChangesSizeOrPosition | + nsChangeHint_ClearAncestorIntrinsics; + } + + bool HasFilters() const { + return !mFilters.IsEmpty(); + } + + nsTArray mFilters; // [reset] + RefPtr mBoxShadow; // [reset] nullptr for 'none' + nsRect mClip; // [reset] offsets from UL border edge + float mOpacity; // [reset] + uint8_t mClipFlags; // [reset] see nsStyleConsts.h + uint8_t mMixBlendMode; // [reset] see nsStyleConsts.h +}; + #endif /* nsStyleStruct_h___ */ diff --git a/layout/style/nsStyleStructInlines.h b/layout/style/nsStyleStructInlines.h index 6e845646ad..577e500be6 100644 --- a/layout/style/nsStyleStructInlines.h +++ b/layout/style/nsStyleStructInlines.h @@ -145,7 +145,7 @@ nsStyleDisplay::IsFixedPosContainingBlock(const nsIFrame* aContextFrame) const return (IsContainPaint() || HasTransform(aContextFrame) || HasPerspectiveStyle() || (mWillChangeBitField & NS_STYLE_WILL_CHANGE_FIXPOS_CB) || - aContextFrame->StyleSVGReset()->HasFilters()) && + aContextFrame->StyleEffects()->HasFilters()) && !aContextFrame->IsSVGText(); } @@ -176,7 +176,7 @@ nsStyleDisplay::IsAbsolutelyPositioned(const nsIFrame* aContextFrame) const } uint8_t -nsStyleVisibility::GetEffectivePointerEvents(nsIFrame* aFrame) const +nsStyleUserInterface::GetEffectivePointerEvents(nsIFrame* aFrame) const { if (aFrame->GetContent() && !aFrame->GetContent()->GetParent()) { // The root element has a cluster of frames associated with it @@ -185,7 +185,7 @@ nsStyleVisibility::GetEffectivePointerEvents(nsIFrame* aFrame) const // frame. nsIFrame* f = aFrame->GetContent()->GetPrimaryFrame(); if (f) { - return f->StyleVisibility()->mPointerEvents; + return f->StyleUserInterface()->mPointerEvents; } } return mPointerEvents; diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index 15afebd39e..6d90173de5 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -1368,6 +1368,14 @@ var gCSSProperties = { alias_for: "box-sizing", subproperties: [ "box-sizing" ], }, + "color-adjust": { + domProp: "colorAdjust", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "economy" ], + other_values: [ "exact" ], + invalid_values: [] + }, "-moz-columns": { domProp: "MozColumns", inherited: false, @@ -2221,6 +2229,10 @@ var gCSSProperties = { /* error inside functions */ "-moz-image-rect(url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==), rubbish, 50%, 30%, 0) transparent", "-moz-element(#a rubbish) black", + "text", + "text border-box", + "content-box text text", + "padding-box text url(404.png) text", ] }, "background-attachment": { @@ -5433,11 +5445,15 @@ if (IsCSSPropertyPrefEnabled("layout.css.text-combine-upright.enabled")) { inherited: true, type: CSS_TYPE_LONGHAND, initial_values: [ "none" ], - other_values: [ "all", "digits", "digits 2", "digits 3", "digits 4", "digits 3" ], + other_values: [ "all" ], invalid_values: [ "auto", "all 2", "none all", "digits -3", "digits 0", "digits 12", "none 3", "digits 3.1415", "digits3", "digits 1", "digits 3 all", "digits foo", "digits all", "digits 3.0" ] }; + if (IsCSSPropertyPrefEnabled("layout.css.text-combine-upright-digits.enabled")) { + gCSSProperties["text-combine-upright"].other_values.push( + "digits", "digits 2", "digits 3", "digits 4", "digits 3"); + } } if (IsCSSPropertyPrefEnabled("layout.css.masking.enabled")) { @@ -7024,6 +7040,15 @@ if (IsCSSPropertyPrefEnabled("layout.css.prefixes.webkit")) { alias_for: "filter", subproperties: [ "filter" ], }; + gCSSProperties["-webkit-text-fill-color"] = { + domProp: "webkitTextFillColor", + inherited: true, + type: CSS_TYPE_LONGHAND, + prerequisites: { "color": "black" }, + initial_values: [ "currentColor", "black", "#000", "#000000", "rgb(0,0,0)" ], + other_values: [ "red", "rgba(255,255,255,0.5)", "transparent" ], + invalid_values: [ "#0", "#00", "#0000", "#00000", "#0000000", "#00000000", "#000000000", "000000", "ff00ff", "rgb(255,xxx,255)" ] + }; gCSSProperties["-webkit-text-size-adjust"] = { domProp: "webkitTextSizeAdjust", inherited: true, @@ -7393,6 +7418,30 @@ if (IsCSSPropertyPrefEnabled("layout.css.text-emphasis.enabled")) { }; } +if (IsCSSPropertyPrefEnabled("layout.css.background-clip-text.enabled")) { + gCSSProperties["background-clip"].other_values.push( + "text", + "content-box, text", + "text, border-box", + "text, text" + ); + gCSSProperties["background"].other_values.push( + "url(404.png) green padding-box text", + "content-box text url(404.png) blue" + ); +} else { + gCSSProperties["background-clip"].invalid_values.push( + "text", + "content-box, text", + "text, border-box", + "text, text" + ); + gCSSProperties["background"].invalid_values.push( + "url(404.png) green padding-box text", + "content-box text url(404.png) blue" + ); +} + // Copy aliased properties' fields from their alias targets. for (var prop in gCSSProperties) { var entry = gCSSProperties[prop]; diff --git a/layout/style/test/test_bug657143.html b/layout/style/test/test_bug657143.html index 83b7ac682b..4b9980eb79 100644 --- a/layout/style/test/test_bug657143.html +++ b/layout/style/test/test_bug657143.html @@ -1,102 +1,132 @@ - - - - - Test for Bug 657143 - - - - - -Mozilla Bug 657143 -

- -
-
-
- - + + + + + Test for Bug 657143 + + + + + +Mozilla Bug 657143 +

+ + +
+
+
+ + diff --git a/layout/style/test/test_transitions_events.html b/layout/style/test/test_transitions_events.html index e2d30d711d..6dd6a4eae6 100644 --- a/layout/style/test/test_transitions_events.html +++ b/layout/style/test/test_transitions_events.html @@ -23,6 +23,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=531585 text-decoration-color: black; /* don't derive from color */ outline-color: black; /* don't derive from color */ text-emphasis-color: black; /* don't derive from color */ + -webkit-text-fill-color: black; /* don't derive from color */ } #four { @@ -75,6 +76,7 @@ var got_one_target_borderleft = false; var got_one_target_columnrule = false; var got_one_target_textdecorationcolor = false; var got_one_target_textemphasiscolor = false; +var got_one_target_webkittextfillcolor = false; var got_one_target_outlinecolor = false; var got_two_target = false; var got_three_top = false; @@ -194,6 +196,12 @@ $("one").addEventListener("transitionend", got_one_target_textemphasiscolor = true; event.stopPropagation(); break; + case "-webkit-text-fill-color": + ok(!got_one_target_webkittextfillcolor, + "transitionend on one on target (-webkit-text-fill-color)"); + got_one_target_webkittextfillcolor = true; + event.stopPropagation(); + break; case "outline-color": ok(!got_one_target_outlinecolor, "transitionend on one on target (outline-color)"); @@ -222,6 +230,9 @@ started_test(); // text-decoration-color on #one if (SpecialPowers.getBoolPref("layout.css.text-emphasis.enabled")) { started_test(); // text-emphasis-color on #one } +if (SpecialPowers.getBoolPref("layout.css.prefixes.webkit")) { + started_test(); // -webkit-text-fill-color on #one +} started_test(); // outline-color on #one $("one").style.color = "lime"; diff --git a/layout/style/test/test_transitions_per_property.html b/layout/style/test/test_transitions_per_property.html index b11c51ea6f..4256f2e518 100644 --- a/layout/style/test/test_transitions_per_property.html +++ b/layout/style/test/test_transitions_per_property.html @@ -257,6 +257,8 @@ var supported_properties = { test_length_clamped, test_percent_clamped ], "word-spacing": [ test_length_transition, test_length_unclamped ], "z-index": [ test_integer_transition, test_pos_integer_or_auto_transition ], + "-webkit-text-fill-color": [ test_color_transition, + test_border_color_transition ] }; if (SupportsMaskShorthand()) { diff --git a/layout/svg/SVGTextFrame.cpp b/layout/svg/SVGTextFrame.cpp index fac7e8b76b..3d9bef5420 100644 --- a/layout/svg/SVGTextFrame.cpp +++ b/layout/svg/SVGTextFrame.cpp @@ -2757,13 +2757,11 @@ public: void NotifySelectionBackgroundNeedsFill(const Rect& aBackgroundRect, nscolor aColor, DrawTarget& aDrawTarget) override; + void PaintDecorationLine(Rect aPath, nscolor aColor) override; + void PaintSelectionDecorationLine(Rect aPath, nscolor aColor) override; void NotifyBeforeText(nscolor aColor) override; void NotifyGlyphPathEmitted() override; void NotifyAfterText() override; - void NotifyBeforeDecorationLine(nscolor aColor) override; - void NotifyDecorationLinePathEmitted() override; - void NotifyBeforeSelectionDecorationLine(nscolor aColor) override; - void NotifySelectionDecorationLinePathEmitted() override; private: void SetupContext(); @@ -2858,22 +2856,24 @@ SVGTextDrawPathCallbacks::NotifyAfterText() } void -SVGTextDrawPathCallbacks::NotifyBeforeDecorationLine(nscolor aColor) +SVGTextDrawPathCallbacks::PaintDecorationLine(Rect aPath, nscolor aColor) { mColor = aColor; - SetupContext(); -} + AntialiasMode aaMode = + nsSVGUtils::ToAntialiasMode(mFrame->StyleText()->mTextRendering); -void -SVGTextDrawPathCallbacks::NotifyDecorationLinePathEmitted() -{ + gfx->Save(); + gfx->NewPath(); + gfx->SetAntialiasMode(aaMode); + gfx->Rectangle(ThebesRect(aPath)); HandleTextGeometry(); gfx->NewPath(); gfx->Restore(); } void -SVGTextDrawPathCallbacks::NotifyBeforeSelectionDecorationLine(nscolor aColor) +SVGTextDrawPathCallbacks::PaintSelectionDecorationLine(Rect aPath, + nscolor aColor) { if (IsClipPathChild()) { // Don't paint selection decorations when in a clip path. @@ -2881,17 +2881,10 @@ SVGTextDrawPathCallbacks::NotifyBeforeSelectionDecorationLine(nscolor aColor) } mColor = aColor; + gfx->Save(); -} - -void -SVGTextDrawPathCallbacks::NotifySelectionDecorationLinePathEmitted() -{ - if (IsClipPathChild()) { - // Don't paint selection decorations when in a clip path. - return; - } - + gfx->NewPath(); + gfx->Rectangle(ThebesRect(aPath)); FillAndStrokeGeometry(); gfx->Restore(); } @@ -2904,7 +2897,7 @@ SVGTextDrawPathCallbacks::SetupContext() // XXX This is copied from nsSVGGlyphFrame::Render, but cairo doesn't actually // seem to do anything with the antialias mode. So we can perhaps remove it, // or make SetAntialiasMode set cairo text antialiasing too. - switch (mFrame->StyleSVG()->mTextRendering) { + switch (mFrame->StyleText()->mTextRendering) { case NS_STYLE_TEXT_RENDERING_OPTIMIZESPEED: gfx->SetAntialiasMode(AntialiasMode::NONE); break; @@ -3030,7 +3023,7 @@ SVGTextDrawPathCallbacks::StrokeGeometry() /*aContextPaint*/ nullptr); DrawOptions drawOptions; drawOptions.mAntialiasMode = - nsSVGUtils::ToAntialiasMode(mFrame->StyleSVG()->mTextRendering); + nsSVGUtils::ToAntialiasMode(mFrame->StyleText()->mTextRendering); gfx->GetDrawTarget()->Stroke(path, strokePattern, strokeOptions); } } @@ -3756,18 +3749,20 @@ SVGTextFrame::PaintSVG(gfxContext& aContext, aContext.SetMatrix(runTransform); if (drawMode != DrawMode(0)) { - LayoutDeviceRect frameRect = LayoutDevicePixel:: - FromAppUnits(frame->GetVisualOverflowRect(), auPerDevPx); bool paintSVGGlyphs; + nsTextFrame::PaintTextParams params(rendCtx.ThebesContext()); + params.framePt = gfxPoint(); + params.dirtyRect = LayoutDevicePixel:: + FromAppUnits(frame->GetVisualOverflowRect(), auPerDevPx); + params.contextPaint = &contextPaint; if (ShouldRenderAsPath(frame, paintSVGGlyphs)) { SVGTextDrawPathCallbacks callbacks(&rendCtx, frame, matrixForPaintServers, paintSVGGlyphs); - frame->PaintText(&rendCtx, nsPoint(), frameRect, item, - &contextPaint, &callbacks); + params.callbacks = &callbacks; + frame->PaintText(params, item); } else { - frame->PaintText(&rendCtx, nsPoint(), frameRect, item, - &contextPaint, nullptr); + frame->PaintText(params, item); } } @@ -5427,7 +5422,7 @@ SVGTextFrame::UpdateFontSizeScaleFactor() if (!geometricPrecision) { // Unfortunately we can't treat text-rendering:geometricPrecision // separately for each text frame. - geometricPrecision = f->StyleSVG()->mTextRendering == + geometricPrecision = f->StyleText()->mTextRendering == NS_STYLE_TEXT_RENDERING_GEOMETRICPRECISION; } nscoord size = f->StyleFont()->mFont.size; diff --git a/layout/svg/nsFilterInstance.cpp b/layout/svg/nsFilterInstance.cpp index 8f14b810db..783ba13ce1 100644 --- a/layout/svg/nsFilterInstance.cpp +++ b/layout/svg/nsFilterInstance.cpp @@ -63,7 +63,7 @@ nsFilterInstance::PaintFilteredFrame(nsIFrame *aFilteredFrame, nsSVGFilterPaintCallback *aPaintCallback, const nsRegion *aDirtyArea) { - auto& filterChain = aFilteredFrame->StyleSVGReset()->mFilters; + auto& filterChain = aFilteredFrame->StyleEffects()->mFilters; UniquePtr metrics = UserSpaceMetricsForFrame(aFilteredFrame); nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(), *metrics, filterChain, aPaintCallback, aTransform, @@ -83,7 +83,7 @@ nsFilterInstance::GetPostFilterDirtyArea(nsIFrame *aFilteredFrame, } gfxMatrix unused; // aPaintTransform arg not used since we're not painting - auto& filterChain = aFilteredFrame->StyleSVGReset()->mFilters; + auto& filterChain = aFilteredFrame->StyleEffects()->mFilters; UniquePtr metrics = UserSpaceMetricsForFrame(aFilteredFrame); nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(), *metrics, filterChain, nullptr, unused, nullptr, @@ -103,7 +103,7 @@ nsFilterInstance::GetPreFilterNeededArea(nsIFrame *aFilteredFrame, const nsRegion& aPostFilterDirtyRegion) { gfxMatrix unused; // aPaintTransform arg not used since we're not painting - auto& filterChain = aFilteredFrame->StyleSVGReset()->mFilters; + auto& filterChain = aFilteredFrame->StyleEffects()->mFilters; UniquePtr metrics = UserSpaceMetricsForFrame(aFilteredFrame); nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(), *metrics, filterChain, nullptr, unused, @@ -134,7 +134,7 @@ nsFilterInstance::GetPostFilterBounds(nsIFrame *aFilteredFrame, } gfxMatrix unused; // aPaintTransform arg not used since we're not painting - auto& filterChain = aFilteredFrame->StyleSVGReset()->mFilters; + auto& filterChain = aFilteredFrame->StyleEffects()->mFilters; UniquePtr metrics = UserSpaceMetricsForFrame(aFilteredFrame); nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(), *metrics, filterChain, nullptr, unused, nullptr, @@ -354,7 +354,7 @@ nsFilterInstance::BuildSourcePaint(SourceInfo *aSource, RefPtr offscreenDT = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget( neededRect.Size(), SurfaceFormat::B8G8R8A8); - if (!offscreenDT) { + if (!offscreenDT || !offscreenDT->IsValid()) { return NS_ERROR_OUT_OF_MEMORY; } @@ -364,7 +364,8 @@ nsFilterInstance::BuildSourcePaint(SourceInfo *aSource, } if (!mPaintTransform.IsSingular()) { - RefPtr gfx = new gfxContext(offscreenDT); + RefPtr gfx = gfxContext::ForDrawTarget(offscreenDT); + MOZ_ASSERT(gfx); // already checked the draw target above gfx->Save(); gfx->Multiply(mPaintTransform * deviceToFilterSpace * @@ -418,7 +419,7 @@ nsFilterInstance::BuildSourceImage(DrawTarget* aTargetDT) RefPtr offscreenDT = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget( neededRect.Size(), SurfaceFormat::B8G8R8A8); - if (!offscreenDT) { + if (!offscreenDT || !offscreenDT->IsValid()) { return NS_ERROR_OUT_OF_MEMORY; } @@ -443,7 +444,8 @@ nsFilterInstance::BuildSourceImage(DrawTarget* aTargetDT) if (!deviceToFilterSpace.Invert()) { return NS_ERROR_FAILURE; } - RefPtr ctx = new gfxContext(offscreenDT); + RefPtr ctx = gfxContext::ForDrawTarget(offscreenDT); + MOZ_ASSERT(ctx); // already checked the draw target above ctx->SetMatrix( ctx->CurrentMatrix().Translate(-neededRect.TopLeft()). PreMultiply(deviceToFilterSpace)); diff --git a/layout/svg/nsSVGClipPathFrame.cpp b/layout/svg/nsSVGClipPathFrame.cpp index 74264a293a..cc5c879476 100644 --- a/layout/svg/nsSVGClipPathFrame.cpp +++ b/layout/svg/nsSVGClipPathFrame.cpp @@ -118,7 +118,11 @@ nsSVGClipPathFrame::GetClipMask(gfxContext& aReferenceContext, // Paint this clipPath's contents into maskDT: { - RefPtr ctx = new gfxContext(maskDT); + RefPtr ctx = gfxContext::ForDrawTarget(maskDT); + if (!ctx) { + gfxCriticalError() << "SVGClipPath context problem " << gfx::hexa(maskDT); + return nullptr; + } ctx->SetMatrix(mat); // We need to set mMatrixForChildren here so that under the PaintSVG calls @@ -227,8 +231,7 @@ nsSVGClipPathFrame::GetClipMask(gfxContext& aReferenceContext, devSpaceClipExtents.height)); maskDT->MaskSurface(SurfacePattern(currentMask, ExtendMode::CLAMP), aExtraMask, - Point(aExtraMasksTransform._31 - devSpaceClipExtents.x, - aExtraMasksTransform._32 - devSpaceClipExtents.y)); + Point(aExtraMasksTransform._31, aExtraMasksTransform._32)); } *aMaskTransform = ToMatrix(mat); diff --git a/layout/svg/nsSVGContainerFrame.cpp b/layout/svg/nsSVGContainerFrame.cpp index 9661fd8c24..21758a1156 100644 --- a/layout/svg/nsSVGContainerFrame.cpp +++ b/layout/svg/nsSVGContainerFrame.cpp @@ -259,8 +259,7 @@ nsSVGDisplayContainerFrame::PaintSVG(gfxContext& aContext, "If display lists are enabled, only painting of non-display " "SVG should take this code path"); - const nsStyleDisplay *display = StyleDisplay(); - if (display->mOpacity == 0.0) { + if (StyleEffects()->mOpacity == 0.0) { return NS_OK; } diff --git a/layout/svg/nsSVGEffects.cpp b/layout/svg/nsSVGEffects.cpp index e1bc463803..8627567b01 100644 --- a/layout/svg/nsSVGEffects.cpp +++ b/layout/svg/nsSVGEffects.cpp @@ -487,15 +487,15 @@ GetEffectProperty(nsIURI *aURI, nsIFrame *aFrame, static nsSVGFilterProperty* GetOrCreateFilterProperty(nsIFrame *aFrame) { - const nsStyleSVGReset* style = aFrame->StyleSVGReset(); - if (!style->HasFilters()) + const nsStyleEffects* effects = aFrame->StyleEffects(); + if (!effects->HasFilters()) return nullptr; FrameProperties props = aFrame->Properties(); nsSVGFilterProperty *prop = props.Get(nsSVGEffects::FilterProperty()); if (prop) return prop; - prop = new nsSVGFilterProperty(style->mFilters, aFrame); + prop = new nsSVGFilterProperty(effects->mFilters, aFrame); if (!prop) return nullptr; NS_ADDREF(prop); @@ -684,7 +684,7 @@ nsSVGEffects::GetFilterProperty(nsIFrame *aFrame) { NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation"); - if (!aFrame->StyleSVGReset()->HasFilters()) + if (!aFrame->StyleEffects()->HasFilters()) return nullptr; return static_cast diff --git a/layout/svg/nsSVGForeignObjectFrame.cpp b/layout/svg/nsSVGForeignObjectFrame.cpp index 7897967e3e..0649b39fff 100644 --- a/layout/svg/nsSVGForeignObjectFrame.cpp +++ b/layout/svg/nsSVGForeignObjectFrame.cpp @@ -377,7 +377,7 @@ nsSVGForeignObjectFrame::ReflowSVG() // If we have a filter, we need to invalidate ourselves because filter // output can change even if none of our descendants need repainting. - if (StyleSVGReset()->HasFilters()) { + if (StyleEffects()->HasFilters()) { InvalidateFrame(); } diff --git a/layout/svg/nsSVGImageFrame.cpp b/layout/svg/nsSVGImageFrame.cpp index 20d9b75d14..78f71626d3 100644 --- a/layout/svg/nsSVGImageFrame.cpp +++ b/layout/svg/nsSVGImageFrame.cpp @@ -21,6 +21,7 @@ #include "mozilla/dom/SVGImageElement.h" #include "nsContentUtils.h" #include "nsIReflowCallback.h" +#include "mozilla/unused.h" using namespace mozilla; using namespace mozilla::dom; @@ -354,10 +355,10 @@ nsSVGImageFrame::PaintSVG(gfxContext& aContext, // image into the current canvas is just the group opacity. float opacity = 1.0f; if (nsSVGUtils::CanOptimizeOpacity(this)) { - opacity = StyleDisplay()->mOpacity; + opacity = StyleEffects()->mOpacity; } - if (opacity != 1.0f || StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { + if (opacity != 1.0f || StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { aContext.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity); } @@ -401,7 +402,8 @@ nsSVGImageFrame::PaintSVG(gfxContext& aContext, // Note: Can't use DrawSingleUnscaledImage for the TYPE_VECTOR case. // That method needs our image to have a fixed native width & height, // and that's not always true for TYPE_VECTOR images. - nsLayoutUtils::DrawSingleImage( + // FIXME We should use the return value, see bug 1258510. + Unused << nsLayoutUtils::DrawSingleImage( aContext, PresContext(), mImageContainer, @@ -411,7 +413,8 @@ nsSVGImageFrame::PaintSVG(gfxContext& aContext, &context, drawFlags); } else { // mImageContainer->GetType() == TYPE_RASTER - nsLayoutUtils::DrawSingleUnscaledImage( + // FIXME We should use the return value, see bug 1258510. + Unused << nsLayoutUtils::DrawSingleUnscaledImage( aContext, PresContext(), mImageContainer, @@ -421,7 +424,7 @@ nsSVGImageFrame::PaintSVG(gfxContext& aContext, drawFlags); } - if (opacity != 1.0f || StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { + if (opacity != 1.0f || StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { aContext.PopGroupAndBlend(); } // gfxContextAutoSaveRestore goes out of scope & cleans up our gfxContext @@ -571,7 +574,7 @@ nsSVGImageFrame::GetHitTestFlags() { uint16_t flags = 0; - switch(StyleVisibility()->mPointerEvents) { + switch (StyleUserInterface()->mPointerEvents) { case NS_STYLE_POINTER_EVENTS_NONE: break; case NS_STYLE_POINTER_EVENTS_VISIBLEPAINTED: diff --git a/layout/svg/nsSVGInnerSVGFrame.cpp b/layout/svg/nsSVGInnerSVGFrame.cpp index 035fcaddd3..f1f3084020 100644 --- a/layout/svg/nsSVGInnerSVGFrame.cpp +++ b/layout/svg/nsSVGInnerSVGFrame.cpp @@ -120,7 +120,7 @@ nsSVGInnerSVGFrame::ReflowSVG() // If we have a filter, we need to invalidate ourselves because filter // output can change even if none of our descendants need repainting. - if (StyleSVGReset()->HasFilters()) { + if (StyleEffects()->HasFilters()) { InvalidateFrame(); } diff --git a/layout/svg/nsSVGIntegrationUtils.cpp b/layout/svg/nsSVGIntegrationUtils.cpp index 6b177062cd..1b2c8b5862 100644 --- a/layout/svg/nsSVGIntegrationUtils.cpp +++ b/layout/svg/nsSVGIntegrationUtils.cpp @@ -25,6 +25,7 @@ #include "BasicLayers.h" #include "mozilla/gfx/Point.h" #include "nsCSSRendering.h" +#include "mozilla/unused.h" using namespace mozilla; using namespace mozilla::layers; @@ -154,9 +155,9 @@ nsSVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame* aFrame) // checking the SDL prefs here, since we don't know if we're being called for // painting or hit-testing anyway. const nsStyleSVGReset *style = aFrame->StyleSVGReset(); - bool hasValidLayers = style->mMask.HasLayerWithImage(); - - return (style->HasFilters() || style->HasClipPath() || hasValidLayers); + return aFrame->StyleEffects()->HasFilters() || + style->HasClipPath() || + style->mMask.HasLayerWithImage(); } // For non-SVG frames, this gives the offset to the frame's "user space". @@ -450,7 +451,7 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(gfxContext& aContext, } } - float opacity = aFrame->StyleDisplay()->mOpacity; + float opacity = aFrame->StyleEffects()->mOpacity; if (opacity == 0.0f) { return; } @@ -515,18 +516,28 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(gfxContext& aContext, // This source is not a svg mask, but it still can be a correct mask image. nsSVGMaskFrame *svgMaskFrame = effectProperties.GetMaskFrame(&isOK); - bool complexEffects = false; - bool hasValidLayers = svgReset->mMask.HasLayerWithImage(); - // These are used if we require a temporary surface for a custom blend mode. RefPtr target = &aContext; IntPoint targetOffset; + // hasMaskToDraw is true means we have at least one drawable mask resource. + // We need to apply mask only if hasMaskToDraw is true. + bool hasMaskToDraw = (svgMaskFrame != nullptr); + if (!hasMaskToDraw) { + NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, svgReset->mMask) { + if (!svgReset->mMask.mLayers[i].mImage.IsEmpty()) { + hasMaskToDraw = true; + break; + } + } + } + + bool complexEffects = false; /* Check if we need to do additional operations on this child's * rendering, which necessitates rendering into another surface. */ if (opacity != 1.0f || (clipPathFrame && !isTrivialClip) - || aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL - || svgMaskFrame || hasValidLayers) { + || aFrame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL + || hasMaskToDraw) { complexEffects = true; aContext.Save(); @@ -539,12 +550,15 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(gfxContext& aContext, Matrix maskTransform; RefPtr maskSurface; if (svgMaskFrame) { + // Generate maskSurface from a SVG mask. maskSurface = svgMaskFrame->GetMaskForMaskedFrame(&aContext, - aFrame, - cssPxToDevPxMatrix, - opacity, - &maskTransform); - } else if (hasValidLayers) { + aFrame, + cssPxToDevPxMatrix, + opacity, + &maskTransform, + svgReset->mMask.mLayers[0].mMaskMode); + } else if (hasMaskToDraw) { + // Create maskSuface. gfxRect clipRect = aContext.GetClipExtents(); { gfxContextMatrixAutoSaveRestore matRestore(&aContext); @@ -553,27 +567,39 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(gfxContext& aContext, clipRect = aContext.GetClipExtents(); } IntRect drawRect = RoundedOut(ToRect(clipRect)); - RefPtr targetDT = aContext.GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(), SurfaceFormat::A8); - if (!targetDT) { + + // Mask composition result on CoreGraphic::A8 surface is not correct + // when mask-mode is not add(source over). Switch to skia when CG backend + // detected. + RefPtr targetDT = + (aContext.GetDrawTarget()->GetBackendType() == BackendType::COREGRAPHICS) ? + Factory::CreateDrawTarget(BackendType::SKIA, drawRect.Size(), + SurfaceFormat::A8) : + aContext.GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(), + SurfaceFormat::A8); + + if (!targetDT || !targetDT->IsValid()) { aContext.Restore(); return; } - RefPtr target = new gfxContext(targetDT); + RefPtr target = gfxContext::ForDrawTarget(targetDT); + MOZ_ASSERT(target); // alrady checked the draw target above target->SetMatrix(matrixAutoSaveRestore.Matrix() * gfxMatrix::Translation(-drawRect.TopLeft())); - // Generate mask surface. + // Compose all mask-images onto maskSurface. uint32_t flags = aBuilder->GetBackgroundPaintFlags() | nsCSSRendering::PAINTBG_MASK_IMAGE; nsRenderingContext rc(target); - nsCSSRendering::PaintBackgroundWithSC(aFrame->PresContext(), - rc, - aFrame, - aDirtyRect, - aBorderArea, - firstFrame->StyleContext(), - *aFrame->StyleBorder(), - flags); + // FIXME We should use the return value, see bug 1258510. + Unused << nsCSSRendering::PaintBackgroundWithSC(aFrame->PresContext(), + rc, + aFrame, + aDirtyRect, + aBorderArea, + firstFrame->StyleContext(), + *aFrame->StyleBorder(), + flags); maskSurface = targetDT->Snapshot(); // Compute mask transform. @@ -582,13 +608,13 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(gfxContext& aContext, maskTransform = Matrix::Translation(drawRect.x, drawRect.y) * mat; } - if ((svgMaskFrame || hasValidLayers) && !maskSurface) { + if (hasMaskToDraw && !maskSurface) { // Entire surface is clipped out. aContext.Restore(); return; } - if (aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { + if (aFrame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { // Create a temporary context to draw to so we can blend it back with // another operator. gfxRect clipRect; @@ -602,11 +628,12 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(gfxContext& aContext, IntRect drawRect = RoundedOut(ToRect(clipRect)); RefPtr targetDT = aContext.GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(), SurfaceFormat::B8G8R8A8); - if (!targetDT) { + if (!targetDT || !targetDT->IsValid()) { aContext.Restore(); return; } - target = new gfxContext(targetDT); + target = gfxContext::ForDrawTarget(targetDT); + MOZ_ASSERT(target); // already checked the draw target above target->SetMatrix(aContext.CurrentMatrix() * gfxMatrix::Translation(-drawRect.TopLeft())); targetOffset = drawRect.TopLeft(); } @@ -622,8 +649,7 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(gfxContext& aContext, } } - if (opacity != 1.0f || svgMaskFrame || hasValidLayers || - (clipPathFrame && !isTrivialClip)) { + if (opacity != 1.0f || hasMaskToDraw || (clipPathFrame && !isTrivialClip)) { target->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity, maskSurface, maskTransform); } } @@ -667,12 +693,11 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(gfxContext& aContext, return; } - if (opacity != 1.0f || svgMaskFrame || hasValidLayers || - (clipPathFrame && !isTrivialClip)) { + if (opacity != 1.0f || hasMaskToDraw || (clipPathFrame && !isTrivialClip)) { target->PopGroupAndBlend(); } - if (aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { + if (aFrame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { RefPtr targetDT = target->GetDrawTarget(); target = nullptr; RefPtr targetSurf = targetDT->Snapshot(); diff --git a/layout/svg/nsSVGMaskFrame.cpp b/layout/svg/nsSVGMaskFrame.cpp index a4f3368a59..ba2af4c406 100644 --- a/layout/svg/nsSVGMaskFrame.cpp +++ b/layout/svg/nsSVGMaskFrame.cpp @@ -205,7 +205,8 @@ nsSVGMaskFrame::GetMaskForMaskedFrame(gfxContext* aContext, nsIFrame* aMaskedFrame, const gfxMatrix &aMatrix, float aOpacity, - Matrix* aMaskTransform) + Matrix* aMaskTransform, + uint8_t aMaskOp) { // If the flag is set when we get here, it means this mask frame // has already been used painting the current mask, and the document @@ -252,14 +253,15 @@ nsSVGMaskFrame::GetMaskForMaskedFrame(gfxContext* aContext, RefPtr maskDT = Factory::CreateDrawTarget(BackendType::CAIRO, maskSurfaceSize, SurfaceFormat::B8G8R8A8); - if (!maskDT) { + if (!maskDT || !maskDT->IsValid()) { return nullptr; } gfxMatrix maskSurfaceMatrix = aContext->CurrentMatrix() * gfxMatrix::Translation(-maskSurfaceRect.TopLeft()); - RefPtr tmpCtx = new gfxContext(maskDT); + RefPtr tmpCtx = gfxContext::ForDrawTarget(maskDT); + MOZ_ASSERT(tmpCtx); // already checked the draw target above tmpCtx->SetMatrix(maskSurfaceMatrix); mMatrixForChildren = GetMaskTransform(aMaskedFrame) * aMatrix; @@ -306,7 +308,15 @@ nsSVGMaskFrame::GetMaskForMaskedFrame(gfxContext* aContext, return nullptr; } - if (StyleSVGReset()->mMaskType == NS_STYLE_MASK_TYPE_LUMINANCE) { + uint8_t maskType; + if (aMaskOp == NS_STYLE_MASK_MODE_MATCH_SOURCE) { + maskType = StyleSVGReset()->mMaskType; + } else { + maskType = aMaskOp == NS_STYLE_MASK_MODE_LUMINANCE ? + NS_STYLE_MASK_TYPE_LUMINANCE : NS_STYLE_MASK_TYPE_ALPHA; + } + + if (maskType == NS_STYLE_MASK_TYPE_LUMINANCE) { if (StyleSVG()->mColorInterpolation == NS_STYLE_COLOR_INTERPOLATION_LINEARRGB) { ComputeLinearRGBLuminanceMask(map.mData, map.mStride, diff --git a/layout/svg/nsSVGMaskFrame.h b/layout/svg/nsSVGMaskFrame.h index f3e4cfbef1..fbd37edd3c 100644 --- a/layout/svg/nsSVGMaskFrame.h +++ b/layout/svg/nsSVGMaskFrame.h @@ -58,7 +58,8 @@ public: nsIFrame* aMaskedFrame, const gfxMatrix &aMatrix, float aOpacity, - Matrix* aMaskTransform); + Matrix* aMaskTransform, + uint8_t aMaskOp = NS_STYLE_MASK_MODE_MATCH_SOURCE); virtual nsresult AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, diff --git a/layout/svg/nsSVGPathGeometryFrame.cpp b/layout/svg/nsSVGPathGeometryFrame.cpp index 8613c1fcfb..c1b0def8c7 100644 --- a/layout/svg/nsSVGPathGeometryFrame.cpp +++ b/layout/svg/nsSVGPathGeometryFrame.cpp @@ -153,9 +153,9 @@ nsSVGPathGeometryFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) nsSVGPathGeometryFrameBase::DidSetStyleContext(aOldStyleContext); if (aOldStyleContext) { - auto oldStyleDisplay = aOldStyleContext->PeekStyleDisplay(); - if (oldStyleDisplay && - StyleDisplay()->mOpacity != oldStyleDisplay->mOpacity && + auto oldStyleEffects = aOldStyleContext->PeekStyleEffects(); + if (oldStyleEffects && + StyleEffects()->mOpacity != oldStyleEffects->mOpacity && nsSVGUtils::CanOptimizeOpacity(this)) { // nsIFrame::BuildDisplayListForStackingContext() is not going to create an // nsDisplayOpacity display list item, so DLBI won't invalidate for us. diff --git a/layout/svg/nsSVGPatternFrame.cpp b/layout/svg/nsSVGPatternFrame.cpp index 591c177475..aafcb638b5 100644 --- a/layout/svg/nsSVGPatternFrame.cpp +++ b/layout/svg/nsSVGPatternFrame.cpp @@ -372,12 +372,13 @@ nsSVGPatternFrame::PaintPattern(const DrawTarget* aDrawTarget, RefPtr dt = aDrawTarget->CreateSimilarDrawTarget(surfaceSize, SurfaceFormat::B8G8R8A8); - if (!dt) { + if (!dt || !dt->IsValid()) { return nullptr; } dt->ClearRect(Rect(0, 0, surfaceSize.width, surfaceSize.height)); - RefPtr gfx = new gfxContext(dt); + RefPtr gfx = gfxContext::ForDrawTarget(dt); + MOZ_ASSERT(gfx); // already checked the draw target above if (aGraphicOpacity != 1.0f) { gfx->Save(); diff --git a/layout/svg/nsSVGSwitchFrame.cpp b/layout/svg/nsSVGSwitchFrame.cpp index 7b5fc87ea2..a0f447ca7f 100644 --- a/layout/svg/nsSVGSwitchFrame.cpp +++ b/layout/svg/nsSVGSwitchFrame.cpp @@ -114,7 +114,7 @@ nsSVGSwitchFrame::PaintSVG(gfxContext& aContext, "If display lists are enabled, only painting of non-display " "SVG should take this code path"); - if (StyleDisplay()->mOpacity == 0.0) + if (StyleEffects()->mOpacity == 0.0) return NS_OK; nsIFrame *kid = GetActiveChildFrame(); diff --git a/layout/svg/nsSVGUseFrame.cpp b/layout/svg/nsSVGUseFrame.cpp index a530858f06..c0f14c51fb 100644 --- a/layout/svg/nsSVGUseFrame.cpp +++ b/layout/svg/nsSVGUseFrame.cpp @@ -196,7 +196,7 @@ nsSVGUseFrame::ReflowSVG() // If we have a filter, we need to invalidate ourselves because filter // output can change even if none of our descendants need repainting. - if (StyleSVGReset()->HasFilters()) { + if (StyleEffects()->HasFilters()) { InvalidateFrame(); } diff --git a/layout/svg/nsSVGUtils.cpp b/layout/svg/nsSVGUtils.cpp index ea93fa96de..aacbda949e 100644 --- a/layout/svg/nsSVGUtils.cpp +++ b/layout/svg/nsSVGUtils.cpp @@ -499,7 +499,7 @@ nsSVGUtils::PaintFrameWithEffects(nsIFrame *aFrame, if (!svgChildFrame) return; - float opacity = aFrame->StyleDisplay()->mOpacity; + float opacity = aFrame->StyleEffects()->mOpacity; if (opacity == 0.0f) return; @@ -590,7 +590,7 @@ nsSVGUtils::PaintFrameWithEffects(nsIFrame *aFrame, /* Check if we need to do additional operations on this child's * rendering, which necessitates rendering into another surface. */ if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip) - || aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { + || aFrame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { complexEffects = true; Matrix maskTransform; @@ -622,7 +622,7 @@ nsSVGUtils::PaintFrameWithEffects(nsIFrame *aFrame, *drawTarget)); } - if (aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { + if (aFrame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { // Create a temporary context to draw to so we can blend it back with // another operator. gfxRect clipRect; @@ -636,7 +636,11 @@ nsSVGUtils::PaintFrameWithEffects(nsIFrame *aFrame, IntRect drawRect = RoundedOut(ToRect(clipRect)); RefPtr targetDT = aContext.GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(), SurfaceFormat::B8G8R8A8); - target = new gfxContext(targetDT); + target = gfxContext::ForDrawTarget(targetDT); + if (!target) { + gfxDevCrash(LogReason::InvalidContext) << "SVGPaintWithEffects context problem " << gfx::hexa(targetDT); + return; + } target->SetMatrix(aContext.CurrentMatrix() * gfxMatrix::Translation(-drawRect.TopLeft())); targetOffset = drawRect.TopLeft(); } @@ -707,7 +711,7 @@ nsSVGUtils::PaintFrameWithEffects(nsIFrame *aFrame, target->PopGroupAndBlend(); } - if (aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { + if (aFrame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { RefPtr targetDT = target->GetDrawTarget(); target = nullptr; RefPtr targetSurf = targetDT->Snapshot(); @@ -877,9 +881,10 @@ nsSVGUtils::GetClipRectForFrame(nsIFrame *aFrame, float aX, float aY, float aWidth, float aHeight) { const nsStyleDisplay* disp = aFrame->StyleDisplay(); + const nsStyleEffects* effects = aFrame->StyleEffects(); - if (!(disp->mClipFlags & NS_STYLE_CLIP_RECT)) { - NS_ASSERTION(disp->mClipFlags == NS_STYLE_CLIP_AUTO, + if (!(effects->mClipFlags & NS_STYLE_CLIP_RECT)) { + NS_ASSERTION(effects->mClipFlags == NS_STYLE_CLIP_AUTO, "We don't know about this type of clip."); return gfxRect(aX, aY, aWidth, aHeight); } @@ -888,14 +893,14 @@ nsSVGUtils::GetClipRectForFrame(nsIFrame *aFrame, disp->mOverflowY == NS_STYLE_OVERFLOW_HIDDEN) { nsIntRect clipPxRect = - disp->mClip.ToOutsidePixels(aFrame->PresContext()->AppUnitsPerDevPixel()); + effects->mClip.ToOutsidePixels(aFrame->PresContext()->AppUnitsPerDevPixel()); gfxRect clipRect = gfxRect(clipPxRect.x, clipPxRect.y, clipPxRect.width, clipPxRect.height); - if (NS_STYLE_CLIP_RIGHT_AUTO & disp->mClipFlags) { + if (NS_STYLE_CLIP_RIGHT_AUTO & effects->mClipFlags) { clipRect.width = aWidth - clipRect.X(); } - if (NS_STYLE_CLIP_BOTTOM_AUTO & disp->mClipFlags) { + if (NS_STYLE_CLIP_BOTTOM_AUTO & effects->mClipFlags) { clipRect.height = aHeight - clipRect.Y(); } @@ -1121,7 +1126,7 @@ nsSVGUtils::CanOptimizeOpacity(nsIFrame *aFrame) type != nsGkAtoms::svgPathGeometryFrame) { return false; } - if (aFrame->StyleSVGReset()->HasFilters()) { + if (aFrame->StyleEffects()->HasFilters()) { return false; } // XXX The SVG WG is intending to allow fill, stroke and markers on @@ -1292,7 +1297,7 @@ nsSVGUtils::GetFallbackOrPaintColor(nsStyleContext *aStyleContext, static float MaybeOptimizeOpacity(nsIFrame *aFrame, float aFillOrStrokeOpacity) { - float opacity = aFrame->StyleDisplay()->mOpacity; + float opacity = aFrame->StyleEffects()->mOpacity; if (opacity < 1 && nsSVGUtils::CanOptimizeOpacity(aFrame)) { return aFillOrStrokeOpacity * opacity; } @@ -1593,7 +1598,7 @@ nsSVGUtils::GetGeometryHitTestFlags(nsIFrame* aFrame) { uint16_t flags = 0; - switch(aFrame->StyleVisibility()->mPointerEvents) { + switch (aFrame->StyleUserInterface()->mPointerEvents) { case NS_STYLE_POINTER_EVENTS_NONE: break; case NS_STYLE_POINTER_EVENTS_AUTO: diff --git a/layout/tables/nsTableCellFrame.cpp b/layout/tables/nsTableCellFrame.cpp index ec1264344b..db845d001f 100644 --- a/layout/tables/nsTableCellFrame.cpp +++ b/layout/tables/nsTableCellFrame.cpp @@ -489,8 +489,7 @@ nsTableCellFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, if (StyleVisibility()->IsVisible() && (NS_STYLE_TABLE_EMPTY_CELLS_HIDE != emptyCellStyle)) { // display outset box-shadows if we need to. - const nsStyleBorder* borderStyle = StyleBorder(); - bool hasBoxShadow = !!borderStyle->mBoxShadow; + bool hasBoxShadow = !!StyleEffects()->mBoxShadow; if (hasBoxShadow) { aLists.BorderBackground()->AppendNewToTop( new (aBuilder) nsDisplayBoxShadowOuter(aBuilder, this)); @@ -677,7 +676,7 @@ nsTableCellFrame::UpdateOverflow() uint8_t nsTableCellFrame::GetVerticalAlign() const { - const nsStyleCoord& verticalAlign = StyleTextReset()->mVerticalAlign; + const nsStyleCoord& verticalAlign = StyleDisplay()->mVerticalAlign; if (verticalAlign.GetUnit() == eStyleUnit_Enumerated) { uint8_t value = verticalAlign.GetIntValue(); if (value == NS_STYLE_VERTICAL_ALIGN_TOP || diff --git a/layout/tables/nsTableFrame.cpp b/layout/tables/nsTableFrame.cpp index 40433086b9..3ac5b95ae9 100644 --- a/layout/tables/nsTableFrame.cpp +++ b/layout/tables/nsTableFrame.cpp @@ -1239,7 +1239,7 @@ nsTableFrame::DisplayGenericTablePart(nsDisplayListBuilder* aBuilder, } // Paint the outset box-shadows for the table frames - bool hasBoxShadow = aFrame->StyleBorder()->mBoxShadow != nullptr; + bool hasBoxShadow = aFrame->StyleEffects()->mBoxShadow != nullptr; if (hasBoxShadow) { lists->BorderBackground()->AppendNewToTop( new (aBuilder) nsDisplayBoxShadowOuter(aBuilder, aFrame)); diff --git a/layout/tables/nsTableOuterFrame.cpp b/layout/tables/nsTableOuterFrame.cpp index 2e32b28de0..a49b921712 100644 --- a/layout/tables/nsTableOuterFrame.cpp +++ b/layout/tables/nsTableOuterFrame.cpp @@ -474,7 +474,7 @@ uint8_t nsTableOuterFrame::GetCaptionVerticalAlign() { const nsStyleCoord& va = - mCaptionFrames.FirstChild()->StyleTextReset()->mVerticalAlign; + mCaptionFrames.FirstChild()->StyleDisplay()->mVerticalAlign; return (va.GetUnit() == eStyleUnit_Enumerated) ? va.GetIntValue() : NS_STYLE_VERTICAL_ALIGN_TOP; diff --git a/layout/xul/nsTextBoxFrame.cpp b/layout/xul/nsTextBoxFrame.cpp index 06330d87c0..58680d3bf9 100644 --- a/layout/xul/nsTextBoxFrame.cpp +++ b/layout/xul/nsTextBoxFrame.cpp @@ -476,12 +476,15 @@ nsTextBoxFrame::DrawText(nsRenderingContext& aRenderingContext, presContext->RoundAppUnitsToNearestDevPixels(aTextRect.y + ascent); } - gfxPoint pt(presContext->AppUnitsToGfxUnits(aTextRect.x), - presContext->AppUnitsToGfxUnits(aTextRect.y)); - gfxFloat width = presContext->AppUnitsToGfxUnits(aTextRect.width); - gfxFloat ascentPixel = presContext->AppUnitsToGfxUnits(ascent); - Float xInFrame = Float(PresContext()->AppUnitsToGfxUnits(mTextDrawRect.x)); - gfxRect dirtyRect(presContext->AppUnitsToGfxUnits(aDirtyRect)); + nsCSSRendering::PaintDecorationLineParams params; + params.dirtyRect = ToRect(presContext->AppUnitsToGfxUnits(aDirtyRect)); + params.pt = Point(presContext->AppUnitsToGfxUnits(aTextRect.x), + presContext->AppUnitsToGfxUnits(aTextRect.y)); + params.icoordInFrame = + Float(PresContext()->AppUnitsToGfxUnits(mTextDrawRect.x)); + params.lineSize = Size(presContext->AppUnitsToGfxUnits(aTextRect.width), 0); + params.ascent = presContext->AppUnitsToGfxUnits(ascent); + params.vertical = vertical; // XXX todo: vertical-mode support for decorations not tested yet, // probably won't be positioned correctly @@ -494,25 +497,22 @@ nsTextBoxFrame::DrawText(nsRenderingContext& aRenderingContext, if (decorations & (NS_STYLE_TEXT_DECORATION_LINE_OVERLINE | NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE)) { fontMet->GetUnderline(offset, size); - gfxFloat offsetPixel = presContext->AppUnitsToGfxUnits(offset); - gfxFloat sizePixel = presContext->AppUnitsToGfxUnits(size); + params.lineSize.height = presContext->AppUnitsToGfxUnits(size); if ((decorations & NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE) && underStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE) { - nsCSSRendering::PaintDecorationLine(this, *drawTarget, - ToRect(dirtyRect), underColor, - pt, xInFrame, gfxSize(width, sizePixel), - ascentPixel, offsetPixel, - NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, underStyle, - vertical); + params.color = underColor; + params.offset = presContext->AppUnitsToGfxUnits(offset); + params.decoration = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE; + params.style = underStyle; + nsCSSRendering::PaintDecorationLine(this, *drawTarget, params); } if ((decorations & NS_STYLE_TEXT_DECORATION_LINE_OVERLINE) && overStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE) { - nsCSSRendering::PaintDecorationLine(this, *drawTarget, - ToRect(dirtyRect), overColor, - pt, xInFrame, gfxSize(width, sizePixel), - ascentPixel, ascentPixel, - NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, overStyle, - vertical); + params.color = overColor; + params.offset = params.ascent; + params.decoration = NS_STYLE_TEXT_DECORATION_LINE_OVERLINE; + params.style = overStyle; + nsCSSRendering::PaintDecorationLine(this, *drawTarget, params); } } @@ -589,13 +589,12 @@ nsTextBoxFrame::DrawText(nsRenderingContext& aRenderingContext, if ((decorations & NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) && strikeStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE) { fontMet->GetStrikeout(offset, size); - gfxFloat offsetPixel = presContext->AppUnitsToGfxUnits(offset); - gfxFloat sizePixel = presContext->AppUnitsToGfxUnits(size); - nsCSSRendering::PaintDecorationLine(this, *drawTarget, ToRect(dirtyRect), - strikeColor, - pt, xInFrame, gfxSize(width, sizePixel), ascentPixel, - offsetPixel, NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH, - strikeStyle, vertical); + params.color = strikeColor; + params.lineSize.height = presContext->AppUnitsToGfxUnits(size); + params.offset = presContext->AppUnitsToGfxUnits(offset); + params.decoration = NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH; + params.style = strikeStyle; + nsCSSRendering::PaintDecorationLine(this, *drawTarget, params); } } diff --git a/layout/xul/tree/nsTreeBodyFrame.cpp b/layout/xul/tree/nsTreeBodyFrame.cpp index 727361063a..be72bdbe87 100644 --- a/layout/xul/tree/nsTreeBodyFrame.cpp +++ b/layout/xul/tree/nsTreeBodyFrame.cpp @@ -3515,7 +3515,7 @@ nsTreeBodyFrame::PaintImage(int32_t aRowIndex, nsStyleContext* imageContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeimage); // Obtain opacity value for the image. - float opacity = imageContext->StyleDisplay()->mOpacity; + float opacity = imageContext->StyleEffects()->mOpacity; // Obtain the margins for the image and then deflate our rect by that // amount. The image is assumed to be contained within the deflated rect. @@ -3691,7 +3691,7 @@ nsTreeBodyFrame::PaintText(int32_t aRowIndex, nsStyleContext* textContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecelltext); // Obtain opacity value for the image. - float opacity = textContext->StyleDisplay()->mOpacity; + float opacity = textContext->StyleEffects()->mOpacity; // Obtain the margins for the text and then deflate our rect by that // amount. The text is assumed to be contained within the deflated rect. diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 8816acae52..96d71e56a1 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -577,6 +577,7 @@ pref("apz.content_response_timeout", 300); pref("apz.drag.enabled", false); pref("apz.danger_zone_x", 50); pref("apz.danger_zone_y", 100); +pref("apz.disable_for_scroll_linked_effects", false); pref("apz.displayport_expiry_ms", 15000); pref("apz.enlarge_displayport_when_clipped", false); pref("apz.fling_accel_base_mult", "1.0"); @@ -2062,6 +2063,7 @@ pref("network.prefetch-next", false); // enables the predictive service pref("network.predictor.enabled", false); pref("network.predictor.enable-hover-on-ssl", false); +pref("network.predictor.enable-prefetch", true); pref("network.predictor.page-degradation.day", 0); pref("network.predictor.page-degradation.week", 5); pref("network.predictor.page-degradation.month", 10); @@ -2072,9 +2074,12 @@ pref("network.predictor.subresource-degradation.week", 10); pref("network.predictor.subresource-degradation.month", 25); pref("network.predictor.subresource-degradation.year", 50); pref("network.predictor.subresource-degradation.max", 100); +pref("network.predictor.prefetch-rolling-load-count", 10); +pref("network.predictor.prefetch-min-confidence", 100); pref("network.predictor.preconnect-min-confidence", 90); pref("network.predictor.preresolve-min-confidence", 60); pref("network.predictor.redirect-likely-confidence", 75); +pref("network.predictor.prefetch-force-valid-for", 10); pref("network.predictor.max-resources-per-entry", 100); pref("network.predictor.max-uri-length", 500); pref("network.predictor.cleaned-up", false); @@ -2697,11 +2702,16 @@ pref("layout.css.scope-pseudo.enabled", true); // Is support for background-blend-mode enabled? pref("layout.css.background-blend-mode.enabled", true); +// Is support for background-clip:text enabled? (bug 1263516) +pref("layout.css.background-clip-text.enabled", false); + // Is support for CSS vertical text enabled? pref("layout.css.vertical-text.enabled", true); // Is support for CSS text-combine-upright (tate-chu-yoko) enabled? -pref("layout.css.text-combine-upright.enabled", false); +pref("layout.css.text-combine-upright.enabled", true); +// Is support for CSS text-combine-upright: digits 2-4 enabled? +pref("layout.css.text-combine-upright-digits.enabled", false); // Is support for object-fit and object-position enabled? pref("layout.css.object-fit-and-position.enabled", true); @@ -4652,6 +4662,7 @@ pref("layers.low-precision-buffer", false); pref("layers.progressive-paint", false); pref("layers.tile-width", 256); pref("layers.tile-height", 256); +pref("layers.child-process-shutdown", true); // Max number of layers per container. See Overwrite in mobile prefs. pref("layers.max-active", -1); // If this is set the tile size will only be treated as a suggestion. @@ -4661,8 +4672,6 @@ pref("layers.max-active", -1); // if you change the tile size. pref("layers.tiles.adjust", true); -// Set the default values, and then override per-platform as needed -pref("layers.offmainthreadcomposition.enabled", true); // Compositor target frame rate. NOTE: If vsync is enabled the compositor // frame rate will still be capped. // -1 -> default (match layout.frame_rate or 60 FPS) @@ -4732,6 +4741,7 @@ pref("layers.prefer-opengl", false); pref("layers.prefer-d3d9", false); pref("layers.d3d11.force-warp", false); pref("layers.d3d11.disable-warp", false); + #endif // Force all possible layers to be always active layers @@ -5473,4 +5483,7 @@ pref("dom.input.fallbackUploadDir", ""); // Turn rewriting of youtube embeds on/off pref("plugins.rewrite_youtube_embeds", true); +// Is support for 'color-adjust' CSS property enabled? +pref("layout.css.color-adjust.enabled", true); + pref("devtools.serviceWorkers.testing.enabled", false); diff --git a/netwerk/base/LoadInfo.cpp b/netwerk/base/LoadInfo.cpp index c1830ca538..3db29a7753 100644 --- a/netwerk/base/LoadInfo.cpp +++ b/netwerk/base/LoadInfo.cpp @@ -114,6 +114,20 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal, aLoadingContext->OwnerDoc()->GetUpgradeInsecureRequests(true)); } + if (!(mSecurityFlags & nsILoadInfo::SEC_FORCE_PRIVATE_BROWSING)) { + if (aLoadingContext) { + nsCOMPtr loadContext = + aLoadingContext->OwnerDoc()->GetLoadContext(); + if (loadContext) { + bool usePrivateBrowsing; + nsresult rv = loadContext->GetUsePrivateBrowsing(&usePrivateBrowsing); + if (NS_SUCCEEDED(rv) && usePrivateBrowsing) { + mSecurityFlags |= nsILoadInfo::SEC_FORCE_PRIVATE_BROWSING; + } + } + } + } + mOriginAttributes = BasePrincipal::Cast(mLoadingPrincipal)->OriginAttributesRef(); } @@ -284,7 +298,7 @@ LoadInfo::CloneForNewRequest() const NS_IMETHODIMP LoadInfo::GetLoadingPrincipal(nsIPrincipal** aLoadingPrincipal) { - NS_ADDREF(*aLoadingPrincipal = mLoadingPrincipal); + NS_IF_ADDREF(*aLoadingPrincipal = mLoadingPrincipal); return NS_OK; } @@ -420,6 +434,14 @@ LoadInfo::GetDontFollowRedirects(bool* aResult) return NS_OK; } +NS_IMETHODIMP +LoadInfo::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing) +{ + *aUsePrivateBrowsing = (mSecurityFlags & + nsILoadInfo::SEC_FORCE_PRIVATE_BROWSING); + return NS_OK; +} + NS_IMETHODIMP LoadInfo::GetExternalContentPolicyType(nsContentPolicyType* aResult) { diff --git a/netwerk/base/Predictor.cpp b/netwerk/base/Predictor.cpp index caddcebfa1..210428d677 100644 --- a/netwerk/base/Predictor.cpp +++ b/netwerk/base/Predictor.cpp @@ -10,14 +10,18 @@ #include "nsAppDirectoryServiceDefs.h" #include "nsICacheStorage.h" #include "nsICacheStorageService.h" +#include "nsICachingChannel.h" #include "nsICancelable.h" #include "nsIChannel.h" #include "nsContentUtils.h" #include "nsIDNSService.h" #include "nsIDocument.h" #include "nsIFile.h" +#include "nsIHttpChannel.h" +#include "nsIInputStream.h" #include "nsIIOService.h" #include "nsILoadContext.h" +#include "nsILoadContextInfo.h" #include "nsILoadGroup.h" #include "nsINetworkPredictorVerifier.h" #include "nsIObserverService.h" @@ -28,6 +32,8 @@ #include "nsIURI.h" #include "nsNetUtil.h" #include "nsServiceManagerUtils.h" +#include "nsStreamUtils.h" +#include "nsString.h" #include "nsThreadUtils.h" #ifdef MOZ_NUWA_PROCESS #include "ipc/Nuwa.h" @@ -70,72 +76,94 @@ static LazyLogModule gPredictorLog("NetworkPredictor"); #define NOW_IN_SECONDS() static_cast(PR_Now() / PR_USEC_PER_SEC) -const char PREDICTOR_ENABLED_PREF[] = "network.predictor.enabled"; -const char PREDICTOR_SSL_HOVER_PREF[] = "network.predictor.enable-hover-on-ssl"; +static const char PREDICTOR_ENABLED_PREF[] = "network.predictor.enabled"; +static const char PREDICTOR_SSL_HOVER_PREF[] = "network.predictor.enable-hover-on-ssl"; +static const char PREDICTOR_PREFETCH_PREF[] = "network.predictor.enable-prefetch"; -const char PREDICTOR_PAGE_DELTA_DAY_PREF[] = +static const char PREDICTOR_PAGE_DELTA_DAY_PREF[] = "network.predictor.page-degradation.day"; -const int32_t PREDICTOR_PAGE_DELTA_DAY_DEFAULT = 0; -const char PREDICTOR_PAGE_DELTA_WEEK_PREF[] = +static const int32_t PREDICTOR_PAGE_DELTA_DAY_DEFAULT = 0; +static const char PREDICTOR_PAGE_DELTA_WEEK_PREF[] = "network.predictor.page-degradation.week"; -const int32_t PREDICTOR_PAGE_DELTA_WEEK_DEFAULT = 5; -const char PREDICTOR_PAGE_DELTA_MONTH_PREF[] = +static const int32_t PREDICTOR_PAGE_DELTA_WEEK_DEFAULT = 5; +static const char PREDICTOR_PAGE_DELTA_MONTH_PREF[] = "network.predictor.page-degradation.month"; -const int32_t PREDICTOR_PAGE_DELTA_MONTH_DEFAULT = 10; -const char PREDICTOR_PAGE_DELTA_YEAR_PREF[] = +static const int32_t PREDICTOR_PAGE_DELTA_MONTH_DEFAULT = 10; +static const char PREDICTOR_PAGE_DELTA_YEAR_PREF[] = "network.predictor.page-degradation.year"; -const int32_t PREDICTOR_PAGE_DELTA_YEAR_DEFAULT = 25; -const char PREDICTOR_PAGE_DELTA_MAX_PREF[] = +static const int32_t PREDICTOR_PAGE_DELTA_YEAR_DEFAULT = 25; +static const char PREDICTOR_PAGE_DELTA_MAX_PREF[] = "network.predictor.page-degradation.max"; -const int32_t PREDICTOR_PAGE_DELTA_MAX_DEFAULT = 50; -const char PREDICTOR_SUB_DELTA_DAY_PREF[] = +static const int32_t PREDICTOR_PAGE_DELTA_MAX_DEFAULT = 50; +static const char PREDICTOR_SUB_DELTA_DAY_PREF[] = "network.predictor.subresource-degradation.day"; -const int32_t PREDICTOR_SUB_DELTA_DAY_DEFAULT = 1; -const char PREDICTOR_SUB_DELTA_WEEK_PREF[] = +static const int32_t PREDICTOR_SUB_DELTA_DAY_DEFAULT = 1; +static const char PREDICTOR_SUB_DELTA_WEEK_PREF[] = "network.predictor.subresource-degradation.week"; -const int32_t PREDICTOR_SUB_DELTA_WEEK_DEFAULT = 10; -const char PREDICTOR_SUB_DELTA_MONTH_PREF[] = +static const int32_t PREDICTOR_SUB_DELTA_WEEK_DEFAULT = 10; +static const char PREDICTOR_SUB_DELTA_MONTH_PREF[] = "network.predictor.subresource-degradation.month"; -const int32_t PREDICTOR_SUB_DELTA_MONTH_DEFAULT = 25; -const char PREDICTOR_SUB_DELTA_YEAR_PREF[] = +static const int32_t PREDICTOR_SUB_DELTA_MONTH_DEFAULT = 25; +static const char PREDICTOR_SUB_DELTA_YEAR_PREF[] = "network.predictor.subresource-degradation.year"; -const int32_t PREDICTOR_SUB_DELTA_YEAR_DEFAULT = 50; -const char PREDICTOR_SUB_DELTA_MAX_PREF[] = +static const int32_t PREDICTOR_SUB_DELTA_YEAR_DEFAULT = 50; +static const char PREDICTOR_SUB_DELTA_MAX_PREF[] = "network.predictor.subresource-degradation.max"; -const int32_t PREDICTOR_SUB_DELTA_MAX_DEFAULT = 100; +static const int32_t PREDICTOR_SUB_DELTA_MAX_DEFAULT = 100; -const char PREDICTOR_PRECONNECT_MIN_PREF[] = +static const char PREDICTOR_PREFETCH_ROLLING_LOAD_PREF[] = + "network.predictor.prefetch-rolling-load-count"; +static const int32_t PREFETCH_ROLLING_LOAD_DEFAULT = 10; +static const char PREDICTOR_PREFETCH_MIN_PREF[] = + "network.predictor.prefetch-min-confidence"; +static const int32_t PREFETCH_MIN_DEFAULT = 100; +static const char PREDICTOR_PRECONNECT_MIN_PREF[] = "network.predictor.preconnect-min-confidence"; -const int32_t PRECONNECT_MIN_DEFAULT = 90; -const char PREDICTOR_PRERESOLVE_MIN_PREF[] = +static const int32_t PRECONNECT_MIN_DEFAULT = 90; +static const char PREDICTOR_PRERESOLVE_MIN_PREF[] = "network.predictor.preresolve-min-confidence"; -const int32_t PRERESOLVE_MIN_DEFAULT = 60; -const char PREDICTOR_REDIRECT_LIKELY_PREF[] = +static const int32_t PRERESOLVE_MIN_DEFAULT = 60; +static const char PREDICTOR_REDIRECT_LIKELY_PREF[] = "network.predictor.redirect-likely-confidence"; -const int32_t REDIRECT_LIKELY_DEFAULT = 75; +static const int32_t REDIRECT_LIKELY_DEFAULT = 75; -const char PREDICTOR_MAX_RESOURCES_PREF[] = +static const char PREDICTOR_PREFETCH_FORCE_VALID_PREF[] = + "network.predictor.prefetch-force-valid-for"; +static const int32_t PREFETCH_FORCE_VALID_DEFAULT = 10; + +static const char PREDICTOR_MAX_RESOURCES_PREF[] = "network.predictor.max-resources-per-entry"; -const uint32_t PREDICTOR_MAX_RESOURCES_DEFAULT = 100; +static const uint32_t PREDICTOR_MAX_RESOURCES_DEFAULT = 100; // This is selected in concert with max-resources-per-entry to keep memory usage // low-ish. The default of the combo of the two is ~50k -const char PREDICTOR_MAX_URI_LENGTH_PREF[] = +static const char PREDICTOR_MAX_URI_LENGTH_PREF[] = "network.predictor.max-uri-length"; -const uint32_t PREDICTOR_MAX_URI_LENGTH_DEFAULT = 500; +static const uint32_t PREDICTOR_MAX_URI_LENGTH_DEFAULT = 500; -const char PREDICTOR_CLEANED_UP_PREF[] = "network.predictor.cleaned-up"; +static const char PREDICTOR_CLEANED_UP_PREF[] = "network.predictor.cleaned-up"; // All these time values are in sec -const uint32_t ONE_DAY = 86400U; -const uint32_t ONE_WEEK = 7U * ONE_DAY; -const uint32_t ONE_MONTH = 30U * ONE_DAY; -const uint32_t ONE_YEAR = 365U * ONE_DAY; +static const uint32_t ONE_DAY = 86400U; +static const uint32_t ONE_WEEK = 7U * ONE_DAY; +static const uint32_t ONE_MONTH = 30U * ONE_DAY; +static const uint32_t ONE_YEAR = 365U * ONE_DAY; -const uint32_t STARTUP_WINDOW = 5U * 60U; // 5min +static const uint32_t STARTUP_WINDOW = 5U * 60U; // 5min // Version of metadata entries we expect -const uint32_t METADATA_VERSION = 1; +static const uint32_t METADATA_VERSION = 1; + +// Flags available in entries +// FLAG_PREFETCHABLE - we have determined that this item is eligible for prefetch +static const uint32_t FLAG_PREFETCHABLE = 1 << 0; + +// We save 12 bits in the "flags" section of our metadata for actual flags, the +// rest are to keep track of a rolling count of which loads a resource has been +// used on to determine if we can prefetch that resource or not; +static const uint8_t kRollingLoadOffset = 12; +static const int32_t kMaxPrefetchRollingLoadCount = 20; +static const uint32_t kFlagsMask = ((1 << kRollingLoadOffset) - 1); // ID Extensions for cache entries #define PREDICTOR_ORIGIN_EXTENSION "predictor-origin" @@ -293,6 +321,7 @@ Predictor::Predictor() :mInitialized(false) ,mEnabled(true) ,mEnableHoverOnSSL(false) + ,mEnablePrefetch(true) ,mPageDegradationDay(PREDICTOR_PAGE_DELTA_DAY_DEFAULT) ,mPageDegradationWeek(PREDICTOR_PAGE_DELTA_WEEK_DEFAULT) ,mPageDegradationMonth(PREDICTOR_PAGE_DELTA_MONTH_DEFAULT) @@ -303,9 +332,12 @@ Predictor::Predictor() ,mSubresourceDegradationMonth(PREDICTOR_SUB_DELTA_MONTH_DEFAULT) ,mSubresourceDegradationYear(PREDICTOR_SUB_DELTA_YEAR_DEFAULT) ,mSubresourceDegradationMax(PREDICTOR_SUB_DELTA_MAX_DEFAULT) + ,mPrefetchRollingLoadCount(PREFETCH_ROLLING_LOAD_DEFAULT) + ,mPrefetchMinConfidence(PREFETCH_MIN_DEFAULT) ,mPreconnectMinConfidence(PRECONNECT_MIN_DEFAULT) ,mPreresolveMinConfidence(PRERESOLVE_MIN_DEFAULT) ,mRedirectLikelyConfidence(REDIRECT_LIKELY_DEFAULT) + ,mPrefetchForceValidFor(PREFETCH_FORCE_VALID_DEFAULT) ,mMaxResourcesPerEntry(PREDICTOR_MAX_RESOURCES_DEFAULT) ,mStartupCount(1) ,mMaxURILength(PREDICTOR_MAX_URI_LENGTH_DEFAULT) @@ -347,6 +379,7 @@ Predictor::InstallObserver() Preferences::AddBoolVarCache(&mEnabled, PREDICTOR_ENABLED_PREF, true); Preferences::AddBoolVarCache(&mEnableHoverOnSSL, PREDICTOR_SSL_HOVER_PREF, false); + Preferences::AddBoolVarCache(&mEnablePrefetch, PREDICTOR_PREFETCH_PREF, true); Preferences::AddIntVarCache(&mPageDegradationDay, PREDICTOR_PAGE_DELTA_DAY_PREF, PREDICTOR_PAGE_DELTA_DAY_DEFAULT); @@ -379,6 +412,12 @@ Predictor::InstallObserver() PREDICTOR_SUB_DELTA_MAX_PREF, PREDICTOR_SUB_DELTA_MAX_DEFAULT); + Preferences::AddIntVarCache(&mPrefetchRollingLoadCount, + PREDICTOR_PREFETCH_ROLLING_LOAD_PREF, + PREFETCH_ROLLING_LOAD_DEFAULT); + Preferences::AddIntVarCache(&mPrefetchMinConfidence, + PREDICTOR_PREFETCH_MIN_PREF, + PREFETCH_MIN_DEFAULT); Preferences::AddIntVarCache(&mPreconnectMinConfidence, PREDICTOR_PRECONNECT_MIN_PREF, PRECONNECT_MIN_DEFAULT); @@ -389,6 +428,10 @@ Predictor::InstallObserver() PREDICTOR_REDIRECT_LIKELY_PREF, REDIRECT_LIKELY_DEFAULT); + Preferences::AddIntVarCache(&mPrefetchForceValidFor, + PREDICTOR_PREFETCH_FORCE_VALID_PREF, + PREFETCH_FORCE_VALID_DEFAULT); + Preferences::AddIntVarCache(&mMaxResourcesPerEntry, PREDICTOR_MAX_RESOURCES_PREF, PREDICTOR_MAX_RESOURCES_DEFAULT); @@ -931,7 +974,7 @@ Predictor::PredictInternal(PredictorPredictReason reason, nsICacheEntry *entry, switch (reason) { case nsINetworkPredictor::PREDICT_LOAD: - rv = PredictForPageload(entry, stackCount, verifier); + rv = PredictForPageload(entry, targetURI, stackCount, verifier); break; case nsINetworkPredictor::PREDICT_STARTUP: rv = PredictForStartup(entry, verifier); @@ -974,9 +1017,10 @@ Predictor::PredictForLink(nsIURI *targetURI, nsIURI *sourceURI, } // This is the driver for prediction based on a new pageload. -const uint8_t MAX_PAGELOAD_DEPTH = 10; +static const uint8_t MAX_PAGELOAD_DEPTH = 10; bool -Predictor::PredictForPageload(nsICacheEntry *entry, uint8_t stackCount, +Predictor::PredictForPageload(nsICacheEntry *entry, nsIURI *targetURI, + uint8_t stackCount, nsINetworkPredictorVerifier *verifier) { MOZ_ASSERT(NS_IsMainThread()); @@ -993,6 +1037,7 @@ Predictor::PredictForPageload(nsICacheEntry *entry, uint8_t stackCount, NS_ENSURE_SUCCESS(rv, false); int32_t globalDegradation = CalculateGlobalDegradation(lastLoad); + PREDICTOR_LOG((" globalDegradation = %d", globalDegradation)); int32_t loadCount; rv = entry->GetFetchCount(&loadCount); @@ -1018,12 +1063,12 @@ Predictor::PredictForPageload(nsICacheEntry *entry, uint8_t stackCount, nsICacheStorage::CHECK_MULTITHREADED; mCacheDiskStorage->AsyncOpenURI(redirectURI, EmptyCString(), openFlags, redirectAction); - return RunPredictions(verifier); + return RunPredictions(nullptr, verifier); } - CalculatePredictions(entry, lastLoad, loadCount, globalDegradation); + CalculatePredictions(entry, targetURI, lastLoad, loadCount, globalDegradation); - return RunPredictions(verifier); + return RunPredictions(targetURI, verifier); } // This is the driver for predicting at browser startup time based on pages that @@ -1036,9 +1081,9 @@ Predictor::PredictForStartup(nsICacheEntry *entry, PREDICTOR_LOG(("Predictor::PredictForStartup")); int32_t globalDegradation = CalculateGlobalDegradation(mLastStartupTime); - CalculatePredictions(entry, mLastStartupTime, mStartupCount, + CalculatePredictions(entry, nullptr, mLastStartupTime, mStartupCount, globalDegradation); - return RunPredictions(verifier); + return RunPredictions(nullptr, verifier); } // This calculates how much to degrade our confidence in our data based on @@ -1129,12 +1174,64 @@ Predictor::CalculateConfidence(uint32_t hitCount, uint32_t hitsPossible, return confidence; } +static void +MakeMetadataEntry(const uint32_t hitCount, const uint32_t lastHit, + const uint32_t flags, nsCString &newValue) +{ + newValue.Truncate(); + newValue.AppendInt(METADATA_VERSION); + newValue.Append(','); + newValue.AppendInt(hitCount); + newValue.Append(','); + newValue.AppendInt(lastHit); + newValue.Append(','); + newValue.AppendInt(flags); +} + +// On every page load, the rolling window gets shifted by one bit, leaving the +// lowest bit at 0, to indicate that the subresource in question has not been +// seen on the most recent page load. If, at some point later during the page load, +// the subresource is seen again, we will then set the lowest bit to 1. This is +// how we keep track of how many of the last n pageloads (for n <= 20) a particular +// subresource has been seen. +// The rolling window is kept in the upper 20 bits of the flags element of the +// metadata. This saves 12 bits for regular old flags. void -Predictor::CalculatePredictions(nsICacheEntry *entry, uint32_t lastLoad, - uint32_t loadCount, int32_t globalDegradation) +Predictor::UpdateRollingLoadCount(nsICacheEntry *entry, const uint32_t flags, + const char *key, const uint32_t hitCount, + const uint32_t lastHit) +{ + // Extract just the rolling load count from the flags, shift it to clear the + // lowest bit, and put the new value with the existing flags. + uint32_t rollingLoadCount = flags & ~kFlagsMask; + rollingLoadCount <<= 1; + uint32_t newFlags = (flags & kFlagsMask) | rollingLoadCount; + + // Finally, update the metadata on the cache entry. + nsAutoCString newValue; + MakeMetadataEntry(hitCount, lastHit, newFlags, newValue); + entry->SetMetaDataElement(key, newValue.BeginReading()); +} + +void +Predictor::SanitizePrefs() +{ + if (mPrefetchRollingLoadCount < 0) { + mPrefetchRollingLoadCount = 0; + } else if (mPrefetchRollingLoadCount > kMaxPrefetchRollingLoadCount) { + mPrefetchRollingLoadCount = kMaxPrefetchRollingLoadCount; + } +} + +void +Predictor::CalculatePredictions(nsICacheEntry *entry, nsIURI *referrer, + uint32_t lastLoad, uint32_t loadCount, + int32_t globalDegradation) { MOZ_ASSERT(NS_IsMainThread()); + SanitizePrefs(); + // Since the visitor gets called under a cache lock, all we do there is get // copies of the keys/values we care about, and then do the real work here entry->VisitMetaData(this); @@ -1157,27 +1254,94 @@ Predictor::CalculatePredictions(nsICacheEntry *entry, uint32_t lastLoad, int32_t confidence = CalculateConfidence(hitCount, loadCount, lastHit, lastLoad, globalDegradation); - SetupPrediction(confidence, uri); + UpdateRollingLoadCount(entry, flags, key, hitCount, lastHit); + PREDICTOR_LOG(("CalculatePredictions key=%s value=%s confidence=%d", key, value, confidence)); + if (!referrer) { + // No referrer means we can't prefetch, so pretend it's non-cacheable, + // no matter what. + PREDICTOR_LOG((" forcing non-cacheability - no referrer")); + flags &= ~FLAG_PREFETCHABLE; + } else { + uint32_t expectedRollingLoadCount = (1 << mPrefetchRollingLoadCount) - 1; + expectedRollingLoadCount <<= kRollingLoadOffset; + if ((flags & expectedRollingLoadCount) != expectedRollingLoadCount) { + PREDICTOR_LOG((" forcing non-cacheability - missed a load")); + flags &= ~FLAG_PREFETCHABLE; + } + } + + PREDICTOR_LOG((" setting up prediction")); + SetupPrediction(confidence, flags, uri); } } // (Maybe) adds a predictive action to the prediction runner, based on our // calculated confidence for the subresource in question. void -Predictor::SetupPrediction(int32_t confidence, nsIURI *uri) +Predictor::SetupPrediction(int32_t confidence, uint32_t flags, nsIURI *uri) { MOZ_ASSERT(NS_IsMainThread()); - if (confidence >= mPreconnectMinConfidence) { + nsAutoCString uriStr; + uri->GetAsciiSpec(uriStr); + PREDICTOR_LOG(("SetupPrediction mEnablePrefetch=%d mPrefetchMinConfidence=%d " + "mPreconnectMinConfidence=%d mPreresolveMinConfidence=%d " + "flags=%d confidence=%d uri=%s", mEnablePrefetch, + mPrefetchMinConfidence, mPreconnectMinConfidence, + mPreresolveMinConfidence, flags, confidence, uriStr.get())); + if (mEnablePrefetch && (flags & FLAG_PREFETCHABLE) && + (mPrefetchRollingLoadCount || (confidence >= mPrefetchMinConfidence))) { + mPrefetches.AppendElement(uri); + } else if (confidence >= mPreconnectMinConfidence) { mPreconnects.AppendElement(uri); } else if (confidence >= mPreresolveMinConfidence) { mPreresolves.AppendElement(uri); } } +nsresult +Predictor::Prefetch(nsIURI *uri, nsIURI *referrer, + nsINetworkPredictorVerifier *verifier) +{ + nsAutoCString strUri, strReferrer; + uri->GetAsciiSpec(strUri); + referrer->GetAsciiSpec(strReferrer); + PREDICTOR_LOG(("Predictor::Prefetch uri=%s referrer=%s verifier=%p", + strUri.get(), strReferrer.get(), verifier)); + nsCOMPtr channel; + nsresult rv = NS_NewChannelInternal(getter_AddRefs(channel), uri, nullptr, + nullptr, nullptr, + nsIRequest::LOAD_BACKGROUND); + if (NS_FAILED(rv)) { + PREDICTOR_LOG((" NS_NewChannelInternal failed rv=0x%X", rv)); + return rv; + } + + nsCOMPtr httpChannel; + httpChannel = do_QueryInterface(channel); + if (!httpChannel) { + PREDICTOR_LOG((" Could not get HTTP Channel from new channel!")); + return NS_ERROR_UNEXPECTED; + } + + httpChannel->SetReferrer(referrer); + // XXX - set a header here to indicate this is a prefetch? + + nsCOMPtr listener = new PrefetchListener(verifier, uri, + this); + PREDICTOR_LOG((" calling AsyncOpen listener=%p channel=%p", listener.get(), + channel.get())); + rv = channel->AsyncOpen(listener, nullptr); + if (NS_FAILED(rv)) { + PREDICTOR_LOG((" AsyncOpen failed rv=0x%X", rv)); + } + + return rv; +} + // Runs predictions that have been set up. bool -Predictor::RunPredictions(nsINetworkPredictorVerifier *verifier) +Predictor::RunPredictions(nsIURI *referrer, nsINetworkPredictorVerifier *verifier) { MOZ_ASSERT(NS_IsMainThread(), "Running prediction off main thread"); @@ -1430,10 +1594,41 @@ Predictor::LearnInternal(PredictorLearnReason reason, nsICacheEntry *entry, switch (reason) { case nsINetworkPredictor::LEARN_LOAD_TOPLEVEL: - // This actually has no work associated with it, since all we need to do - // is update the timestamps and fetch count, and that's done for us by - // opening the cache entry. - PREDICTOR_LOG((" nothing to do for toplevel")); + // This case only exists to be used during tests - code outside the + // predictor tests should NEVER call Learn with LEARN_LOAD_TOPLEVEL. + // The predictor xpcshell test needs this branch, however, because we + // have no real page loads in xpcshell, and this is how we fake it up + // so that all the work that normally happens behind the scenes in a + // page load can be done for testing purposes. + if (fullUri) { + PREDICTOR_LOG((" WARNING - updating rolling load count. " + "If you see this outside tests, you did it wrong")); + SanitizePrefs(); + + // Since the visitor gets called under a cache lock, all we do there is get + // copies of the keys/values we care about, and then do the real work here + entry->VisitMetaData(this); + nsTArray keysToOperateOn, valuesToOperateOn; + keysToOperateOn.SwapElements(mKeysToOperateOn); + valuesToOperateOn.SwapElements(mValuesToOperateOn); + + MOZ_ASSERT(keysToOperateOn.Length() == valuesToOperateOn.Length()); + for (size_t i = 0; i < keysToOperateOn.Length(); ++i) { + const char *key = keysToOperateOn[i].BeginReading(); + const char *value = valuesToOperateOn[i].BeginReading(); + + nsCOMPtr uri; + uint32_t hitCount, lastHit, flags; + if (!ParseMetaDataEntry(key, value, getter_AddRefs(uri), hitCount, lastHit, flags)) { + // This failed, get rid of it so we don't waste space + entry->SetMetaDataElement(key, nullptr); + continue; + } + UpdateRollingLoadCount(entry, flags, key, hitCount, lastHit); + } + } else { + PREDICTOR_LOG((" nothing to do for toplevel")); + } break; case nsINetworkPredictor::LEARN_LOAD_REDIRECT: if (fullUri) { @@ -1575,21 +1770,19 @@ Predictor::LearnForSubresource(nsICacheEntry *entry, nsIURI *targetURI) return; } hitCount = 1; + flags = 0; } else { PREDICTOR_LOG((" existing resource")); hitCount = std::min(hitCount + 1, static_cast(loadCount)); } + // Update the rolling load count to mark this sub-resource as seen on the + // most-recent pageload so it can be eligible for prefetch (assuming all + // the other stars align). + flags |= (1 << kRollingLoadOffset); + nsCString newValue; - newValue.AppendInt(METADATA_VERSION); - newValue.AppendLiteral(","); - newValue.AppendInt(hitCount); - newValue.AppendLiteral(","); - newValue.AppendInt(lastLoad); - // These are for flags, that will be used for prefetch and possibly other - // things later on - newValue.AppendLiteral(","); - newValue.AppendInt(0); + MakeMetadataEntry(hitCount, lastLoad, flags, newValue); rv = entry->SetMetaDataElement(key.BeginReading(), newValue.BeginReading()); PREDICTOR_LOG((" SetMetaDataElement -> 0x%08X", rv)); if (NS_FAILED(rv) && isNewResource) { @@ -2027,8 +2220,28 @@ PredictorLearnRedirect(nsIURI *targetURI, nsIChannel *channel, // nsINetworkPredictorVerifier /** - * Call through to the child's verifier (only during tests). + * Call through to the child's verifier (only during tests) */ +NS_IMETHODIMP +Predictor::OnPredictPrefetch(nsIURI *aURI, uint32_t httpStatus) +{ + if (IsNeckoChild()) { + MOZ_DIAGNOSTIC_ASSERT(mChildVerifier); + return mChildVerifier->OnPredictPrefetch(aURI, httpStatus); + } + + MOZ_DIAGNOSTIC_ASSERT(gNeckoParent); + + ipc::URIParams serURI; + SerializeURI(aURI, serURI); + + if (!gNeckoParent->SendPredOnPredictPrefetch(serURI, httpStatus)) { + return NS_ERROR_NOT_AVAILABLE; + } + + return NS_OK; +} + NS_IMETHODIMP Predictor::OnPredictPreconnect(nsIURI *aURI) { if (IsNeckoChild()) { @@ -2048,9 +2261,6 @@ Predictor::OnPredictPreconnect(nsIURI *aURI) { return NS_OK; } -/** - * Call through to the child's verifier (only during tests) - */ NS_IMETHODIMP Predictor::OnPredictDNS(nsIURI *aURI) { if (IsNeckoChild()) { @@ -2070,5 +2280,214 @@ Predictor::OnPredictDNS(nsIURI *aURI) { return NS_OK; } +// Predictor::PrefetchListener +// nsISupports +NS_IMPL_ISUPPORTS(Predictor::PrefetchListener, + nsIStreamListener, + nsIRequestObserver) + +// nsIRequestObserver +NS_IMETHODIMP +Predictor::PrefetchListener::OnStartRequest(nsIRequest *aRequest, + nsISupports *aContext) +{ + mStartTime = TimeStamp::Now(); + return NS_OK; +} + +NS_IMETHODIMP +Predictor::PrefetchListener::OnStopRequest(nsIRequest *aRequest, + nsISupports *aContext, + nsresult aStatusCode) +{ + PREDICTOR_LOG(("OnStopRequest this=%p aStatusCode=0x%X", this, aStatusCode)); + NS_ENSURE_ARG(aRequest); + if (NS_FAILED(aStatusCode)) { + return aStatusCode; + } + Telemetry::AccumulateTimeDelta(Telemetry::PREDICTOR_PREFETCH_TIME, mStartTime); + + nsCOMPtr httpChannel = do_QueryInterface(aRequest); + if (!httpChannel) { + PREDICTOR_LOG((" Could not get HTTP Channel!")); + return NS_ERROR_UNEXPECTED; + } + nsCOMPtr cachingChannel = do_QueryInterface(httpChannel); + if (!cachingChannel) { + PREDICTOR_LOG((" Could not get caching channel!")); + return NS_ERROR_UNEXPECTED; + } + + nsresult rv = NS_OK; + uint32_t httpStatus; + rv = httpChannel->GetResponseStatus(&httpStatus); + if (NS_SUCCEEDED(rv) && httpStatus == 200) { + rv = cachingChannel->ForceCacheEntryValidFor(mPredictor->mPrefetchForceValidFor); + PREDICTOR_LOG((" forcing entry valid for %d seconds rv=%X", + mPredictor->mPrefetchForceValidFor, rv)); + } else { + rv = cachingChannel->ForceCacheEntryValidFor(0); + PREDICTOR_LOG((" removing any forced validity rv=%X", rv)); + } + + nsAutoCString reqName; + rv = aRequest->GetName(reqName); + if (NS_FAILED(rv)) { + reqName.AssignLiteral(""); + } + + PREDICTOR_LOG((" request %s status %u", reqName.get(), httpStatus)); + + if (mVerifier) { + mVerifier->OnPredictPrefetch(mURI, httpStatus); + } + + return rv; +} + +// nsIStreamListener +NS_IMETHODIMP +Predictor::PrefetchListener::OnDataAvailable(nsIRequest *aRequest, + nsISupports *aContext, + nsIInputStream *aInputStream, + uint64_t aOffset, + const uint32_t aCount) +{ + uint32_t result; + return aInputStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, &result); +} + +// Miscellaneous Predictor + +void +Predictor::UpdateCacheability(nsIURI *sourceURI, nsIURI *targetURI, + uint32_t httpStatus, + nsHttpRequestHead &requestHead, + nsHttpResponseHead *responseHead, + nsILoadContextInfo *lci) +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (lci && lci->IsPrivate()) { + PREDICTOR_LOG(("Predictor::UpdateCacheability in PB mode - ignoring")); + return; + } + + RefPtr self = sSelf; + if (self) { + const nsCString method = requestHead.Method(); + self->UpdateCacheabilityInternal(sourceURI, targetURI, httpStatus, + method); + } +} + +void +Predictor::UpdateCacheabilityInternal(nsIURI *sourceURI, nsIURI *targetURI, + uint32_t httpStatus, + const nsCString &method) +{ + PREDICTOR_LOG(("Predictor::UpdateCacheability httpStatus=%u", httpStatus)); + uint32_t openFlags = nsICacheStorage::OPEN_READONLY | + nsICacheStorage::OPEN_SECRETLY | + nsICacheStorage::CHECK_MULTITHREADED; + RefPtr action = + new Predictor::CacheabilityAction(targetURI, httpStatus, method, this); + nsAutoCString uri; + targetURI->GetAsciiSpec(uri); + PREDICTOR_LOG((" uri=%s action=%p", uri.get(), action.get())); + mCacheDiskStorage->AsyncOpenURI(sourceURI, EmptyCString(), openFlags, action); +} + +NS_IMPL_ISUPPORTS(Predictor::CacheabilityAction, + nsICacheEntryOpenCallback, + nsICacheEntryMetaDataVisitor); + +NS_IMETHODIMP +Predictor::CacheabilityAction::OnCacheEntryCheck(nsICacheEntry *entry, + nsIApplicationCache *appCache, + uint32_t *result) +{ + *result = nsICacheEntryOpenCallback::ENTRY_WANTED; + return NS_OK; +} + +NS_IMETHODIMP +Predictor::CacheabilityAction::OnCacheEntryAvailable(nsICacheEntry *entry, + bool isNew, + nsIApplicationCache *appCache, + nsresult result) +{ + MOZ_ASSERT(NS_IsMainThread()); + // This is being opened read-only, so isNew should always be false + MOZ_ASSERT(!isNew); + + PREDICTOR_LOG(("CacheabilityAction::OnCacheEntryAvailable this=%p", this)); + if (NS_FAILED(result)) { + // Nothing to do + PREDICTOR_LOG((" nothing to do result=%X isNew=%d", result, isNew)); + return NS_OK; + } + + nsresult rv = entry->VisitMetaData(this); + if (NS_FAILED(rv)) { + PREDICTOR_LOG((" VisitMetaData returned %x", rv)); + return NS_OK; + } + + nsTArray keysToCheck, valuesToCheck; + keysToCheck.SwapElements(mKeysToCheck); + valuesToCheck.SwapElements(mValuesToCheck); + + MOZ_ASSERT(keysToCheck.Length() == valuesToCheck.Length()); + for (size_t i = 0; i < keysToCheck.Length(); ++i) { + const char *key = keysToCheck[i].BeginReading(); + const char *value = valuesToCheck[i].BeginReading(); + nsCOMPtr uri; + uint32_t hitCount, lastHit, flags; + + if (!mPredictor->ParseMetaDataEntry(key, value, getter_AddRefs(uri), + hitCount, lastHit, flags)) { + PREDICTOR_LOG((" failed to parse key=%s value=%s", key, value)); + continue; + } + + bool eq = false; + if (NS_SUCCEEDED(uri->Equals(mTargetURI, &eq)) && eq) { + if (mHttpStatus == 200 && mMethod.EqualsLiteral("GET")) { + PREDICTOR_LOG((" marking %s cacheable", key)); + flags |= FLAG_PREFETCHABLE; + } else { + PREDICTOR_LOG((" marking %s uncacheable", key)); + flags &= ~FLAG_PREFETCHABLE; + } + nsCString newValue; + MakeMetadataEntry(hitCount, lastHit, flags, newValue); + entry->SetMetaDataElement(key, newValue.BeginReading()); + break; + } + } + + return NS_OK; +} + +NS_IMETHODIMP +Predictor::CacheabilityAction::OnMetaDataElement(const char *asciiKey, + const char *asciiValue) +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (!IsURIMetadataElement(asciiKey)) { + return NS_OK; + } + + nsCString key, value; + key.AssignASCII(asciiKey); + value.AssignASCII(asciiValue); + mKeysToCheck.AppendElement(key); + mValuesToCheck.AppendElement(value); + + return NS_OK; +} + } // namespace net } // namespace mozilla diff --git a/netwerk/base/Predictor.h b/netwerk/base/Predictor.h index aa2f679cb7..7f5c27a559 100644 --- a/netwerk/base/Predictor.h +++ b/netwerk/base/Predictor.h @@ -17,6 +17,7 @@ #include "nsIInterfaceRequestor.h" #include "nsIObserver.h" #include "nsISpeculativeConnect.h" +#include "nsIStreamListener.h" #include "mozilla/RefPtr.h" #include "nsString.h" #include "nsTArray.h" @@ -26,11 +27,15 @@ class nsICacheStorage; class nsIDNSService; class nsIIOService; +class nsILoadContextInfo; class nsITimer; namespace mozilla { namespace net { +class nsHttpRequestHead; +class nsHttpResponseHead; + class Predictor : public nsINetworkPredictor , public nsIObserver , public nsISpeculativeConnectionOverrider @@ -53,6 +58,15 @@ public: void Shutdown(); static nsresult Create(nsISupports *outer, const nsIID& iid, void **result); + // Used to update whether a particular URI was cacheable or not. + // sourceURI and targetURI are the same as the arguments to Learn + // and httpStatus is the status code we got while loading targetURI. + static void UpdateCacheability(nsIURI *sourceURI, nsIURI *targetURI, + uint32_t httpStatus, + nsHttpRequestHead &requestHead, + nsHttpResponseHead *reqponseHead, + nsILoadContextInfo *lci); + private: virtual ~Predictor(); @@ -115,6 +129,33 @@ private: RefPtr mPredictor; }; + class CacheabilityAction : public nsICacheEntryOpenCallback + , public nsICacheEntryMetaDataVisitor + { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSICACHEENTRYOPENCALLBACK + NS_DECL_NSICACHEENTRYMETADATAVISITOR + + CacheabilityAction(nsIURI *targetURI, uint32_t httpStatus, + const nsCString &method, Predictor *predictor) + :mTargetURI(targetURI) + ,mHttpStatus(httpStatus) + ,mMethod(method) + ,mPredictor(predictor) + { } + + private: + virtual ~CacheabilityAction() { } + + nsCOMPtr mTargetURI; + uint32_t mHttpStatus; + nsCString mMethod; + RefPtr mPredictor; + nsTArray mKeysToCheck; + nsTArray mValuesToCheck; + }; + class Resetter : public nsICacheEntryOpenCallback, public nsICacheEntryMetaDataVisitor, public nsICacheStorageVisitor @@ -160,6 +201,29 @@ private: RefPtr mPredictor; }; + class PrefetchListener : public nsIStreamListener + { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSISTREAMLISTENER + + PrefetchListener(nsINetworkPredictorVerifier *verifier, nsIURI *uri, + Predictor *predictor) + :mVerifier(verifier) + ,mURI(uri) + ,mPredictor(predictor) + { } + + private: + virtual ~PrefetchListener() { } + + nsCOMPtr mVerifier; + nsCOMPtr mURI; + RefPtr mPredictor; + TimeStamp mStartTime; + }; + // Observer-related stuff nsresult InstallObserver(); void RemoveObserver(); @@ -199,6 +263,7 @@ private: // being the target of a redirect). All arguments are the same as for // PredictInternal. Returns true if any predictions were queued up. bool PredictForPageload(nsICacheEntry *entry, + nsIURI *targetURI, uint8_t stackCount, nsINetworkPredictorVerifier *verifier); @@ -210,6 +275,18 @@ private: // Utilities related to prediction + // Used to update our rolling load count (how many of the last n loads was a + // partular resource loaded on?) + // * entry - cache entry of page we're loading + // * flags - value that contains our rolling count as the top 20 bits (but + // we may use fewer than those 20 bits for calculations) + // * key - metadata key that we will update on entry + // * hitCount - part of the metadata we need to preserve + // * lastHit - part of the metadata we need to preserve + void UpdateRollingLoadCount(nsICacheEntry *entry, const uint32_t flags, + const char *key, const uint32_t hitCount, + const uint32_t lastHit); + // Used to calculate how much to degrade our confidence for all resources // on a particular page, because of how long ago the most recent load of that // page was. Returns a value between 0 (very recent most recent load) and 100 @@ -234,22 +311,32 @@ private: // Used to calculate all confidence values for all resources associated with a // page. // * entry - the cache entry with all necessary information about this page + // * referrer - the URI that we are loading (may be null) // * lastLoad - timestamp of the last time this page was loaded // * loadCount - number of times this page has been loaded // * gloablDegradation - value calculated by CalculateGlobalDegradation for // this page - void CalculatePredictions(nsICacheEntry *entry, uint32_t lastLoad, - uint32_t loadCount, int32_t globalDegradation); + void CalculatePredictions(nsICacheEntry *entry, nsIURI *referrer, + uint32_t lastLoad, uint32_t loadCount, + int32_t globalDegradation); // Used to prepare any necessary prediction for a resource on a page // * confidence - value calculated by CalculateConfidence for this resource + // * flags - the flags taken from the resource // * uri - the URI of the resource - void SetupPrediction(int32_t confidence, nsIURI *uri); + void SetupPrediction(int32_t confidence, uint32_t flags, nsIURI *uri); + + // Used to kick off a prefetch from RunPredictions if necessary + // * uri - the URI to prefetch + // * referrer - the URI of the referring page + // * verifier - used for testing to ensure the expected prefetch happens + nsresult Prefetch(nsIURI *uri, nsIURI *referrer, nsINetworkPredictorVerifier *verifier); // Used to actually perform any predictions set up via SetupPrediction. // Returns true if any predictions were performed. + // * referrer - the URI we are predicting from // * verifier - used for testing to ensure the expected predictions happen - bool RunPredictions(nsINetworkPredictorVerifier *verifier); + bool RunPredictions(nsIURI *referrer, nsINetworkPredictorVerifier *verifier); // Used to guess whether a page will redirect to another page or not. Returns // true if a redirection is likely. @@ -315,11 +402,21 @@ private: uint32_t &hitCount, uint32_t &lastHit, uint32_t &flags); + // Used to update whether a particular URI was cacheable or not. + // sourceURI and targetURI are the same as the arguments to Learn + // and httpStatus is the status code we got while loading targetURI. + void UpdateCacheabilityInternal(nsIURI *sourceURI, nsIURI *targetURI, + uint32_t httpStatus, const nsCString &method); + + // Make sure our prefs are in their expected range of values + void SanitizePrefs(); + // Our state bool mInitialized; bool mEnabled; bool mEnableHoverOnSSL; + bool mEnablePrefetch; int32_t mPageDegradationDay; int32_t mPageDegradationWeek; @@ -333,10 +430,14 @@ private: int32_t mSubresourceDegradationYear; int32_t mSubresourceDegradationMax; + int32_t mPrefetchRollingLoadCount; + int32_t mPrefetchMinConfidence; int32_t mPreconnectMinConfidence; int32_t mPreresolveMinConfidence; int32_t mRedirectLikelyConfidence; + int32_t mPrefetchForceValidFor; + int32_t mMaxResourcesPerEntry; bool mCleanedUp; @@ -361,6 +462,7 @@ private: RefPtr mDNSListener; + nsTArray> mPrefetches; nsTArray> mPreconnects; nsTArray> mPreresolves; diff --git a/netwerk/base/moz.build b/netwerk/base/moz.build index 78569da23b..846473503d 100644 --- a/netwerk/base/moz.build +++ b/netwerk/base/moz.build @@ -181,6 +181,7 @@ EXPORTS.mozilla.net += [ 'DashboardTypes.h', 'MemoryDownloader.h', 'OfflineObserver.h', + 'Predictor.h', 'ReferrerPolicy.h', ] diff --git a/netwerk/base/nsBaseChannel.cpp b/netwerk/base/nsBaseChannel.cpp index ef30feb417..08525dbbb9 100644 --- a/netwerk/base/nsBaseChannel.cpp +++ b/netwerk/base/nsBaseChannel.cpp @@ -653,6 +653,8 @@ nsBaseChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt) NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); NS_ENSURE_ARG(listener); + NS_CompareLoadInfoAndLoadContext(this); + // Ensure that this is an allowed port before proceeding. nsresult rv = NS_CheckPortSafety(mURI); if (NS_FAILED(rv)) { diff --git a/netwerk/base/nsICachingChannel.idl b/netwerk/base/nsICachingChannel.idl index c0e1abbbd3..63f65b1a44 100644 --- a/netwerk/base/nsICachingChannel.idl +++ b/netwerk/base/nsICachingChannel.idl @@ -58,6 +58,14 @@ interface nsICachingChannel : nsICacheInfoChannel */ attribute boolean pin; + /** + * Overrides cache validation for a time specified in seconds. + * + * @param aSecondsToTheFuture + * + */ + void forceCacheEntryValidFor(in unsigned long aSecondsToTheFuture); + /************************************************************************** * Caching channel specific load flags: */ diff --git a/netwerk/base/nsILoadInfo.idl b/netwerk/base/nsILoadInfo.idl index f108892e31..e5bcf0b25c 100644 --- a/netwerk/base/nsILoadInfo.idl +++ b/netwerk/base/nsILoadInfo.idl @@ -157,6 +157,15 @@ interface nsILoadInfo : nsISupports */ const unsigned long SEC_DONT_FOLLOW_REDIRECTS = (1<<11); + /** + * Force private browsing. Setting this flag the private browsing can be + * enforce even when a loading is not happening in the context of a document. + * + * If the flag is true, even if a document context is present, + * GetUsePrivateBrowsing will always return true. + */ + const unsigned long SEC_FORCE_PRIVATE_BROWSING = (1<<12); + /** * The loadingPrincipal is the principal that is responsible for the load. * It is *NOT* the principal tied to the resource/URI that this @@ -282,6 +291,11 @@ interface nsILoadInfo : nsISupports */ [infallible] readonly attribute boolean aboutBlankInherits; + /** + * If usePrivateBrowsing is true, private browsing will be used. + */ + [infallible] readonly attribute boolean usePrivateBrowsing; + /** * If allowChrome is true, then use nsIScriptSecurityManager::ALLOW_CHROME * when calling CheckLoadURIWithPrincipal(). diff --git a/netwerk/base/nsINetworkPredictorVerifier.idl b/netwerk/base/nsINetworkPredictorVerifier.idl index f38027e079..b00aecc076 100644 --- a/netwerk/base/nsINetworkPredictorVerifier.idl +++ b/netwerk/base/nsINetworkPredictorVerifier.idl @@ -12,9 +12,18 @@ interface nsIURI; -[scriptable, uuid(00360c7d-a046-4f8d-a1fc-8bdc0f0fb444)] +[scriptable, uuid(2e43bb32-dabf-4494-9f90-2b3195b1c73d)] interface nsINetworkPredictorVerifier : nsISupports { + /** + * Callback for when we do a predictive prefetch + * + * @param uri - The URI that was prefetched + * @param status - The request status code returned by the + * prefetch attempt e.g. 200 (OK):w + */ + void onPredictPrefetch(in nsIURI uri, in uint32_t status); + /** * Callback for when we do a predictive preconnect * diff --git a/netwerk/base/nsIRequest.idl b/netwerk/base/nsIRequest.idl index 24cad5af15..459c01e44d 100644 --- a/netwerk/base/nsIRequest.idl +++ b/netwerk/base/nsIRequest.idl @@ -143,8 +143,7 @@ interface nsIRequest : nsISupports /** * This flag prevents caching on disk (or other persistent media), which - * may be needed to preserve privacy. For HTTPS, this flag is set auto- - * matically. + * may be needed to preserve privacy. */ const unsigned long INHIBIT_PERSISTENT_CACHING = 1 << 8; diff --git a/netwerk/base/nsNetUtil.cpp b/netwerk/base/nsNetUtil.cpp index 313f4765d2..08cfe9c533 100644 --- a/netwerk/base/nsNetUtil.cpp +++ b/netwerk/base/nsNetUtil.cpp @@ -1293,6 +1293,8 @@ NS_HasBeenCrossOrigin(nsIChannel* aChannel, bool aReport) { nsCOMPtr loadInfo = aChannel->GetLoadInfo(); MOZ_RELEASE_ASSERT(loadInfo, "Origin tracking only works for channels created with a loadinfo"); + MOZ_ASSERT(loadInfo->GetExternalContentPolicyType() != nsIContentPolicy::TYPE_DOCUMENT, + "calling NS_HasBeenCrossOrigin on a top level load"); // Always treat tainted channels as cross-origin. if (loadInfo->GetTainting() != LoadTainting::Basic) { @@ -2254,9 +2256,11 @@ NS_ShouldSecureUpgrade(nsIURI* aURI, // Please note that cross origin top level navigations are not subject // to upgrade-insecure-requests, see: // http://www.w3.org/TR/upgrade-insecure-requests/#examples + // Compare the principal we are navigating to (aChannelResultPrincipal) + // with the referring/triggering Principal. bool crossOriginNavigation = (aLoadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_DOCUMENT) && - (!aChannelResultPrincipal->Equals(aLoadInfo->LoadingPrincipal())); + (!aChannelResultPrincipal->Equals(aLoadInfo->TriggeringPrincipal())); if (aLoadInfo->GetUpgradeInsecureRequests() && !crossOriginNavigation) { // let's log a message to the console that we are upgrading a request @@ -2357,6 +2361,72 @@ NS_GetSecureUpgradedURI(nsIURI* aURI, nsIURI** aUpgradedURI) return NS_OK; } +nsresult +NS_CompareLoadInfoAndLoadContext(nsIChannel *aChannel) +{ + nsCOMPtr loadInfo; + aChannel->GetLoadInfo(getter_AddRefs(loadInfo)); + + nsCOMPtr loadContext; + NS_QueryNotificationCallbacks(aChannel, loadContext); + if (loadInfo && loadContext) { + + uint32_t loadContextAppId = 0; + nsresult rv = loadContext->GetAppId(&loadContextAppId); + if (NS_FAILED(rv)) { + return NS_ERROR_UNEXPECTED; + } + + bool loadContextIsInBE = false; + rv = loadContext->GetIsInBrowserElement(&loadContextIsInBE); + if (NS_FAILED(rv)) { + return NS_ERROR_UNEXPECTED; + } + + OriginAttributes originAttrsLoadInfo = loadInfo->GetOriginAttributes(); + OriginAttributes originAttrsLoadContext; + loadContext->GetOriginAttributes(originAttrsLoadContext); + + bool loadInfoUsePB = false; + rv = loadInfo->GetUsePrivateBrowsing(&loadInfoUsePB); + if (NS_FAILED(rv)) { + return NS_ERROR_UNEXPECTED; + } + bool loadContextUsePB = false; + rv = loadContext->GetUsePrivateBrowsing(&loadContextUsePB); + if (NS_FAILED(rv)) { + return NS_ERROR_UNEXPECTED; + } + + LOG(("NS_CompareLoadInfoAndLoadContext - loadInfo: %d, %d, %d, %d; " + "loadContext: %d %d, %d, %d. [channel=%p]", + originAttrsLoadInfo.mAppId, originAttrsLoadInfo.mInBrowser, + originAttrsLoadInfo.mUserContextId, loadInfoUsePB, + loadContextAppId, loadContextUsePB, + originAttrsLoadContext.mUserContextId, loadContextIsInBE, + aChannel)); + + MOZ_ASSERT(originAttrsLoadInfo.mAppId == loadContextAppId, + "AppId in the loadContext and in the loadInfo are not the " + "same!"); + + MOZ_ASSERT(originAttrsLoadInfo.mInIsolatedMozBrowser == + loadContextIsInBE, + "The value of InIsolatedMozBrowser in the loadContext and in " + "the loadInfo are not the same!"); + + MOZ_ASSERT(originAttrsLoadInfo.mUserContextId == + originAttrsLoadContext.mUserContextId, + "The value of mUserContextId in the loadContext and in the " + "loadInfo are not the same!"); + + MOZ_ASSERT(loadInfoUsePB == loadContextUsePB, + "The value of usePrivateBrowsing in the loadContext and in the loadInfo " + "are not the same!"); + } + return NS_OK; +} + namespace mozilla { namespace net { diff --git a/netwerk/base/nsNetUtil.h b/netwerk/base/nsNetUtil.h index d9574093bc..c03b1c9e25 100644 --- a/netwerk/base/nsNetUtil.h +++ b/netwerk/base/nsNetUtil.h @@ -1003,6 +1003,7 @@ nsresult NS_ShouldSecureUpgrade(nsIURI* aURI, */ nsresult NS_GetSecureUpgradedURI(nsIURI* aURI, nsIURI** aUpgradedURI); +nsresult NS_CompareLoadInfoAndLoadContext(nsIChannel *aChannel); namespace mozilla { namespace net { diff --git a/netwerk/base/security-prefs.js b/netwerk/base/security-prefs.js index cdacba445b..decc9ffe2e 100644 --- a/netwerk/base/security-prefs.js +++ b/netwerk/base/security-prefs.js @@ -11,7 +11,6 @@ pref("security.tls.version.max", 4); pref("security.tls.version.fallback-limit", 3); pref("security.tls.insecure_fallback_hosts", ""); -pref("security.tls.insecure_fallback_hosts.use_static_list", true); pref("security.tls.unrestricted_rc4_fallback", false); pref("security.tls.enable_0rtt_data", false); @@ -81,10 +80,28 @@ pref("security.OCSP.require", false); pref("security.OCSP.GET.enabled", false); pref("security.pki.cert_short_lifetime_in_days", 10); +// NB: Changes to this pref affect CERT_CHAIN_SHA1_POLICY_STATUS telemetry. +// See the comment in CertVerifier.cpp. +// 3 = allow SHA-1 for certificates issued before 2016 or by an imported root. +pref("security.pki.sha1_enforcement_level", 3); + +// security.pki.name_matching_mode controls how the platform matches hostnames +// to name information in TLS certificates. The possible values are: +// 0: always fall back to the subject common name if necessary (as in, if the +// subject alternative name extension is either not present or does not +// contain any DNS names or IP addresses) +// 1: fall back to the subject common name for certificates valid before 23 +// August 2016 if necessary +// 2: only use name information from the subject alternative name extension +#ifdef RELEASE_BUILD +pref("security.pki.name_matching_mode", 1); +#else +pref("security.pki.name_matching_mode", 2); +#endif pref("security.webauth.u2f", false); -pref("security.webauth.u2f.softtoken", false); -pref("security.webauth.u2f.usbtoken", false); +pref("security.webauth.u2f_enable_softtoken", false); +pref("security.webauth.u2f_enable_usbtoken", false); pref("security.ssl.errorReporting.enabled", false); pref("security.ssl.errorReporting.url", "https://data.mozilla.com/submit/sslreports"); diff --git a/netwerk/cache2/CacheStorageService.cpp b/netwerk/cache2/CacheStorageService.cpp index 664b2ded10..76558942b2 100644 --- a/netwerk/cache2/CacheStorageService.cpp +++ b/netwerk/cache2/CacheStorageService.cpp @@ -1152,6 +1152,8 @@ void CacheStorageService::RemoveEntryForceValid(nsACString const &aContextKey, { mozilla::MutexAutoLock lock(mForcedValidEntriesLock); + LOG(("CacheStorageService::RemoveEntryForceValid context='%s' entryKey=%s", + aContextKey.BeginReading(), aEntryKey.BeginReading())); mForcedValidEntries.Remove(aContextKey + aEntryKey); } @@ -1800,15 +1802,19 @@ CacheStorageService::DoomStorageEntries(nsCSubstring const& aContextKey, { mozilla::MutexAutoLock lock(mForcedValidEntriesLock); - for (auto iter = mForcedValidEntries.Iter(); !iter.Done(); iter.Next()) { - bool matches; - DebugOnly rv = CacheFileUtils::KeyMatchesLoadContextInfo( - iter.Key(), aContext, &matches); - MOZ_ASSERT(NS_SUCCEEDED(rv)); + if (aContext) { + for (auto iter = mForcedValidEntries.Iter(); !iter.Done(); iter.Next()) { + bool matches; + DebugOnly rv = CacheFileUtils::KeyMatchesLoadContextInfo( + iter.Key(), aContext, &matches); + MOZ_ASSERT(NS_SUCCEEDED(rv)); - if (matches) { - iter.Remove(); + if (matches) { + iter.Remove(); + } } + } else { + mForcedValidEntries.Clear(); } } diff --git a/netwerk/cache2/nsICacheEntry.idl b/netwerk/cache2/nsICacheEntry.idl index ec6f48379a..a6bd084563 100644 --- a/netwerk/cache2/nsICacheEntry.idl +++ b/netwerk/cache2/nsICacheEntry.idl @@ -71,6 +71,10 @@ interface nsICacheEntry : nsISupports * This means that there is a potential problem if the number of forced valid * entries grows to take up more space than the cache size allows. * + * NOTE: entries that have been forced valid will STILL be ignored by HTTP + * channels if they have expired AND the resource in question requires + * validation after expiring. This is to avoid using known-stale content. + * * @param aSecondsToTheFuture * the number of seconds the default cache validation behavior will be * overridden before it returns to normal diff --git a/netwerk/ipc/NeckoChild.cpp b/netwerk/ipc/NeckoChild.cpp index 3ceb8bea58..85dfad052c 100644 --- a/netwerk/ipc/NeckoChild.cpp +++ b/netwerk/ipc/NeckoChild.cpp @@ -343,6 +343,25 @@ NeckoChild::RecvAsyncAuthPromptForNestedFrame(const TabId& aNestedFrameId, } /* Predictor Messages */ +bool +NeckoChild::RecvPredOnPredictPrefetch(const URIParams& aURI, + const uint32_t& aHttpStatus) +{ + MOZ_ASSERT(NS_IsMainThread(), "PredictorChild::RecvOnPredictPrefetch " + "off main thread."); + + nsCOMPtr uri = DeserializeURI(aURI); + + // Get the current predictor + nsresult rv = NS_OK; + nsCOMPtr predictor = + do_GetService("@mozilla.org/network/predictor;1", &rv); + NS_ENSURE_SUCCESS(rv, false); + + predictor->OnPredictPrefetch(uri, aHttpStatus); + return true; +} + bool NeckoChild::RecvPredOnPredictPreconnect(const URIParams& aURI) { @@ -391,6 +410,17 @@ NeckoChild::RecvAppOfflineStatus(const uint32_t& aId, const bool& aOffline) return true; } +bool +NeckoChild::RecvSpeculativeConnectRequest(const nsCString& aNotificationData) +{ + nsCOMPtr obsService = services::GetObserverService(); + if (obsService) { + obsService->NotifyObservers(nullptr, "speculative-connect-request", + NS_ConvertUTF8toUTF16(aNotificationData).get()); + } + return true; +} + } // namespace net } // namespace mozilla diff --git a/netwerk/ipc/NeckoChild.h b/netwerk/ipc/NeckoChild.h index 19f481dfe1..17519b70f2 100644 --- a/netwerk/ipc/NeckoChild.h +++ b/netwerk/ipc/NeckoChild.h @@ -85,8 +85,12 @@ protected: virtual bool DeallocPWebSocketEventListenerChild(PWebSocketEventListenerChild*) override; /* Predictor Messsages */ + virtual bool RecvPredOnPredictPrefetch(const URIParams& aURI, + const uint32_t& aHttpStatus) override; virtual bool RecvPredOnPredictPreconnect(const URIParams& aURI) override; virtual bool RecvPredOnPredictDNS(const URIParams& aURI) override; + + virtual bool RecvSpeculativeConnectRequest(const nsCString& aNotificationData) override; }; /** diff --git a/netwerk/ipc/NeckoParent.cpp b/netwerk/ipc/NeckoParent.cpp index 7e3d8f644f..92b437748b 100644 --- a/netwerk/ipc/NeckoParent.cpp +++ b/netwerk/ipc/NeckoParent.cpp @@ -93,6 +93,7 @@ NeckoParent::NeckoParent() NeckoParent::~NeckoParent() { + gNeckoParent = nullptr; if (mObserver) { mObserver->RemoveObserver(); } diff --git a/netwerk/ipc/PNecko.ipdl b/netwerk/ipc/PNecko.ipdl index c3befb0fee..1c0d7aaa35 100644 --- a/netwerk/ipc/PNecko.ipdl +++ b/netwerk/ipc/PNecko.ipdl @@ -126,9 +126,12 @@ child: async AppOfflineStatus(uint32_t appId, bool offline); /* Predictor Methods */ + async PredOnPredictPrefetch(URIParams uri, uint32_t httpStatus); async PredOnPredictPreconnect(URIParams uri); async PredOnPredictDNS(URIParams uri); + async SpeculativeConnectRequest(nsCString notificationData); + both: // Actually we need PTCPSocket() for parent. But ipdl disallows us having different // signatures on parent and child. So when constructing the parent side object, we just diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index 8f6c0fc816..a871a463ba 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -103,6 +103,7 @@ HttpBaseChannel::HttpBaseChannel() , mRedirectMode(nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW) , mFetchCacheMode(nsIHttpChannelInternal::FETCH_CACHE_MODE_DEFAULT) , mOnStartRequestCalled(false) + , mOnStopRequestCalled(false) , mTransferSize(0) , mDecodedBodySize(0) , mEncodedBodySize(0) @@ -2636,8 +2637,13 @@ HttpBaseChannel::DoNotifyListener() mIsPending = false; if (mListener) { + MOZ_ASSERT(!mOnStopRequestCalled, + "We should not call OnStopRequest twice"); + nsCOMPtr listener = mListener; listener->OnStopRequest(this, mListenerContext, mStatus); + + mOnStopRequestCalled = true; } // We have to make sure to drop the references to listeners and callbacks @@ -3211,6 +3217,36 @@ HttpBaseChannel::GetPerformance() if (!mTimingEnabled) { return nullptr; } + + nsCOMPtr pDomWindow = GetInnerDOMWindow(); + if (!pDomWindow) { + return nullptr; + } + + nsPerformance* docPerformance = pDomWindow->GetPerformance(); + if (!docPerformance) { + return nullptr; + } + // iframes should be added to the parent's entries list. + if (mLoadFlags & LOAD_DOCUMENT_URI) { + return docPerformance->GetParentPerformance(); + } + return docPerformance; +} + +nsIURI* +HttpBaseChannel::GetReferringPage() +{ + nsCOMPtr pDomWindow = GetInnerDOMWindow(); + if (!pDomWindow) { + return nullptr; + } + return pDomWindow->GetDocumentURI(); +} + +nsPIDOMWindow* +HttpBaseChannel::GetInnerDOMWindow() +{ nsCOMPtr loadContext; NS_QueryNotificationCallbacks(this, loadContext); if (!loadContext) { @@ -3240,15 +3276,7 @@ HttpBaseChannel::GetPerformance() } } - nsPerformance* docPerformance = pDomWindow->GetPerformance(); - if (!docPerformance) { - return nullptr; - } - // iframes should be added to the parent's entries list. - if (mLoadFlags & LOAD_DOCUMENT_URI) { - return docPerformance->GetParentPerformance(); - } - return docPerformance; + return pDomWindow; } //------------------------------------------------------------------------------ diff --git a/netwerk/protocol/http/HttpBaseChannel.h b/netwerk/protocol/http/HttpBaseChannel.h index b90a0ed010..621d100a61 100644 --- a/netwerk/protocol/http/HttpBaseChannel.h +++ b/netwerk/protocol/http/HttpBaseChannel.h @@ -316,6 +316,8 @@ protected: void ReleaseListeners(); nsPerformance* GetPerformance(); + nsIURI* GetReferringPage(); + nsPIDOMWindow* GetInnerDOMWindow(); void AddCookiesToRequest(); virtual nsresult SetupReplacementChannel(nsIURI *, @@ -491,9 +493,10 @@ protected: uint32_t mRedirectMode; uint32_t mFetchCacheMode; - // This parameter is used to ensure that we do not call OnStartRequest more - // than once. + // These parameters are used to ensure that we do not call OnStartRequest and + // OnStopRequest more than once. bool mOnStartRequestCalled; + bool mOnStopRequestCalled; uint64_t mTransferSize; uint64_t mDecodedBodySize; diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp index 501afc9fa5..612a020505 100644 --- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -942,7 +942,10 @@ HttpChannelChild::DoOnStopRequest(nsIRequest* aRequest, nsresult aChannelStatus, nsChannelClassifier::SetBlockedTrackingContent(this); } + MOZ_ASSERT(!mOnStopRequestCalled, + "We should not call OnStopRequest twice"); mListener->OnStopRequest(aRequest, aContext, mStatus); + mOnStopRequestCalled = true; mListener = 0; mListenerContext = 0; diff --git a/netwerk/protocol/http/nsCORSListenerProxy.cpp b/netwerk/protocol/http/nsCORSListenerProxy.cpp index ff7caa6113..aa1a434302 100644 --- a/netwerk/protocol/http/nsCORSListenerProxy.cpp +++ b/netwerk/protocol/http/nsCORSListenerProxy.cpp @@ -1345,6 +1345,10 @@ nsCORSListenerProxy::StartCORSPreflight(nsIChannel* aRequestChannel, "how did we end up here?"); nsCOMPtr principal = originalLoadInfo->LoadingPrincipal(); + MOZ_ASSERT(principal && + originalLoadInfo->GetExternalContentPolicyType() != + nsIContentPolicy::TYPE_DOCUMENT, + "Should not do CORS loads for top-level loads, so a loadingPrincipal should always exist."); bool withCredentials = originalLoadInfo->GetCookiePolicy() == nsILoadInfo::SEC_COOKIES_INCLUDE; diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 606c25af39..3090ac7e5a 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -75,6 +75,7 @@ #include "nsISocketTransport.h" #include "nsIStreamConverterService.h" #include "nsISiteSecurityService.h" +#include "nsString.h" #include "nsCRT.h" #include "nsPerformance.h" #include "CacheObserver.h" @@ -91,6 +92,7 @@ #include "nsICompressConvStats.h" #include "nsCORSListenerProxy.h" #include "nsISocketProvider.h" +#include "mozilla/net/Predictor.h" namespace mozilla { namespace net { @@ -1614,6 +1616,25 @@ nsHttpChannel::ProcessResponse() LOG(("nsHttpChannel::ProcessResponse [this=%p httpStatus=%u]\n", this, httpStatus)); + // Let the predictor know whether this was a cacheable response or not so + // that it knows whether or not to possibly prefetch this resource in the + // future. + // We use GetReferringPage because mReferrer may not be set at all, or may + // not be a full URI (HttpBaseChannel::SetReferrer has the gorey details). + // If that's null, though, we'll fall back to mReferrer just in case (this + // is especially useful in xpcshell tests, where we don't have an actual + // pageload to get a referrer from). + nsCOMPtr referrer = GetReferringPage(); + if (!referrer) { + referrer = mReferrer; + } + if (referrer) { + nsCOMPtr lci = GetLoadContextInfo(this); + mozilla::net::Predictor::UpdateCacheability(mReferrer, mURI, httpStatus, + mRequestHead, mResponseHead, + lci); + } + if (mTransaction->ProxyConnectFailed()) { // Only allow 407 (authentication required) to continue if (httpStatus != 407) @@ -2154,7 +2175,8 @@ nsHttpChannel::OpenRedirectChannel(nsresult rv) { AutoRedirectVetoNotifier notifier(this); - // Make sure to do this _after_ calling OnChannelRedirect + // Make sure to do this after we received redirect veto answer, + // i.e. after all sinks had been notified mRedirectChannel->SetOriginalURI(mOriginalURI); // And now, notify observers the deprecated way @@ -2234,7 +2256,8 @@ nsHttpChannel::ContinueDoReplaceWithProxy(nsresult rv) NS_PRECONDITION(mRedirectChannel, "No redirect channel?"); - // Make sure to do this _after_ calling OnChannelRedirect + // Make sure to do this after we received redirect veto answer, + // i.e. after all sinks had been notified mRedirectChannel->SetOriginalURI(mOriginalURI); // open new channel @@ -2495,12 +2518,13 @@ nsHttpChannel::IsResumable(int64_t partialLen, int64_t contentLength, bool ignoreMissingPartialLen) const { bool hasContentEncoding = - mCachedResponseHead->PeekHeader(nsHttp::Content_Encoding) - != nullptr; + (mCachedResponseHead->PeekHeader(nsHttp::Content_Encoding) != nullptr); + const char *etag = mCachedResponseHead->PeekHeader(nsHttp::ETag); + bool hasWeakEtag = etag && !strncmp(etag, "W/", 2); return (partialLen < contentLength) && (partialLen > 0 || ignoreMissingPartialLen) && - !hasContentEncoding && + !hasContentEncoding && !hasWeakEtag && mCachedResponseHead->IsResumable() && !mCustomConditionalRequest && !mCachedResponseHead->NoStore(); @@ -2900,7 +2924,8 @@ nsHttpChannel::ContinueProcessFallback(nsresult rv) NS_PRECONDITION(mRedirectChannel, "No redirect channel?"); - // Make sure to do this _after_ calling OnChannelRedirect + // Make sure to do this after we received redirect veto answer, + // i.e. after all sinks had been notified mRedirectChannel->SetOriginalURI(mOriginalURI); if (mLoadInfo && mLoadInfo->GetEnforceSecurity()) { @@ -3036,7 +3061,7 @@ nsHttpChannel::OpenCacheEntry(bool isHttps) uint32_t cacheEntryOpenFlags; bool offline = gIOService->IsOffline() || appOffline; if (offline || (mLoadFlags & INHIBIT_CACHING)) { - if (BYPASS_LOCAL_CACHE(mLoadFlags) && !offline) { + if (BYPASS_LOCAL_CACHE(mLoadFlags) && !offline && !PossiblyIntercepted()) { goto bypassCacheEntryOpen; } cacheEntryOpenFlags = nsICacheStorage::OPEN_READONLY; @@ -3358,9 +3383,13 @@ nsHttpChannel::OnCacheEntryCheck(nsICacheEntry* entry, nsIApplicationCache* appC canAddImsHeader = false; doValidation = true; } - // Check isForcedValid to see if it is possible to skip validation + // Check isForcedValid to see if it is possible to skip validation. + // Don't skip validation if we have serious reason to believe that this + // content is invalid (it's expired). // See netwerk/cache2/nsICacheEntry.idl for details - else if (isForcedValid) { + else if (isForcedValid && + (!mCachedResponseHead->ExpiresInPast() || + !mCachedResponseHead->MustValidateIfExpired())) { LOG(("NOT validating based on isForcedValid being true.\n")); doValidation = false; } @@ -3506,12 +3535,14 @@ nsHttpChannel::OnCacheEntryCheck(nsICacheEntry* entry, nsIApplicationCache* appC // expect it to be cached. (we only keep it in our cache for the // purposes of back/forward, etc.) // - // the request method MUST be either GET or HEAD (see bug 175641). + // the request method MUST be either GET or HEAD (see bug 175641) and + // the cached response code must be < 400 // // do not override conditional headers when consumer has defined its own if (!mCachedResponseHead->NoStore() && (mRequestHead.IsGet() || mRequestHead.IsHead()) && - !mCustomConditionalRequest) { + !mCustomConditionalRequest && + (mCachedResponseHead->Status() < 400)) { if (mConcurentCacheAccess) { // In case of concurrent read and also validation request we @@ -4849,7 +4880,8 @@ nsHttpChannel::ContinueProcessRedirection(nsresult rv) NS_PRECONDITION(mRedirectChannel, "No redirect channel?"); - // Make sure to do this _after_ calling OnChannelRedirect + // Make sure to do this after we received redirect veto answer, + // i.e. after all sinks had been notified mRedirectChannel->SetOriginalURI(mOriginalURI); // And now, the deprecated way @@ -5075,6 +5107,8 @@ nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context) LOG(("nsHttpChannel::AsyncOpen [this=%p]\n", this)); + NS_CompareLoadInfoAndLoadContext(this); + NS_ENSURE_ARG_POINTER(listener); NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); @@ -6124,7 +6158,10 @@ nsHttpChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult st if (mListener) { LOG((" calling OnStopRequest\n")); + MOZ_ASSERT(!mOnStopRequestCalled, + "We should not call OnStopRequest twice"); mListener->OnStopRequest(this, mListenerContext, status); + mOnStopRequestCalled = true; } CloseCacheEntry(!contentComplete); @@ -6612,6 +6649,26 @@ nsHttpChannel::SetPin(bool aPin) return NS_OK; } +NS_IMETHODIMP +nsHttpChannel::ForceCacheEntryValidFor(uint32_t aSecondsToTheFuture) +{ + if (!mCacheEntry) { + LOG(("nsHttpChannel::ForceCacheEntryValidFor found no cache entry " + "for this channel [this=%p].", this)); + } else { + mCacheEntry->ForceValidFor(aSecondsToTheFuture); + + nsAutoCString key; + mCacheEntry->GetKey(key); + + LOG(("nsHttpChannel::ForceCacheEntryValidFor successfully forced valid " + "entry with key %s for %d seconds. [this=%p]", ToNewCString(key), + aSecondsToTheFuture, this)); + } + + return NS_OK; +} + //----------------------------------------------------------------------------- // nsHttpChannel::nsIResumableChannel //----------------------------------------------------------------------------- diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp index 21abd9c9e6..fa49e6e284 100644 --- a/netwerk/protocol/http/nsHttpHandler.cpp +++ b/netwerk/protocol/http/nsHttpHandler.cpp @@ -56,7 +56,9 @@ #include "nsIOService.h" #include "mozilla/net/NeckoChild.h" +#include "mozilla/net/NeckoParent.h" #include "mozilla/ipc/URIUtils.h" +#include "mozilla/unused.h" #if defined(XP_UNIX) #include @@ -2158,6 +2160,9 @@ nsHttpHandler::SpeculativeConnectInternal(nsIURI *aURI, obsService->NotifyObservers(nullptr, "speculative-connect-request", NS_ConvertUTF8toUTF16(spec).get()); + if (!IsNeckoChild() && gNeckoParent) { + Unused << gNeckoParent->SendSpeculativeConnectRequest(spec); + } } nsISiteSecurityService* sss = gHttpHandler->GetSSService(); diff --git a/netwerk/protocol/http/nsHttpResponseHead.cpp b/netwerk/protocol/http/nsHttpResponseHead.cpp index a0e8df68d8..1fb68c3371 100644 --- a/netwerk/protocol/http/nsHttpResponseHead.cpp +++ b/netwerk/protocol/http/nsHttpResponseHead.cpp @@ -442,6 +442,20 @@ nsHttpResponseHead::ComputeFreshnessLifetime(uint32_t *result) const return NS_OK; } + // These responses can be cached indefinitely. + if ((mStatus == 300) || (mStatus == 410) || nsHttp::IsPermanentRedirect(mStatus)) { + LOG(("nsHttpResponseHead::ComputeFreshnessLifetime [this = %p] " + "Assign an infinite heuristic lifetime\n", this)); + *result = uint32_t(-1); + return NS_OK; + } + + if (mStatus >= 400) { + LOG(("nsHttpResponseHead::ComputeFreshnessLifetime [this = %p] " + "Do not calculate heuristic max-age for most responses >= 400\n", this)); + return NS_OK; + } + // Fallback on heuristic using last modified header... if (NS_SUCCEEDED(GetLastModifiedValue(&date2))) { LOG(("using last-modified to determine freshness-lifetime\n")); @@ -453,13 +467,7 @@ nsHttpResponseHead::ComputeFreshnessLifetime(uint32_t *result) const } } - // These responses can be cached indefinitely. - if ((mStatus == 300) || nsHttp::IsPermanentRedirect(mStatus)) { - *result = uint32_t(-1); - return NS_OK; - } - - LOG(("nsHttpResponseHead::ComputeFreshnessLifetime [this = %x] " + LOG(("nsHttpResponseHead::ComputeFreshnessLifetime [this = %p] " "Insufficient information to compute a non-zero freshness " "lifetime!\n", this)); @@ -485,6 +493,8 @@ nsHttpResponseHead::MustValidate() const case 304: case 307: case 308: + // Gone forever + case 410: break; // Uncacheable redirects case 303: diff --git a/netwerk/protocol/http/nsIHttpChannel.idl b/netwerk/protocol/http/nsIHttpChannel.idl index b842441c52..dd0e581e9f 100644 --- a/netwerk/protocol/http/nsIHttpChannel.idl +++ b/netwerk/protocol/http/nsIHttpChannel.idl @@ -14,7 +14,7 @@ interface nsIHttpHeaderVisitor; * the inspection of the resulting HTTP response status and headers when they * become available. */ -[builtinclass, scriptable, uuid(b2596105-3d0d-4e6a-824f-0539713bb879)] +[builtinclass, scriptable, uuid(c5a4a073-4539-49c7-a3f2-cec3f0619c6c)] interface nsIHttpChannel : nsIChannel { /************************************************************************** diff --git a/netwerk/streamconv/converters/nsIndexedToHTML.cpp b/netwerk/streamconv/converters/nsIndexedToHTML.cpp index c7300fb4d3..2a46bf78ff 100644 --- a/netwerk/streamconv/converters/nsIndexedToHTML.cpp +++ b/netwerk/streamconv/converters/nsIndexedToHTML.cpp @@ -12,7 +12,6 @@ #include "nsIFileURL.h" #include "nsEscape.h" #include "nsIDirIndex.h" -#include "nsDateTimeFormatCID.h" #include "nsURLHelper.h" #include "nsIPlatformCharset.h" #include "nsIPrefService.h" @@ -70,9 +69,9 @@ nsIndexedToHTML::Init(nsIStreamListener* aListener) { mListener = aListener; - mDateTime = do_CreateInstance(NS_DATETIMEFORMAT_CONTRACTID, &rv); - if (NS_FAILED(rv)) - return rv; + mDateTime = nsIDateTimeFormat::Create(); + if (!mDateTime) + return NS_ERROR_FAILURE; nsCOMPtr sbs = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv); diff --git a/netwerk/test/mochitests/test_rel_preconnect.html b/netwerk/test/mochitests/test_rel_preconnect.html index 0226f447c2..4e94fb4cee 100644 --- a/netwerk/test/mochitests/test_rel_preconnect.html +++ b/netwerk/test/mochitests/test_rel_preconnect.html @@ -11,44 +11,18 @@ SimpleTest.waitForExplicitFinish(); const Cc = SpecialPowers.Cc, Ci = SpecialPowers.Ci, Cr = SpecialPowers.Cr; -var srv; - -function TestServer1(nextTest) { - this.listener= Cc["@mozilla.org/network/server-socket;1"] - .createInstance(Ci.nsIServerSocket); - this.listener.init(-1, true, -1); - this.listener.asyncListen(SpecialPowers.wrapCallbackObject(this)); - this.nextTest = nextTest; -} - -TestServer1.prototype = { - QueryInterface: function(iid) { - iid = SpecialPowers.wrap(iid); - if (iid.equals(Ci.nsIServerSocketListener) || - iid.equals(Ci.nsISupports)) - return this; - throw Cr.NS_ERROR_NO_INTERFACE; - }, - onSocketAccepted: function(socket, trans) { - try { socket.close(); } catch(e) {} - try { trans.close(); } catch(e) {} - }, - onStopListening: function(socket) {} -}; var remainder = 4; var observer; function doTest() { - srv = new TestServer1(); SpecialPowers.setBoolPref("network.http.debug-observations", true); observer = SpecialPowers.wrapCallback(function(subject, topic, data) { remainder--; ok(true, "observed remainder = " + remainder); if (!remainder) { - srv.listener.close(); SpecialPowers.removeObserver(observer, "speculative-connect-request"); SpecialPowers.setBoolPref("network.http.debug-observations", false); SimpleTest.finish(); @@ -60,18 +34,18 @@ function doTest() // and crossOrigin=anonymous var link = document.createElement("link"); link.rel = "preconnect"; - link.href = "//localhost:" + srv.listener.port; + link.href = "//localhost:8888"; document.head.appendChild(link); link = document.createElement("link"); link.rel = "preconnect"; - link.href = "//localhost:" + srv.listener.port; + link.href = "//localhost:8888"; link.crossOrigin = "anonymous"; document.head.appendChild(link); // test the http link response header - the test contains both a // normal and anonymous preconnect link header var iframe = document.createElement('iframe'); - iframe.src = 'rel_preconnect.sjs?//localhost:' + srv.listener.port; + iframe.src = 'rel_preconnect.sjs?//localhost:8888'; document.body.appendChild(iframe); } diff --git a/netwerk/test/unit/test_auth_proxy.js b/netwerk/test/unit/test_auth_proxy.js index 871f122b56..ae5260ade8 100644 --- a/netwerk/test/unit/test_auth_proxy.js +++ b/netwerk/test/unit/test_auth_proxy.js @@ -11,7 +11,7 @@ */ Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); const FLAG_RETURN_FALSE = 1 << 0; const FLAG_WRONG_PASSWORD = 1 << 1; @@ -213,19 +213,9 @@ var listener = { function makeChan(url) { if (!url) url = "http://somesite/"; - var ios = Cc["@mozilla.org/network/io-service;1"] - .getService(Ci.nsIIOService); - var chan = ios.newChannel2(url, - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER) - .QueryInterface(Ci.nsIHttpChannel); - return chan; + return NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true}) + .QueryInterface(Ci.nsIHttpChannel); } var current_test = 0; @@ -255,7 +245,7 @@ function test_proxy_returnfalse() { var chan = makeChan(); chan.notificationCallbacks = new Requestor(FLAG_RETURN_FALSE, 0); listener.expectedCode = 407; // Proxy Unauthorized - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); do_test_pending(); } @@ -265,7 +255,7 @@ function test_proxy_wrongpw() { var chan = makeChan(); chan.notificationCallbacks = new Requestor(FLAG_WRONG_PASSWORD, 0); listener.expectedCode = 200; // Eventually OK - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); do_test_pending(); } @@ -274,7 +264,7 @@ function test_all_ok() { var chan = makeChan(); chan.notificationCallbacks = new Requestor(0, 0); listener.expectedCode = 200; // OK - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); do_test_pending(); } @@ -283,7 +273,7 @@ function test_proxy_407_cookie() { chan.notificationCallbacks = new Requestor(FLAG_RETURN_FALSE, 0); chan.setRequestHeader("X-Set-407-Cookie", "1", false); listener.expectedCode = 407; // Proxy Unauthorized - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); do_test_pending(); } @@ -293,7 +283,7 @@ function test_proxy_200_cookie() { chan.notificationCallbacks = new Requestor(0, 0); chan.setRequestHeader("X-Set-407-Cookie", "1", false); listener.expectedCode = 200; // OK - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); do_test_pending(); } @@ -302,7 +292,7 @@ function test_host_returnfalse() { var chan = makeChan(); chan.notificationCallbacks = new Requestor(0, FLAG_RETURN_FALSE); listener.expectedCode = 401; // Host Unauthorized - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); do_test_pending(); } @@ -312,7 +302,7 @@ function test_host_wrongpw() { var chan = makeChan(); chan.notificationCallbacks = new Requestor(0, FLAG_WRONG_PASSWORD); listener.expectedCode = 200; // Eventually OK - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); do_test_pending(); } @@ -322,7 +312,7 @@ function test_proxy_wrongpw_host_wrongpw() { chan.notificationCallbacks = new Requestor(FLAG_WRONG_PASSWORD, FLAG_WRONG_PASSWORD); listener.expectedCode = 200; // OK - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); do_test_pending(); } @@ -332,7 +322,7 @@ function test_proxy_wrongpw_host_returnfalse() { chan.notificationCallbacks = new Requestor(FLAG_WRONG_PASSWORD, FLAG_RETURN_FALSE); listener.expectedCode = 401; // Host Unauthorized - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); do_test_pending(); } diff --git a/netwerk/test/unit/test_authentication.js b/netwerk/test/unit/test_authentication.js index 24ad835f86..3657485f08 100644 --- a/netwerk/test/unit/test_authentication.js +++ b/netwerk/test/unit/test_authentication.js @@ -2,7 +2,7 @@ // TODO NIT use do_check_eq(expected, actual) consistently, not sometimes eq(actual, expected) Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); // Turn off the authentication dialog blocking for this test. var prefs = Cc["@mozilla.org/preferences-service;1"]. @@ -275,19 +275,8 @@ var listener = { }; function makeChan(url) { - var ios = Components.classes["@mozilla.org/network/io-service;1"] - .getService(Components.interfaces.nsIIOService); - var chan = ios.newChannel2(url, - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER) + return NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true}) .QueryInterface(Components.interfaces.nsIHttpChannel); - - return chan; } var tests = [test_noauth, test_returnfalse1, test_wrongpw1, test_prompt1, @@ -318,7 +307,7 @@ function test_noauth() { var chan = makeChan(URL + "/auth"); listener.expectedCode = 401; // Unauthorized - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); do_test_pending(); } @@ -328,7 +317,7 @@ function test_returnfalse1() { chan.notificationCallbacks = new Requestor(FLAG_RETURN_FALSE, 1); listener.expectedCode = 401; // Unauthorized - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); do_test_pending(); } @@ -338,7 +327,7 @@ function test_wrongpw1() { chan.notificationCallbacks = new Requestor(FLAG_WRONG_PASSWORD, 1); listener.expectedCode = 200; // OK - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); do_test_pending(); } @@ -348,7 +337,7 @@ function test_prompt1() { chan.notificationCallbacks = new Requestor(0, 1); listener.expectedCode = 200; // OK - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); do_test_pending(); } @@ -358,7 +347,7 @@ function test_returnfalse2() { chan.notificationCallbacks = new Requestor(FLAG_RETURN_FALSE, 2); listener.expectedCode = 401; // Unauthorized - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); do_test_pending(); } @@ -368,7 +357,7 @@ function test_wrongpw2() { chan.notificationCallbacks = new Requestor(FLAG_WRONG_PASSWORD, 2); listener.expectedCode = 200; // OK - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); do_test_pending(); } @@ -378,7 +367,7 @@ function test_prompt2() { chan.notificationCallbacks = new Requestor(0, 2); listener.expectedCode = 200; // OK - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); do_test_pending(); } @@ -388,7 +377,7 @@ function test_ntlm() { chan.notificationCallbacks = new Requestor(FLAG_RETURN_FALSE, 2); listener.expectedCode = 401; // Unauthorized - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); do_test_pending(); } @@ -398,7 +387,7 @@ function test_basicrealm() { chan.notificationCallbacks = new RealmTestRequestor(); listener.expectedCode = 401; // Unauthorized - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); do_test_pending(); } @@ -408,7 +397,7 @@ function test_digest_noauth() { //chan.notificationCallbacks = new Requestor(FLAG_RETURN_FALSE, 2); listener.expectedCode = 401; // Unauthorized - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); do_test_pending(); } @@ -418,7 +407,7 @@ function test_digest() { chan.notificationCallbacks = new Requestor(0, 2); listener.expectedCode = 200; // OK - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); do_test_pending(); } @@ -427,7 +416,7 @@ function test_digest_bogus_user() { var chan = makeChan(URL + "/auth/digest"); chan.notificationCallbacks = new Requestor(FLAG_BOGUS_USER, 2); listener.expectedCode = 401; // unauthorized - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); do_test_pending(); } @@ -2030,7 +2019,7 @@ function test_large_realm() { var chan = makeChan(URL + "/largeRealm"); listener.expectedCode = 401; // Unauthorized - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); do_test_pending(); } @@ -2039,7 +2028,7 @@ function test_large_domain() { var chan = makeChan(URL + "/largeDomain "); listener.expectedCode = 401; // Unauthorized - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); do_test_pending(); } diff --git a/netwerk/test/unit/test_bug1064258.js b/netwerk/test/unit/test_bug1064258.js index e7b6bdc56d..3f76837ae9 100644 --- a/netwerk/test/unit/test_bug1064258.js +++ b/netwerk/test/unit/test_bug1064258.js @@ -11,7 +11,7 @@ */ Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); XPCOMUtils.defineLazyGetter(this, "URL", function() { return "http://localhost:" + httpServer.identity.primaryPort; @@ -20,16 +20,7 @@ XPCOMUtils.defineLazyGetter(this, "URL", function() { var httpServer = null; function make_channel(url, callback, ctx) { - var ios = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - return ios.newChannel2(url, - "", - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + return NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true}); } const responseBody1 = "response body 1"; @@ -81,7 +72,7 @@ function run_test_content1a() var chan = make_channel(URL + "/content1"); caching = chan.QueryInterface(Ci.nsICachingChannel); caching.cacheOnlyMetadata = true; - chan.asyncOpen(new ChannelListener(contentListener1a, null), null); + chan.asyncOpen2(new ChannelListener(contentListener1a, null)); } function contentListener1a(request, buffer) @@ -105,7 +96,7 @@ function cacheCheck1(status, entry) var chan = make_channel(URL + "/content1"); caching = chan.QueryInterface(Ci.nsICachingChannel); caching.cacheOnlyMetadata = true; - chan.asyncOpen(new ChannelListener(contentListener1b, null, CL_IGNORE_CL), null); + chan.asyncOpen2(new ChannelListener(contentListener1b, null, CL_IGNORE_CL)); } function contentListener1b(request, buffer) @@ -126,7 +117,7 @@ function run_test_content2a() var chan = make_channel(URL + "/content2"); caching = chan.QueryInterface(Ci.nsICachingChannel); caching.cacheOnlyMetadata = true; - chan.asyncOpen(new ChannelListener(contentListener2a, null), null); + chan.asyncOpen2(new ChannelListener(contentListener2a, null)); } function contentListener2a(request, buffer) @@ -151,7 +142,7 @@ function cacheCheck2(status, entry) var chan = make_channel(URL + "/content2"); caching = chan.QueryInterface(Ci.nsICachingChannel); caching.cacheOnlyMetadata = true; - chan.asyncOpen(new ChannelListener(contentListener2b, null), null); + chan.asyncOpen2(new ChannelListener(contentListener2b, null)); } function contentListener2b(request, buffer) diff --git a/netwerk/test/unit/test_bug263127.js b/netwerk/test/unit/test_bug263127.js index 91949d7640..8262c07e84 100644 --- a/netwerk/test/unit/test_bug263127.js +++ b/netwerk/test/unit/test_bug263127.js @@ -1,5 +1,5 @@ Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); var server; const BUGID = "263127"; @@ -39,18 +39,10 @@ function run_test() { server.start(-1); // Initialize downloader - var channel = Cc["@mozilla.org/network/io-service;1"] - .getService(Ci.nsIIOService) - .newChannel2("http://localhost:" + - server.identity.primaryPort + "/", - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); - + var channel = NetUtil.newChannel({ + uri: "http://localhost:" + server.identity.primaryPort + "/", + loadUsingSystemPrincipal: true + }); var targetFile = Cc["@mozilla.org/file/directory_service;1"] .getService(Ci.nsIProperties) .get("TmpD", Ci.nsIFile); @@ -63,7 +55,7 @@ function run_test() { downloader.init(listener, targetFile); // Start download - channel.asyncOpen(downloader, null); + channel.asyncOpen2(downloader); do_test_pending(); } diff --git a/netwerk/test/unit/test_bug412945.js b/netwerk/test/unit/test_bug412945.js index da1e22cbdd..e8b39774b3 100644 --- a/netwerk/test/unit/test_bug412945.js +++ b/netwerk/test/unit/test_bug412945.js @@ -1,5 +1,5 @@ Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); var httpserv; @@ -21,22 +21,14 @@ function run_test() { httpserv.start(-1); // make request - var channel = - Components.classes["@mozilla.org/network/io-service;1"]. - getService(Components.interfaces.nsIIOService). - newChannel2("http://localhost:" + httpserv.identity.primaryPort + - "/bug412945", - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + var channel = NetUtil.newChannel({ + uri: "http://localhost:" + httpserv.identity.primaryPort + "/bug412945", + loadUsingSystemPrincipal: true + }); channel.QueryInterface(Components.interfaces.nsIHttpChannel); channel.requestMethod = "POST"; - channel.asyncOpen(new TestListener(), null); + channel.asyncOpen2(new TestListener(), null); do_test_pending(); } diff --git a/netwerk/test/unit/test_bug482601.js b/netwerk/test/unit/test_bug482601.js index 20089a34f3..fde70f005e 100644 --- a/netwerk/test/unit/test_bug482601.js +++ b/netwerk/test/unit/test_bug482601.js @@ -1,5 +1,5 @@ Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); var httpserv = null; var test_nr = 0; @@ -80,16 +80,8 @@ var results = ["http-on-examine-response", "http-on-examine-cached-response"]; function makeChan(url) { - var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); - var chan = ios.newChannel2(url, - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER).QueryInterface(Ci.nsIHttpChannel); - return chan; + return NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true}) + .QueryInterface(Ci.nsIHttpChannel); } function storeCache(aCacheEntry, aResponseHeads, aContent) { @@ -113,7 +105,7 @@ function test_nocache() { var chan = makeChan("http://localhost:" + httpserv.identity.primaryPort + "/bug482601/nocache"); - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); } function test_partial() { @@ -139,7 +131,7 @@ function test_partial2(status, entry) { var chan = makeChan("http://localhost:" + httpserv.identity.primaryPort + "/bug482601/partial"); - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); } function test_cached() { @@ -166,7 +158,7 @@ function test_cached2(status, entry) { var chan = makeChan("http://localhost:" + httpserv.identity.primaryPort + "/bug482601/cached"); chan.loadFlags = Ci.nsIRequest.VALIDATE_ALWAYS; - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); } function test_only_from_cache() { @@ -193,7 +185,7 @@ function test_only_from_cache2(status, entry) { var chan = makeChan("http://localhost:" + httpserv.identity.primaryPort + "/bug482601/only_from_cache"); chan.loadFlags = Ci.nsICachingChannel.LOAD_ONLY_FROM_CACHE; - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); } diff --git a/netwerk/test/unit/test_bug490095.js b/netwerk/test/unit/test_bug490095.js index f75d26c9ef..8b588b1a8a 100644 --- a/netwerk/test/unit/test_bug490095.js +++ b/netwerk/test/unit/test_bug490095.js @@ -4,7 +4,7 @@ // Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); var httpserver = new HttpServer(); var index = 0; @@ -40,18 +40,10 @@ function logit(i, data) { } function setupChannel(suffix, value) { - var ios = Components.classes["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - var chan = ios.newChannel2("http://localhost:" + - httpserver.identity.primaryPort + - suffix, - "", - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + var chan = NetUtil.newChannel({ + uri: "http://localhost:" + httpserver.identity.primaryPort + suffix, + loadUsingSystemPrincipal: true + }); var httpChan = chan.QueryInterface(Components.interfaces.nsIHttpChannel); httpChan.requestMethod = "GET"; httpChan.setRequestHeader("x-request", value, false); @@ -62,7 +54,7 @@ function triggerNextTest() { var test = tests[index]; var channel = setupChannel(test.url, test.server); if (test.flags) channel.loadFlags = test.flags; - channel.asyncOpen(new ChannelListener(checkValueAndTrigger, null), null); + channel.asyncOpen2(new ChannelListener(checkValueAndTrigger, null)); } function checkValueAndTrigger(request, data, ctx) { diff --git a/netwerk/test/unit/test_bug561276.js b/netwerk/test/unit/test_bug561276.js index ca62f1f9ef..fd67d24dc6 100644 --- a/netwerk/test/unit/test_bug561276.js +++ b/netwerk/test/unit/test_bug561276.js @@ -4,25 +4,17 @@ // Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); var httpserver = new HttpServer(); var iteration = 0; function setupChannel(suffix) { - var ios = - Components.classes["@mozilla.org/network/io-service;1"] - .getService(Ci.nsIIOService); - var chan = ios.newChannel2("http://localhost:" + - httpserver.identity.primaryPort + suffix, - "", - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + var chan = NetUtil.newChannel({ + uri: "http://localhost:" + httpserver.identity.primaryPort + suffix, + loadUsingSystemPrincipal: true + }); var httpChan = chan.QueryInterface(Components.interfaces.nsIHttpChannel); httpChan.requestMethod = "GET"; return httpChan; @@ -45,7 +37,7 @@ function run_test() // load first time var channel = setupChannel("/redirect1"); - channel.asyncOpen(new ChannelListener(checkValueAndTrigger, null), null); + channel.asyncOpen2(new ChannelListener(checkValueAndTrigger, null)); do_test_pending(); } diff --git a/netwerk/test/unit/test_bug596443.js b/netwerk/test/unit/test_bug596443.js index 731534066d..e86bdf817b 100644 --- a/netwerk/test/unit/test_bug596443.js +++ b/netwerk/test/unit/test_bug596443.js @@ -1,23 +1,15 @@ Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); var httpserver = new HttpServer(); var expectedOnStopRequests = 3; function setupChannel(suffix, xRequest, flags) { - var ios = Components.classes["@mozilla.org/network/io-service;1"] - .getService(Ci.nsIIOService); - var chan = ios.newChannel2("http://localhost:" + - httpserver.identity.primaryPort + - suffix, - "", - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + var chan = NetUtil.newChannel({ + uri: "http://localhost:" + httpserver.identity.primaryPort + suffix, + loadUsingSystemPrincipal: true + }); if (flags) chan.loadFlags |= flags; @@ -68,13 +60,13 @@ function run_test() { evict_cache_entries(); var ch0 = setupChannel("/bug596443", "Response0", Ci.nsIRequest.LOAD_BYPASS_CACHE); - ch0.asyncOpen(new Listener("Response0"), null); + ch0.asyncOpen2(new Listener("Response0")); var ch1 = setupChannel("/bug596443", "Response1", Ci.nsIRequest.LOAD_BYPASS_CACHE); - ch1.asyncOpen(new Listener("Response1"), null); + ch1.asyncOpen2(new Listener("Response1")); var ch2 = setupChannel("/bug596443", "Should not be used"); - ch2.asyncOpen(new Listener("Response1"), null); // Note param: we expect this to come from cache + ch2.asyncOpen2(new Listener("Response1")); // Note param: we expect this to come from cache do_test_pending(); } diff --git a/netwerk/test/unit/test_bug618835.js b/netwerk/test/unit/test_bug618835.js index 423858f7b2..811608a619 100644 --- a/netwerk/test/unit/test_bug618835.js +++ b/netwerk/test/unit/test_bug618835.js @@ -12,22 +12,13 @@ // Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); var httpserv; function setupChannel(path) { - var ios = - Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); - return chan = ios.newChannel2(path, - "", - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER) - .QueryInterface(Ci.nsIHttpChannel); + return NetUtil.newChannel({uri: path, loadUsingSystemPrincipal: true}) + .QueryInterface(Ci.nsIHttpChannel); } // Verify that Content-Location-URI has been loaded once, load post_target @@ -40,7 +31,7 @@ InitialListener.prototype = { var channel = setupChannel("http://localhost:" + httpserv.identity.primaryPort + "/post"); channel.requestMethod = "POST"; - channel.asyncOpen(new RedirectingListener(), null); + channel.asyncOpen2(new RedirectingListener()); }); } }; @@ -55,7 +46,7 @@ RedirectingListener.prototype = { var channel = setupChannel("http://localhost:" + httpserv.identity.primaryPort + "/post"); channel.requestMethod = "POST"; - channel.asyncOpen(new VerifyingListener(), null); + channel.asyncOpen2(new VerifyingListener()); }); } }; @@ -69,7 +60,7 @@ VerifyingListener.prototype = { do_check_eq(2, numberOfHandlerCalls); var channel = setupChannel("http://localhost:" + httpserv.identity.primaryPort + "/cl"); - channel.asyncOpen(new FinalListener(), null); + channel.asyncOpen2(new FinalListener()); } }; @@ -97,7 +88,7 @@ function run_test() { // Load Content-Location URI into cache and start the chain of loads var channel = setupChannel("http://localhost:" + httpserv.identity.primaryPort + "/cl"); - channel.asyncOpen(new InitialListener(), null); + channel.asyncOpen2(new InitialListener()); do_test_pending(); } diff --git a/netwerk/test/unit/test_bug856978.js b/netwerk/test/unit/test_bug856978.js index f8408a79e3..9624ef64e1 100644 --- a/netwerk/test/unit/test_bug856978.js +++ b/netwerk/test/unit/test_bug856978.js @@ -10,7 +10,7 @@ // passes iff both succeeds. Components.utils.import("resource://testing-common/httpd.js"); -Components.utils.import("resource://gre/modules/Services.jsm"); +Components.utils.import("resource://gre/modules/NetUtil.jsm"); var notification = "http-on-modify-request"; @@ -97,16 +97,8 @@ var listener = { }; function makeChan(url) { - var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); - var chan = ios.newChannel2(url, - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER).QueryInterface(Ci.nsIHttpChannel); - return chan; + return NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true}) + .QueryInterface(Ci.nsIHttpChannel); } var tests = [startAuthHeaderTest, removeAuthHeaderTest]; @@ -125,7 +117,7 @@ function run_test() { function startAuthHeaderTest() { var chan = makeChan(authCredsURL); - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); do_test_pending(); } @@ -137,7 +129,7 @@ function removeAuthHeaderTest() { var chan = makeChan(authURL); // Indicating that the request is coming from the second test. chan.setRequestHeader("Test", "1", false); - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); do_test_pending(); } diff --git a/netwerk/test/unit/test_cacheflags.js b/netwerk/test/unit/test_cacheflags.js index 84e9362a15..f1d1febc62 100644 --- a/netwerk/test/unit/test_cacheflags.js +++ b/netwerk/test/unit/test_cacheflags.js @@ -1,5 +1,5 @@ Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); var httpserver = new HttpServer(); httpserver.start(-1); @@ -13,6 +13,8 @@ var longexpPath = "/longexp/" + suffix; var longexp2Path = "/longexp/2/" + suffix; var nocachePath = "/nocache" + suffix; var nostorePath = "/nostore" + suffix; +var test410Path = "/test410" + suffix; +var test404Path = "/test404" + suffix; // We attach this to channel when we want to test Private Browsing mode function LoadContext(usePrivateBrowsing) { @@ -42,16 +44,7 @@ LoadContext.prototype = { PrivateBrowsingLoadContext = new LoadContext(true); function make_channel(url, flags, usePrivateBrowsing) { - var ios = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - var req = ios.newChannel2(url, - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + var req = NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true}); req.loadFlags = flags; if (usePrivateBrowsing) { req.notificationCallbacks = PrivateBrowsingLoadContext; @@ -112,7 +105,7 @@ Test.prototype = { "\n " + this.hitServer + "\n"); gHitServer = false; var channel = make_channel(this.path, this.flags, this.usePrivateBrowsing); - channel.asyncOpen(this, null); + channel.asyncOpen2(this); } }; @@ -266,8 +259,26 @@ var gTests = [ Ci.nsIRequest.VALIDATE_NEVER, false, // expect success false, // read from cache - false) // hit server - ]; + false), // hit server + + new Test(httpBase + test410Path, 0, + true, // expect success + false, // read from cache + true), // hit server + new Test(httpBase + test410Path, 0, + true, // expect success + true, // read from cache + false), // hit server + + new Test(httpBase + test404Path, 0, + true, // expect success + false, // read from cache + true), // hit server + new Test(httpBase + test404Path, 0, + true, // expect success + false, // read from cache + true) // hit server +]; function run_next_test() { @@ -280,7 +291,7 @@ function run_next_test() test.run(); } -function handler(metadata, response) { +function handler(httpStatus, metadata, response) { gHitServer = true; try { var etag = metadata.getHeader("If-None-Match"); @@ -291,7 +302,7 @@ function handler(metadata, response) { // Allow using the cached data response.setStatusLine(metadata.httpVersion, 304, "Not Modified"); } else { - response.setStatusLine(metadata.httpVersion, 200, "OK"); + response.setStatusLine(metadata.httpVersion, httpStatus, "Useless Phrase"); response.setHeader("Content-Type", "text/plain", false); response.setHeader("ETag", "testtag", false); const body = "data"; @@ -301,28 +312,36 @@ function handler(metadata, response) { function nocache_handler(metadata, response) { response.setHeader("Cache-Control", "no-cache", false); - handler(metadata, response); + handler(200, metadata, response); } function nostore_handler(metadata, response) { response.setHeader("Cache-Control", "no-store", false); - handler(metadata, response); + handler(200, metadata, response); +} + +function test410_handler(metadata, response) { + handler(410, metadata, response); +} + +function test404_handler(metadata, response) { + handler(404, metadata, response); } function shortexp_handler(metadata, response) { response.setHeader("Cache-Control", "max-age=0", false); - handler(metadata, response); + handler(200, metadata, response); } function longexp_handler(metadata, response) { response.setHeader("Cache-Control", "max-age=10000", false); - handler(metadata, response); + handler(200, metadata, response); } // test spaces around max-age value token function longexp2_handler(metadata, response) { response.setHeader("Cache-Control", "max-age = 10000", false); - handler(metadata, response); + handler(200, metadata, response); } function run_test() { @@ -331,6 +350,8 @@ function run_test() { httpserver.registerPathHandler(longexp2Path, longexp2_handler); httpserver.registerPathHandler(nocachePath, nocache_handler); httpserver.registerPathHandler(nostorePath, nostore_handler); + httpserver.registerPathHandler(test410Path, test410_handler); + httpserver.registerPathHandler(test404Path, test404_handler); run_next_test(); do_test_pending(); diff --git a/netwerk/test/unit/test_content_encoding_gzip.js b/netwerk/test/unit/test_content_encoding_gzip.js index ab945e1c21..a3e93b5f41 100644 --- a/netwerk/test/unit/test_content_encoding_gzip.js +++ b/netwerk/test/unit/test_content_encoding_gzip.js @@ -1,5 +1,5 @@ Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); var httpserver = new HttpServer(); var index = 0; @@ -52,18 +52,10 @@ var tests = [ ]; function setupChannel(url) { - var ios = Components.classes["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - var chan = ios.newChannel2("http://localhost:" + - httpserver.identity.primaryPort + url, - "", - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); - return chan; + return NetUtil.newChannel({ + uri: "http://localhost:" + httpserver.identity.primaryPort + url, + loadUsingSystemPrincipal: true + }); } function startIter() { @@ -72,7 +64,7 @@ function startIter() { prefs.setCharPref("network.http.accept-encoding", "gzip, deflate"); } var channel = setupChannel(tests[index].url); - channel.asyncOpen(new ChannelListener(completeIter, channel, tests[index].flags), null); + channel.asyncOpen2(new ChannelListener(completeIter, channel, tests[index].flags)); } function completeIter(request, data, ctx) { diff --git a/netwerk/test/unit/test_content_sniffer.js b/netwerk/test/unit/test_content_sniffer.js index f87c402f51..c422e0497c 100644 --- a/netwerk/test/unit/test_content_sniffer.js +++ b/netwerk/test/unit/test_content_sniffer.js @@ -1,7 +1,7 @@ // This file tests nsIContentSniffer, introduced in bug 324985 Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); const unknownType = "application/x-unknown-content-type"; const sniffedType = "application/x-sniffed"; @@ -71,16 +71,7 @@ var listener = { }; function makeChan(url) { - var ios = Components.classes["@mozilla.org/network/io-service;1"] - .getService(Components.interfaces.nsIIOService); - var chan = ios.newChannel2(url, - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + var chan = NetUtil.newChannel({ uri: url, loadUsingSystemPrincipal: true}); if (sniffing_enabled) chan.loadFlags |= Components.interfaces.nsIChannel.LOAD_CALL_CONTENT_SNIFFERS; @@ -133,7 +124,7 @@ function run_test_iteration(index) { var chan = makeChan(urls[index - 1]); listener._iteration++; - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); do_test_pending(); } diff --git a/netwerk/test/unit/test_data_protocol.js b/netwerk/test/unit/test_data_protocol.js index a8bac95fcf..5d92c2f9a8 100644 --- a/netwerk/test/unit/test_data_protocol.js +++ b/netwerk/test/unit/test_data_protocol.js @@ -1,5 +1,5 @@ /* run some tests on the data: protocol handler */ -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); // The behaviour wrt spaces is: // - Textual content keeps all spaces @@ -40,16 +40,12 @@ function run_test() { for (var i = 0; i < urls.length; ++i) { dump("*** opening channel " + i + "\n"); do_test_pending(); - var chan = ios.newChannel2(urls[i][0], - "", - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + var chan = NetUtil.newChannel({ + uri: urls[i][0], + loadUsingSystemPrincipal: true + }); chan.contentType = "foo/bar"; // should be ignored - chan.asyncOpen(new ChannelListener(on_read_complete, i), null); + chan.asyncOpen2(new ChannelListener(on_read_complete, i)); } } diff --git a/netwerk/test/unit/test_duplicate_headers.js b/netwerk/test/unit/test_duplicate_headers.js index 0bf406e91c..80c1708871 100644 --- a/netwerk/test/unit/test_duplicate_headers.js +++ b/netwerk/test/unit/test_duplicate_headers.js @@ -7,7 +7,7 @@ // Test infrastructure Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); XPCOMUtils.defineLazyGetter(this, "URL", function() { return "http://localhost:" + httpserver.identity.primaryPort; @@ -33,22 +33,16 @@ function run_test_number(num) var channel = setupChannel(testPath); flags = test_flags[num]; // OK if flags undefined for test - channel.asyncOpen(new ChannelListener(eval("completeTest" + num), - channel, flags), null); + channel.asyncOpen2(new ChannelListener(eval("completeTest" + num), + channel, flags)); } function setupChannel(url) { - var ios = Components.classes["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - var chan = ios.newChannel2(URL + url, - "", - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + var chan = NetUtil.newChannel({ + uri: URL + url, + loadUsingSystemPrincipal: true + }); var httpChan = chan.QueryInterface(Components.interfaces.nsIHttpChannel); return httpChan; } diff --git a/netwerk/test/unit/test_fallback_no-cache-entry_canceled.js b/netwerk/test/unit/test_fallback_no-cache-entry_canceled.js index 5ce21bd48d..35e9e2be41 100644 --- a/netwerk/test/unit/test_fallback_no-cache-entry_canceled.js +++ b/netwerk/test/unit/test_fallback_no-cache-entry_canceled.js @@ -99,7 +99,7 @@ function run_test() chan.notificationCallbacks = new ChannelEventSink(ES_ABORT_REDIRECT); var chanac = chan.QueryInterface(Ci.nsIApplicationCacheChannel); chanac.chooseApplicationCache = true; - chan.asyncOpen(new ChannelListener(finish_test, null, CL_EXPECT_FAILURE), null); + chan.asyncOpen2(new ChannelListener(finish_test, null, CL_EXPECT_FAILURE)); }} var os = Cc["@mozilla.org/observer-service;1"]. diff --git a/netwerk/test/unit/test_fallback_no-cache-entry_passing.js b/netwerk/test/unit/test_fallback_no-cache-entry_passing.js index 4ce1ee3710..138dc9e57a 100644 --- a/netwerk/test/unit/test_fallback_no-cache-entry_passing.js +++ b/netwerk/test/unit/test_fallback_no-cache-entry_passing.js @@ -1,5 +1,6 @@ Cu.import("resource://testing-common/httpd.js"); Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); var httpServer = null; // Need to randomize, because apparently no one clears our cache @@ -98,7 +99,7 @@ function run_test() Ci.nsICachingChannel.LOAD_ONLY_FROM_CACHE); var chanac = chan.QueryInterface(Ci.nsIApplicationCacheChannel); chanac.chooseApplicationCache = true; - chan.asyncOpen(new ChannelListener(finish_test), null); + chan.asyncOpen2(new ChannelListener(finish_test)); }} var os = Cc["@mozilla.org/observer-service;1"]. diff --git a/netwerk/test/unit/test_fallback_redirect-to-different-origin_canceled.js b/netwerk/test/unit/test_fallback_redirect-to-different-origin_canceled.js index bba4240b2d..029319ef84 100644 --- a/netwerk/test/unit/test_fallback_redirect-to-different-origin_canceled.js +++ b/netwerk/test/unit/test_fallback_redirect-to-different-origin_canceled.js @@ -1,5 +1,6 @@ Cu.import("resource://testing-common/httpd.js"); Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); var httpServer = null; // Need to randomize, because apparently no one clears our cache @@ -103,7 +104,7 @@ function run_test() chan.notificationCallbacks = new ChannelEventSink(ES_ABORT_REDIRECT); var chanac = chan.QueryInterface(Ci.nsIApplicationCacheChannel); chanac.chooseApplicationCache = true; - chan.asyncOpen(new ChannelListener(finish_test, null, CL_EXPECT_FAILURE), null); + chan.asyncOpen2(new ChannelListener(finish_test, null, CL_EXPECT_FAILURE)); }} var os = Cc["@mozilla.org/observer-service;1"]. diff --git a/netwerk/test/unit/test_fallback_redirect-to-different-origin_passing.js b/netwerk/test/unit/test_fallback_redirect-to-different-origin_passing.js index 9c12b9a71b..213fff21a2 100644 --- a/netwerk/test/unit/test_fallback_redirect-to-different-origin_passing.js +++ b/netwerk/test/unit/test_fallback_redirect-to-different-origin_passing.js @@ -1,5 +1,6 @@ Cu.import("resource://testing-common/httpd.js"); Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); var httpServer = null; // Need to randomize, because apparently no one clears our cache @@ -102,7 +103,7 @@ function run_test() var chan = make_channel(randomURI); var chanac = chan.QueryInterface(Ci.nsIApplicationCacheChannel); chanac.chooseApplicationCache = true; - chan.asyncOpen(new ChannelListener(finish_test), null); + chan.asyncOpen2(new ChannelListener(finish_test)); }} var os = Cc["@mozilla.org/observer-service;1"]. diff --git a/netwerk/test/unit/test_gzipped_206.js b/netwerk/test/unit/test_gzipped_206.js index c541d0eb5e..8e4eaa4c76 100644 --- a/netwerk/test/unit/test_gzipped_206.js +++ b/netwerk/test/unit/test_gzipped_206.js @@ -1,5 +1,7 @@ Cu.import("resource://testing-common/httpd.js"); Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); + var httpserver = null; @@ -10,16 +12,7 @@ const responseBody = [0x1f, 0x8b, 0x08, 0x08, 0xef, 0x70, 0xe6, 0x4c, 0x00, 0x03 0x92, 0xd4, 0xe2, 0x12, 0x2e, 0x2e, 0x00, 0x00, 0xe5, 0xe6, 0xf0, 0x20, 0x00, 0x00, 0x00]; function make_channel(url, callback, ctx) { - var ios = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - return ios.newChannel2(url, - "", - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + return NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true}); } var doRangeResponse = false; @@ -68,7 +61,7 @@ function continue_test(request, data) { do_check_eq(17, data.length); var chan = make_channel("http://localhost:" + httpserver.identity.primaryPort + "/cached/test.gz"); - chan.asyncOpen(new ChannelListener(finish_test, null, CL_EXPECT_GZIP), null); + chan.asyncOpen2(new ChannelListener(finish_test, null, CL_EXPECT_GZIP)); } var enforcePref; @@ -96,6 +89,6 @@ function run_test() { var chan = make_channel("http://localhost:" + httpserver.identity.primaryPort + "/cached/test.gz"); - chan.asyncOpen(new ChannelListener(continue_test, null, CL_EXPECT_GZIP | CL_IGNORE_CL), null); + chan.asyncOpen2(new ChannelListener(continue_test, null, CL_EXPECT_GZIP | CL_IGNORE_CL)); do_test_pending(); } diff --git a/netwerk/test/unit/test_multipart_streamconv.js b/netwerk/test/unit/test_multipart_streamconv.js index 119207b5bd..c35628faa4 100644 --- a/netwerk/test/unit/test_multipart_streamconv.js +++ b/netwerk/test/unit/test_multipart_streamconv.js @@ -1,5 +1,5 @@ Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); var httpserver = null; @@ -8,33 +8,11 @@ XPCOMUtils.defineLazyGetter(this, "uri", function() { }); function make_channel(url) { - var ios = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - return ios.newChannel2(url, - "", - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + return NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true}); } var multipartBody = "--boundary\r\n\r\nSome text\r\n--boundary\r\n\r\n\r\n--boundary--"; -function make_channel(url) { - var ios = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - return ios.newChannel2(url, - "", - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); -} - function contentHandler(metadata, response) { response.setHeader("Content-Type", 'multipart/mixed; boundary="boundary"'); @@ -110,6 +88,6 @@ function run_test() null); var chan = make_channel(uri); - chan.asyncOpen(conv, null); + chan.asyncOpen2(conv); do_test_pending(); } diff --git a/netwerk/test/unit/test_nojsredir.js b/netwerk/test/unit/test_nojsredir.js index 5a15835ad5..d61c41a183 100644 --- a/netwerk/test/unit/test_nojsredir.js +++ b/netwerk/test/unit/test_nojsredir.js @@ -1,5 +1,5 @@ Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); var httpserver = new HttpServer(); var index = 0; @@ -16,23 +16,15 @@ var tests = [ ]; function setupChannel(url) { - var ios = Components.classes["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - var chan = ios.newChannel2("http://localhost:" + - httpserver.identity.primaryPort + url, - "", - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); - return chan; + return NetUtil.newChannel({ + uri: "http://localhost:" + httpserver.identity.primaryPort + url, + loadUsingSystemPrincipal: true + }); } function startIter() { var channel = setupChannel(tests[index].url); - channel.asyncOpen(new ChannelListener(completeIter, channel, tests[index].flags), null); + channel.asyncOpen2(new ChannelListener(completeIter, channel, tests[index].flags)); } function completeIter(request, data, ctx) { diff --git a/netwerk/test/unit/test_plaintext_sniff.js b/netwerk/test/unit/test_plaintext_sniff.js index 3c12b99d5b..cf4bc461dc 100644 --- a/netwerk/test/unit/test_plaintext_sniff.js +++ b/netwerk/test/unit/test_plaintext_sniff.js @@ -1,6 +1,7 @@ // Test the plaintext-or-binary sniffer Cu.import("resource://testing-common/httpd.js"); +Cu.import("resource://gre/modules/NetUtil.jsm"); // List of Content-Type headers to test. For each header we have an array. // The first element in the array is the Content-Type header string. The @@ -73,19 +74,11 @@ for (i = 0; i <= 127; ++i) { } function makeChan(headerIdx, bodyIdx) { - var ios = Components.classes["@mozilla.org/network/io-service;1"] - .getService(Components.interfaces.nsIIOService); - var chan = - ios.newChannel2("http://localhost:" + httpserv.identity.primaryPort + - "/" + headerIdx + "/" + bodyIdx, - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER) - .QueryInterface(Components.interfaces.nsIHttpChannel); + var chan = NetUtil.newChannel({ + uri: "http://localhost:" + httpserv.identity.primaryPort + + "/" + headerIdx + "/" + bodyIdx, + loadUsingSystemPrincipal: true + }).QueryInterface(Components.interfaces.nsIHttpChannel); chan.loadFlags |= Components.interfaces.nsIChannel.LOAD_CALL_CONTENT_SNIFFERS; @@ -156,7 +149,7 @@ function doTest(headerIdx, bodyIdx) { var listener = makeListener(headerIdx, bodyIdx); - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); do_test_pending(); } diff --git a/netwerk/test/unit/test_predictor.js b/netwerk/test/unit/test_predictor.js index c1097b6ec1..6f99fcf0a7 100644 --- a/netwerk/test/unit/test_predictor.js +++ b/netwerk/test/unit/test_predictor.js @@ -1,8 +1,9 @@ -var Cc = Components.classes; var Ci = Components.interfaces; var Cu = Components.utils; var Cr = Components.results; +var Cc = Components.classes; +Cu.import("resource://testing-common/httpd.js"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/LoadContextInfo.jsm"); @@ -46,8 +47,39 @@ LoadContext.prototype = { var load_context = new LoadContext(); -var Verifier = function _verifier(testing, expected_preconnects, expected_preresolves) { +var ValidityChecker = function(verifier, httpStatus) { + this.verifier = verifier; + this.httpStatus = httpStatus; +}; + +ValidityChecker.prototype = { + verifier: null, + httpStatus: 0, + + QueryInterface: function listener_qi(iid) { + if (iid.equals(Ci.nsISupports) || + iid.equals(Ci.nsICacheEntryOpenCallback)) { + return this; + } + throw Components.results.NS_ERROR_NO_INTERFACE; + }, + + onCacheEntryCheck: function(entry, appCache) + { + return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED; + }, + + onCacheEntryAvailable: function(entry, isnew, appCache, status) + { + // Check if forced valid + do_check_eq(entry.isForcedValid, this.httpStatus === 200); + this.verifier.maybe_run_next_test(); + } +} + +var Verifier = function _verifier(testing, expected_prefetches, expected_preconnects, expected_preresolves) { this.verifying = testing; + this.expected_prefetches = expected_prefetches; this.expected_preconnects = expected_preconnects; this.expected_preresolves = expected_preresolves; }; @@ -55,6 +87,7 @@ var Verifier = function _verifier(testing, expected_preconnects, expected_preres Verifier.prototype = { complete: false, verifying: null, + expected_prefetches: null, expected_preconnects: null, expected_preresolves: null, @@ -72,7 +105,8 @@ Verifier.prototype = { }, maybe_run_next_test: function verifier_maybe_run_next_test() { - if (this.expected_preconnects.length === 0 && + if (this.expected_prefetches.length === 0 && + this.expected_preconnects.length === 0 && this.expected_preresolves.length === 0 && !this.complete) { this.complete = true; @@ -82,6 +116,21 @@ Verifier.prototype = { } }, + onPredictPrefetch: function verifier_onPredictPrefetch(uri, status) { + var index = this.expected_prefetches.indexOf(uri.asciiSpec); + if (index == -1 && !this.complete) { + do_check_true(false, "Got prefetch for unexpected uri " + uri.asciiSpec); + } else { + this.expected_prefetches.splice(index, 1); + } + + dump("checking validity of entry for " + uri.spec + "\n"); + var checker = new ValidityChecker(this, status); + asyncOpenCacheEntry(uri.spec, "disk", + Ci.nsICacheStorage.OPEN_NORMALLY, LoadContextInfo.default, + checker); + }, + onPredictPreconnect: function verifier_onPredictPreconnect(uri) { var origin = extract_origin(uri); var index = this.expected_preconnects.indexOf(origin); @@ -172,7 +221,7 @@ function test_link_hover() { var referrer = newURI("http://localhost:4444/foo"); var preconns = ["http://localhost:4444"]; - var verifier = new Verifier("hover", preconns, []); + var verifier = new Verifier("hover", [], preconns, []); predictor.predict(uri, referrer, predictor.PREDICT_LINK, load_context, verifier); } @@ -194,7 +243,7 @@ function continue_test_pageload() { preconns.push(extract_origin(sruri)); } - var verifier = new Verifier("pageload", preconns, []); + var verifier = new Verifier("pageload", [], preconns, []); predictor.predict(pageload_toplevel, null, predictor.PREDICT_LOAD, load_context, verifier); } @@ -230,7 +279,7 @@ function continue_test_redrect() { preconns.push(extract_origin(sruri)); } - var verifier = new Verifier("redirect", preconns, []); + var verifier = new Verifier("redirect", [], preconns, []); predictor.predict(redirect_inituri, null, predictor.PREDICT_LOAD, load_context, verifier); } @@ -263,7 +312,7 @@ function test_startup() { preconns.push(extract_origin(uri)); } - var verifier = new Verifier("startup", preconns, []); + var verifier = new Verifier("startup", [], preconns, []); predictor.predict(null, null, predictor.PREDICT_STARTUP, load_context, verifier); } @@ -277,7 +326,7 @@ function continue_test_dns() { predictor.learn(sruri, dns_toplevel, predictor.LEARN_LOAD_SUBRESOURCE, load_context); var preresolves = [extract_origin(sruri)]; - var verifier = new Verifier("dns", [], preresolves); + var verifier = new Verifier("dns", [], [], preresolves); predictor.predict(dns_toplevel, null, predictor.PREDICT_LOAD, load_context, verifier); } @@ -313,7 +362,7 @@ function continue_test_origin() { } var loaduri = newURI("http://localhost:4444/anotherpage.html"); - var verifier = new Verifier("origin", preconns, []); + var verifier = new Verifier("origin", [], preconns, []); predictor.predict(loaduri, null, predictor.PREDICT_LOAD, load_context, verifier); } @@ -327,8 +376,135 @@ function test_origin() { }); } +var httpserv = null; +var prefetch_tluri; +var prefetch_sruri; + +function prefetchHandler(metadata, response) { + response.setStatusLine(metadata.httpVersion, 200, "OK"); + var body = "Success (meow meow meow)."; + + response.bodyOutputStream.write(body, body.length); +} + +var prefetchListener = { + onStartRequest: function(request, ctx) { + do_check_eq(request.status, Cr.NS_OK); + }, + + onDataAvailable: function(request, cx, stream, offset, cnt) { + read_stream(stream, cnt); + }, + + onStopRequest: function(request, ctx, status) { + run_next_test(); + } +}; + +function test_prefetch_setup() { + // Disable preconnects and preresolves + Services.prefs.setIntPref("network.predictor.preconnect-min-confidence", 101); + Services.prefs.setIntPref("network.predictor.preresolve-min-confidence", 101); + + Services.prefs.setBoolPref("network.predictor.enable-prefetch", true); + + // Makes it so we only have to call test_prefetch_prime twice to make prefetch + // do its thing. + Services.prefs.setIntPref("network.predictor.prefetch-rolling-load-count", 2); + + // This test does not run in e10s-mode, so we'll just go ahead and skip it. + // We've left the e10s test code in below, just in case someone wants to try + // to make it work at some point in the future. + if (!running_single_process) { + dump("skipping test_prefetch_setup due to e10s\n"); + run_next_test(); + return; + } + + httpserv = new HttpServer(); + httpserv.registerPathHandler("/cat.jpg", prefetchHandler); + httpserv.start(-1); + + var tluri = "http://127.0.0.1:" + httpserv.identity.primaryPort + "/index.html"; + var sruri = "http://127.0.0.1:" + httpserv.identity.primaryPort + "/cat.jpg"; + prefetch_tluri = newURI(tluri); + prefetch_sruri = newURI(sruri); + if (!running_single_process && !is_child_process()) { + // Give the child process access to these values + sendCommand("prefetch_tluri = newURI(\"" + tluri + "\");"); + sendCommand("prefetch_sruri = newURI(\"" + sruri + "\");"); + } + + run_next_test(); +} + +// Used to "prime the pump" for prefetch - it makes sure all our learns go +// through as expected so that prefetching will happen. +function test_prefetch_prime() { + // This test does not run in e10s-mode, so we'll just go ahead and skip it. + // We've left the e10s test code in below, just in case someone wants to try + // to make it work at some point in the future. + if (!running_single_process) { + dump("skipping test_prefetch_prime due to e10s\n"); + run_next_test(); + return; + } + + open_and_continue([prefetch_tluri], function() { + if (running_single_process) { + predictor.learn(prefetch_tluri, null, predictor.LEARN_LOAD_TOPLEVEL, load_context); + predictor.learn(prefetch_sruri, prefetch_tluri, predictor.LEARN_LOAD_SUBRESOURCE, load_context); + } else { + sendCommand("predictor.learn(prefetch_tluri, null, predictor.LEARN_LOAD_TOPLEVEL, load_context);"); + sendCommand("predictor.learn(prefetch_sruri, prefetch_tluri, predictor.LEARN_LOAD_SUBRESOURCE, load_context);"); + } + + // This runs in the parent or only process + var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); + var channel = ios.newChannel2(prefetch_sruri.asciiSpec, null, null, null, + Services.scriptSecurityManager.getSystemPrincipal(), null, + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER).QueryInterface(Ci.nsIHttpChannel); + channel.requestMethod = "GET"; + channel.referrer = prefetch_tluri; + channel.asyncOpen(prefetchListener, channel); + }); +} + +function test_prefetch() { + // This test does not run in e10s-mode, so we'll just go ahead and skip it. + // We've left the e10s test code in below, just in case someone wants to try + // to make it work at some point in the future. + if (!running_single_process) { + dump("skipping test_prefetch due to e10s\n"); + run_next_test(); + return; + } + + // Setup for this has all been taken care of by test_prefetch_prime, so we can + // continue on without pausing here. + if (running_single_process) { + continue_test_prefetch(); + } else { + sendCommand("continue_test_prefetch();"); + } +} + +function continue_test_prefetch() { + var prefetches = [prefetch_sruri.asciiSpec]; + var verifier = new Verifier("prefetch", prefetches, [], []); + predictor.predict(prefetch_tluri, null, predictor.PREDICT_LOAD, load_context, verifier); +} + function cleanup() { observer.cleaningUp = true; + if (running_single_process) { + // The http server is required (and started) by the prefetch test, which + // only runs in single-process mode, so don't try to shut it down if we're + // in e10s mode. + do_test_pending(); + httpserv.stop(do_test_finished); + } reset_predictor(); } @@ -343,6 +519,10 @@ var tests = [ // END DISABLED TESTS test_origin, test_dns, + test_prefetch_setup, + test_prefetch_prime, + test_prefetch_prime, + test_prefetch, // This must ALWAYS come last, to ensure we clean up after ourselves cleanup ]; @@ -397,6 +577,9 @@ function run_test_real() { Services.prefs.clearUserPref("network.predictor.cleaned-up"); Services.prefs.clearUserPref("browser.cache.use_new_backend_temp"); Services.prefs.clearUserPref("browser.cache.use_new_backend"); + Services.prefs.clearUserPref("network.predictor.preresolve-min-confidence"); + Services.prefs.clearUserPref("network.predictor.enable-prefetch"); + Services.prefs.clearUserPref("network.predictor.prefetch-rolling-load-count"); }); run_next_test(); diff --git a/netwerk/test/unit/test_range_requests.js b/netwerk/test/unit/test_range_requests.js index 423d1d2d6a..1850ade438 100644 --- a/netwerk/test/unit/test_range_requests.js +++ b/netwerk/test/unit/test_range_requests.js @@ -19,7 +19,7 @@ // for each. None of the handlers should see a Range-header. Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); var httpserver = null; @@ -35,17 +35,8 @@ const partial_data_length = 4; var port = null; // set in run_test function make_channel(url, callback, ctx) { - var ios = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - var chan = ios.newChannel2(url, - "", - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); - return chan.QueryInterface(Ci.nsIHttpChannel); + return NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true}) + .QueryInterface(Ci.nsIHttpChannel); } // StreamListener which cancels its request on first data available @@ -138,7 +129,7 @@ function handler_2(metadata, response) { function received_partial_2(request, data) { do_check_eq(data, undefined); var chan = make_channel("http://localhost:" + port + "/test_2"); - chan.asyncOpen(new ChannelListener(received_cleartext, null), null); + chan.asyncOpen2(new ChannelListener(received_cleartext, null)); } var case_3_request_no = 0; @@ -166,7 +157,7 @@ function handler_3(metadata, response) { function received_partial_3(request, data) { do_check_eq(partial_data_length, data.length); var chan = make_channel("http://localhost:" + port + "/test_3"); - chan.asyncOpen(new ChannelListener(received_cleartext, null), null); + chan.asyncOpen2(new ChannelListener(received_cleartext, null)); } var case_4_request_no = 0; @@ -199,7 +190,7 @@ function received_partial_4(request, data) { // checking length does not work with encoded data // do_check_eq(partial_data_length, data.length); var chan = make_channel("http://localhost:" + port + "/test_4"); - chan.asyncOpen(new MyListener(received_cleartext), null); + chan.asyncOpen2(new MyListener(received_cleartext)); } var case_5_request_no = 0; @@ -227,7 +218,7 @@ function received_partial_5(request, data) { do_check_eq(partial_data_length, data.length); var chan = make_channel("http://localhost:" + port + "/test_5"); chan.setRequestHeader("If-Match", "Some eTag", false); - chan.asyncOpen(new ChannelListener(received_cleartext, null), null); + chan.asyncOpen2(new ChannelListener(received_cleartext, null)); } var case_6_request_no = 0; @@ -257,7 +248,7 @@ function received_partial_6(request, data) { // would like to verify that the response does not have Accept-Ranges do_check_eq(partial_data_length, data.length); var chan = make_channel("http://localhost:" + port + "/test_6"); - chan.asyncOpen(new ChannelListener(received_cleartext, null), null); + chan.asyncOpen2(new ChannelListener(received_cleartext, null)); } const simpleBody = "0123456789"; @@ -305,7 +296,7 @@ function received_partial_7(request, data) { do_check_eq(4, data.length); // do it again to get the rest var chan = make_channel("http://localhost:" + port + "/test_7"); - chan.asyncOpen(new ChannelListener(received_simple, null), null); + chan.asyncOpen2(new ChannelListener(received_simple, null)); } var case_8_request_no = 0; @@ -344,7 +335,43 @@ function received_partial_8(request, data) { do_check_eq(4, data.length); // do it again to get the rest var chan = make_channel("http://localhost:" + port + "/test_8"); - chan.asyncOpen(new FailedChannelListener(testFinished, null, CL_EXPECT_LATE_FAILURE), null); + chan.asyncOpen2(new FailedChannelListener(testFinished, null, CL_EXPECT_LATE_FAILURE)); +} + +var case_9_request_no = 0; +function handler_9(metadata, response) { + switch (case_9_request_no) { + case 0: + do_check_false(metadata.hasHeader("Range")); + response.setHeader("Content-Type", "text/plain", false); + response.setHeader("ETag", "W/test9WeakEtag"); + response.setHeader("Accept-Ranges", "bytes"); + response.setHeader("Cache-Control", "max-age=360000"); + response.setHeader("Content-Length", "10"); + response.processAsync(); + response.bodyOutputStream.write(simpleBody.slice(0, 4), 4); + response.finish(); // truncated response + break; + case 1: + do_check_false(metadata.hasHeader("Range")); + response.setHeader("Content-Type", "text/plain", false); + response.setHeader("ETag", "W/test9WeakEtag"); + response.setHeader("Accept-Ranges", "bytes"); + response.setHeader("Cache-Control", "max-age=360000"); + response.setHeader("Content-Length", "10"); + response.processAsync(); + response.bodyOutputStream.write(simpleBody, 10); + response.finish(); // full response + break; + default: + response.setStatusLine(metadata.httpVersion, 404, "Not Found"); + } + case_9_request_no++; +} +function received_partial_9(request, data) { + do_check_eq(partial_data_length, data.length); + var chan = make_channel("http://localhost:" + port + "/test_9"); + chan.asyncOpen2(new ChannelListener(received_simple, null)); } // Simple mechanism to keep track of tests and stop the server @@ -363,6 +390,7 @@ function run_test() { httpserver.registerPathHandler("/test_6", handler_6); httpserver.registerPathHandler("/test_7", handler_7); httpserver.registerPathHandler("/test_8", handler_8); + httpserver.registerPathHandler("/test_9", handler_9); httpserver.start(-1); port = httpserver.identity.primaryPort; @@ -372,31 +400,35 @@ function run_test() { // Case 2: zero-length partial entry must not trigger range-request var chan = make_channel("http://localhost:" + port + "/test_2"); - chan.asyncOpen(new Canceler(received_partial_2), null); + chan.asyncOpen2(new Canceler(received_partial_2)); // Case 3: no-store response must not trigger range-request var chan = make_channel("http://localhost:" + port + "/test_3"); - chan.asyncOpen(new MyListener(received_partial_3), null); + chan.asyncOpen2(new MyListener(received_partial_3)); // Case 4: response with content-encoding must not trigger range-request var chan = make_channel("http://localhost:" + port + "/test_4"); - chan.asyncOpen(new MyListener(received_partial_4), null); + chan.asyncOpen2(new MyListener(received_partial_4)); // Case 5: conditional request-header set by client var chan = make_channel("http://localhost:" + port + "/test_5"); - chan.asyncOpen(new MyListener(received_partial_5), null); + chan.asyncOpen2(new MyListener(received_partial_5)); // Case 6: response is not resumable (drop the Accept-Ranges header) var chan = make_channel("http://localhost:" + port + "/test_6"); - chan.asyncOpen(new MyListener(received_partial_6), null); + chan.asyncOpen2(new MyListener(received_partial_6)); // Case 7: a basic positive test var chan = make_channel("http://localhost:" + port + "/test_7"); - chan.asyncOpen(new MyListener(received_partial_7), null); + chan.asyncOpen2(new MyListener(received_partial_7)); // Case 8: check that mismatched 206 and 200 sizes throw error var chan = make_channel("http://localhost:" + port + "/test_8"); - chan.asyncOpen(new MyListener(received_partial_8), null); + chan.asyncOpen2(new MyListener(received_partial_8)); + + // Case 9: check that weak etag is not used for a range request + var chan = make_channel("http://localhost:" + port + "/test_9"); + chan.asyncOpen2(new MyListener(received_partial_9)); do_test_pending(); } diff --git a/netwerk/test/unit/test_redirect-caching_canceled.js b/netwerk/test/unit/test_redirect-caching_canceled.js index 668bc28bc9..2371078654 100644 --- a/netwerk/test/unit/test_redirect-caching_canceled.js +++ b/netwerk/test/unit/test_redirect-caching_canceled.js @@ -1,5 +1,5 @@ Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); XPCOMUtils.defineLazyGetter(this, "URL", function() { return "http://localhost:" + httpServer.identity.primaryPort; @@ -14,16 +14,7 @@ XPCOMUtils.defineLazyGetter(this, "randomURI", function() { }); function make_channel(url, callback, ctx) { - var ios = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - return ios.newChannel2(url, - "", - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + return NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true}); } const responseBody = "response body"; @@ -46,7 +37,7 @@ function firstTimeThrough(request, buffer) { do_check_eq(buffer, responseBody); var chan = make_channel(randomURI); - chan.asyncOpen(new ChannelListener(secondTimeThrough, null), null); + chan.asyncOpen2(new ChannelListener(secondTimeThrough, null)); } function secondTimeThrough(request, buffer) @@ -55,7 +46,7 @@ function secondTimeThrough(request, buffer) var chan = make_channel(randomURI); chan.loadFlags |= Ci.nsIRequest.LOAD_FROM_CACHE; chan.notificationCallbacks = new ChannelEventSink(ES_ABORT_REDIRECT); - chan.asyncOpen(new ChannelListener(finish_test, null, CL_EXPECT_FAILURE), null); + chan.asyncOpen2(new ChannelListener(finish_test, null, CL_EXPECT_FAILURE)); } function finish_test(request, buffer) @@ -72,6 +63,6 @@ function run_test() httpServer.start(-1); var chan = make_channel(randomURI); - chan.asyncOpen(new ChannelListener(firstTimeThrough, null), null); + chan.asyncOpen2(new ChannelListener(firstTimeThrough, null)); do_test_pending(); } diff --git a/netwerk/test/unit/test_redirect_history.js b/netwerk/test/unit/test_redirect_history.js index d56691cec8..07da064787 100644 --- a/netwerk/test/unit/test_redirect_history.js +++ b/netwerk/test/unit/test_redirect_history.js @@ -1,5 +1,5 @@ Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); XPCOMUtils.defineLazyGetter(this, "URL", function() { return "http://localhost:" + httpServer.identity.primaryPort; @@ -11,16 +11,7 @@ var redirects = []; const numRedirects = 10; function make_channel(url, callback, ctx) { - var ios = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - return ios.newChannel2(url, - "", - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + return NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true}); } const responseBody = "response body"; @@ -68,6 +59,6 @@ function run_test() httpServer.start(-1); var chan = make_channel(URL + redirects[0]); - chan.asyncOpen(new ChannelListener(finish_test, null), null); + chan.asyncOpen2(new ChannelListener(finish_test, null)); do_test_pending(); } diff --git a/netwerk/test/unit/test_resumable_channel.js b/netwerk/test/unit/test_resumable_channel.js index 7c6895b476..57a4481b96 100644 --- a/netwerk/test/unit/test_resumable_channel.js +++ b/netwerk/test/unit/test_resumable_channel.js @@ -1,7 +1,7 @@ /* Tests various aspects of nsIResumableChannel in combination with HTTP */ Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); XPCOMUtils.defineLazyGetter(this, "URL", function() { return "http://localhost:" + httpserver.identity.primaryPort; @@ -15,16 +15,7 @@ const NS_ERROR_NOT_RESUMABLE = 0x804b0019; const rangeBody = "Body of the range request handler.\r\n"; function make_channel(url, callback, ctx) { - var ios = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - return ios.newChannel2(url, - "", - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + return NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true}); } function AuthPrompt2() { @@ -99,7 +90,7 @@ function run_test() { // Try a non-resumable URL (responds with 200) var chan = make_channel(URL); chan.nsIResumableChannel.resumeAt(1, entityID); - chan.asyncOpen(new ChannelListener(try_resume, null, CL_EXPECT_FAILURE), null); + chan.asyncOpen2(new ChannelListener(try_resume, null, CL_EXPECT_FAILURE)); } function try_resume(request, data, ctx) { @@ -109,7 +100,7 @@ function run_test() { // Try a successful resume var chan = make_channel(URL + "/range"); chan.nsIResumableChannel.resumeAt(1, entityID); - chan.asyncOpen(new ChannelListener(try_resume_zero, null), null); + chan.asyncOpen2(new ChannelListener(try_resume_zero, null)); } function try_resume_zero(request, data, ctx) { @@ -121,7 +112,7 @@ function run_test() { var chan = make_channel(URL + "/acceptranges"); chan.nsIResumableChannel.resumeAt(0, entityID); chan.nsIHttpChannel.setRequestHeader("X-Range-Type", "none", false); - chan.asyncOpen(new ChannelListener(try_no_range, null, CL_EXPECT_FAILURE), null); + chan.asyncOpen2(new ChannelListener(try_no_range, null, CL_EXPECT_FAILURE)); } function try_no_range(request, data, ctx) { @@ -133,7 +124,7 @@ function run_test() { var chan = make_channel(URL + "/acceptranges"); chan.nsIResumableChannel.resumeAt(0, entityID); chan.nsIHttpChannel.setRequestHeader("X-Range-Type", "bytes", false); - chan.asyncOpen(new ChannelListener(try_bytes_range, null), null); + chan.asyncOpen2(new ChannelListener(try_bytes_range, null)); } function try_bytes_range(request, data, ctx) { @@ -145,7 +136,7 @@ function run_test() { var chan = make_channel(URL + "/acceptranges"); chan.nsIResumableChannel.resumeAt(0, entityID); chan.nsIHttpChannel.setRequestHeader("X-Range-Type", "foo, bar", false); - chan.asyncOpen(new ChannelListener(try_foo_bar_range, null, CL_EXPECT_FAILURE), null); + chan.asyncOpen2(new ChannelListener(try_foo_bar_range, null, CL_EXPECT_FAILURE)); } function try_foo_bar_range(request, data, ctx) { @@ -157,7 +148,7 @@ function run_test() { var chan = make_channel(URL + "/acceptranges"); chan.nsIResumableChannel.resumeAt(0, entityID); chan.nsIHttpChannel.setRequestHeader("X-Range-Type", "foobar", false); - chan.asyncOpen(new ChannelListener(try_foobar_range, null, CL_EXPECT_FAILURE), null); + chan.asyncOpen2(new ChannelListener(try_foobar_range, null, CL_EXPECT_FAILURE)); } function try_foobar_range(request, data, ctx) { @@ -169,7 +160,7 @@ function run_test() { var chan = make_channel(URL + "/acceptranges"); chan.nsIResumableChannel.resumeAt(0, entityID); chan.nsIHttpChannel.setRequestHeader("X-Range-Type", "bytes, foobar", false); - chan.asyncOpen(new ChannelListener(try_bytes_foobar_range, null), null); + chan.asyncOpen2(new ChannelListener(try_bytes_foobar_range, null)); } function try_bytes_foobar_range(request, data, ctx) { @@ -181,7 +172,7 @@ function run_test() { var chan = make_channel(URL + "/acceptranges"); chan.nsIResumableChannel.resumeAt(0, entityID); chan.nsIHttpChannel.setRequestHeader("X-Range-Type", "bytesfoo, bar", false); - chan.asyncOpen(new ChannelListener(try_bytesfoo_bar_range, null, CL_EXPECT_FAILURE), null); + chan.asyncOpen2(new ChannelListener(try_bytesfoo_bar_range, null, CL_EXPECT_FAILURE)); } function try_bytesfoo_bar_range(request, data, ctx) { @@ -192,7 +183,7 @@ function run_test() { // Try a server which doesn't send Accept-Ranges header at all var chan = make_channel(URL + "/acceptranges"); chan.nsIResumableChannel.resumeAt(0, entityID); - chan.asyncOpen(new ChannelListener(try_no_accept_ranges, null), null); + chan.asyncOpen2(new ChannelListener(try_no_accept_ranges, null)); } function try_no_accept_ranges(request, data, ctx) { @@ -203,8 +194,8 @@ function run_test() { // Try a successful suspend/resume from 0 var chan = make_channel(URL + "/range"); chan.nsIResumableChannel.resumeAt(0, entityID); - chan.asyncOpen(new ChannelListener(try_suspend_resume, null, - CL_SUSPEND | CL_EXPECT_3S_DELAY), null); + chan.asyncOpen2(new ChannelListener(try_suspend_resume, null, + CL_SUSPEND | CL_EXPECT_3S_DELAY)); } function try_suspend_resume(request, data, ctx) { @@ -215,7 +206,7 @@ function run_test() { // Try a successful resume from 0 var chan = make_channel(URL + "/range"); chan.nsIResumableChannel.resumeAt(0, entityID); - chan.asyncOpen(new ChannelListener(success, null), null); + chan.asyncOpen2(new ChannelListener(success, null)); } function success(request, data, ctx) { @@ -229,7 +220,7 @@ function run_test() { var chan = make_channel(URL + "/range"); chan.nsIResumableChannel.resumeAt(1, entityID); chan.nsIHttpChannel.setRequestHeader("X-Need-Auth", "true", false); - chan.asyncOpen(new ChannelListener(test_auth_nopw, null, CL_EXPECT_FAILURE), null); + chan.asyncOpen2(new ChannelListener(test_auth_nopw, null, CL_EXPECT_FAILURE)); } function test_auth_nopw(request, data, ctx) { @@ -242,7 +233,7 @@ function run_test() { httpserver.identity.primaryPort + "/auth"); chan.nsIResumableChannel.resumeAt(1, entityID); chan.notificationCallbacks = new Requestor(); - chan.asyncOpen(new ChannelListener(test_auth, null, CL_EXPECT_FAILURE), null); + chan.asyncOpen2(new ChannelListener(test_auth, null, CL_EXPECT_FAILURE)); } function test_auth(request, data, ctx) { dump("*** test_auth()\n"); @@ -255,7 +246,7 @@ function run_test() { chan.nsIResumableChannel.resumeAt(1, entityID); chan.notificationCallbacks = new Requestor(); chan.nsIHttpChannel.setRequestHeader("X-Need-Auth", "true", false); - chan.asyncOpen(new ChannelListener(test_auth_resume, null), null); + chan.asyncOpen2(new ChannelListener(test_auth_resume, null)); } function test_auth_resume(request, data, ctx) { @@ -267,7 +258,7 @@ function run_test() { var chan = make_channel(URL + "/range"); chan.nsIResumableChannel.resumeAt(1, entityID); chan.nsIHttpChannel.setRequestHeader("X-Want-404", "true", false); - chan.asyncOpen(new ChannelListener(test_404, null, CL_EXPECT_FAILURE), null); + chan.asyncOpen2(new ChannelListener(test_404, null, CL_EXPECT_FAILURE)); } function test_404(request, data, ctx) { @@ -278,7 +269,7 @@ function run_test() { // 416 Requested Range Not Satisfiable var chan = make_channel(URL + "/range"); chan.nsIResumableChannel.resumeAt(1000, entityID); - chan.asyncOpen(new ChannelListener(test_416, null, CL_EXPECT_FAILURE), null); + chan.asyncOpen2(new ChannelListener(test_416, null, CL_EXPECT_FAILURE)); } function test_416(request, data, ctx) { @@ -290,7 +281,7 @@ function run_test() { var chan = make_channel(URL + "/redir"); chan.nsIHttpChannel.setRequestHeader("X-Redir-To", URL + "/range", false); chan.nsIResumableChannel.resumeAt(1, entityID); - chan.asyncOpen(new ChannelListener(test_redir_resume, null), null); + chan.asyncOpen2(new ChannelListener(test_redir_resume, null)); } function test_redir_resume(request, data, ctx) { @@ -303,7 +294,7 @@ function run_test() { var chan = make_channel(URL + "/redir"); chan.nsIHttpChannel.setRequestHeader("X-Redir-To", URL + "/", false); chan.nsIResumableChannel.resumeAt(1, entityID); - chan.asyncOpen(new ChannelListener(test_redir_noresume, null, CL_EXPECT_FAILURE), null); + chan.asyncOpen2(new ChannelListener(test_redir_noresume, null, CL_EXPECT_FAILURE)); } function test_redir_noresume(request, data, ctx) { @@ -315,7 +306,7 @@ function run_test() { httpserver.start(-1); var chan = make_channel(URL + "/range"); - chan.asyncOpen(new ChannelListener(get_entity_id, null), null); + chan.asyncOpen2(new ChannelListener(get_entity_id, null)); do_test_pending(); } diff --git a/netwerk/test/unit/test_synthesized_response.js b/netwerk/test/unit/test_synthesized_response.js index 770a8673e8..bad8047fe0 100644 --- a/netwerk/test/unit/test_synthesized_response.js +++ b/netwerk/test/unit/test_synthesized_response.js @@ -3,7 +3,7 @@ Cu.import("resource://testing-common/httpd.js"); Cu.import("resource://gre/modules/NetUtil.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); XPCOMUtils.defineLazyGetter(this, "URL", function() { return "http://localhost:" + httpServer.identity.primaryPort; @@ -26,16 +26,8 @@ var gotOnStatus; function make_channel(url, body, cb) { gotOnProgress = false; gotOnStatus = false; - var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); - var chan = ios.newChannel2(url, - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER) - .QueryInterface(Ci.nsIHttpChannel); + var chan = NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true}) + .QueryInterface(Ci.nsIHttpChannel); chan.notificationCallbacks = { numChecks: 0, QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInterceptController, @@ -120,13 +112,13 @@ add_test(function() { var chan = make_channel(URL + '/body', null, function(chan) { chan.resetInterception(); }); - chan.asyncOpen(new ChannelListener(handle_remote_response, null), null); + chan.asyncOpen2(new ChannelListener(handle_remote_response, null)); }); // synthesize a response add_test(function() { var chan = make_channel(URL + '/body', NON_REMOTE_BODY); - chan.asyncOpen(new ChannelListener(handle_synthesized_response, null, CL_ALLOW_UNKNOWN_CL), null); + chan.asyncOpen2(new ChannelListener(handle_synthesized_response, null, CL_ALLOW_UNKNOWN_CL)); }); // hit the network instead of synthesizing, to test that no previous synthesized @@ -135,13 +127,13 @@ add_test(function() { var chan = make_channel(URL + '/body', null, function(chan) { chan.resetInterception(); }); - chan.asyncOpen(new ChannelListener(handle_remote_response, null), null); + chan.asyncOpen2(new ChannelListener(handle_remote_response, null)); }); // synthesize a different response to ensure no previous response is cached add_test(function() { var chan = make_channel(URL + '/body', NON_REMOTE_BODY_2); - chan.asyncOpen(new ChannelListener(handle_synthesized_response_2, null, CL_ALLOW_UNKNOWN_CL), null); + chan.asyncOpen2(new ChannelListener(handle_synthesized_response_2, null, CL_ALLOW_UNKNOWN_CL)); }); // ensure that the channel waits for a decision and synthesizes headers correctly @@ -157,7 +149,7 @@ add_test(function() { }); }); }); - chan.asyncOpen(new ChannelListener(handle_synthesized_response, null), null); + chan.asyncOpen2(new ChannelListener(handle_synthesized_response, null)); }); // ensure that the channel waits for a decision @@ -167,7 +159,7 @@ add_test(function() { chan.resetInterception(); }); }); - chan.asyncOpen(new ChannelListener(handle_remote_response, null), null); + chan.asyncOpen2(new ChannelListener(handle_remote_response, null)); }); // ensure that the intercepted channel supports suspend/resume @@ -184,8 +176,8 @@ add_test(function() { intercepted.finishSynthesizedResponse(''); }); }); - chan.asyncOpen(new ChannelListener(handle_synthesized_response, null, - CL_ALLOW_UNKNOWN_CL | CL_SUSPEND | CL_EXPECT_3S_DELAY), null); + chan.asyncOpen2(new ChannelListener(handle_synthesized_response, null, + CL_ALLOW_UNKNOWN_CL | CL_SUSPEND | CL_EXPECT_3S_DELAY)); }); // ensure that the intercepted channel can be cancelled @@ -193,8 +185,7 @@ add_test(function() { var chan = make_channel(URL + '/body', null, function(intercepted) { intercepted.cancel(Cr.NS_BINDING_ABORTED); }); - chan.asyncOpen(new ChannelListener(run_next_test, null, - CL_EXPECT_FAILURE), null); + chan.asyncOpen2(new ChannelListener(run_next_test, null, CL_EXPECT_FAILURE)); }); // ensure that the channel can't be cancelled via nsIInterceptedChannel after making a decision @@ -211,7 +202,7 @@ add_test(function() { do_check_true(gotexception); }); }); - chan.asyncOpen(new ChannelListener(handle_remote_response, null), null); + chan.asyncOpen2(new ChannelListener(handle_remote_response, null)); }); // ensure that the intercepted channel can be canceled during the response @@ -227,8 +218,8 @@ add_test(function() { channel.cancel(Cr.NS_BINDING_ABORTED); }); }); - chan.asyncOpen(new ChannelListener(run_next_test, null, - CL_EXPECT_FAILURE | CL_ALLOW_UNKNOWN_CL), null); + chan.asyncOpen2(new ChannelListener(run_next_test, null, + CL_EXPECT_FAILURE | CL_ALLOW_UNKNOWN_CL)); }); // ensure that the intercepted channel can be canceled before the response @@ -243,8 +234,8 @@ add_test(function() { intercepted.finishSynthesizedResponse(''); }); }); - chan.asyncOpen(new ChannelListener(run_next_test, null, - CL_EXPECT_FAILURE | CL_ALLOW_UNKNOWN_CL), null); + chan.asyncOpen2(new ChannelListener(run_next_test, null, + CL_EXPECT_FAILURE | CL_ALLOW_UNKNOWN_CL)); }); add_test(function() { diff --git a/netwerk/test/unit/test_traceable_channel.js b/netwerk/test/unit/test_traceable_channel.js index b9bf1fb022..00ccbb1277 100644 --- a/netwerk/test/unit/test_traceable_channel.js +++ b/netwerk/test/unit/test_traceable_channel.js @@ -4,7 +4,7 @@ // is correctly modified. Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); var httpserver = new HttpServer(); httpserver.start(-1); @@ -129,17 +129,8 @@ function test_handler(metadata, response) { } function make_channel(url) { - var ios = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - return ios.newChannel2(url, - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER) - .QueryInterface(Components.interfaces.nsIHttpChannel); + return NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true}) + .QueryInterface(Components.interfaces.nsIHttpChannel); } // Check if received body is correctly modified. @@ -154,6 +145,6 @@ function run_test() { httpserver.registerPathHandler("/testdir", test_handler); var channel = make_channel("http://localhost:" + PORT + "/testdir"); - channel.asyncOpen(new ChannelListener(channel_finished), null); + channel.asyncOpen2(new ChannelListener(channel_finished)); do_test_pending(); } diff --git a/netwerk/test/unit_ipc/test_predictor_wrap.js b/netwerk/test/unit_ipc/test_predictor_wrap.js index a796e97159..de4ddc174e 100644 --- a/netwerk/test/unit_ipc/test_predictor_wrap.js +++ b/netwerk/test/unit_ipc/test_predictor_wrap.js @@ -1,3 +1,27 @@ +// This is the bit that runs in the parent process when the test begins. Here's +// what happens: +// +// - Load the entire single-process test in the parent +// - Setup the test harness within the child process +// - Send a command to the child to have the single-process test loaded there, as well +// - Send a command to the child to make the predictor available +// - run_test_real in the parent +// - run_test_real does a bunch of pref-setting and other fun things on the parent +// - once all that's done, it calls run_next_test IN THE PARENT +// - every time run_next_test is called, it is called IN THE PARENT +// - the test that gets started then does any parent-side setup that's necessary +// - once the parent-side setup is done, it calls continue_ IN THE CHILD +// - when the test is done running on the child, the child calls predictor.reset +// this causes some code to be run on the parent which, when complete, sends an +// obserer service notification IN THE PARENT, which causes run_next_test to be +// called again, bumping us up to the top of the loop outlined here +// - when the final test is done, cleanup happens IN THE PARENT and we're done +// +// This is a little confusing, but it's what we have to do in order to have some +// things that must run on the parent (the setup - opening cache entries, etc) +// but with most of the test running on the child (calls to the predictor api, +// verification, etc). +// function run_test() { var test_path = do_get_file("../unit/test_predictor.js").path.replace(/\\/g, "/"); load(test_path); diff --git a/security/apps/AppSignatureVerification.cpp b/security/apps/AppSignatureVerification.cpp index aaf75026fb..1bf97caae6 100644 --- a/security/apps/AppSignatureVerification.cpp +++ b/security/apps/AppSignatureVerification.cpp @@ -7,32 +7,33 @@ #include "nsNSSCertificateDB.h" #include "AppTrustDomain.h" +#include "CryptoTask.h" +#include "NSSCertDBTrustDomain.h" +#include "ScopedNSSTypes.h" #include "base64.h" #include "certdb.h" -#include "CryptoTask.h" +#include "mozilla/Logging.h" #include "mozilla/RefPtr.h" #include "mozilla/UniquePtr.h" -#include "nsComponentManagerUtils.h" #include "nsCOMPtr.h" +#include "nsComponentManagerUtils.h" #include "nsDataSignatureVerifier.h" #include "nsHashKeys.h" +#include "nsIDirectoryEnumerator.h" #include "nsIFile.h" #include "nsIFileStreams.h" #include "nsIInputStream.h" #include "nsIStringEnumerator.h" -#include "nsIDirectoryEnumerator.h" #include "nsIZipReader.h" -#include "nsNetUtil.h" #include "nsNSSCertificate.h" +#include "nsNetUtil.h" #include "nsProxyRelease.h" -#include "nssb64.h" -#include "NSSCertDBTrustDomain.h" #include "nsString.h" #include "nsTHashtable.h" -#include "plstr.h" -#include "mozilla/Logging.h" +#include "nssb64.h" #include "pkix/pkix.h" #include "pkix/pkixnss.h" +#include "plstr.h" #include "secmime.h" @@ -877,10 +878,9 @@ VerifySignedManifest(AppTrustedRoot aTrustedRoot, } // Get base64 encoded string from manifest buffer digest - UniquePtr + UniquePORTString base64EncDigest(NSSBase64_EncodeItem(nullptr, nullptr, 0, - const_cast(&manifestCalculatedDigest.get())), - PORT_Free); + const_cast(&manifestCalculatedDigest.get()))); if (NS_WARN_IF(!base64EncDigest)) { return NS_ERROR_OUT_OF_MEMORY; } diff --git a/security/apps/AppTrustDomain.cpp b/security/apps/AppTrustDomain.cpp index 7995aa2246..4f6bc68ff2 100644 --- a/security/apps/AppTrustDomain.cpp +++ b/security/apps/AppTrustDomain.cpp @@ -165,8 +165,8 @@ AppTrustDomain::SetTrustedRoot(AppTrustedRoot trustedRoot) return SECFailure; } - mTrustedRoot = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), - &trustedDER, nullptr, false, true); + mTrustedRoot.reset(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), + &trustedDER, nullptr, false, true)); if (!mTrustedRoot) { return SECFailure; } @@ -244,7 +244,7 @@ AppTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA, // expose it in any other easy-to-use fashion. SECItem candidateCertDERSECItem = UnsafeMapInputToSECItem(candidateCertDER); - ScopedCERTCertificate candidateCert( + UniqueCERTCertificate candidateCert( CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &candidateCertDERSECItem, nullptr, false, true)); if (!candidateCert) { diff --git a/security/apps/AppTrustDomain.h b/security/apps/AppTrustDomain.h index cb9a2dd25b..29a9ac20bb 100644 --- a/security/apps/AppTrustDomain.h +++ b/security/apps/AppTrustDomain.h @@ -69,7 +69,7 @@ public: private: /*out*/ ScopedCERTCertList& mCertChain; void* mPinArg; // non-owning! - ScopedCERTCertificate mTrustedRoot; + UniqueCERTCertificate mTrustedRoot; unsigned int mMinRSABits; static StaticMutex sMutex; diff --git a/security/certverifier/BRNameMatchingPolicy.cpp b/security/certverifier/BRNameMatchingPolicy.cpp new file mode 100644 index 0000000000..47e6a1c2b9 --- /dev/null +++ b/security/certverifier/BRNameMatchingPolicy.cpp @@ -0,0 +1,38 @@ +/* -*- 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 "BRNameMatchingPolicy.h" + +#include "mozilla/Assertions.h" + +using namespace mozilla::psm; +using namespace mozilla::pkix; + +Result +BRNameMatchingPolicy::FallBackToCommonName( + Time notBefore, + /*out*/ FallBackToSearchWithinSubject& fallBackToCommonName) +{ + // (new Date("2016-08-23T00:00:00Z")).getTime() / 1000 + static const Time AUGUST_23_2016 = TimeFromEpochInSeconds(1471910400); + switch (mMode) + { + case Mode::Enforce: + fallBackToCommonName = FallBackToSearchWithinSubject::No; + break; + case Mode::EnforceAfter23August2016: + fallBackToCommonName = notBefore > AUGUST_23_2016 + ? FallBackToSearchWithinSubject::No + : FallBackToSearchWithinSubject::Yes; + break; + case Mode::DoNotEnforce: + fallBackToCommonName = FallBackToSearchWithinSubject::Yes; + break; + default: + MOZ_CRASH("Unexpected Mode"); + } + return Success; +} diff --git a/security/certverifier/BRNameMatchingPolicy.h b/security/certverifier/BRNameMatchingPolicy.h new file mode 100644 index 0000000000..3b817ec8b4 --- /dev/null +++ b/security/certverifier/BRNameMatchingPolicy.h @@ -0,0 +1,56 @@ +/* -*- 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/. */ + +#ifndef BRNameMatchingPolicy_h +#define BRNameMatchingPolicy_h + +#include "pkix/pkixtypes.h" + +namespace mozilla { namespace psm { + +// According to the Baseline Requirements version 1.3.3 section 7.1.4.2.2.a, +// the requirements of the subject common name field are as follows: +// "If present, this field MUST contain a single IP address or Fully?Qualified +// Domain Name that is one of the values contained in the Certificate?™s +// subjectAltName extension". Consequently, since any name information present +// in the common name must be present in the subject alternative name extension, +// when performing name matching, it should not be necessary to fall back to the +// common name. Because this consequence has not commonly been enforced, this +// implementation provides a mechanism to start enforcing it gradually while +// maintaining some backwards compatibility. If configured with the mode +// "EnforceAfter23August2016", name matching will only fall back to using the +// subject common name for certificates where the notBefore field is before 23 +// August 2016. +// Note that this implementation does not actually directly enforce that if the +// subject common name is present, its value corresponds to a dNSName or +// iPAddress entry in the subject alternative name extension. + +class BRNameMatchingPolicy : public mozilla::pkix::NameMatchingPolicy +{ +public: + enum class Mode { + DoNotEnforce = 0, + EnforceAfter23August2016 = 1, + Enforce = 2, + }; + + explicit BRNameMatchingPolicy(Mode mode) + : mMode(mode) + { + } + + virtual mozilla::pkix::Result FallBackToCommonName( + mozilla::pkix::Time notBefore, + /*out*/ mozilla::pkix::FallBackToSearchWithinSubject& fallBacktoCommonName) + override; + +private: + Mode mMode; +}; + +} } // namespace mozilla::psm + +#endif // BRNameMatchingPolicy_h diff --git a/security/certverifier/CertVerifier.cpp b/security/certverifier/CertVerifier.cpp index 629f270cf4..0f229bc598 100644 --- a/security/certverifier/CertVerifier.cpp +++ b/security/certverifier/CertVerifier.cpp @@ -8,6 +8,7 @@ #include +#include "BRNameMatchingPolicy.h" #include "ExtendedValidation.h" #include "NSSCertDBTrustDomain.h" #include "NSSErrorsService.h" @@ -38,13 +39,15 @@ CertVerifier::CertVerifier(OcspDownloadConfig odc, OcspGetConfig ogc, uint32_t certShortLifetimeInDays, PinningMode pinningMode, - SHA1Mode sha1Mode) + SHA1Mode sha1Mode, + BRNameMatchingPolicy::Mode nameMatchingMode) : mOCSPDownloadConfig(odc) , mOCSPStrict(osc == ocspStrict) , mOCSPGETEnabled(ogc == ocspGetEnabled) , mCertShortLifetimeInDays(certShortLifetimeInDays) , mPinningMode(pinningMode) , mSHA1Mode(sha1Mode) + , mNameMatchingMode(nameMatchingMode) { } @@ -323,11 +326,6 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage, for (size_t i = 0; i < sha1ModeConfigurationsCount && rv != Success && srv == SECSuccess; i++) { - // Because of the try-strict and fallback approach, we have to clear any - // previously noted telemetry information - if (pinningTelemetryInfo) { - pinningTelemetryInfo->Reset(); - } // Don't attempt verification if the SHA1 mode set by preferences // (mSHA1Mode) is more restrictive than the SHA1 mode option we're on. // (To put it another way, only attempt verification if the SHA1 mode @@ -337,6 +335,13 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage, if (SHA1ModeMoreRestrictiveThanGivenMode(sha1ModeConfigurations[i])) { continue; } + + // Because of the try-strict and fallback approach, we have to clear any + // previously noted telemetry information + if (pinningTelemetryInfo) { + pinningTelemetryInfo->Reset(); + } + NSSCertDBTrustDomain trustDomain(trustSSL, evOCSPFetching, mOCSPCache, pinArg, ocspGETConfig, @@ -412,11 +417,6 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage, for (size_t i = 0; i < keySizeOptionsCount && rv != Success; i++) { for (size_t j = 0; j < sha1ModeConfigurationsCount && rv != Success; j++) { - // invalidate any telemetry info relating to failed chains - if (pinningTelemetryInfo) { - pinningTelemetryInfo->Reset(); - } - // Don't attempt verification if the SHA1 mode set by preferences // (mSHA1Mode) is more restrictive than the SHA1 mode option we're on. // (To put it another way, only attempt verification if the SHA1 mode @@ -427,6 +427,11 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage, continue; } + // invalidate any telemetry info relating to failed chains + if (pinningTelemetryInfo) { + pinningTelemetryInfo->Reset(); + } + NSSCertDBTrustDomain trustDomain(trustSSL, defaultOCSPFetching, mOCSPCache, pinArg, ocspGETConfig, mCertShortLifetimeInDays, @@ -484,7 +489,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage, // Only collect CERT_CHAIN_SHA1_POLICY_STATUS telemetry indicating a // failure when mSHA1Mode is the default. // NB: When we change the default, we have to change this. - if (sha1ModeResult && mSHA1Mode == SHA1Mode::Allowed) { + if (sha1ModeResult && mSHA1Mode == SHA1Mode::ImportedRoot) { *sha1ModeResult = SHA1ModeResult::Failed; } @@ -640,7 +645,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage, } SECStatus -CertVerifier::VerifySSLServerCert(CERTCertificate* peerCert, +CertVerifier::VerifySSLServerCert(const UniqueCERTCertificate& peerCert, /*optional*/ const SECItem* stapledOCSPResponse, Time time, /*optional*/ void* pinarg, @@ -670,9 +675,10 @@ CertVerifier::VerifySSLServerCert(CERTCertificate* peerCert, // CreateCertErrorRunnable assumes that CheckCertHostname is only called // if VerifyCert succeeded. - SECStatus rv = VerifyCert(peerCert, certificateUsageSSLServer, time, pinarg, - hostname, builtChain, flags, stapledOCSPResponse, - evOidPolicy, ocspStaplingStatus, keySizeStatus, + SECStatus rv = VerifyCert(peerCert.get(), certificateUsageSSLServer, time, + pinarg, hostname, builtChain, flags, + stapledOCSPResponse, evOidPolicy, + ocspStaplingStatus, keySizeStatus, sha1ModeResult, pinningTelemetryInfo); if (rv != SECSuccess) { return rv; @@ -714,7 +720,16 @@ CertVerifier::VerifySSLServerCert(CERTCertificate* peerCert, PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure; } - result = CheckCertHostname(peerCertInput, hostnameInput); + bool isBuiltInRoot; + result = IsCertChainRootBuiltInRoot(builtChain, isBuiltInRoot); + if (result != Success) { + PR_SetError(MapResultToPRErrorCode(result), 0); + return SECFailure; + } + BRNameMatchingPolicy nameMatchingPolicy( + isBuiltInRoot ? mNameMatchingMode + : BRNameMatchingPolicy::Mode::DoNotEnforce); + result = CheckCertHostname(peerCertInput, hostnameInput, nameMatchingPolicy); if (result != Success) { // Treat malformed name information as a domain mismatch. if (result == Result::ERROR_BAD_DER) { diff --git a/security/certverifier/CertVerifier.h b/security/certverifier/CertVerifier.h index 8edb1f7422..7a73d28e2e 100644 --- a/security/certverifier/CertVerifier.h +++ b/security/certverifier/CertVerifier.h @@ -7,10 +7,11 @@ #ifndef mozilla_psm__CertVerifier_h #define mozilla_psm__CertVerifier_h -#include "mozilla/Telemetry.h" -#include "pkix/pkixtypes.h" +#include "BRNameMatchingPolicy.h" #include "OCSPCache.h" #include "ScopedNSSTypes.h" +#include "mozilla/Telemetry.h" +#include "pkix/pkixtypes.h" namespace mozilla { namespace psm { @@ -83,7 +84,7 @@ public: /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr); SECStatus VerifySSLServerCert( - CERTCertificate* peerCert, + const UniqueCERTCertificate& peerCert, /*optional*/ const SECItem* stapledOCSPResponse, mozilla::pkix::Time time, /*optional*/ void* pinarg, @@ -121,7 +122,8 @@ public: CertVerifier(OcspDownloadConfig odc, OcspStrictConfig osc, OcspGetConfig ogc, uint32_t certShortLifetimeInDays, - PinningMode pinningMode, SHA1Mode sha1Mode); + PinningMode pinningMode, SHA1Mode sha1Mode, + BRNameMatchingPolicy::Mode nameMatchingMode); ~CertVerifier(); void ClearOCSPCache() { mOCSPCache.Clear(); } @@ -132,6 +134,7 @@ public: const uint32_t mCertShortLifetimeInDays; const PinningMode mPinningMode; const SHA1Mode mSHA1Mode; + const BRNameMatchingPolicy::Mode mNameMatchingMode; private: OCSPCache mOCSPCache; diff --git a/security/certverifier/NSSCertDBTrustDomain.cpp b/security/certverifier/NSSCertDBTrustDomain.cpp index 2610e99520..e99df42989 100644 --- a/security/certverifier/NSSCertDBTrustDomain.cpp +++ b/security/certverifier/NSSCertDBTrustDomain.cpp @@ -22,6 +22,7 @@ #include "pk11pub.h" #include "pkix/pkix.h" #include "pkix/pkixnss.h" +#include "pkix/Result.h" #include "prerror.h" #include "prmem.h" #include "prprf.h" @@ -179,7 +180,7 @@ NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA, // performance problem because NSS will just find the existing // CERTCertificate in its in-memory cache and return it. SECItem candidateCertDERSECItem = UnsafeMapInputToSECItem(candidateCertDER); - ScopedCERTCertificate candidateCert( + UniqueCERTCertificate candidateCert( CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &candidateCertDERSECItem, nullptr, false, true)); if (!candidateCert) { @@ -285,19 +286,24 @@ OCSPFetchingTypeToTimeoutTime(NSSCertDBTrustDomain::OCSPFetching ocspFetching) } // Copied and modified from CERT_GetOCSPAuthorityInfoAccessLocation and -// CERT_GetGeneralNameByType. Returns SECFailure on error, SECSuccess -// with url == nullptr when an OCSP URI was not found, and SECSuccess with +// CERT_GetGeneralNameByType. Returns a non-Result::Success result on error, +// Success with url == nullptr when an OCSP URI was not found, and Success with // url != nullptr when an OCSP URI was found. The output url will be owned // by the arena. static Result -GetOCSPAuthorityInfoAccessLocation(PLArenaPool* arena, +GetOCSPAuthorityInfoAccessLocation(const UniquePLArenaPool& arena, Input aiaExtension, /*out*/ char const*& url) { + MOZ_ASSERT(arena.get()); + if (!arena.get()) { + return Result::FATAL_ERROR_INVALID_ARGS; + } + url = nullptr; SECItem aiaExtensionSECItem = UnsafeMapInputToSECItem(aiaExtension); CERTAuthInfoAccess** aia = - CERT_DecodeAuthInfoAccessExtension(arena, &aiaExtensionSECItem); + CERT_DecodeAuthInfoAccessExtension(arena.get(), &aiaExtensionSECItem); if (!aia) { return Result::ERROR_CERT_BAD_ACCESS_LOCATION; } @@ -318,8 +324,8 @@ GetOCSPAuthorityInfoAccessLocation(PLArenaPool* arena, return Result::ERROR_CERT_BAD_ACCESS_LOCATION; } // Copy the non-null-terminated SECItem into a null-terminated string. - char* nullTerminatedURL(static_cast( - PORT_ArenaAlloc(arena, location.len + 1))); + char* nullTerminatedURL( + static_cast(PORT_ArenaAlloc(arena.get(), location.len + 1))); if (!nullTerminatedURL) { return Result::FATAL_ERROR_NO_MEMORY; } @@ -496,7 +502,7 @@ NSSCertDBTrustDomain::CheckRevocation(EndEntityOrCA endEntityOrCA, return Result::ERROR_OCSP_UNKNOWN_CERT; } - ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); + UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); if (!arena) { return Result::FATAL_ERROR_NO_MEMORY; } @@ -505,7 +511,7 @@ NSSCertDBTrustDomain::CheckRevocation(EndEntityOrCA endEntityOrCA, const char* url = nullptr; // owned by the arena if (aiaExtension) { - rv = GetOCSPAuthorityInfoAccessLocation(arena.get(), *aiaExtension, url); + rv = GetOCSPAuthorityInfoAccessLocation(arena, *aiaExtension, url); if (rv != Success) { return rv; } @@ -552,7 +558,7 @@ NSSCertDBTrustDomain::CheckRevocation(EndEntityOrCA endEntityOrCA, // Owned by arena SECItem* responseSECItem = nullptr; Result tempRV = - DoOCSPRequest(arena.get(), url, &ocspRequestItem, + DoOCSPRequest(arena, url, &ocspRequestItem, OCSPFetchingTypeToTimeoutTime(mOCSPFetching), mOCSPGetConfig == CertVerifier::ocspGetEnabled, responseSECItem); @@ -1007,10 +1013,9 @@ LoadLoadableRoots(/*optional*/ const char* dir, const char* modNameUTF8) return SECFailure; } - UniquePtr - escaped_fullLibraryPath(nss_addEscape(fullLibraryPath.get(), '\"'), - PORT_Free); - if (!escaped_fullLibraryPath) { + UniquePORTString escapedFullLibraryPath(nss_addEscape(fullLibraryPath.get(), + '\"')); + if (!escapedFullLibraryPath) { return SECFailure; } @@ -1020,7 +1025,7 @@ LoadLoadableRoots(/*optional*/ const char* dir, const char* modNameUTF8) UniquePtr pkcs11ModuleSpec(PR_smprintf("name=\"%s\" library=\"%s\"", modNameUTF8, - escaped_fullLibraryPath.get()), + escapedFullLibraryPath.get()), PR_smprintf_free); if (!pkcs11ModuleSpec) { return SECFailure; diff --git a/security/certverifier/OCSPRequestor.cpp b/security/certverifier/OCSPRequestor.cpp index f1281bd4b1..b19b476f39 100644 --- a/security/certverifier/OCSPRequestor.cpp +++ b/security/certverifier/OCSPRequestor.cpp @@ -8,8 +8,8 @@ #include +#include "ScopedNSSTypes.h" #include "mozilla/Base64.h" -#include "mozilla/Scoped.h" #include "nsIURLParser.h" #include "nsNSSCallbacks.h" #include "nsNetCID.h" @@ -32,13 +32,13 @@ ReleaseHttpRequestSession(nsNSSHttpRequestSession* httpRequestSession) httpRequestSession->Release(); } -MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedHTTPServerSession, - nsNSSHttpServerSession, - ReleaseHttpServerSession) +MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueHTTPServerSession, + nsNSSHttpServerSession, + ReleaseHttpServerSession) -MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedHTTPRequestSession, - nsNSSHttpRequestSession, - ReleaseHttpRequestSession) +MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueHTTPRequestSession, + nsNSSHttpRequestSession, + ReleaseHttpRequestSession) } // namespace mozilla @@ -71,12 +71,16 @@ AppendEscapedBase64Item(const SECItem* encodedRequest, nsACString& path) } Result -DoOCSPRequest(PLArenaPool* arena, const char* url, +DoOCSPRequest(const UniquePLArenaPool& arena, const char* url, const SECItem* encodedRequest, PRIntervalTime timeout, bool useGET, /*out*/ SECItem*& encodedResponse) { - if (!arena || !url || !encodedRequest || !encodedRequest->data) { + MOZ_ASSERT(arena.get()); + MOZ_ASSERT(url); + MOZ_ASSERT(encodedRequest); + MOZ_ASSERT(encodedRequest->data); + if (!arena.get() || !url || !encodedRequest || !encodedRequest->data) { return Result::FATAL_ERROR_INVALID_ARGS; } uint32_t urlLen = PL_strlen(url); @@ -142,7 +146,7 @@ DoOCSPRequest(PLArenaPool* arena, const char* url, if (rv != Success) { return rv; } - ScopedHTTPServerSession serverSession( + UniqueHTTPServerSession serverSession( reinterpret_cast(serverSessionPtr)); nsAutoCString path; @@ -173,7 +177,7 @@ DoOCSPRequest(PLArenaPool* arena, const char* url, return rv; } - ScopedHTTPRequestSession requestSession( + UniqueHTTPRequestSession requestSession( reinterpret_cast(requestSessionPtr)); if (!useGET) { @@ -200,7 +204,7 @@ DoOCSPRequest(PLArenaPool* arena, const char* url, return Result::ERROR_OCSP_SERVER_ERROR; } - encodedResponse = SECITEM_AllocItem(arena, nullptr, httpResponseDataLen); + encodedResponse = SECITEM_AllocItem(arena.get(), nullptr, httpResponseDataLen); if (!encodedResponse) { return Result::FATAL_ERROR_NO_MEMORY; } diff --git a/security/certverifier/OCSPRequestor.h b/security/certverifier/OCSPRequestor.h index 2310c8cdb4..06790a985b 100644 --- a/security/certverifier/OCSPRequestor.h +++ b/security/certverifier/OCSPRequestor.h @@ -4,8 +4,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef mozilla_psm_OCSPRequestor_h -#define mozilla_psm_OCSPRequestor_h +#ifndef OCSPRequestor_h +#define OCSPRequestor_h #include "CertVerifier.h" #include "secmodt.h" @@ -13,11 +13,11 @@ namespace mozilla { namespace psm { // The memory returned via |encodedResponse| is owned by the given arena. -Result DoOCSPRequest(PLArenaPool* arena, const char* url, +Result DoOCSPRequest(const UniquePLArenaPool& arena, const char* url, const SECItem* encodedRequest, PRIntervalTime timeout, bool useGET, /*out*/ SECItem*& encodedResponse); } } // namespace mozilla::psm -#endif // mozilla_psm_OCSPRequestor_h +#endif // OCSPRequestor_h diff --git a/security/certverifier/moz.build b/security/certverifier/moz.build index be3548360e..5cb28c8200 100644 --- a/security/certverifier/moz.build +++ b/security/certverifier/moz.build @@ -5,11 +5,13 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. EXPORTS += [ + 'BRNameMatchingPolicy.h', 'CertVerifier.h', 'OCSPCache.h', ] UNIFIED_SOURCES += [ + 'BRNameMatchingPolicy.cpp', 'CertVerifier.cpp', 'NSSCertDBTrustDomain.cpp', 'OCSPCache.cpp', diff --git a/security/manager/locales/en-US/chrome/pipnss/pipnss.properties b/security/manager/locales/en-US/chrome/pipnss/pipnss.properties index 902fa984ab..30ed49ea28 100755 --- a/security/manager/locales/en-US/chrome/pipnss/pipnss.properties +++ b/security/manager/locales/en-US/chrome/pipnss/pipnss.properties @@ -285,7 +285,6 @@ certErrorMismatch=The certificate is not valid for the name %S. certErrorMismatchSingle2=The certificate is only valid for %1$S certErrorMismatchSinglePlain=The certificate is only valid for %S certErrorMismatchMultiple=The certificate is only valid for the following names: -certErrorMismatchNoNames=The certificate is not valid for any server names. # LOCALIZATION NOTE (certErrorExpiredNow): Do not translate %1$S (date+time of expired certificate) or %2$S (current date+time) certErrorExpiredNow=The certificate expired on %1$S. The current time is %2$S. diff --git a/security/manager/pki/nsNSSDialogs.cpp b/security/manager/pki/nsNSSDialogs.cpp index f83f424fbb..af192b7d27 100644 --- a/security/manager/pki/nsNSSDialogs.cpp +++ b/security/manager/pki/nsNSSDialogs.cpp @@ -7,32 +7,31 @@ /* * Dialog services for PIP. */ -#include "nsCOMPtr.h" -#include "nsString.h" -#include "nsXPIDLString.h" -#include "nsReadableUtils.h" #include "nsIDOMWindow.h" -#include "nsIDialogParamBlock.h" +#include "nsArray.h" +#include "nsCOMPtr.h" +#include "nsDateTimeFormatCID.h" +#include "nsEmbedCID.h" #include "nsIComponentManager.h" -#include "nsIServiceManager.h" -#include "nsIStringBundle.h" +#include "nsIDateTimeFormat.h" +#include "nsIDialogParamBlock.h" #include "nsIInterfaceRequestor.h" #include "nsIInterfaceRequestorUtils.h" -#include "nsIX509Cert.h" -#include "nsIX509CertDB.h" -#include "nsIDateTimeFormat.h" -#include "nsDateTimeFormatCID.h" -#include "nsPromiseFlatString.h" - -#include "nsNSSDialogs.h" #include "nsIKeygenThread.h" -#include "nsIProtectedAuthThread.h" -#include "nsNSSDialogHelper.h" -#include "nsIWindowWatcher.h" -#include "nsIX509CertValidity.h" - -#include "nsEmbedCID.h" #include "nsIPromptService.h" +#include "nsIProtectedAuthThread.h" +#include "nsIServiceManager.h" +#include "nsIStringBundle.h" +#include "nsIWindowWatcher.h" +#include "nsIX509CertDB.h" +#include "nsIX509Cert.h" +#include "nsIX509CertValidity.h" +#include "nsNSSDialogHelper.h" +#include "nsNSSDialogs.h" +#include "nsPromiseFlatString.h" +#include "nsReadableUtils.h" +#include "nsString.h" +#include "nsXPIDLString.h" #define PIPSTRING_BUNDLE_URL "chrome://pippki/locale/pippki.properties" @@ -109,7 +108,7 @@ nsNSSDialogs::ConfirmDownloadCACert(nsIInterfaceRequestor *ctx, { nsresult rv; - nsCOMPtr dlgArray(do_CreateInstance(NS_ARRAY_CONTRACTID)); + nsCOMPtr dlgArray = nsArrayBase::Create(); if (!dlgArray) { return NS_ERROR_FAILURE; } @@ -360,7 +359,7 @@ nsNSSDialogs::GetPKCS12FilePassword(nsIInterfaceRequestor* ctx, NS_IMETHODIMP nsNSSDialogs::ViewCert(nsIInterfaceRequestor* ctx, nsIX509Cert* cert) { - nsCOMPtr dlgArray(do_CreateInstance(NS_ARRAY_CONTRACTID)); + nsCOMPtr dlgArray = nsArrayBase::Create(); if (!dlgArray) { return NS_ERROR_FAILURE; } diff --git a/security/manager/ssl/CSTrustDomain.cpp b/security/manager/ssl/CSTrustDomain.cpp new file mode 100644 index 0000000000..3c18340f7b --- /dev/null +++ b/security/manager/ssl/CSTrustDomain.cpp @@ -0,0 +1,217 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "CSTrustDomain.h" +#include "mozilla/Base64.h" +#include "mozilla/Preferences.h" +#include "nsNSSCertificate.h" +#include "nsNSSComponent.h" +#include "nsServiceManagerUtils.h" +#include "nsThreadUtils.h" +#include "pkix/pkixnss.h" + +using namespace mozilla::pkix; + +namespace mozilla { namespace psm { + +static LazyLogModule gTrustDomainPRLog("CSTrustDomain"); +#define CSTrust_LOG(args) MOZ_LOG(gTrustDomainPRLog, LogLevel::Debug, args) + +CSTrustDomain::CSTrustDomain(ScopedCERTCertList& certChain) + : mCertChain(certChain) + , mCertBlocklist(do_GetService(NS_CERTBLOCKLIST_CONTRACTID)) +{ +} + +Result +CSTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA, + const CertPolicyId& policy, Input candidateCertDER, + /*out*/ TrustLevel& trustLevel) +{ + MOZ_ASSERT(policy.IsAnyPolicy()); + if (!policy.IsAnyPolicy()) { + return Result::FATAL_ERROR_INVALID_ARGS; + } + + SECItem candidateCertDERSECItem = UnsafeMapInputToSECItem(candidateCertDER); + ScopedCERTCertificate candidateCert( + CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &candidateCertDERSECItem, + nullptr, false, true)); + if (!candidateCert) { + return MapPRErrorCodeToResult(PR_GetError()); + } + + bool isCertRevoked; + nsresult nsrv = mCertBlocklist->IsCertRevoked( + candidateCert->derIssuer.data, + candidateCert->derIssuer.len, + candidateCert->serialNumber.data, + candidateCert->serialNumber.len, + candidateCert->derSubject.data, + candidateCert->derSubject.len, + candidateCert->derPublicKey.data, + candidateCert->derPublicKey.len, + &isCertRevoked); + if (NS_FAILED(nsrv)) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + + if (isCertRevoked) { + CSTrust_LOG(("CSTrustDomain: certificate is revoked\n")); + return Result::ERROR_REVOKED_CERTIFICATE; + } + + // Is this cert our built-in content signing root? + bool isRoot = false; + nsCOMPtr component(do_GetService(PSM_COMPONENT_CONTRACTID)); + if (!component) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + nsrv = component->IsCertContentSigningRoot(candidateCert.get(), isRoot); + if (NS_FAILED(nsrv)) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + if (isRoot) { + CSTrust_LOG(("CSTrustDomain: certificate is a trust anchor\n")); + trustLevel = TrustLevel::TrustAnchor; + return Success; + } + CSTrust_LOG(("CSTrustDomain: certificate is *not* a trust anchor\n")); + + trustLevel = TrustLevel::InheritsTrust; + return Success; +} + +Result +CSTrustDomain::FindIssuer(Input encodedIssuerName, IssuerChecker& checker, + Time time) +{ + // Loop over the chain, look for a matching subject + for (CERTCertListNode* n = CERT_LIST_HEAD(mCertChain); + !CERT_LIST_END(n, mCertChain); n = CERT_LIST_NEXT(n)) { + Input certDER; + Result rv = certDER.Init(n->cert->derCert.data, n->cert->derCert.len); + if (rv != Success) { + continue; // probably too big + } + + // if the subject does not match, try the next certificate + Input subjectDER; + rv = subjectDER.Init(n->cert->derSubject.data, n->cert->derSubject.len); + if (rv != Success) { + continue; // just try the next one + } + if (!InputsAreEqual(subjectDER, encodedIssuerName)) { + CSTrust_LOG(("CSTrustDomain: subjects don't match\n")); + continue; + } + + // If the subject does match, try the next step + bool keepGoing; + rv = checker.Check(certDER, nullptr/*additionalNameConstraints*/, + keepGoing); + if (rv != Success) { + return rv; + } + if (!keepGoing) { + CSTrust_LOG(("CSTrustDomain: don't keep going\n")); + break; + } + } + + return Success; +} + +Result +CSTrustDomain::CheckRevocation(EndEntityOrCA endEntityOrCA, + const CertID& certID, Time time, + Duration validityDuration, + /*optional*/ const Input* stapledOCSPresponse, + /*optional*/ const Input* aiaExtension) +{ + // We're relying solely on the CertBlocklist for revocation - and we're + // performing checks on this in GetCertTrust (as per nsNSSCertDBTrustDomain) + return Success; +} + +Result +CSTrustDomain::IsChainValid(const DERArray& certChain, Time time) +{ + // Check that our chain is not empty + if (certChain.GetLength() == 0) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + + return Success; +} + +Result +CSTrustDomain::CheckSignatureDigestAlgorithm(DigestAlgorithm digestAlg, + EndEntityOrCA endEntityOrCA, + Time notBefore) +{ + if (digestAlg == DigestAlgorithm::sha1) { + return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED; + } + return Success; +} + +Result +CSTrustDomain::CheckRSAPublicKeyModulusSizeInBits( + EndEntityOrCA endEntityOrCA, unsigned int modulusSizeInBits) +{ + if (modulusSizeInBits < 2048) { + return Result::ERROR_INADEQUATE_KEY_SIZE; + } + return Success; +} + +Result +CSTrustDomain::VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) +{ + return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo, + nullptr); +} + +Result +CSTrustDomain::CheckECDSACurveIsAcceptable(EndEntityOrCA endEntityOrCA, + NamedCurve curve) +{ + switch (curve) { + case NamedCurve::secp256r1: // fall through + case NamedCurve::secp384r1: // fall through + case NamedCurve::secp521r1: + return Success; + } + + return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE; +} + +Result +CSTrustDomain::VerifyECDSASignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) +{ + return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo, + nullptr); +} + +Result +CSTrustDomain::CheckValidityIsAcceptable(Time notBefore, Time notAfter, + EndEntityOrCA endEntityOrCA, + KeyPurposeId keyPurpose) +{ + return Success; +} + +Result +CSTrustDomain::DigestBuf(Input item, DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, size_t digestBufLen) +{ + return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen); +} + +} } // end namespace mozilla::psm diff --git a/security/manager/ssl/CSTrustDomain.h b/security/manager/ssl/CSTrustDomain.h new file mode 100644 index 0000000000..66f846a6d0 --- /dev/null +++ b/security/manager/ssl/CSTrustDomain.h @@ -0,0 +1,75 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CSTrustDomain_h +#define CSTrustDomain_h + +#include "pkix/pkixtypes.h" +#include "mozilla/StaticMutex.h" +#include "mozilla/UniquePtr.h" +#include "nsDebug.h" +#include "nsICertBlocklist.h" +#include "nsIX509CertDB.h" +#include "ScopedNSSTypes.h" + +namespace mozilla { namespace psm { + +class CSTrustDomain final : public mozilla::pkix::TrustDomain +{ +public: + typedef mozilla::pkix::Result Result; + + explicit CSTrustDomain(ScopedCERTCertList& certChain); + + virtual Result GetCertTrust( + mozilla::pkix::EndEntityOrCA endEntityOrCA, + const mozilla::pkix::CertPolicyId& policy, + mozilla::pkix::Input candidateCertDER, + /*out*/ mozilla::pkix::TrustLevel& trustLevel) override; + virtual Result FindIssuer(mozilla::pkix::Input encodedIssuerName, + IssuerChecker& checker, + mozilla::pkix::Time time) override; + virtual Result CheckRevocation( + mozilla::pkix::EndEntityOrCA endEntityOrCA, + const mozilla::pkix::CertID& certID, mozilla::pkix::Time time, + mozilla::pkix::Duration validityDuration, + /*optional*/ const mozilla::pkix::Input* stapledOCSPresponse, + /*optional*/ const mozilla::pkix::Input* aiaExtension) override; + virtual Result IsChainValid(const mozilla::pkix::DERArray& certChain, + mozilla::pkix::Time time) override; + virtual Result CheckSignatureDigestAlgorithm( + mozilla::pkix::DigestAlgorithm digestAlg, + mozilla::pkix::EndEntityOrCA endEntityOrCA, + mozilla::pkix::Time notBefore) override; + virtual Result CheckRSAPublicKeyModulusSizeInBits( + mozilla::pkix::EndEntityOrCA endEntityOrCA, + unsigned int modulusSizeInBits) override; + virtual Result VerifyRSAPKCS1SignedDigest( + const mozilla::pkix::SignedDigest& signedDigest, + mozilla::pkix::Input subjectPublicKeyInfo) override; + virtual Result CheckECDSACurveIsAcceptable( + mozilla::pkix::EndEntityOrCA endEntityOrCA, + mozilla::pkix::NamedCurve curve) override; + virtual Result VerifyECDSASignedDigest( + const mozilla::pkix::SignedDigest& signedDigest, + mozilla::pkix::Input subjectPublicKeyInfo) override; + virtual Result CheckValidityIsAcceptable( + mozilla::pkix::Time notBefore, mozilla::pkix::Time notAfter, + mozilla::pkix::EndEntityOrCA endEntityOrCA, + mozilla::pkix::KeyPurposeId keyPurpose) override; + virtual Result DigestBuf(mozilla::pkix::Input item, + mozilla::pkix::DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, + size_t digestBufLen) override; + +private: + /*out*/ ScopedCERTCertList& mCertChain; + nsCOMPtr mCertBlocklist; +}; + +} } // namespace mozilla::psm + +#endif // CSTrustDomain_h diff --git a/security/manager/ssl/ContentSignatureVerifier.cpp b/security/manager/ssl/ContentSignatureVerifier.cpp new file mode 100644 index 0000000000..fbce68ecb1 --- /dev/null +++ b/security/manager/ssl/ContentSignatureVerifier.cpp @@ -0,0 +1,372 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ContentSignatureVerifier.h" + +#include "BRNameMatchingPolicy.h" +#include "cryptohi.h" +#include "keyhi.h" +#include "nsCOMPtr.h" +#include "nsNSSComponent.h" +#include "nssb64.h" +#include "nsWhitespaceTokenizer.h" +#include "nsXPCOMStrings.h" +#include "pkix/pkix.h" +#include "pkix/pkixtypes.h" +#include "secerr.h" +#include "SharedCertVerifier.h" +#include "nsSecurityHeaderParser.h" + +NS_IMPL_ISUPPORTS(ContentSignatureVerifier, nsIContentSignatureVerifier) + +using namespace mozilla; +using namespace mozilla::pkix; +using namespace mozilla::psm; + +static LazyLogModule gCSVerifierPRLog("ContentSignatureVerifier"); +#define CSVerifier_LOG(args) MOZ_LOG(gCSVerifierPRLog, LogLevel::Debug, args) + +// Content-Signature prefix +const nsLiteralCString kPREFIX = NS_LITERAL_CSTRING("Content-Signature:\x00"); + +ContentSignatureVerifier::~ContentSignatureVerifier() +{ + nsNSSShutDownPreventionLock locker; + if (isAlreadyShutDown()) { + return; + } + destructorSafeDestroyNSSReference(); + shutdown(calledFromObject); +} + +nsresult +ContentSignatureVerifier::VerifyContentSignature( + const nsACString& aData, const nsACString& aCSHeader, + const nsACString& aCertChain, const uint32_t aSource, bool* _retval) +{ + nsresult rv = CreateContext(aData, aCSHeader, aCertChain, aSource); + if (NS_FAILED(rv)) { + *_retval = false; + CSVerifier_LOG(("CSVerifier: Signature verification failed\n")); + if (rv == NS_ERROR_INVALID_SIGNATURE) { + return NS_OK; + } + return rv; + } + + return End(_retval); +} + +bool +IsNewLine(char16_t c) +{ + return c == '\n' || c == '\r'; +} + +nsresult +ReadChainIntoCertList(const nsACString& aCertChain, CERTCertList* aCertList, + const nsNSSShutDownPreventionLock& /*proofOfLock*/) +{ + bool inBlock = false; + bool certFound = false; + + const nsCString header = NS_LITERAL_CSTRING("-----BEGIN CERTIFICATE-----"); + const nsCString footer = NS_LITERAL_CSTRING("-----END CERTIFICATE-----"); + + nsCWhitespaceTokenizerTemplate tokenizer(aCertChain); + + nsAutoCString blockData; + while (tokenizer.hasMoreTokens()) { + nsDependentCSubstring token = tokenizer.nextToken(); + if (token.IsEmpty()) { + continue; + } + if (inBlock) { + if (token.Equals(footer)) { + inBlock = false; + certFound = true; + // base64 decode data, make certs, append to chain + UniqueSECItem der(::SECITEM_AllocItem(nullptr, nullptr, 0)); + if (!der || !NSSBase64_DecodeBuffer(nullptr, der.get(), + blockData.BeginReading(), + blockData.Length())) { + CSVerifier_LOG(("CSVerifier: decoding the signature failed\n")); + return NS_ERROR_FAILURE; + } + CERTCertificate* tmpCert = + CERT_NewTempCertificate(CERT_GetDefaultCertDB(), der.get(), nullptr, + false, true); + if (!tmpCert) { + return NS_ERROR_FAILURE; + } + // if adding tmpCert succeeds, tmpCert will now be owned by aCertList + SECStatus res = CERT_AddCertToListTail(aCertList, tmpCert); + if (res != SECSuccess) { + CERT_DestroyCertificate(tmpCert); + return MapSECStatus(res); + } + } else { + blockData.Append(token); + } + } else if (token.Equals(header)) { + inBlock = true; + blockData = ""; + } + } + if (inBlock || !certFound) { + // the PEM data did not end; bad data. + CSVerifier_LOG(("CSVerifier: supplied chain contains bad data\n")); + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +// Create a context for a content signature verification. +// It sets signature, certificate chain, and context that shold be used to +// verify the data. The optional data parameter is added to the data to verify. +NS_IMETHODIMP +ContentSignatureVerifier::CreateContext(const nsACString& aData, + const nsACString& aCSHeader, + const nsACString& aCertChain, + const uint32_t aSource) +{ + MutexAutoLock lock(mMutex); + nsNSSShutDownPreventionLock locker; + if (isAlreadyShutDown()) { + CSVerifier_LOG(("CSVerifier: nss is already shutdown\n")); + return NS_ERROR_FAILURE; + } + + if (mCx) { + return NS_ERROR_ALREADY_INITIALIZED; + } + + ScopedCERTCertList certCertList(CERT_NewCertList()); + + if (!certCertList) { + return NS_ERROR_OUT_OF_MEMORY; + } + + nsresult rv = ReadChainIntoCertList(aCertChain, certCertList.get(), locker); + if (NS_FAILED(rv)) { + return rv; + } + + CERTCertListNode* node = CERT_LIST_HEAD(certCertList.get()); + if (!node || !node->cert) { + return NS_ERROR_FAILURE; + } + + SECItem* certSecItem = &node->cert->derCert; + + Input certDER; + Result result = + certDER.Init(reinterpret_cast(certSecItem->data), + certSecItem->len); + if (result != Success) { + return NS_ERROR_FAILURE; + } + + + // Check the signerCert chain is good + CSTrustDomain trustDomain(certCertList); + result = BuildCertChain(trustDomain, certDER, Now(), + EndEntityOrCA::MustBeEndEntity, + KeyUsage::noParticularKeyUsageRequired, + KeyPurposeId::id_kp_codeSigning, + CertPolicyId::anyPolicy, + nullptr/*stapledOCSPResponse*/); + if (result != Success) { + // the chain is bad + CSVerifier_LOG(("CSVerifier: The supplied chain is bad\n")); + return NS_ERROR_INVALID_SIGNATURE; + } + + // Check the SAN + nsAutoCString hostname; + + Input hostnameInput; + + switch (aSource) { + case ABOUT_NEWTAB: + hostname = "remote-newtab-signer.mozilla.org"; + break; + case ONECRL: + hostname = "oneCRL-signer.mozilla.org"; + break; + default: + CSVerifier_LOG(("CSVerifier: bad context\n")); + return NS_ERROR_INVALID_ARG; + } + + result = hostnameInput.Init(uint8_t_ptr_cast(hostname.BeginReading()), + hostname.Length()); + if (result != Success) { + return NS_ERROR_FAILURE; + } + + BRNameMatchingPolicy nameMatchingPolicy(BRNameMatchingPolicy::Mode::Enforce); + result = CheckCertHostname(certDER, hostnameInput, nameMatchingPolicy); + if (result != Success) { + return NS_ERROR_INVALID_SIGNATURE; + } + + mKey.reset(CERT_ExtractPublicKey(node->cert)); + + // in case we were not able to extract a key + if (!mKey) { + CSVerifier_LOG(("CSVerifier: unable to extract a key\n")); + return NS_ERROR_INVALID_SIGNATURE; + } + + // we get the raw content-signature header here, so first parse aCSHeader + rv = ParseContentSignatureHeader(aCSHeader); + if (NS_FAILED(rv)) { + return rv; + } + + // Base 64 decode the signature + UniqueSECItem rawSignatureItem(::SECITEM_AllocItem(nullptr, nullptr, 0)); + if (!rawSignatureItem || + !NSSBase64_DecodeBuffer(nullptr, rawSignatureItem.get(), + mSignature.get(), mSignature.Length())) { + CSVerifier_LOG(("CSVerifier: decoding the signature failed\n")); + return NS_ERROR_FAILURE; + } + + // get signature object + UniqueSECItem signatureItem(::SECITEM_AllocItem(nullptr, nullptr, 0)); + if (!signatureItem) { + return NS_ERROR_FAILURE; + } + // We have a raw ecdsa signature r||s so we have to DER-encode it first + // Note that we have to check rawSignatureItem->len % 2 here as + // DSAU_EncodeDerSigWithLen asserts this + if (rawSignatureItem->len == 0 || rawSignatureItem->len % 2 != 0) { + CSVerifier_LOG(("CSVerifier: signature length is bad\n")); + return NS_ERROR_FAILURE; + } + if (DSAU_EncodeDerSigWithLen(signatureItem.get(), rawSignatureItem.get(), + rawSignatureItem->len) != SECSuccess) { + CSVerifier_LOG(("CSVerifier: encoding the signature failed\n")); + return NS_ERROR_FAILURE; + } + + // this is the only OID we support for now + SECOidTag oid = SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE; + + mCx = UniqueVFYContext( + VFY_CreateContext(mKey.get(), signatureItem.get(), oid, nullptr)); + + if (!mCx) { + return NS_ERROR_INVALID_SIGNATURE; + } + + if (VFY_Begin(mCx.get()) != SECSuccess) { + return NS_ERROR_INVALID_SIGNATURE; + } + + rv = UpdateInternal(kPREFIX, lock, locker); + if (NS_FAILED(rv)) { + return rv; + } + // add data if we got any + return UpdateInternal(aData, lock, locker); +} + +nsresult +ContentSignatureVerifier::UpdateInternal(const nsACString& aData, + MutexAutoLock& /*proofOfLock*/, + const nsNSSShutDownPreventionLock& /*proofOfLock*/) +{ + if (!aData.IsEmpty()) { + if (VFY_Update(mCx.get(), (const unsigned char*)nsPromiseFlatCString(aData).get(), + aData.Length()) != SECSuccess){ + return NS_ERROR_INVALID_SIGNATURE; + } + } + return NS_OK; +} + +/** + * Add data to the context that shold be verified. + */ +NS_IMETHODIMP +ContentSignatureVerifier::Update(const nsACString& aData) +{ + nsNSSShutDownPreventionLock locker; + if (isAlreadyShutDown()) { + CSVerifier_LOG(("CSVerifier: nss is already shutdown\n")); + return NS_ERROR_FAILURE; + } + MutexAutoLock lock(mMutex); + return UpdateInternal(aData, lock, locker); +} + +/** + * Finish signature verification and return the result in _retval. + */ +NS_IMETHODIMP +ContentSignatureVerifier::End(bool* _retval) +{ + NS_ENSURE_ARG(_retval); + MutexAutoLock lock(mMutex); + nsNSSShutDownPreventionLock locker; + if (isAlreadyShutDown()) { + CSVerifier_LOG(("CSVerifier: nss is already shutdown\n")); + return NS_ERROR_FAILURE; + } + + *_retval = (VFY_End(mCx.get()) == SECSuccess); + + return NS_OK; +} + +nsresult +ContentSignatureVerifier::ParseContentSignatureHeader( + const nsACString& aContentSignatureHeader) +{ + // We only support p384 ecdsa according to spec + NS_NAMED_LITERAL_CSTRING(signature_var, "p384ecdsa"); + + nsSecurityHeaderParser parser(aContentSignatureHeader.BeginReading()); + nsresult rv = parser.Parse(); + if (NS_FAILED(rv)) { + CSVerifier_LOG(("CSVerifier: could not parse ContentSignature header\n")); + return NS_ERROR_FAILURE; + } + LinkedList* directives = parser.GetDirectives(); + + for (nsSecurityHeaderDirective* directive = directives->getFirst(); + directive != nullptr; directive = directive->getNext()) { + CSVerifier_LOG(("CSVerifier: found directive %s\n", directive->mName.get())); + if (directive->mName.Length() == signature_var.Length() && + directive->mName.EqualsIgnoreCase(signature_var.get(), + signature_var.Length())) { + if (!mSignature.IsEmpty()) { + CSVerifier_LOG(("CSVerifier: found two ContentSignatures\n")); + return NS_ERROR_INVALID_SIGNATURE; + } + + CSVerifier_LOG(("CSVerifier: found a ContentSignature directive\n")); + mSignature = directive->mValue; + } + } + + // we have to ensure that we found a signature at this point + if (mSignature.IsEmpty()) { + CSVerifier_LOG(("CSVerifier: got a Content-Signature header but didn't find a signature.\n")); + return NS_ERROR_FAILURE; + } + + // Bug 769521: We have to change b64 url to regular encoding as long as we + // don't have a b64 url decoder. This should change soon, but in the meantime + // we have to live with this. + mSignature.ReplaceChar('-', '+'); + mSignature.ReplaceChar('_', '/'); + + return NS_OK; +} diff --git a/security/manager/ssl/ContentSignatureVerifier.h b/security/manager/ssl/ContentSignatureVerifier.h new file mode 100644 index 0000000000..88accb5d6f --- /dev/null +++ b/security/manager/ssl/ContentSignatureVerifier.h @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +#ifndef ContentSignatureVerifier_h +#define ContentSignatureVerifier_h + +#include "cert.h" +#include "CSTrustDomain.h" +#include "nsIContentSignatureVerifier.h" +#include "nsNSSShutDown.h" +#include "ScopedNSSTypes.h" + +// 45a5fe2f-c350-4b86-962d-02d5aaaa955a +#define NS_CONTENTSIGNATUREVERIFIER_CID \ + { 0x45a5fe2f, 0xc350, 0x4b86, \ + { 0x96, 0x2d, 0x02, 0xd5, 0xaa, 0xaa, 0x95, 0x5a } } +#define NS_CONTENTSIGNATUREVERIFIER_CONTRACTID \ + "@mozilla.org/security/contentsignatureverifier;1" + +class ContentSignatureVerifier final : public nsIContentSignatureVerifier + , public nsNSSShutDownObject +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSICONTENTSIGNATUREVERIFIER + + ContentSignatureVerifier() + : mCx(nullptr) + , mMutex("CSVerifier::mMutex") + { + } + + // nsNSSShutDownObject + virtual void virtualDestroyNSSReference() override + { + destructorSafeDestroyNSSReference(); + } + +private: + ~ContentSignatureVerifier(); + + nsresult UpdateInternal(const nsACString& aData, + MutexAutoLock& /*proofOfLock*/, + const nsNSSShutDownPreventionLock& /*proofOfLock*/); + + void destructorSafeDestroyNSSReference() + { + mCx = nullptr; + mKey = nullptr; + } + + nsresult ParseContentSignatureHeader(const nsACString& aContentSignatureHeader); + + // verifier context for incremental verifications + mozilla::UniqueVFYContext mCx; + // signature to verify + nsCString mSignature; + // verification key + mozilla::UniqueSECKEYPublicKey mKey; + mozilla::Mutex mMutex; +}; + +#endif // ContentSignatureVerifier_h diff --git a/security/manager/ssl/IntolerantFallbackList.inc b/security/manager/ssl/IntolerantFallbackList.inc deleted file mode 100644 index f3bf283441..0000000000 --- a/security/manager/ssl/IntolerantFallbackList.inc +++ /dev/null @@ -1,1070 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/////////////////////////////////////////////////////////////////////////////// -// This is an automatically generated file. If you're not -// nsNSSIOLayer.cpp, you shouldn't be #including it. -/////////////////////////////////////////////////////////////////////////////// - -static const char* const kIntolerantFallbackList[] = -{ - "1800registry.com", - "5linx.com", - "aa.com", // bug 1141604 - "aa.com.br", // bug 1141604 - "aa.com.do", // bug 1141604 - "aa.com.pe", // bug 1141604 - "aa.com.ve", // bug 1141604 - "aacoprod.aacounty.org", - "aavacations.com", // bug 1141604 - "absolwenci.amu.edu.pl", - "access.boekhuis.nl", // bug 1151580 - "access.dogproblems.com", - "account.61.com.tw", - "account.loknstore.co.uk", - "accountmanager.8x8.com", - "acs.sia.eu", // RC4 - "actu.macif.fr", - "adman.you.gr", - "admin.leaguestat.com", - "admin.sagefundraisingonline.com", - "adminweb.uthscsa.edu", - "affiliates.dragonoptions.com", - "afs.fortunaaffiliates.com", - "ag.bcbhomes.com", - "agenda.uc.cl", - "agentweb.manulife.co.jp", - "aim.nextjump.com", - "airportwifi.com", // bug 1116891 - "allbankonline.in", // bug 1156441 - "allyours.virginmedia.com", // bug 1129887 - "altitude.aircanada.com", // bug 1143325 - "alumni.georgetown.edu", - "american-airlines.co.kr", // bug 1141604 - "american-airlines.nl", // bug 1141604 - "americanairlines.be", // bug 1141604 - "americanairlines.ch", // bug 1141604 - "americanairlines.cl", // bug 1141604 - "americanairlines.cn", // bug 1141604 - "americanairlines.co.cr", // bug 1141604 - "americanairlines.co.uk", // bug 1141604 - "americanairlines.com", // bug 1141604 - "americanairlines.com.au", // bug 1141604 - "americanairlines.com.ru", // bug 1141604 - "americanairlines.es", // bug 1141604 - "americanairlines.fr", // bug 1141604 - "americanairlines.hu", // bug 1141604 - "americanairlines.ie", // bug 1141604 - "americanairlines.in", // bug 1141604 - "americanairlines.jp", // bug 1141604 - "ap.meitetsuunyu.co.jp", - "applications.marykayintouch.com", - "apply.hkbn.net", // bug 1138451 - "apps.amerch.com", - "apps.boostmobile.com", - "apps.sasken.com", - "apps.sgu.edu", - "apps.state.or.us", // bug 1130472 - "appsrv.restat.com", - "arcgames.com", // bug 1182932 - "asko.fi", // bug 1158584 - "auth.hitachi-capital.co.jp", - "auth.visiblemeasures.com", - "b2b.feib.com.tw", - "baybloorradio.com", // bug 1173661 - "be-my-guest.com", - "beehive.miit.ru", - "bgw.wangyin.com", // bug 1145521 - "bianmin.chinapay.com", // bug 1137983 - "bigflix.com", - "biiab.bii.org", - "biibas.bii.org", - "billing.surfsafevpn.com", - "blackboard.tru.ca", - "blastam.com", - "blogwatcher.co.jp", - "bmypage.kuronekoyamato.co.jp", - "boards.lineage2.com", - "bonds.euronext.com", // bug 1136091 - "books.wwnorton.com", // bug 1116891 - "borrowsmart.afford.com", - "bursar.ou.edu", - "buttons.verticalresponse.com", - "buy.lh.or.kr", - "bv.ac-lille.fr", - "bv.dealbase.vc", - "c2g.jupiter.fl.us", - "campusnet.csuohio.edu", - "canadaca.geotrust.com", // bug 1137677 - "careers.aucklandcouncil.govt.nz", - "careers.aut.ac.nz", - "careers.nzte.govt.nz", - "careers.state.co.nz", - "catalog.lapl.org", - "cbsfnotes1.blood.org.tw", - "celebrity.ebay.com", - "central.acadiau.ca", // bug 1152377 - "centrezone.bii.org", - "charity.5linx.com", - "chat.youtradefx.com", - "cherry.de", // bug 1141521 - "cias.rit.edu", - "civilization.com", // bug 1156004 - "client.alor.ru", - "clientes.chilectra.cl", - "club.guosen.com.cn", - "clubs.bii.org", - "cmypage.kuronekoyamato.co.jp", - "coagov.aurora-il.org", - "codem.codemasters.com", - "community.bentley.edu", - "comune.milano.it", - "consulting.nonghyup.com", - "controlpanel.globalinx.com", - "cooeps.coomeva.com.co", - "copyfaxes.com", - "corporbank.nbcb.com.cn", - "crm.et2008.com", - "cualerts.dupaco.com", // bug 1116892 - "cuconnect.colorado.edu", - "customers.logistafrance.fr", // bug 1153951 - "cwu.edu", - "daveosborne.com", - "dbank.hxb.com.cn", - "dealer.autobytel.com", - "demo.hotforex.com", - "device.panasonic.cn", - "dheb.delavska-hranilnica.si", - "dialog.tatrabanka.sk", - "digibet.com", - "digitalsecurity.intel.com", // bug 1148744 - "dismoiou.fr", - "docs.otpp.com", - "doktorski.unizg.hr", - "drc.ohiolink.edu", - "dream-prize.com", - "dwwsyw.bjgjj.gov.cn", - "eatm.scsb.com.tw", - "eb.bankcomm.com.hk", // bug 1141742 - "ebank-public.hzbank.com.cn", - "ebank.hxb.com.cn", - "ebank.hzbank.com.cn", - "ebank.rcbcy.com", // bug 1146755 - "ebanking.vietabank.com.vn", - "ebill2.virginmedia.com", // bug 1129887 - "ebpp.airtel.lk", - "ebspay.boc.cn", // bug 1155567 - "ec-line.cn", - "echo.com", - "echotrak.com", - "ecom.morethangourmet.com", - "ecommerce.saa.org", - "ecourses.uthscsa.edu", - "edu.ingang.go.kr", - "egov.town-menasha.com", // bug 1157536 - "emaildvla.direct.gov.uk", // bug 1116891 - "enrollment.fcbresource.com", - "epay.regionalacceptance.com", - "epicreg.com", - "eps.hrdkorea.or.kr", - "eremit.sbising.com", - "eremit.unionbankofindia.co.in", - "erewards.regionalacceptance.com", - "eseries.ansi.org", - "eservice.americangeneral.com", - "eservices.palomar.edu", - "espaceparticuliers.meteofrance.com", - "espaceprofessionnels.meteofrance.com", - "espanol.raileurope-world.com", - "etest.chosun.com", - "event.kasite.net", - "events.illinoisstate.edu", - "ewh.ieee.org", - "eximonline.exim.gov", - "experience.edinboro.edu", - "extranet.eurocontrol.int", - "extranet.meilleursagents.com", - "ez.cityofchesapeake.net", - "ezpay.com.tw", - "fabulousfurs.com", - "facultadmedicina.uc.cl", - "fallback.test", // Used by gtest - "famigros.migros.ch", - "fastcheck.sita.aero", // bug 1174974 - "fastlane.echo.com", - "fhsaa.org", - "fibi-online.co.il", // bug 1165580 - "finance.bankhapoalim.co.il", - "finance.car.com", - "finweb.rit.edu", - "flyingcart.com", - "form.kuronekoyamato.co.jp", - "fr.starbucks.ca", - "friends.freshandeasy.com", - "fubar.com", - "gateway.halton.gov.uk", - "gay.jadedvideo.com", - "gbe-bund.de", - "gc.guitarcenter.com", - "gestionesytramites.madrid.org", - "giftcertificates.com", - "gradschool.edinboro.edu", - "hercle.com", - "hospitals.unm.edu", - "hpshop.gr", - "hq.flyingcart.com", - "ibank.scsb.com.tw", - "identity.virginmedia.com", // bug 1129887 - "ifobs.kredobank.com.ua", - "ifund.allianzglobalinvestors.com.tw", - "ig1.i-grasp.com", // bug 1167894 - "ig4.i-grasp.com", // bug 1167894 - "ihr.suburbanpropane.com", - "images.d2pass.com", - "inetbank.zapsibkombank.ru", - "info.hottracks.co.kr", - "inside.i-med.ac.at", - "international.amu.edu.pl", - "international.unizg.hr", - "ioumate.com", - "its.bocmacau.com", - "jbclick.jaxbchfl.net", // bug 1158465 - "jobpostings.adecco.de", - "jobsstatic.civilservice.gov.uk", - "jst.doded.mil", // bug 1152627 - "justtherightbook.com", - "keirin.jp", - "killfish.ru", - "king-solarman.com", // bug 1190706 - "kjp.keinet.ne.jp", - "kjp.oo.kawai-juku.ac.jp", - "len.scsb.gov.sa", - "lewisham.gov.uk", - "life.xl.co.id", - "live.hotforex.com", - "lm-order.de", - "login.bankhapoalim.co.il", - "login.chicagopolice.org", - "login.ermis.gov.gr", - "login.ivenue.com", - "lovetyres.com", - "m.e-hon.ne.jp", - "mail.izhnet.ru", - "map.infonavit.org.mx", - "mchrono.com", - "media.preachingtoday.com", - "media.renewedvision.com", - "member.at.or.kr", - "member.edenredticket.com", - "member.franchise.org", - "member.shinsegaepoint.com", - "members.otpp.com", - "membership.usairways.com", - "merchant.edenredticket.com", - "meta-ehealth.com", - "mobile.aa.com", // bug 1141604 - "mobile.dream-prize.com", - "mobile.globalinx.com", - "mon-ulb.ulb.ac.be", - "my.if.com", // bug 1173592 - "my.kyivstar.ua", - "my.miit.ru", - "my.mimeo.com", - "myaccount.allstate.com", // bug 1143031 - "myaccount.mobicom.mn", - "myaccountum.sdge.com", - "myaccountum.socalgas.com", - "myadecco.adecco.de", - "mybank.nbcb.com.cn", - "mycuinfo.colorado.edu", - "myhancock.hancockcollege.edu", - "mypinoy.tv", - "myportal.brother.co.jp", - "myvia.viainfo.net", - "mywebreservations.com", - "na.aiononline.com", // bug 1139782 - "national.virginmedia.com", // bug 1129887 - "nbank.hxb.com.cn", - "netbanking.yesbank.co.in", // bug 1146090 - "new.fibi-online.co.il", // bug 1187242 - "newunivera.com", - "nmsmp.alsok.co.jp", - "no1.nipponrentacar.co.jp", - "obos1.obos.no", - "officials.fhsaa.org", - "ois.emu.ee", - "ok.sberbank.ru", - "online.bankotsar.co.il", // bug 1187242 - "online.cibeg.com", - "online.edinboro.edu", - "online.newindia.co.in", - "online.sainsburysbank.co.uk", - "openwebosproject.org", // bug 1151990 - "opus.unizg.hr", - "order.ps-webhosting.de", - "otherpeoplespixels.com", - "owa.byui.edu", - "ozone.ou.edu", - "parents.ou.edu", - "parking.leicester.gov.uk", - "partnerweb.vmware.com", // bug 1142187 - "parts.andersenstormdoors.com", - "parts.andersenwindows.com", - "payment.condor.com", // bug 1152347 - "payment.safepass.cn", - "payments.virginmedia.com", // bug 1129887 - "payments2.tutorvista.com", - "piloto.viajanet.com.ve", - "pointofsale.milo.com", - "policies.usc.edu", - "portal.cooley.edu", - "portal.eztec.com.br", - "portal.questonline.gr", - "portal.testvalley.gov.uk", - "portal.uem.es", - "pr.myacn.com", - "pracownicy.amu.edu.pl", - "preventivo.conte.it", - "profiles.uthscsa.edu", - "prolinestadium.alc.ca", - "promo.grosvenorcasinos.com", - "ps.btl.gov.il", - "publicjobs.ie", - "publicrecords.com", - "purebaby.com.au", - "pwm.svsu.edu", - "racenet.codemasters.com", // bug 1163716 - "recoup.com", - "recruitment.santos.com", - "redlaser.com", - "reg.ymcabv.org", - "register.ohioathletics.com", - "registration.o2.co.uk", - "renewals.cipd.co.uk", - "repair.kuroneko-kadendr.jp", // bug 1128366 - "repairmb.kuroneko-kadendr.jp", // bug 1128366 - "repo.clalbit.co.il", - "reputation.com", - "reservations.loknstore.co.uk", - "reservations.rwsentosa.com", - "reservations.usairways.com", // bug 1165400 - "reward-me.nescafe.co.uk", - "rietumu.lv", - "roxyaffiliates.com", - "sales.newchinalife.com", - "sbank.hxb.com.cn", - "sboseweb.mcpsweb.org", - "secure.aroundhawaii.com", - "secure.bankersinsurance.com", - "secure.bg-mania.jp", - "secure.blackbearprinting.com", - "secure.collegesavingsmd.org", - "secure.crbonline.gov.uk", // bug 1166644 - "secure.eoncode.com", - "secure.fortisbc.com", - "secure.groupspaces.com", - "secure.lhj.com", - "secure.massresort.com", - "secure.math.ubc.ca", - "secure.ncsoft.com", // bug 1139782 - "secure.nissan.co.jp", - "secure.particleweb.com", - "secure.pinnion.com", - "secure.rxprint.com", - "secure.smartcart.com", - "secure.thorlo.com", - "secure.whatsonsale.com.au", - "secure.wish.org", - "secure.zooprinting.com", - "secureonline.dwp.gov.uk", - "selfcare.tdc.dk", - "sems.hrd.ccsd.net", - "service.d2pass.com", - "services.apvma.gov.au", - "services.geotrust.com", // bug 1137677 - "servizionline.infogroup.it", - "shop.icewarp.com", - "shop.inlinevision.com", - "shop.kagome.co.jp", - "shop.nanairo.coop", // bug 1128318 - "shop.naturemill.com", - "shop.wildstar-online.com", // bug 1139782 - "shopping.girlsgonewild.com", - "shopping.usairways.com", - "siv.interieur.gouv.fr", - "slovanet.sk", - "smartcart.com", - "smlogin.aa.com", - "sna1.coomeva.com.co", - "soeasy.sodexo.be", // bug 1117157 - "sportsbookings.leicester.gov.uk", - "ss5.sfcollege.edu", - "ssb.okbu.edu", // for port 8910, bug 1153749 - "sso.acadiau.ca", // bug 1152377 - "sso.newsobserver.com", - "starbucks.co.uk", - "static.markilux.com", - "stenhouse.com", - "store.bibleworks.com", - "store.lineage2.com", - "store.tretorn.com", - "store.vicorpower.com", - "studenci.amu.edu.pl", - "suggestions.lyonaeroports.com", - "surfsafevpn.com", - "swdownloads.blackberry.com", // bug 1182997 - "syncd.com", - "syzygy.co.uk", - "tarjetacencosud.cl", - "tele2.hr", - "telebank.nomos.ru", - "termlife.allstate.com", - "thewigcompany.com", - "tiendas.mediamarkt.es", - "transer.com", - "transfers.edinboro.edu", - "uralsg.megafon.ru", // bug 1153168 - "us.ncsoft.com", - "usacycling.org", // bug 1163791 - "usc.unist.ac.kr", - "utradehub.or.kr", - "vdesk.pugetsound.edu", - "visawebapp.boca.gov.tw", - "vo.5linx.com", - "wapp.adecco.ch", - "watch.sportsnet.ca", // bug 1144769 - "web.asta.org", - "web.nccu.edu", - "web.svsu.edu", - "webapps.ou.edu", - "webatm.landbank.com.tw", - "webmail.ac-reunion.fr", - "webmail.iyte.edu.tr", - "webmail.unavarra.es", - "websites.zooprinting.com", - "websiti.cnbv.gob.mx", - "webtv.tv2.no", - "weddings.realresorts.com", - "wis.ntu.edu.sg", - "wszg.nbcs.gov.cn", - "www.101phones.com", - "www.2kgames.com", - "www.2ksports.com", - "www.5x2.de", - "www.a-pat.jra.go.jp", - "www.aa.co.uk", // bug 1141604 - "www.aa.com", // bug 1141604 - "www.aa.com.br", // bug 1141604 - "www.aa.com.do", // bug 1141604 - "www.aa.com.pe", // bug 1141604 - "www.aa.com.ve", // bug 1141604 - "www.aacargo.com", - "www.aavacations.com", // bug 1141604 - "www.abetterstay.com", - "www.absak.com", - "www.accessallstate.com", - "www.accessingram.com", - "www.advanceweb.com", - "www.aeroplan.com", // bug 1137543 - "www.affiliatepowergroup.com", - "www.afford.com", - "www.agenttrax.com", - "www.aimp.ru", - "www.akmembers.com", - "www.alaskacommunications.com", - "www.alc.ca", - "www.all-spec.com", - "www.allamericanswim.com", - "www.allbankonline.in", // bug 1156441 - "www.allfoodequipment.com.au", - "www.allinpay.com", - "www.allnatura.de", - "www.alphashirt.com", - "www.american-airlines.co.kr", // bug 1141604 - "www.american-airlines.nl", // bug 1141604 - "www.americanairlines.be", // bug 1141604 - "www.americanairlines.ch", // bug 1141604 - "www.americanairlines.cl", // bug 1141604 - "www.americanairlines.cn", // bug 1141604 - "www.americanairlines.co.cr", // bug 1141604 - "www.americanairlines.co.uk", // bug 1141604 - "www.americanairlines.com", // bug 1141604 - "www.americanairlines.com.au", // bug 1141604 - "www.americanairlines.com.ru", // bug 1141604 - "www.americanairlines.de", // bug 1141604 - "www.americanairlines.es", // bug 1141604 - "www.americanairlines.fi", // bug 1141604 - "www.americanairlines.fr", // bug 1141604 - "www.americanairlines.hu", // bug 1141604 - "www.americanairlines.ie", // bug 1141604 - "www.americanairlines.in", // bug 1141604 - "www.americanairlines.it", // bug 1141604 - "www.americanairlines.jp", // bug 1141604 - "www.americanexpress.co.il", - "www.amica.com", // bug 1139563 - "www.amleo.com", - "www.amu.edu.pl", - "www.amway.ca", - "www.amway.co.jp", - "www.amway.com", - "www.amway.com.ar", - "www.amway.com.co", - "www.amway.com.do", - "www.amway.my", - "www.amztrainingacademy.com", - "www.ancelutil.com.uy", - "www.angelsport.de", - "www.animate-onlineshop.jp", // bug 1126652 - "www.apeasternpower.com", - "www.arcgames.com", // bug 1182932 - "www.arex.or.kr", - "www.argentina.citibank.com", - "www.art-of-craft.co.uk", - "www.ase.org.uk", - "www.asko.fi", // bug 1158584 - "www.assuralia.be", - "www.auroragov.org", - "www.bancocredichile.cl", - "www.bankcomm.com.hk", // bug 1141742 - "www.bankhapoalim.biz", - "www.bankhapoalim.co.il", // bug 1138231 - "www.bannersusa.com", - "www.barefootconsultants.com", - "www.baseballrampage.com", - "www.baseballwarehouse.com", - "www.bauschonline.com", - "www.baybloorradio.com", // bug 1173661 - "www.bbsfonline.com", - "www.bectu.org.uk", - "www.belwue.de", - "www.betterhealthusa.com", - "www.bger.ch", - "www.bigflix.com", - "www.bigshotbikes.com", - "www.bikejames.com", - "www.bill36524.com", - "www.bizzclick.com", - "www.blastam.com", - "www.blissmo.com", - "www.blogwatcher.co.jp", - "www.blueprintonline.co.za", - "www.bluewateryachting.com", - "www.bookdirect2save.com.au", - "www.bookpubco.com", - "www.bookstore.usu.edu", - "www.boostmobilesales.com", // bug 1112178 - "www.borsaitaliana.it", - "www.bottegaverde.es", - "www.bottegaverde.it", - "www.bottegaverde.pt", - "www.bpmcarte.it", - "www.builderdepot.com", - "www.buildinggreen.com", - "www.buppin.e-aichi.jp", - "www.businessdirect.bt.com", - "www.buy-trees.co.uk", - "www.buzone.com.mx", - "www.cafedumonde.jp", - "www.cafis.jp", - "www.calguns.net", - "www.calvinkleinpreferred.com", - "www.cambridgenetwork.co.uk", - "www.carbidedepot.com", - "www.carbonlessondemand.com", - "www.careers.asio.gov.au", - "www.carewireless.com", - "www.carhistory.or.kr", - "www.carpartparadise.com", - "www.cbd.ae", - "www.cellcom.co.il", - "www.centralbank.net.in", - "www.centurynovelty.com", - "www.cg-express.com", - "www.cgfns.org", - "www.cherry.de", // bug 1141521 - "www.chinapay.com", // bug 1137983 - "www.cincinnatibell.com", - "www.cincinnatichildrens.org", - "www.cipd.co.uk", - "www.citychiconline.com", - "www.civilization.com", // bug 1156004 - "www.clalbit.co.il", - "www.club-animate.jp", - "www.clublacosta.com", - "www.codan.dk", - "www.companyformations.ie", - "www.companyformations123.co.uk", - "www.connexuscu.org", - "www.contraloria.cl", - "www.coolcarpartsonline.com", - "www.corpone.org", - "www.crazyegg.com", - "www.credem.it", - "www.crediscotia.com.mx", - "www.creditagricole.info", - "www.css-club.net", - "www.ctfeshop.com.cn", - "www.cubizone.com", - "www.cwu.edu", // bug 1143035 - "www.d2pass.com", - "www.dabs.com", - "www.dabs.ie", - "www.dabs4work.ie", - "www.daiichi-engei.co.jp", - "www.davidyurman.com", - "www.dburnsdesign.com", - "www.decopac.com", - "www.deepdiscount.com", - "www.delcity.net", - "www.dennismillerradio.com", - "www.derayah.com", - "www.diamondresorts.com", - "www.digibet.com", - "www.digieco.co.kr", - "www.diplom.de", - "www.diplomarbeiten24.de", - "www.doortodoor.co.kr", - "www.dotbank.md", - "www.drawing-tutorials-online.com", - "www.drcsurveys.com", - "www.dream-prize.com", - "www.drewaltizer.com", - "www.drlaura.com", - "www.drvoip.com", - "www.duskin.co.jp", - "www.duskin.jp", - "www.e-hapi.com", - "www.easy.cl", - "www.eatright.org", - "www.ebs.co.kr", - "www.ebse.co.kr", - "www.ebsi.co.kr", - "www.ec-line.cn", - "www.echo.com", - "www.echodesign.com", - "www.echotrak.com", - "www.edinboro.edu", - "www.educatingforsuccess.com", - "www.edunet4u.net", - "www.emailtracker.cargill.com", - "www.emich.edu", - "www.encompassinsurance.com", - "www.eoutlet4u.com", - "www.epicreg.com", - "www.equity.org.uk", - "www.ermis.gov.gr", - "www.esadealumni.net", - "www.esavingsaccount.co.uk", - "www.esgbl.com", - "www.euplatesc.ro", - "www.events.runningroom.com", - "www.everyd.com", - "www.evirtualservices.com", - "www.examicus.de", - "www.examiner.com", - "www.expertpay.com", - "www.expireddomains.co.nz", - "www.expo2012.kr", - "www.ezding.com.tw", - "www.ezpay.com.tw", - "www.f1autocentres.co.uk", - "www.faeriesdance.com", - "www.familyvideo.com", - "www.farmtek.com", - "www.farnell.com", - "www.fashionscarvesandshawls.com", - "www.favori.com.tr", - "www.ffbh.com", - "www.fgmarket.com", - "www.fhsaa.org", - "www.fibi-online.co.il", // bug 1165580 - "www.ficg.mx", - "www.finestwatches.com", - "www.fj96336.com", - "www.fontdiner.com", - "www.foodpyramidonline.com", - "www.fordpartsuk.com", - "www.forplaycatalog.com", - "www.forplayinc.com", - "www.foundersc.com", - "www.framesbymail.com", - "www.fubar.com", - "www.gamers-onlineshop.jp", // bug 1126654 - "www.gardens4you.co.uk", - "www.gbe-bund.de", - "www.getkombucha.com", - "www.getpark.co.uk", - "www.giftcertificates.com", - "www.girlsdressline.com", - "www.giving.runningroom.com", - "www.globalinx.com", - "www.golf18network.com", - "www.goodvibeuniversity.com", - "www.gopresto.com", - "www.goyada.com", - "www.gpknives.com", - "www.greatinternetmarketingtraining.com", - "www.grin.com", - "www.growerssupply.com", - "www.gsmeasy.nl", - "www.gtja.com", - "www.guiders.de", - "www.hacademia.com", - "www.halkbank.com.tr", - "www.handshoemouse.com", - "www.hanyang.ac.kr", - "www.harlequin.com", - "www.hausarbeiten.de", - "www.hbc.com", - "www.hdis.com", - "www.healthaffairs.org", - "www.heart.org", - "www.heidiandfrank.com", - "www.hercle.com", - "www.highstreetvouchers.com", - "www.hipgirlclips.com", - "www.hn.10086.cn", - "www.hockeystrengthandconditioning.com", - "www.holidaysplease.co.uk", - "www.hornerschool.com", - "www.horror-shop.com", - "www.hotkeys.com", - "www.hottopic.com", - "www.hottracks.co.kr", - "www.hpshop.gr", - "www.hsbank.cc", - "www.hursthardwoods.com", - "www.hx168.com.cn", - "www.i-mom.co.kr", - "www.id90.com", - "www.ilsos.gov", - "www.infomex.org.mx", - "www.ingramentertainment.com", - "www.innerwolf.co.uk", - "www.institut-entreprise.fr", - "www.insuranceonline.state.co.nz", - "www.insureportal.co.uk", - "www.interpark.com", - "www.interspire.com", - "www.ipsww.com", - "www.isracard.co.il", // bug 1165582 - "www.itsinyourjeans.co.uk", - "www.jacksonandperkins.com", - "www.jadedvideo.com", - "www.jaf.or.jp", - "www.jafp.or.jp", - "www.jewelboxco.com", - "www.jewelsbyparklane.com", - "www.jumbo.cl", - "www.kab.co.il", - "www.kasite.net", - "www.keyclient.it", - "www.khan.co.kr", - "www.kik-textilien.com", - "www.kimptonhotels.com", - "www.kinesissurvey.com", - "www.king-solarman.com", // bug 1190706 - "www.kissmycart.net", - "www.kitchenstuff.com", - "www.knifecave.com", - "www.knou.ac.kr", - "www.korea.ac.kr", - "www.koroad.or.kr", - "www.kredodirect.com.ua", // bug 1095507 - "www.krivet.re.kr", - "www.kt.com", - "www.kubotacreditusa.com", - "www.kuponan.ph", - "www.lauraingraham.com", - "www.law888.com.tw", - "www.lespac.com", - "www.lewisham.gov.uk", - "www.lexus.ca", - "www.libraryvideo.com", - "www.lightonvedicastrology.com", - "www.lightwerk.de", - "www.lineage2.com", - "www.lionbrand.com", - "www.liveexpert.ru", - "www.livinglebanese.com", - "www.lm-order.de", - "www.location-u.com", - "www.londonmagicstore.co.uk", - "www.londonstockexchange.com", - "www.lotte.co.kr", - "www.lottesuper.co.kr", - "www.love2reward.co.uk", - "www.lovelineshow.com", - "www.loweslink.com", - "www.luggagepoint.com", - "www.lurongliving.com", - "www.maccjcc.org", - "www.madriverglen.com", - "www.magnation.com", - "www.marcospecialties.com", - "www.math.ubc.ca", - "www.matkahuolto.info", - "www.matrics.or.jp", - "www.maxkravmaga.com", - "www.mchrono.com", - "www.med-ed.virginia.edu", - "www.medicalartspress.com", - "www.meilleursagents.com", - "www.meingartenshop.de", - "www.membershipsiteowner.com", - "www.mergernetwork.com", - "www.meta-ehealth.com", - "www.mikarose.com", - "www.mimple.net", - "www.ministrymatters.com", - "www.minttwist.com", - "www.mirrormate.com", - "www.misterdonut.jp", - "www.mit.gov.tr", - "www.mitsubishimotors-mirage.com", - "www.mixapparel.com.au", - "www.monsterhouseplans.com", - "www.moody.edu", - "www.moodyradio.org", - "www.mp2.aeroport.fr", - "www.mpay.co.th", - "www.mtsindia.in", // RC4 - "www.my.airdo.jp", // bug 1129773 - "www.myacn.com", - "www.myacncanada.ca", - "www.myagent.gov.ab.ca", // bug 1152827 - "www.myanycar.com", - "www.mycarpaltunnel.com", - "www.mychabad.org", - "www.mycontact.co.nz", - "www.myfloridacounty.com", - "www.myfloridaremit.com", - "www.myleather.com", - "www.mymeetings.com", - "www.myricoh.com", - "www.mysick.com", - "www.myticketin.com", - "www.mywebreservations.com", - "www.myxxxchurch.com", - "www.ncsoft.com", // bug 1139782 - "www.nec-nexs.com", - "www.nec.go.kr", - "www.nescafe.es", - "www.nestle-family.com", - "www.nestle.com.br", - "www.nestle.com.ec", - "www.nestle.es", - "www.nestlebaby.es", - "www.nestleprofessional.com", - "www.newchinalife.com", - "www.newvitality.com", - "www.nexon.net", - "www.nicheology.com", - "www.nite.org.il", - "www.nomercysupply.nl", - "www.ntsource.com", - "www.oakstore.de", - "www.ocbcwhhk.com", // bug 1141746 - "www.olatheks.org", - "www.onlinephonestore.com", - "www.ooshirts.com", - "www.openwebosproject.org", // bug 1151990 - "www.oralsteroids.com", - "www.osk188.com", - "www.ou.edu", - "www.paypal-media.com", - "www.pbdink.com", - "www.pdfscripting.com", - "www.pen-kanagawa.ed.jp", - "www.perfumania.com", - "www.ph-online.ac.at", - "www.physiciansmutual.com", - "www.pioneer.com", - "www.polla.cl", - "www.pooldeals.com", - "www.preparedpantry.com", - "www.primcast.com", - "www.prosearchplus.com", - "www.ptg.org", - "www.publicjobs.ie", - "www.publicrecords.com", - "www.pulmuoneshop.co.kr", - "www.pvh.com", - "www.pwcrecruiting.com", - "www.q-net.or.kr", - "www.races.runningroom.com", - "www.raileurope-gcc.com", - "www.raileurope-world.com", - "www.raileurope.cn", - "www.raileurope.co.in", - "www.raileurope.com.ar", - "www.raileurope.com.au", - "www.raileurope.com.br", - "www.razorgator.com", - "www.realestatepipeline.com", - "www.reclameaqui.com.br", - "www.recoup.com", - "www.recoverydatabase.net", - "www.redcaptour.com", - "www.redenergy.com.au", - "www.redletterdays.co.uk", - "www.registrarstats.com", - "www.regonline.ca", - "www.regonline.co.uk", - "www.regonline.sg", - "www.remotepc.net", - "www.renaultcredit.com.ar", - "www.reputation.com", - "www.rg.fft.fr", - "www.rietumu.lv", - "www.rimac.com.pe", - "www.riverguide.go.kr", - "www.roadtoroota.com", - "www.rosebrides.com", - "www.roxyaffiliates.com", - "www.rsagroup.com", - "www.runningroom.com", - "www.rwbaird.com", - "www.s-book.net", - "www.s20.co.kr", - "www.sac.or.kr", - "www.sacticket.co.kr", - "www.safepass.cn", - "www.sagetelecom.net", - "www.samba.com", - "www.saminfo.com", - "www.samsungfire.com", - "www.scion.ca", - "www.sda.co.kr", - "www.sdauhak.com", - "www.securesites.com", - "www.seedparade.co.uk", - "www.seeuthere.com", - "www.selfpointonline.it", - "www.seouldesign.or.kr", - "www.sermonsearch.com", - "www.servicebench.com", - "www.sesamnet.ch", - "www.session.ne.jp", - "www.seur.com", - "www.shacomsecurities.com.hk", // bug 1141989 - "www.shakeout.org", - "www.shop.bt.com", - "www.shop.runningroom.com", - "www.shortstay-london.com", - "www.singtelshop.com", - "www.skycasters.com", - "www.sleepphones.com", - "www.slovanet.sk", - "www.smartcart.com", - "www.smileedi.com", - "www.smithbrothers.com", - "www.smotor.com", - "www.soccerdrillstips.com", - "www.sokamocka.com", - "www.sorensystems.com", - "www.soukai.com", - "www.spedireweb.it", - "www.sprint.net", - "www.stanjames.com", - "www.starbucks.ca", - "www.statononline.com", - "www.stenhouse.com", - "www.stickerland.nl", - "www.stiftungen.org", - "www.subscribeonline.co.uk", - "www.sunderland.gov.uk", - "www.surveyequipment.com", - "www.svsu.edu", - "www.systeminsight.co.uk", - "www.syzygy.co.uk", - "www.t1shopper.com", - "www.taiwangun.biz", - "www.taku3.net", - "www.tarjetacencosud.cl", - "www.taxlienuniversity.com", - "www.tealife.co.jp", - "www.teddytank.com", - "www.tele2.hr", - "www.telebrands.net", - "www.telefonbucheintrag.de", - "www.tetsudo.com", - "www.thediscountflooringco.com", - "www.thehandbook.com", - "www.thepermitstore.com", - "www.ticketstogo.com", - "www.toutatice.fr", - "www.toyota.ca", - "www.tpvvirtual.net", - "www.tranas.se", - "www.treasuretrails.co.uk", - "www.trkd.thomsonreuters.com", // bug 1172793 - "www.trustitalia.it", - "www.tscapparel.com", - "www.tsptalk.com", - "www.u-gakugei.ac.jp", - "www.uccard.co.jp", - "www.ukr.jp", - "www.uksmobility.co.uk", - "www.undercovercondoms.com", - "www.undergroundstrengthcoach.com", - "www.upad.co.uk", - "www.uqac.ca", - "www.ur-net.go.jp", - "www.usacycling.org", // bug 1163791 - "www.usairways.com", // bug 1142703 - "www.usairwaysvacations.com", - "www.usbnow.co.uk", - "www.usc.edu", - "www.useaamiles.com", - "www.usg.edu", - "www.utradehub.or.kr", - "www.vandykes.com", - "www.vdolg.ru", - "www.viainfo.net", - "www.viajanet.com.ve", - "www.videosecrets.com", - "www.vidmeup.com", - "www.virgin.net", - "www.virginiats.com", - "www.vivatv.com.tw", - "www.volume.at", - "www.wagggsworld.org", - "www.wallartforless.com", - "www.wavecable.com", - "www.waysidegardens.com", - "www.weberz.com", - "www.webrun.com.br", - "www.webventure.com.br", - "www.wetax.go.kr", - "www.wheelsthroughtime.com", - "www.wingarc.com", - "www.winner.co.il", - "www.wohnservice-wien.at", - "www.workingabroad.com", - "www.workoncruiseships.com", - "www.worshiphousemedia.com", - "www.xrentdvd.com", - "www.yakult.co.kr", - "www.yeoin.com", - "www.youtradefx.com", - "www.ytfxaffiliates.com", - "www.zenfolio.com", - "www.zoominfo.com", - "www.zzzs.si", - "www1.aeroplan.com", // bug 1137543 - "www1.isracard.co.il", // bug 1165582 - "www2.aeroplan.com", // bug 1137543 - "www2.nfb.ca", - "www2.uwplatt.edu", - "www2.wou.edu", - "www3.aeroplan.com", // bug 1137543 - "www3.ibac.co.jp", - "www3.taiheiyo-ferry.co.jp", - "www4.aeroplan.com", // bug 1137543 - "xyk.cebbank.com", // bug 1145524 - "zenfolio.com", - "ziniarasciai.secure.su.lt", - "zoominfo.com", -}; diff --git a/security/manager/ssl/SSLServerCertVerification.cpp b/security/manager/ssl/SSLServerCertVerification.cpp index f172384968..80033e52d5 100644 --- a/security/manager/ssl/SSLServerCertVerification.cpp +++ b/security/manager/ssl/SSLServerCertVerification.cpp @@ -96,41 +96,41 @@ #include -#include "pkix/pkix.h" -#include "pkix/pkixnss.h" +#include "BRNameMatchingPolicy.h" #include "CertVerifier.h" #include "CryptoTask.h" #include "ExtendedValidation.h" #include "NSSCertDBTrustDomain.h" -#include "nsIBadCertListener2.h" -#include "nsICertOverrideService.h" -#include "nsISiteSecurityService.h" -#include "nsNSSComponent.h" -#include "nsNSSIOLayer.h" -#include "nsNSSShutDown.h" - +#include "PSMRunnable.h" +#include "RootCertificateTelemetryUtils.h" +#include "ScopedNSSTypes.h" +#include "SharedSSLState.h" +#include "cert.h" #include "mozilla/Assertions.h" #include "mozilla/Mutex.h" #include "mozilla/Telemetry.h" -#include "mozilla/net/DNS.h" #include "mozilla/UniquePtr.h" +#include "mozilla/net/DNS.h" #include "mozilla/unused.h" -#include "nsIThreadPool.h" -#include "nsISocketProvider.h" -#include "nsXPCOMCIDInternal.h" #include "nsComponentManagerUtils.h" -#include "nsServiceManagerUtils.h" -#include "PSMRunnable.h" -#include "RootCertificateTelemetryUtils.h" -#include "SharedSSLState.h" #include "nsContentUtils.h" +#include "nsIBadCertListener2.h" +#include "nsICertOverrideService.h" +#include "nsISiteSecurityService.h" +#include "nsISocketProvider.h" +#include "nsIThreadPool.h" +#include "nsNSSComponent.h" +#include "nsNSSIOLayer.h" +#include "nsNSSShutDown.h" +#include "nsServiceManagerUtils.h" #include "nsURLHelper.h" - -#include "ssl.h" -#include "cert.h" +#include "nsXPCOMCIDInternal.h" +#include "pkix/pkix.h" +#include "pkix/pkixnss.h" #include "secerr.h" #include "secoidt.h" #include "secport.h" +#include "ssl.h" #include "sslerr.h" extern mozilla::LazyLogModule gPIPNSSLog; @@ -348,7 +348,8 @@ MapCertErrorToProbeValue(PRErrorCode errorCode) } SECStatus -DetermineCertOverrideErrors(CERTCertificate* cert, const char* hostName, +DetermineCertOverrideErrors(const UniqueCERTCertificate& cert, + const char* hostName, PRTime now, PRErrorCode defaultErrorCodeToReport, /*out*/ uint32_t& collectedErrors, /*out*/ PRErrorCode& errorCodeTrust, @@ -378,7 +379,8 @@ DetermineCertOverrideErrors(CERTCertificate* cert, const char* hostName, collectedErrors = nsICertOverrideService::ERROR_UNTRUSTED; errorCodeTrust = defaultErrorCodeToReport; - SECCertTimeValidity validity = CERT_CheckCertValidTimes(cert, now, false); + SECCertTimeValidity validity = CERT_CheckCertValidTimes(cert.get(), now, + false); if (validity == secCertTimeUndetermined) { // This only happens if cert is null. CERT_CheckCertValidTimes will // have set the error code to SEC_ERROR_INVALID_ARGS. We should really @@ -432,13 +434,28 @@ DetermineCertOverrideErrors(CERTCertificate* cert, const char* hostName, PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure; } - result = CheckCertHostname(certInput, hostnameInput); + // Use a lax policy so as to not generate potentially spurious name + // mismatch "hints". + BRNameMatchingPolicy nameMatchingPolicy( + BRNameMatchingPolicy::Mode::DoNotEnforce); + // CheckCertHostname expects that its input represents a certificate that + // has already been successfully validated by BuildCertChain. This is + // obviously not the case, however, because we're in the error path of + // certificate verification. Thus, this is problematic. In the future, it + // would be nice to remove this optimistic additional error checking and + // simply punt to the front-end, which can more easily (and safely) perform + // extra checks to give the user hints as to why verification failed. + result = CheckCertHostname(certInput, hostnameInput, nameMatchingPolicy); // Treat malformed name information as a domain mismatch. if (result == Result::ERROR_BAD_DER || result == Result::ERROR_BAD_CERT_DOMAIN) { collectedErrors |= nsICertOverrideService::ERROR_MISMATCH; errorCodeMismatch = SSL_ERROR_BAD_CERT_DOMAIN; - } else if (result != Success) { + } else if (IsFatalError(result)) { + // Because its input has not been validated by BuildCertChain, + // CheckCertHostname can return an error that is less important than the + // original certificate verification error. Only return an error result + // from this function if we've encountered a fatal error. PR_SetError(MapResultToPRErrorCode(result), 0); return SECFailure; } @@ -623,7 +640,7 @@ CertErrorRunnable* CreateCertErrorRunnable(CertVerifier& certVerifier, PRErrorCode defaultErrorCodeToReport, nsNSSSocketInfo* infoObject, - CERTCertificate* cert, + const UniqueCERTCertificate& cert, const void* fdForLogging, uint32_t providerFlags, PRTime now) @@ -652,7 +669,7 @@ CreateCertErrorRunnable(CertVerifier& certVerifier, return nullptr; } - RefPtr nssCert(nsNSSCertificate::Create(cert)); + RefPtr nssCert(nsNSSCertificate::Create(cert.get())); if (!nssCert) { NS_ERROR("nsNSSCertificate::Create failed"); PR_SetError(SEC_ERROR_NO_MEMORY, 0); @@ -716,7 +733,7 @@ public: static SECStatus Dispatch(const RefPtr& certVerifier, const void* fdForLogging, nsNSSSocketInfo* infoObject, - CERTCertificate* serverCert, + const UniqueCERTCertificate& serverCert, ScopedCERTCertList& peerCertChain, SECItem* stapledOCSPResponse, uint32_t providerFlags, @@ -729,7 +746,7 @@ private: SSLServerCertVerificationJob(const RefPtr& certVerifier, const void* fdForLogging, nsNSSSocketInfo* infoObject, - CERTCertificate* cert, + const UniqueCERTCertificate& cert, CERTCertList* peerCertChain, SECItem* stapledOCSPResponse, uint32_t providerFlags, @@ -738,7 +755,7 @@ private: const RefPtr mCertVerifier; const void* const mFdForLogging; const RefPtr mInfoObject; - const ScopedCERTCertificate mCert; + const UniqueCERTCertificate mCert; ScopedCERTCertList mPeerCertChain; const uint32_t mProviderFlags; const Time mTime; @@ -749,13 +766,13 @@ private: SSLServerCertVerificationJob::SSLServerCertVerificationJob( const RefPtr& certVerifier, const void* fdForLogging, - nsNSSSocketInfo* infoObject, CERTCertificate* cert, + nsNSSSocketInfo* infoObject, const UniqueCERTCertificate& cert, CERTCertList* peerCertChain, SECItem* stapledOCSPResponse, uint32_t providerFlags, Time time, PRTime prtime) : mCertVerifier(certVerifier) , mFdForLogging(fdForLogging) , mInfoObject(infoObject) - , mCert(CERT_DupCertificate(cert)) + , mCert(CERT_DupCertificate(cert.get())) , mPeerCertChain(peerCertChain) , mProviderFlags(providerFlags) , mTime(time) @@ -778,7 +795,7 @@ SSLServerCertVerificationJob::SSLServerCertVerificationJob( // in order to support SPDY's cross-origin connection pooling. static SECStatus BlockServerCertChangeForSpdy(nsNSSSocketInfo* infoObject, - CERTCertificate* serverCert) + const UniqueCERTCertificate& serverCert) { // Get the existing cert. If there isn't one, then there is // no cert change to worry about. @@ -818,9 +835,9 @@ BlockServerCertChangeForSpdy(nsNSSSocketInfo* infoObject, } // Check to see if the cert has actually changed - ScopedCERTCertificate c(cert->GetCert()); + UniqueCERTCertificate c(cert->GetCert()); NS_ASSERTION(c, "very bad and hopefully impossible state"); - bool sameCert = CERT_CompareCerts(c, serverCert); + bool sameCert = CERT_CompareCerts(c.get(), serverCert.get()); if (sameCert) { return SECSuccess; } @@ -891,8 +908,7 @@ GatherBaselineRequirementsTelemetry(const ScopedCERTCertList& certList) if (!cert) { return; } - UniquePtr - commonName(CERT_GetCommonName(&cert->subject), PORT_Free); + UniquePORTString commonName(CERT_GetCommonName(&cert->subject)); // This only applies to certificates issued by authorities in our root // program. CERTCertificate* rootCert = rootNode->cert; @@ -921,9 +937,9 @@ GatherBaselineRequirementsTelemetry(const ScopedCERTCertList& certList) return; } - ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); + UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); CERTGeneralName* subjectAltNames = - CERT_DecodeAltNameExtension(arena, &altNameExtension); + CERT_DecodeAltNameExtension(arena.get(), &altNameExtension); // CERT_FindCertExtension takes a pointer to a SECItem and allocates memory // in its data field. This is a bad way to do this because we can't use a // ScopedSECItem and neither is that memory tracked by an arena. We have to @@ -1202,7 +1218,7 @@ GatherSuccessfulValidationTelemetry(const ScopedCERTCertList& certList) SECStatus AuthCertificate(CertVerifier& certVerifier, nsNSSSocketInfo* infoObject, - CERTCertificate* cert, + const UniqueCERTCertificate& cert, ScopedCERTCertList& peerCertChain, SECItem* stapledOCSPResponse, uint32_t providerFlags, @@ -1244,6 +1260,11 @@ AuthCertificate(CertVerifier& certVerifier, savedErrorCode = PR_GetError(); } + uint32_t evStatus = (rv != SECSuccess) ? 0 // 0 = Failure + : (evOidPolicy == SEC_OID_UNKNOWN) ? 1 // 1 = DV + : 2; // 2 = EV + Telemetry::Accumulate(Telemetry::CERT_EV_STATUS, evStatus); + if (ocspStaplingStatus != CertVerifier::OCSP_STAPLING_NEVER_CHECKED) { Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, ocspStaplingStatus); } @@ -1275,10 +1296,10 @@ AuthCertificate(CertVerifier& certVerifier, if (!status || !status->HasServerCert()) { if( rv == SECSuccess ){ - nsc = nsNSSCertificate::Create(cert, &evOidPolicy); + nsc = nsNSSCertificate::Create(cert.get(), &evOidPolicy); } else { - nsc = nsNSSCertificate::Create(cert); + nsc = nsNSSCertificate::Create(cert.get()); } } @@ -1335,7 +1356,7 @@ SSLServerCertVerificationJob::Dispatch( const RefPtr& certVerifier, const void* fdForLogging, nsNSSSocketInfo* infoObject, - CERTCertificate* serverCert, + const UniqueCERTCertificate& serverCert, ScopedCERTCertList& peerCertChain, SECItem* stapledOCSPResponse, uint32_t providerFlags, @@ -1409,7 +1430,7 @@ SSLServerCertVerificationJob::Run() // Reset the error code here so we can detect if AuthCertificate fails to // set the error code if/when it fails. PR_SetError(0, 0); - SECStatus rv = AuthCertificate(*mCertVerifier, mInfoObject, mCert.get(), + SECStatus rv = AuthCertificate(*mCertVerifier, mInfoObject, mCert, mPeerCertChain, mStapledOCSPResponse, mProviderFlags, mTime); if (rv == SECSuccess) { @@ -1432,9 +1453,8 @@ SSLServerCertVerificationJob::Run() } if (error != 0) { RefPtr runnable( - CreateCertErrorRunnable(*mCertVerifier, error, mInfoObject, - mCert.get(), mFdForLogging, mProviderFlags, - mPRTime)); + CreateCertErrorRunnable(*mCertVerifier, error, mInfoObject, mCert, + mFdForLogging, mProviderFlags, mPRTime)); if (!runnable) { // CreateCertErrorRunnable set a new error code error = PR_GetError(); @@ -1505,7 +1525,7 @@ AuthCertificateHook(void* arg, PRFileDesc* fd, PRBool checkSig, PRBool isServer) nsNSSSocketInfo* socketInfo = static_cast(arg); - ScopedCERTCertificate serverCert(SSL_PeerCertificate(fd)); + UniqueCERTCertificate serverCert(SSL_PeerCertificate(fd)); if (!checkSig || isServer || !socketInfo || !serverCert) { PR_SetError(PR_INVALID_STATE_ERROR, 0); diff --git a/security/manager/ssl/ScopedNSSTypes.h b/security/manager/ssl/ScopedNSSTypes.h index bcd7a89b11..0b1ee1aff4 100644 --- a/security/manager/ssl/ScopedNSSTypes.h +++ b/security/manager/ssl/ScopedNSSTypes.h @@ -329,6 +329,9 @@ typedef UniquePtr name; MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTCertificate, CERTCertificate, CERT_DestroyCertificate) +MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTCertificateList, + CERTCertificateList, + CERT_DestroyCertificateList) MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTCertificatePolicies, CERTCertificatePolicies, CERT_DestroyCertificatePoliciesExtension) @@ -358,14 +361,26 @@ MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePK11SlotInfo, MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePK11SlotList, PK11SlotList, PK11_FreeSlotList) +MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePK11SymKey, + PK11SymKey, + PK11_FreeSymKey) MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePLArenaPool, PLArenaPool, internal::PORT_FreeArena_false) +MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePORTString, + char, + PORT_Free); +MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePRFileDesc, + PRFileDesc, + PR_Close) MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECItem, SECItem, internal::SECITEM_FreeItem_true) +MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECKEYPrivateKey, + SECKEYPrivateKey, + SECKEY_DestroyPrivateKey) MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECKEYPublicKey, SECKEYPublicKey, SECKEY_DestroyPublicKey) diff --git a/security/manager/ssl/SharedCertVerifier.h b/security/manager/ssl/SharedCertVerifier.h index 10241d991f..04957ca3b6 100644 --- a/security/manager/ssl/SharedCertVerifier.h +++ b/security/manager/ssl/SharedCertVerifier.h @@ -21,9 +21,10 @@ public: SharedCertVerifier(OcspDownloadConfig odc, OcspStrictConfig osc, OcspGetConfig ogc, uint32_t certShortLifetimeInDays, - PinningMode pinningMode, SHA1Mode sha1Mode) + PinningMode pinningMode, SHA1Mode sha1Mode, + BRNameMatchingPolicy::Mode nameMatchingMode) : mozilla::psm::CertVerifier(odc, osc, ogc, certShortLifetimeInDays, - pinningMode, sha1Mode) + pinningMode, sha1Mode, nameMatchingMode) { } }; diff --git a/security/manager/ssl/TransportSecurityInfo.cpp b/security/manager/ssl/TransportSecurityInfo.cpp index 41e8682c1e..58476bf5cc 100644 --- a/security/manager/ssl/TransportSecurityInfo.cpp +++ b/security/manager/ssl/TransportSecurityInfo.cpp @@ -12,7 +12,6 @@ #include "nsNSSCertificate.h" #include "nsIX509CertValidity.h" #include "nsIDateTimeFormat.h" -#include "nsDateTimeFormatCID.h" #include "nsICertOverrideService.h" #include "nsIObjectInputStream.h" #include "nsIObjectOutputStream.h" @@ -620,41 +619,33 @@ AppendErrorTextUntrusted(PRErrorCode errTrust, } } -// returns TRUE if SAN was used to produce names -// return FALSE if nothing was produced -// names => a single name or a list of names -// multipleNames => whether multiple names were delivered -static bool -GetSubjectAltNames(CERTCertificate *nssCert, - nsINSSComponent *component, - nsString &allNames, - uint32_t &nameCount) +// Returns the number of dNSName or iPAddress entries encountered in the +// subject alternative name extension of the certificate. +// Returns zero if the extension is not present, could not be decoded, or if it +// does not contain any dNSName or iPAddress entries. +static uint32_t +GetSubjectAltNames(CERTCertificate* nssCert, nsString& allNames) { allNames.Truncate(); - nameCount = 0; - - SECItem altNameExtension = {siBuffer, nullptr, 0 }; - CERTGeneralName *sanNameList = nullptr; + ScopedAutoSECItem altNameExtension; SECStatus rv = CERT_FindCertExtension(nssCert, SEC_OID_X509_SUBJECT_ALT_NAME, &altNameExtension); if (rv != SECSuccess) { - return false; + return 0; } - - ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); + UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); if (!arena) { - return false; + return 0; } - - sanNameList = CERT_DecodeAltNameExtension(arena.get(), &altNameExtension); + CERTGeneralName* sanNameList(CERT_DecodeAltNameExtension(arena.get(), + &altNameExtension)); if (!sanNameList) { - return false; + return 0; } - SECITEM_FreeItem(&altNameExtension, false); - - CERTGeneralName *current = sanNameList; + uint32_t nameCount = 0; + CERTGeneralName* current = sanNameList; do { nsAutoString name; switch (current->type) { @@ -711,97 +702,62 @@ GetSubjectAltNames(CERTCertificate *nssCert, current = CERT_GetNextGeneralName(current); } while (current != sanNameList); // double linked - return true; + return nameCount; } -static void -AppendErrorTextMismatch(const nsString &host, - nsIX509Cert* ix509, - nsINSSComponent *component, - bool wantsHtml, - nsString &returnedMessage) +static nsresult +AppendErrorTextMismatch(const nsString& host, nsIX509Cert* ix509, + nsINSSComponent* component, bool wantsHtml, + nsString& returnedMessage) { - const char16_t *params[1]; - nsresult rv; - - ScopedCERTCertificate nssCert(ix509->GetCert()); + // Prepare a default "not valid for " string in case anything + // goes wrong (or in case the certificate is not valid for any hostnames). + nsAutoString notValidForHostnameString; + const char16_t* params[1]; + params[0] = host.get(); + nsresult rv = component->PIPBundleFormatStringFromName( + "certErrorMismatch", params, 1, notValidForHostnameString); + if (NS_FAILED(rv)) { + return rv; + } + notValidForHostnameString.Append('\n'); + UniqueCERTCertificate nssCert(ix509->GetCert()); if (!nssCert) { - // We are unable to extract the valid names, say "not valid for name". - params[0] = host.get(); - nsString formattedString; - rv = component->PIPBundleFormatStringFromName("certErrorMismatch", - params, 1, - formattedString); - if (NS_SUCCEEDED(rv)) { - returnedMessage.Append(formattedString); - returnedMessage.Append('\n'); - } - return; + returnedMessage.Append(notValidForHostnameString); + return NS_OK; } - nsString allNames; - uint32_t nameCount = 0; - bool useSAN = false; - - if (nssCert) - useSAN = GetSubjectAltNames(nssCert.get(), component, allNames, nameCount); - - if (!useSAN) { - char *certName = CERT_GetCommonName(&nssCert->subject); - if (certName) { - nsDependentCSubstring commonName(certName, strlen(certName)); - if (IsUTF8(commonName)) { - // Bug 1024781 - // We should actually check that the common name is a valid dns name or - // ip address and not any string value before adding it to the display - // list. - ++nameCount; - allNames.Assign(NS_ConvertUTF8toUTF16(commonName)); - } - PORT_Free(certName); - } - } - - if (nameCount > 1) { + nsAutoString allNames; + uint32_t nameCount = GetSubjectAltNames(nssCert.get(), allNames); + if (nameCount == 0) { + returnedMessage.Append(notValidForHostnameString); + } else if (nameCount > 1) { nsString message; - rv = component->GetPIPNSSBundleString("certErrorMismatchMultiple", - message); - if (NS_SUCCEEDED(rv)) { - returnedMessage.Append(message); - returnedMessage.AppendLiteral("\n "); - returnedMessage.Append(allNames); - returnedMessage.AppendLiteral(" \n"); + rv = component->GetPIPNSSBundleString("certErrorMismatchMultiple", message); + if (NS_FAILED(rv)) { + return rv; } - } - else if (nameCount == 1) { - const char16_t *params[1]; + returnedMessage.Append(message); + returnedMessage.AppendLiteral("\n "); + returnedMessage.Append(allNames); + returnedMessage.AppendLiteral(" \n"); + } else if (nameCount == 1) { params[0] = allNames.get(); - - const char *stringID; - if (wantsHtml) - stringID = "certErrorMismatchSingle2"; - else - stringID = "certErrorMismatchSinglePlain"; - nsString formattedString; - rv = component->PIPBundleFormatStringFromName(stringID, - params, 1, + const char* stringID = wantsHtml ? "certErrorMismatchSingle2" + : "certErrorMismatchSinglePlain"; + nsAutoString formattedString; + rv = component->PIPBundleFormatStringFromName(stringID, params, 1, formattedString); - if (NS_SUCCEEDED(rv)) { - returnedMessage.Append(formattedString); - returnedMessage.Append('\n'); - } - } - else { // nameCount == 0 - nsString message; - nsresult rv = component->GetPIPNSSBundleString("certErrorMismatchNoNames", - message); - if (NS_SUCCEEDED(rv)) { - returnedMessage.Append(message); - returnedMessage.Append('\n'); + if (NS_FAILED(rv)) { + return rv; } + returnedMessage.Append(formattedString); + returnedMessage.Append('\n'); } + + return NS_OK; } static void @@ -837,9 +793,10 @@ GetDateBoundary(nsIX509Cert* ix509, trueExpired_falseNotYetValid = false; } - nsCOMPtr dateTimeFormat(do_CreateInstance(NS_DATETIMEFORMAT_CONTRACTID, &rv)); - if (NS_FAILED(rv)) + nsCOMPtr dateTimeFormat = nsIDateTimeFormat::Create(); + if (!dateTimeFormat) { return; + } dateTimeFormat->FormatPRTime(nullptr, kDateFormatLong, kTimeFormatNoSeconds, timeToUse, formattedDate); @@ -970,7 +927,9 @@ formatOverridableCertErrorMessage(nsISSLStatus & sslStatus, rv = sslStatus.GetIsDomainMismatch(&isDomainMismatch); NS_ENSURE_SUCCESS(rv, rv); if (isDomainMismatch) { - AppendErrorTextMismatch(hostWithoutPort, ix509, component, wantsHtml, returnedMessage); + rv = AppendErrorTextMismatch(hostWithoutPort, ix509, component, wantsHtml, + returnedMessage); + NS_ENSURE_SUCCESS(rv, rv); } bool isNotValidAtThisTime; diff --git a/security/manager/ssl/moz.build b/security/manager/ssl/moz.build index 7ace61c46d..23f8ae3ce7 100644 --- a/security/manager/ssl/moz.build +++ b/security/manager/ssl/moz.build @@ -18,10 +18,12 @@ XPIDL_SOURCES += [ 'nsICertOverrideService.idl', 'nsICertPickDialogs.idl', 'nsIClientAuthDialogs.idl', + 'nsIContentSignatureVerifier.idl', 'nsIDataSignatureVerifier.idl', 'nsIGenKeypairInfoDlg.idl', 'nsIKeygenThread.idl', 'nsIKeyModule.idl', + 'nsINSSU2FToken.idl', 'nsINSSVersion.idl', 'nsIPK11Token.idl', 'nsIPK11TokenDB.idl', @@ -60,6 +62,7 @@ EXPORTS += [ 'nsNSSComponent.h', 'nsNSSHelper.h', 'nsNSSShutDown.h', + 'nsNSSU2FToken.h', 'nsRandomGenerator.h', 'nsSecurityHeaderParser.h', 'NSSErrorsService.h', @@ -82,7 +85,9 @@ EXPORTS.ipc += [ UNIFIED_SOURCES += [ 'CertBlocklist.cpp', + 'ContentSignatureVerifier.cpp', 'CryptoTask.cpp', + 'CSTrustDomain.cpp', 'DataStorage.cpp', 'nsCertOverrideService.cpp', 'nsCertPicker.cpp', @@ -103,11 +108,11 @@ UNIFIED_SOURCES += [ 'nsNSSCertificateFakeTransport.cpp', 'nsNSSCertTrust.cpp', 'nsNSSCertValidity.cpp', - 'nsNSSComponent.cpp', 'nsNSSErrors.cpp', 'nsNSSIOLayer.cpp', 'nsNSSModule.cpp', 'nsNSSShutDown.cpp', + 'nsNSSU2FToken.cpp', 'nsNSSVersion.cpp', 'nsNTLMAuthModule.cpp', 'nsPK11TokenDB.cpp', @@ -138,6 +143,7 @@ UNIFIED_SOURCES += [ # nsNSSCertificateDB.cpp needs to include nscert.h before everything else. SOURCES += [ 'nsNSSCertificateDB.cpp', + 'nsNSSComponent.cpp', ] IPDL_SOURCES += [ @@ -162,6 +168,7 @@ FINAL_LIBRARY = 'xul' LOCAL_INCLUDES += [ '/dom/base', + '/dom/crypto', '/security/certverifier', '/security/pkix/include', ] diff --git a/security/manager/ssl/nsCertOverrideService.cpp b/security/manager/ssl/nsCertOverrideService.cpp index f5dbb86c2d..ee8031e4af 100644 --- a/security/manager/ssl/nsCertOverrideService.cpp +++ b/security/manager/ssl/nsCertOverrideService.cpp @@ -346,8 +346,7 @@ GetCertFingerprintByOidTag(nsIX509Cert *aCert, SECOidTag aOidTag, nsCString &fp) { - - ScopedCERTCertificate nsscert(aCert->GetCert()); + UniqueCERTCertificate nsscert(aCert->GetCert()); if (!nsscert) { return NS_ERROR_FAILURE; } @@ -367,7 +366,7 @@ nsCertOverrideService::RememberValidityOverride(const nsACString& aHostName, if (aPort < -1) return NS_ERROR_INVALID_ARG; - ScopedCERTCertificate nsscert(aCert->GetCert()); + UniqueCERTCertificate nsscert(aCert->GetCert()); if (!nsscert) { return NS_ERROR_FAILURE; } diff --git a/security/manager/ssl/nsCertTree.cpp b/security/manager/ssl/nsCertTree.cpp index 8910935961..75fb19321d 100644 --- a/security/manager/ssl/nsCertTree.cpp +++ b/security/manager/ssl/nsCertTree.cpp @@ -4,25 +4,26 @@ #include "nsCertTree.h" -#include "pkix/pkixtypes.h" -#include "nsNSSComponent.h" // for PIPNSS string bundle calls. +#include "ScopedNSSTypes.h" +#include "mozilla/Logging.h" +#include "nsArray.h" +#include "nsArrayUtils.h" +#include "nsHashKeys.h" +#include "nsISupportsPrimitives.h" #include "nsITreeColumns.h" +#include "nsIX509CertDB.h" #include "nsIX509Cert.h" #include "nsIX509CertValidity.h" -#include "nsIX509CertDB.h" -#include "nsXPIDLString.h" -#include "nsReadableUtils.h" -#include "nsUnicharUtils.h" -#include "nsNSSCertificate.h" #include "nsNSSCertHelper.h" -#include "nsIMutableArray.h" -#include "nsArrayUtils.h" -#include "nsISupportsPrimitives.h" -#include "nsXPCOMCID.h" +#include "nsNSSCertificate.h" +#include "nsNSSComponent.h" // for PIPNSS string bundle calls. +#include "nsNSSHelper.h" +#include "nsReadableUtils.h" #include "nsTHashtable.h" -#include "nsHashKeys.h" - -#include "mozilla/Logging.h" +#include "nsUnicharUtils.h" +#include "nsXPCOMCID.h" +#include "nsXPIDLString.h" +#include "pkix/pkixtypes.h" using namespace mozilla; @@ -656,7 +657,7 @@ nsCertTree::UpdateUIContents() mNumOrgs = CountOrganizations(); mTreeArray = new treeArrayEl[mNumOrgs]; - mCellText = do_CreateInstance(NS_ARRAY_CONTRACTID); + mCellText = nsArrayBase::Create(); if (count) { uint32_t j = 0; @@ -762,12 +763,12 @@ nsCertTree::DeleteEntryObject(uint32_t index) // although there are still overrides stored, // so, we keep the cert, but remove the trust - ScopedCERTCertificate nsscert(cert->GetCert()); + UniqueCERTCertificate nsscert(cert->GetCert()); if (nsscert) { CERTCertTrust trust; memset((void*)&trust, 0, sizeof(trust)); - + SECStatus srv = CERT_DecodeTrustString(&trust, ""); // no override if (srv == SECSuccess) { CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), nsscert.get(), diff --git a/security/manager/ssl/nsCryptoHash.cpp b/security/manager/ssl/nsCryptoHash.cpp index b917a2d785..2daf447290 100644 --- a/security/manager/ssl/nsCryptoHash.cpp +++ b/security/manager/ssl/nsCryptoHash.cpp @@ -207,11 +207,10 @@ nsCryptoHash::Finish(bool ascii, nsACString & _retval) if (ascii) { - char *asciiData = BTOA_DataToAscii(buffer, hashLen); + UniquePORTString asciiData(BTOA_DataToAscii(buffer, hashLen)); NS_ENSURE_TRUE(asciiData, NS_ERROR_OUT_OF_MEMORY); - _retval.Assign(asciiData); - PORT_Free(asciiData); + _retval.Assign(asciiData.get()); } else { @@ -403,7 +402,7 @@ nsCryptoHMAC::Finish(bool aASCII, nsACString & _retval) if (!mHMACContext) return NS_ERROR_NOT_INITIALIZED; - + uint32_t hashLen = 0; unsigned char buffer[HASH_LENGTH_MAX]; unsigned char* pbuffer = buffer; @@ -411,11 +410,10 @@ nsCryptoHMAC::Finish(bool aASCII, nsACString & _retval) PK11_DigestFinal(mHMACContext, pbuffer, &hashLen, HASH_LENGTH_MAX); if (aASCII) { - char *asciiData = BTOA_DataToAscii(buffer, hashLen); + UniquePORTString asciiData(BTOA_DataToAscii(buffer, hashLen)); NS_ENSURE_TRUE(asciiData, NS_ERROR_OUT_OF_MEMORY); - _retval.Assign(asciiData); - PORT_Free(asciiData); + _retval.Assign(asciiData.get()); } else { diff --git a/security/manager/ssl/nsDataSignatureVerifier.cpp b/security/manager/ssl/nsDataSignatureVerifier.cpp index 5727f06be4..c8272e72f4 100644 --- a/security/manager/ssl/nsDataSignatureVerifier.cpp +++ b/security/manager/ssl/nsDataSignatureVerifier.cpp @@ -42,59 +42,54 @@ nsDataSignatureVerifier::VerifyData(const nsACString & aData, bool *_retval) { // Allocate an arena to handle the majority of the allocations - PLArenaPool *arena; - arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); - if (!arena) + UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); + if (!arena) { return NS_ERROR_OUT_OF_MEMORY; + } // Base 64 decode the key SECItem keyItem; PORT_Memset(&keyItem, 0, sizeof(SECItem)); - if (!NSSBase64_DecodeBuffer(arena, &keyItem, + if (!NSSBase64_DecodeBuffer(arena.get(), &keyItem, nsPromiseFlatCString(aPublicKey).get(), aPublicKey.Length())) { - PORT_FreeArena(arena, false); return NS_ERROR_FAILURE; } - + // Extract the public key from the data CERTSubjectPublicKeyInfo *pki = SECKEY_DecodeDERSubjectPublicKeyInfo(&keyItem); if (!pki) { - PORT_FreeArena(arena, false); return NS_ERROR_FAILURE; } SECKEYPublicKey *publicKey = SECKEY_ExtractPublicKey(pki); SECKEY_DestroySubjectPublicKeyInfo(pki); pki = nullptr; - + if (!publicKey) { - PORT_FreeArena(arena, false); return NS_ERROR_FAILURE; } - + // Base 64 decode the signature SECItem signatureItem; PORT_Memset(&signatureItem, 0, sizeof(SECItem)); - if (!NSSBase64_DecodeBuffer(arena, &signatureItem, + if (!NSSBase64_DecodeBuffer(arena.get(), &signatureItem, nsPromiseFlatCString(aSignature).get(), aSignature.Length())) { SECKEY_DestroyPublicKey(publicKey); - PORT_FreeArena(arena, false); return NS_ERROR_FAILURE; } - + // Decode the signature and algorithm CERTSignedData sigData; PORT_Memset(&sigData, 0, sizeof(CERTSignedData)); - SECStatus ss = SEC_QuickDERDecodeItem(arena, &sigData, + SECStatus ss = SEC_QuickDERDecodeItem(arena.get(), &sigData, CERT_SignatureDataTemplate, &signatureItem); if (ss != SECSuccess) { SECKEY_DestroyPublicKey(publicKey); - PORT_FreeArena(arena, false); return NS_ERROR_FAILURE; } - + // Perform the final verification DER_ConvertBitString(&(sigData.signature)); ss = VFY_VerifyDataWithAlgorithmID((const unsigned char*)nsPromiseFlatCString(aData).get(), @@ -102,11 +97,10 @@ nsDataSignatureVerifier::VerifyData(const nsACString & aData, &(sigData.signature), &(sigData.signatureAlgorithm), nullptr, nullptr); - + // Clean up remaining objects SECKEY_DestroyPublicKey(publicKey); - PORT_FreeArena(arena, false); - + *_retval = (ss == SECSuccess); return NS_OK; diff --git a/security/manager/ssl/nsIContentSignatureVerifier.idl b/security/manager/ssl/nsIContentSignatureVerifier.idl new file mode 100644 index 0000000000..458dc51bf2 --- /dev/null +++ b/security/manager/ssl/nsIContentSignatureVerifier.idl @@ -0,0 +1,88 @@ +/* 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" + +/** + * An interface for verifying content-signatures, inspired by + * https://tools.ietf.org/html/draft-thomson-http-content-signature-00 + * described here https://github.com/franziskuskiefer/content-signature/tree/pki + * + * A new signature verifier instance should be created for each signature + * verification - you can create these instances with do_CreateInstance. + * + * There are two ways to use this functionality: + * The first allows a signature to be verified all at once by simply calling + * verifyContentSignature. + * The second allows for streaming; call createContext with the signature + * information (and initial data), call update with more data as it becomes + * available then, finally, call end to verify the signature. + */ +[scriptable, uuid(45a5fe2f-c350-4b86-962d-02d5aaaa955a)] +interface nsIContentSignatureVerifier : nsISupports +{ + + /** + * Verification sources. + * If the verification is from ABOUT_NEWTAB, the content signature can only be + * verified with a certificate chain where the end entity is valid for the + * hostname "remote-newtab-signer.mozilla.org". + * If the verification is from ONECRL, the end entity must be valid for the + * hostname "oneCRL-signer.mozilla.org" + */ + const unsigned long ABOUT_NEWTAB = 0; + const unsigned long ONECRL = 1; + + /** + * Verifies that the data matches the data that was used to generate the + * signature. + * + * @param aData The data to be tested. + * @param aContentSignatureHeader The content-signature header, + * url-safe base64 encoded. + * @param aCertificateChain The certificate chain to use for verification. + * PEM encoded string. + * @param aSource The source of this verification (one of the + * values defined above). + * @returns true if the signature matches the data and aCertificateChain is + * valid within aContext, false if not. + */ + boolean verifyContentSignature(in ACString aData, in ACString aSignature, + in ACString aCertificateChain, + in unsigned long aSource); + + /** + * Creates a context to verify a content signature against data that is added + * later with update calls. + * + * @param aData The first chunk of data to be tested. + * This parameter is optional. + * @param aContentSignatureHeader The signature of the data, url-safe base64 + * encoded. + * @param aCertificateChain The certificate chain to use for + * verification. PEM encoded string. + * @param aSource The source of this verification (one of the + * values defined above). + */ + void createContext(in ACString aData, in ACString aSignature, + in ACString aCertificateChain, in unsigned long aSource); + + /** + * Adds data to the context that was used to generate the signature. + * + * @param aData More data to be tested. + */ + void update(in ACString aData); + + /** + * Finalises the signature and returns the result of the signature + * verification. + * + * @returns true if the signature matches the data added with createContext + * and update, false if not. + */ + boolean end(); + +}; diff --git a/security/manager/ssl/nsINSSU2FToken.idl b/security/manager/ssl/nsINSSU2FToken.idl new file mode 100644 index 0000000000..a07fabcba8 --- /dev/null +++ b/security/manager/ssl/nsINSSU2FToken.idl @@ -0,0 +1,75 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsIArray; +/** + * Interface used to interact with the NSS-backed software U2F Token + */ +[scriptable, uuid(d9104a00-140b-4f86-a4b0-4998878ef4e6 )] +interface nsINSSU2FToken : nsISupports { + /** + * Initializes the token and constructs and persists keys, if needed. Asserts + * that it is only called by the main thread. + */ + void init(); + + /** + * Is this token compatible with the provided version? + * + * @param version The offered version to test + * @return True if the offered version is compatible + */ + void isCompatibleVersion(in AString version, [retval] out boolean result); + + /** + * Return whether the provided KeyHandle belongs to this Token + * + * @param keyHandle Key Handle to evaluate. + * @return True if the Key Handle is ours. + */ + void isRegistered([array, size_is(keyHandleLen)] in octet keyHandle, + in uint32_t keyHandleLen, + [retval] out boolean result); + + /** + * Generates a public/private keypair for the provided application + * and challenge, returning the pubkey, challenge response, and + * key handle in the registration data. + * + * @param application The FIDO Application data to associate with the key. + * @param challenge The Challenge to satisfy in the response. + * @param registration An array containing the pubkey, challenge response, + * and key handle. + */ + void register([array, size_is(applicationLen)] in octet application, + in uint32_t applicationLen, + [array, size_is(challengeLen)] in octet challenge, + in uint32_t challengeLen, + [array, size_is(registrationLen)] out octet registration, + out uint32_t registrationLen); + + /** + * Creates a signature over the "param" arguments using the private key + * provided in the key handle argument. + * + * @param application The FIDO Application data to associate with the key. + * @param challenge The Challenge to satisfy in the response. + * @param keyHandle The Key Handle opaque object to use. + * @param signature The resulting signature. + */ + void sign([array, size_is(applicationLen)] in octet application, + in uint32_t applicationLen, + [array, size_is(challengeLen)] in octet challenge, + in uint32_t challengeLen, + [array, size_is(keyHandleLen)] in octet keyHandle, + in uint32_t keyHandleLen, + [array, size_is(signatureLen)] out octet signature, + out uint32_t signatureLen); +}; + +%{C++ +#define NS_NSSU2FTOKEN_CONTRACTID "@mozilla.org/dom/u2f/nss-u2f-token;1" +%} diff --git a/security/manager/ssl/nsKeyModule.cpp b/security/manager/ssl/nsKeyModule.cpp index f4e8c35ff0..6b0f80ac45 100644 --- a/security/manager/ssl/nsKeyModule.cpp +++ b/security/manager/ssl/nsKeyModule.cpp @@ -54,7 +54,7 @@ nsKeyObject::InitKey(int16_t aAlgorithm, PK11SymKey* aKey) return NS_ERROR_NOT_AVAILABLE; } - mSymKey = aKey; + mSymKey.reset(aKey); return NS_OK; } @@ -76,7 +76,7 @@ nsKeyObject::GetKeyObj(PK11SymKey** _retval) return NS_ERROR_NOT_INITIALIZED; } - *_retval = mSymKey; + *_retval = mSymKey.get(); return NS_OK; } @@ -142,14 +142,14 @@ nsKeyObjectFactory::KeyFromString(int16_t aAlgorithm, const nsACString& aKey, return NS_ERROR_FAILURE; } - ScopedPK11SymKey symKey(PK11_ImportSymKey(slot, cipherMech, + UniquePK11SymKey symKey(PK11_ImportSymKey(slot.get(), cipherMech, PK11_OriginUnwrap, cipherOperation, &keyItem, nullptr)); if (!symKey) { return NS_ERROR_FAILURE; } - rv = key->InitKey(aAlgorithm, symKey.forget()); + rv = key->InitKey(aAlgorithm, symKey.release()); if (NS_FAILED(rv)) { return rv; } diff --git a/security/manager/ssl/nsKeyModule.h b/security/manager/ssl/nsKeyModule.h index 7c844be073..2f7c219378 100644 --- a/security/manager/ssl/nsKeyModule.h +++ b/security/manager/ssl/nsKeyModule.h @@ -34,7 +34,7 @@ private: // Disallow copy constructor nsKeyObject(nsKeyObject&); - ScopedPK11SymKey mSymKey; + UniquePK11SymKey mSymKey; virtual void virtualDestroyNSSReference() override; void destructorSafeDestroyNSSReference(); diff --git a/security/manager/ssl/nsKeygenHandler.cpp b/security/manager/ssl/nsKeygenHandler.cpp index f58c89608f..477cc7bc07 100644 --- a/security/manager/ssl/nsKeygenHandler.cpp +++ b/security/manager/ssl/nsKeygenHandler.cpp @@ -27,6 +27,8 @@ #include "nsNSSShutDown.h" #include "nsXULAppAPI.h" +#include "mozilla/Telemetry.h" + //These defines are taken from the PKCS#11 spec #define CKM_RSA_PKCS_KEY_PAIR_GEN 0x00000000 #define CKM_DH_PKCS_KEY_PAIR_GEN 0x00000020 @@ -468,8 +470,7 @@ nsKeygenFormProcessor::GetPublicKey(const nsAString& aValue, SECKEYPrivateKey *privateKey = nullptr; SECKEYPublicKey *publicKey = nullptr; CERTSubjectPublicKeyInfo *spkInfo = nullptr; - PLArenaPool *arena = nullptr; - SECStatus sec_rv = SECFailure; + SECStatus srv = SECFailure; SECItem spkiItem; SECItem pkacItem; SECItem signedItem; @@ -482,6 +483,11 @@ nsKeygenFormProcessor::GetPublicKey(const nsAString& aValue, // permanent and sensitive flags for keygen PK11AttrFlags attrFlags = PK11_ATTR_TOKEN | PK11_ATTR_SENSITIVE | PK11_ATTR_PRIVATE; + UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); + if (!arena) { + goto loser; + } + // Get the key size // for (size_t i = 0; i < number_of_key_size_choices; ++i) { if (aValue.Equals(mSECKeySizeChoiceList[i].name)) { @@ -493,11 +499,6 @@ nsKeygenFormProcessor::GetPublicKey(const nsAString& aValue, goto loser; } - arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); - if (!arena) { - goto loser; - } - // Set the keygen mechanism if (aKeyType.IsEmpty() || aKeyType.LowerCaseEqualsLiteral("rsa")) { keyGenMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN; @@ -577,8 +578,8 @@ nsKeygenFormProcessor::GetPublicKey(const nsAString& aValue, if (NS_FAILED(rv)) goto loser; - sec_rv = PK11_Authenticate(slot, true, m_ctx); - if (sec_rv != SECSuccess) { + srv = PK11_Authenticate(slot, true, m_ctx); + if (srv != SECSuccess) { goto loser; } @@ -631,12 +632,13 @@ nsKeygenFormProcessor::GetPublicKey(const nsAString& aValue, if ( !spkInfo ) { goto loser; } - + /* * Now DER encode the whole subjectPublicKeyInfo. */ - sec_rv=DER_Encode(arena, &spkiItem, CERTSubjectPublicKeyInfoTemplate, spkInfo); - if (sec_rv != SECSuccess) { + srv = DER_Encode(arena.get(), &spkiItem, CERTSubjectPublicKeyInfoTemplate, + spkInfo); + if (srv != SECSuccess) { goto loser; } @@ -650,21 +652,22 @@ nsKeygenFormProcessor::GetPublicKey(const nsAString& aValue, rv = NS_ERROR_OUT_OF_MEMORY; goto loser; } - - sec_rv = DER_Encode(arena, &pkacItem, CERTPublicKeyAndChallengeTemplate, &pkac); - if ( sec_rv != SECSuccess ) { + + srv = DER_Encode(arena.get(), &pkacItem, CERTPublicKeyAndChallengeTemplate, + &pkac); + if (srv != SECSuccess) { goto loser; } /* * now sign the DER encoded PublicKeyAndChallenge */ - sec_rv = SEC_DerSignData(arena, &signedItem, pkacItem.data, pkacItem.len, - privateKey, algTag); - if ( sec_rv != SECSuccess ) { + srv = SEC_DerSignData(arena.get(), &signedItem, pkacItem.data, pkacItem.len, + privateKey, algTag); + if (srv != SECSuccess) { goto loser; } - + /* * Convert the signed public key and challenge into base64/ascii. */ @@ -678,8 +681,10 @@ nsKeygenFormProcessor::GetPublicKey(const nsAString& aValue, free(keystring); rv = NS_OK; + + GatherKeygenTelemetry(keyGenMechanism, keysize, keyparamsString); loser: - if ( sec_rv != SECSuccess ) { + if (srv != SECSuccess) { if ( privateKey ) { PK11_DestroyTokenObject(privateKey->pkcs11Slot,privateKey->pkcs11ID); } @@ -696,9 +701,6 @@ loser: if ( privateKey ) { SECKEY_DestroyPrivateKey(privateKey); } - if ( arena ) { - PORT_FreeArena(arena, true); - } if (slot) { PK11_FreeSlot(slot); } diff --git a/security/manager/ssl/nsNSSASN1Object.cpp b/security/manager/ssl/nsNSSASN1Object.cpp index d118e78246..6b52d8c940 100644 --- a/security/manager/ssl/nsNSSASN1Object.cpp +++ b/security/manager/ssl/nsNSSASN1Object.cpp @@ -2,12 +2,13 @@ * 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 "nsNSSASN1Object.h" -#include "nsIComponentManager.h" -#include "secasn1.h" -#include "nsReadableUtils.h" -#include "nsIMutableArray.h" + +#include "nsArray.h" #include "nsArrayUtils.h" +#include "nsIComponentManager.h" +#include "nsReadableUtils.h" #include "nsXPCOMCID.h" +#include "secasn1.h" NS_IMPL_ISUPPORTS(nsNSSASN1Sequence, nsIASN1Sequence, nsIASN1Object) NS_IMPL_ISUPPORTS(nsNSSASN1PrintableItem, nsIASN1PrintableItem, nsIASN1Object) @@ -222,7 +223,7 @@ NS_IMETHODIMP nsNSSASN1Sequence::GetASN1Objects(nsIMutableArray * *aASN1Objects) { if (!mASN1Objects) { - mASN1Objects = do_CreateInstance(NS_ARRAY_CONTRACTID); + mASN1Objects = nsArrayBase::Create(); } *aASN1Objects = mASN1Objects; NS_IF_ADDREF(*aASN1Objects); diff --git a/security/manager/ssl/nsNSSCallbacks.cpp b/security/manager/ssl/nsNSSCallbacks.cpp index a401e83b97..232baceeb4 100644 --- a/security/manager/ssl/nsNSSCallbacks.cpp +++ b/security/manager/ssl/nsNSSCallbacks.cpp @@ -5,21 +5,24 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsNSSCallbacks.h" -#include "pkix/pkixtypes.h" + #include "mozilla/TimeStamp.h" -#include "nsNSSComponent.h" -#include "nsNSSIOLayer.h" -#include "nsIWebProgressListener.h" -#include "nsProtectedAuthThread.h" +#include "nsContentUtils.h" +#include "nsICertOverrideService.h" +#include "nsIHttpChannelInternal.h" +#include "nsIPrompt.h" +#include "nsISupportsPriority.h" #include "nsITokenDialogs.h" #include "nsIUploadChannel.h" -#include "nsIPrompt.h" -#include "nsProxyRelease.h" -#include "PSMRunnable.h" -#include "nsContentUtils.h" -#include "nsIHttpChannelInternal.h" -#include "nsISupportsPriority.h" +#include "nsIWebProgressListener.h" #include "nsNetUtil.h" +#include "nsNSSComponent.h" +#include "nsNSSIOLayer.h" +#include "nsProtectedAuthThread.h" +#include "nsProxyRelease.h" +#include "pkix/pkixtypes.h" +#include "PSMRunnable.h" +#include "ScopedNSSTypes.h" #include "SharedSSLState.h" #include "ssl.h" #include "sslproto.h" @@ -796,11 +799,6 @@ PreliminaryHandshakeDone(PRFileDesc* fd) if (!infoObject) return; - if (infoObject->IsPreliminaryHandshakeDone()) - return; - - infoObject->SetPreliminaryHandshakeDone(); - SSLChannelInfo channelInfo; if (SSL_GetChannelInfo(fd, &channelInfo, sizeof(channelInfo)) == SECSuccess) { infoObject->SetSSLVersionUsed(channelInfo.protocolVersion); @@ -825,6 +823,11 @@ PreliminaryHandshakeDone(PRFileDesc* fd) } } + // Don't update NPN details on renegotiation. + if (infoObject->IsPreliminaryHandshakeDone()) { + return; + } + // Get the NPN value. SSLNextProtoState state; unsigned char npnbuf[256]; @@ -842,6 +845,8 @@ PreliminaryHandshakeDone(PRFileDesc* fd) else { infoObject->SetNegotiatedNPN(nullptr, 0); } + + infoObject->SetPreliminaryHandshakeDone(); } SECStatus @@ -1001,6 +1006,17 @@ void HandshakeCallback(PRFileDesc* fd, void* client_data) { bool renegotiationUnsafe = !siteSupportsSafeRenego && ioLayerHelpers.treatUnsafeNegotiationAsBroken(); + + /* Set the SSL Status information */ + RefPtr status(infoObject->SSLStatus()); + if (!status) { + status = new nsSSLStatus(); + infoObject->SetSSLStatus(status); + } + + RememberCertErrorsTable::GetInstance().LookupCertErrorBits(infoObject, + status); + uint32_t state; if (usesWeakCipher || renegotiationUnsafe) { state = nsIWebProgressListener::STATE_IS_BROKEN; @@ -1018,6 +1034,39 @@ void HandshakeCallback(PRFileDesc* fd, void* client_data) { infoObject->GetPort()); } } + + if (status->HasServerCert()) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("HandshakeCallback KEEPING existing cert\n")); + } else { + UniqueCERTCertificate serverCert(SSL_PeerCertificate(fd)); + RefPtr nssc(nsNSSCertificate::Create(serverCert.get())); + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("HandshakeCallback using NEW cert %p\n", nssc.get())); + status->SetServerCert(nssc, nsNSSCertificate::ev_status_unknown); + } + + nsCOMPtr overrideService = + do_GetService(NS_CERTOVERRIDE_CONTRACTID); + + if (overrideService) { + bool haveOverride; + uint32_t overrideBits = 0; // Unused. + bool isTemporaryOverride; // Unused. + const nsACString& hostString(infoObject->GetHostName()); + const int32_t port(infoObject->GetPort()); + nsCOMPtr cert; + status->GetServerCert(getter_AddRefs(cert)); + nsresult nsrv = overrideService->HasMatchingOverride(hostString, port, + cert, + &overrideBits, + &isTemporaryOverride, + &haveOverride); + if (NS_SUCCEEDED(nsrv) && haveOverride) { + state |= nsIWebProgressListener::STATE_CERT_USER_OVERRIDDEN; + } + } + infoObject->SetSecurityState(state); // XXX Bug 883674: We shouldn't be formatting messages here in PSM; instead, @@ -1036,27 +1085,6 @@ void HandshakeCallback(PRFileDesc* fd, void* client_data) { nsContentUtils::LogSimpleConsoleError(msg, "SSL"); } - /* Set the SSL Status information */ - RefPtr status(infoObject->SSLStatus()); - if (!status) { - status = new nsSSLStatus(); - infoObject->SetSSLStatus(status); - } - - RememberCertErrorsTable::GetInstance().LookupCertErrorBits(infoObject, - status); - - if (status->HasServerCert()) { - MOZ_LOG(gPIPNSSLog, LogLevel::Debug, - ("HandshakeCallback KEEPING existing cert\n")); - } else { - ScopedCERTCertificate serverCert(SSL_PeerCertificate(fd)); - RefPtr nssc(nsNSSCertificate::Create(serverCert.get())); - MOZ_LOG(gPIPNSSLog, LogLevel::Debug, - ("HandshakeCallback using NEW cert %p\n", nssc.get())); - status->SetServerCert(nssc, nsNSSCertificate::ev_status_unknown); - } - infoObject->NoteTimeUntilReady(); infoObject->SetHandshakeCompleted(); } diff --git a/security/manager/ssl/nsNSSCertHelper.cpp b/security/manager/ssl/nsNSSCertHelper.cpp index c702bfa9b0..634688d984 100644 --- a/security/manager/ssl/nsNSSCertHelper.cpp +++ b/security/manager/ssl/nsNSSCertHelper.cpp @@ -7,6 +7,7 @@ #include #include "mozilla/net/DNS.h" +#include "mozilla/Snprintf.h" #include "mozilla/UniquePtr.h" #include "nsComponentManagerUtils.h" #include "nsCOMPtr.h" @@ -17,9 +18,9 @@ #include "nsNSSCertTrust.h" #include "nsNSSCertValidity.h" #include "nsNSSComponent.h" +#include "nsIDateTimeFormat.h" #include "nsServiceManagerUtils.h" #include "prerror.h" -#include "prprf.h" #include "ScopedNSSTypes.h" #include "secder.h" @@ -211,12 +212,12 @@ GetDefaultOIDFormat(SECItem *oid, unsigned long one = std::min(val/40, 2UL); // never > 2 unsigned long two = val - (one * 40); - written = PR_snprintf(&buf[len], sizeof(buf)-len, "%lu%c%lu", - one, separator, two); + written = snprintf(&buf[len], sizeof(buf) - len, "%lu%c%lu", + one, separator, two); } else { - written = PR_snprintf(&buf[len], sizeof(buf)-len, "%c%lu", - separator, val); + written = snprintf(&buf[len], sizeof(buf) - len, "%c%lu", + separator, val); } } else { @@ -224,13 +225,12 @@ GetDefaultOIDFormat(SECItem *oid, nssComponent->GetPIPNSSBundleString("CertUnknown", unknownText); if (first) { - written = PR_snprintf(&buf[len], sizeof(buf)-len, "%s", - NS_ConvertUTF16toUTF8(unknownText).get()); + written = snprintf(&buf[len], sizeof(buf) - len, "%s", + NS_ConvertUTF16toUTF8(unknownText).get()); } else { - written = PR_snprintf(&buf[len], sizeof(buf)-len, "%c%s", - separator, - NS_ConvertUTF16toUTF8(unknownText).get()); + written = snprintf(&buf[len], sizeof(buf) - len, "%c%s", + separator, NS_ConvertUTF16toUTF8(unknownText).get()); } if (++invalidCount > 3) { @@ -643,7 +643,7 @@ ProcessRawBytes(nsINSSComponent *nssComponent, SECItem *data, uint32_t i; char buffer[5]; for (i=0; ilen; i++) { - PR_snprintf(buffer, 5, "%02x ", data->data[i]); + snprintf_literal(buffer, "%02x ", data->data[i]); AppendASCIItoUTF16(buffer, text); if ((i+1)%16 == 0) { text.AppendLiteral(SEPARATOR); @@ -980,10 +980,10 @@ ProcessGeneralName(const UniquePLArenaPool& arena, CERTGeneralName* current, && guid.len == 16) { char buf[40]; unsigned char *d = guid.data; - PR_snprintf(buf, sizeof(buf), - "{%.2x%.2x%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x%.2x%.2x%.2x%.2x}", - d[3], d[2], d[1], d[0], d[5], d[4], d[7], d[6], - d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); + snprintf_literal(buf, + "{%.2x%.2x%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x%.2x%.2x%.2x%.2x}", + d[3], d[2], d[1], d[0], d[5], d[4], d[7], d[6], + d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); value.AssignASCII(buf); } else { ProcessRawBytes(nssComponent, ¤t->name.OthName.name, value); @@ -1209,7 +1209,7 @@ ProcessUserNotice(SECItem* derNotice, nsAString& text, unsigned long number; char buffer[60]; if (SEC_ASN1DecodeInteger(*itemList, &number) == SECSuccess) { - PR_snprintf(buffer, sizeof(buffer), "#%d", number); + snprintf_literal(buffer, "#%d", number); if (itemList != notice->noticeReference.noticeNumbers) text.AppendLiteral(", "); AppendASCIItoUTF16(buffer, text); @@ -1499,7 +1499,7 @@ ProcessMSCAVersion(SECItem *extData, return ProcessRawBytes(nssComponent, extData, text); /* Apparently, the encoding is , with 16 bits each */ - PR_snprintf(buf, sizeof(buf), "%d.%d", version & 0xFFFF, version>>16); + snprintf_literal(buf, "%d.%d", version & 0xFFFF, version >> 16); text.AppendASCII(buf); return NS_OK; } @@ -1640,11 +1640,9 @@ static nsresult ProcessTime(PRTime dispTime, const char16_t* displayName, nsIASN1Sequence* parentSequence) { - nsresult rv; - nsCOMPtr dateFormatter = - do_CreateInstance(NS_DATETIMEFORMAT_CONTRACTID, &rv); - if (NS_FAILED(rv)) { - return rv; + nsCOMPtr dateFormatter = nsIDateTimeFormat::Create(); + if (!dateFormatter) { + return NS_ERROR_FAILURE; } nsString text; @@ -2116,10 +2114,9 @@ GetCertFingerprintByOidTag(CERTCertificate* nsscert, nsscert->derCert.len); NS_ENSURE_SUCCESS(rv, rv); - char *tmpstr = CERT_Hexify(const_cast(&digest.get()), 1); + UniquePORTString tmpstr(CERT_Hexify(const_cast(&digest.get()), 1)); NS_ENSURE_TRUE(tmpstr, NS_ERROR_OUT_OF_MEMORY); - fp.Assign(tmpstr); - PORT_Free(tmpstr); + fp.Assign(tmpstr.get()); return NS_OK; } diff --git a/security/manager/ssl/nsNSSCertValidity.cpp b/security/manager/ssl/nsNSSCertValidity.cpp index 3095fec9c2..17f41ce43e 100644 --- a/security/manager/ssl/nsNSSCertValidity.cpp +++ b/security/manager/ssl/nsNSSCertValidity.cpp @@ -5,7 +5,6 @@ #include "nsNSSCertValidity.h" #include "nsCOMPtr.h" #include "nsIDateTimeFormat.h" -#include "nsDateTimeFormatCID.h" #include "nsComponentManagerUtils.h" #include "nsReadableUtils.h" #include "nsNSSShutDown.h" @@ -56,10 +55,10 @@ nsX509CertValidity::FormatTime(const PRTime& aTimeDate, if (!mTimesInitialized) return NS_ERROR_FAILURE; - nsresult rv; - nsCOMPtr dateFormatter = - do_CreateInstance(NS_DATETIMEFORMAT_CONTRACTID, &rv); - if (NS_FAILED(rv)) return rv; + nsCOMPtr dateFormatter = nsIDateTimeFormat::Create(); + if (!dateFormatter) { + return NS_ERROR_FAILURE; + } PRExplodedTime explodedTime; PR_ExplodeTime(const_cast(aTimeDate), aParamFn, &explodedTime); diff --git a/security/manager/ssl/nsNSSCertificate.cpp b/security/manager/ssl/nsNSSCertificate.cpp index 720e286024..1ccc243ab7 100644 --- a/security/manager/ssl/nsNSSCertificate.cpp +++ b/security/manager/ssl/nsNSSCertificate.cpp @@ -5,51 +5,49 @@ #include "nsNSSCertificate.h" -#include "prmem.h" -#include "prerror.h" -#include "prprf.h" #include "CertVerifier.h" #include "ExtendedValidation.h" -#include "mozilla/UniquePtr.h" +#include "NSSCertDBTrustDomain.h" +#include "certdb.h" +#include "mozilla/Base64.h" #include "mozilla/unused.h" +#include "nsArray.h" +#include "nsCOMPtr.h" +#include "nsCRT.h" +#include "nsCertVerificationThread.h" +#include "nsICertificateDialogs.h" +#include "nsIClassInfoImpl.h" +#include "nsIObjectInputStream.h" +#include "nsIObjectOutputStream.h" +#include "nsISupportsPrimitives.h" +#include "nsIURI.h" +#include "nsIX509Cert.h" +#include "nsNSSASN1Object.h" +#include "nsNSSCertHelper.h" +#include "nsNSSCertValidity.h" +#include "nsNSSComponent.h" // for PIPNSS string bundle calls. +#include "nsPK11TokenDB.h" +#include "nsPKCS12Blob.h" +#include "nsProxyRelease.h" +#include "nsReadableUtils.h" +#include "nsString.h" +#include "nsThreadUtils.h" +#include "nsUnicharUtils.h" +#include "nsUsageArrayHelper.h" +#include "nsXULAppAPI.h" +#include "nspr.h" +#include "nssb64.h" #include "pkix/pkixnss.h" #include "pkix/pkixtypes.h" -#include "nsNSSComponent.h" // for PIPNSS string bundle calls. -#include "nsCOMPtr.h" -#include "nsIMutableArray.h" -#include "nsNSSCertValidity.h" -#include "nsPKCS12Blob.h" -#include "nsPK11TokenDB.h" -#include "nsIX509Cert.h" -#include "nsIClassInfoImpl.h" -#include "nsNSSASN1Object.h" -#include "nsString.h" -#include "nsXPIDLString.h" -#include "nsReadableUtils.h" -#include "nsIURI.h" -#include "nsCRT.h" -#include "nsUsageArrayHelper.h" -#include "nsICertificateDialogs.h" -#include "nsNSSCertHelper.h" -#include "nsISupportsPrimitives.h" -#include "nsUnicharUtils.h" -#include "nsThreadUtils.h" -#include "nsCertVerificationThread.h" -#include "nsIObjectOutputStream.h" -#include "nsIObjectInputStream.h" -#include "nsXULAppAPI.h" -#include "nsProxyRelease.h" -#include "mozilla/Base64.h" -#include "NSSCertDBTrustDomain.h" -#include "nspr.h" -#include "certdb.h" -#include "pkix/pkixtypes.h" -#include "secerr.h" -#include "nssb64.h" +#include "pkix/Result.h" +#include "plbase64.h" +#include "prerror.h" +#include "prmem.h" +#include "prprf.h" #include "secasn1.h" #include "secder.h" +#include "secerr.h" #include "ssl.h" -#include "plbase64.h" #ifdef XP_WIN #include // for htonl @@ -120,7 +118,7 @@ nsNSSCertificate::InitFromDER(char* certDER, int derLen) aCert->dbhandle = CERT_GetDefaultCertDB(); } - mCert = aCert; + mCert.reset(aCert); return true; } @@ -141,7 +139,7 @@ nsNSSCertificate::nsNSSCertificate(CERTCertificate* cert, return; if (cert) { - mCert = CERT_DupCertificate(cert); + mCert.reset(CERT_DupCertificate(cert)); if (evOidPolicy) { if (*evOidPolicy == SEC_OID_UNKNOWN) { mCachedEVStatus = ev_status_invalid; @@ -229,8 +227,8 @@ nsNSSCertificate::GetIsBuiltInRoot(bool* aIsBuiltInRoot) if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } - Result rv = IsCertBuiltInRoot(mCert, *aIsBuiltInRoot); - if (rv != Success) { + pkix::Result rv = IsCertBuiltInRoot(mCert.get(), *aIsBuiltInRoot); + if (rv != pkix::Result::Success) { return NS_ERROR_FAILURE; } return NS_OK; @@ -517,7 +515,7 @@ nsNSSCertificate::GetDbKey(nsACString& aDbKey) } nsresult -nsNSSCertificate::GetDbKey(CERTCertificate* cert, nsACString& aDbKey) +nsNSSCertificate::GetDbKey(const UniqueCERTCertificate& cert, nsACString& aDbKey) { static_assert(sizeof(uint64_t) == 8, "type size sanity check"); static_assert(sizeof(uint32_t) == 4, "type size sanity check"); @@ -560,8 +558,7 @@ nsNSSCertificate::GetWindowTitle(nsAString& aWindowTitle) return NS_ERROR_FAILURE; } - UniquePtr - commonName(CERT_GetCommonName(&mCert->subject), PORT_Free); + UniquePORTString commonName(CERT_GetCommonName(&mCert->subject)); const char* titleOptions[] = { mCert->nickname, @@ -709,10 +706,9 @@ nsNSSCertificate::GetCommonName(nsAString& aCommonName) aCommonName.Truncate(); if (mCert) { - char* commonName = CERT_GetCommonName(&mCert->subject); + UniquePORTString commonName(CERT_GetCommonName(&mCert->subject)); if (commonName) { - aCommonName = NS_ConvertUTF8toUTF16(commonName); - PORT_Free(commonName); + aCommonName = NS_ConvertUTF8toUTF16(commonName.get()); } } return NS_OK; @@ -727,10 +723,9 @@ nsNSSCertificate::GetOrganization(nsAString& aOrganization) aOrganization.Truncate(); if (mCert) { - char* organization = CERT_GetOrgName(&mCert->subject); + UniquePORTString organization(CERT_GetOrgName(&mCert->subject)); if (organization) { - aOrganization = NS_ConvertUTF8toUTF16(organization); - PORT_Free(organization); + aOrganization = NS_ConvertUTF8toUTF16(organization.get()); } } return NS_OK; @@ -745,10 +740,9 @@ nsNSSCertificate::GetIssuerCommonName(nsAString& aCommonName) aCommonName.Truncate(); if (mCert) { - char* commonName = CERT_GetCommonName(&mCert->issuer); + UniquePORTString commonName(CERT_GetCommonName(&mCert->issuer)); if (commonName) { - aCommonName = NS_ConvertUTF8toUTF16(commonName); - PORT_Free(commonName); + aCommonName = NS_ConvertUTF8toUTF16(commonName.get()); } } return NS_OK; @@ -763,10 +757,9 @@ nsNSSCertificate::GetIssuerOrganization(nsAString& aOrganization) aOrganization.Truncate(); if (mCert) { - char* organization = CERT_GetOrgName(&mCert->issuer); + UniquePORTString organization(CERT_GetOrgName(&mCert->issuer)); if (organization) { - aOrganization = NS_ConvertUTF8toUTF16(organization); - PORT_Free(organization); + aOrganization = NS_ConvertUTF8toUTF16(organization.get()); } } return NS_OK; @@ -781,10 +774,9 @@ nsNSSCertificate::GetIssuerOrganizationUnit(nsAString& aOrganizationUnit) aOrganizationUnit.Truncate(); if (mCert) { - char* organizationUnit = CERT_GetOrgUnitName(&mCert->issuer); + UniquePORTString organizationUnit(CERT_GetOrgUnitName(&mCert->issuer)); if (organizationUnit) { - aOrganizationUnit = NS_ConvertUTF8toUTF16(organizationUnit); - PORT_Free(organizationUnit); + aOrganizationUnit = NS_ConvertUTF8toUTF16(organizationUnit.get()); } } return NS_OK; @@ -829,10 +821,9 @@ nsNSSCertificate::GetOrganizationalUnit(nsAString& aOrganizationalUnit) aOrganizationalUnit.Truncate(); if (mCert) { - char* orgunit = CERT_GetOrgUnitName(&mCert->subject); + UniquePORTString orgunit(CERT_GetOrgUnitName(&mCert->subject)); if (orgunit) { - aOrganizationalUnit = NS_ConvertUTF8toUTF16(orgunit); - PORT_Free(orgunit); + aOrganizationalUnit = NS_ConvertUTF8toUTF16(orgunit.get()); } } return NS_OK; @@ -846,7 +837,6 @@ nsNSSCertificate::GetChain(nsIArray** _rvChain) return NS_ERROR_NOT_AVAILABLE; NS_ENSURE_ARG(_rvChain); - nsresult rv; MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Getting chain for \"%s\"\n", mCert->nickname)); mozilla::pkix::Time now(mozilla::pkix::Now()); @@ -909,10 +899,9 @@ nsNSSCertificate::GetChain(nsIArray** _rvChain) } // enumerate the chain for scripting purposes - nsCOMPtr array = - do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); - if (NS_FAILED(rv)) { - goto done; + nsCOMPtr array = nsArrayBase::Create(); + if (!array) { + return NS_ERROR_FAILURE; } CERTCertListNode* node; for (node = CERT_LIST_HEAD(nssChain.get()); @@ -925,9 +914,7 @@ nsNSSCertificate::GetChain(nsIArray** _rvChain) } *_rvChain = array; NS_IF_ADDREF(*_rvChain); - rv = NS_OK; -done: - return rv; + return NS_OK; } NS_IMETHODIMP @@ -1016,10 +1003,9 @@ nsNSSCertificate::GetSerialNumber(nsAString& _serialNumber) return NS_ERROR_NOT_AVAILABLE; _serialNumber.Truncate(); - char* tmpstr = CERT_Hexify(&mCert->serialNumber, 1); + UniquePORTString tmpstr(CERT_Hexify(&mCert->serialNumber, 1)); if (tmpstr) { - _serialNumber = NS_ConvertASCIItoUTF16(tmpstr); - PORT_Free(tmpstr); + _serialNumber = NS_ConvertASCIItoUTF16(tmpstr.get()); return NS_OK; } return NS_ERROR_FAILURE; @@ -1042,13 +1028,12 @@ nsNSSCertificate::GetCertificateHash(nsAString& aFingerprint, SECOidTag aHashAlg } // CERT_Hexify's second argument is an int that is interpreted as a boolean - char* fpStr = CERT_Hexify(const_cast(&digest.get()), 1); + UniquePORTString fpStr(CERT_Hexify(const_cast(&digest.get()), 1)); if (!fpStr) { return NS_ERROR_FAILURE; } - aFingerprint.AssignASCII(fpStr); - PORT_Free(fpStr); + aFingerprint.AssignASCII(fpStr.get()); return NS_OK; } @@ -1191,18 +1176,19 @@ nsNSSCertificate::ExportAsCMS(uint32_t chainMode, // in the SignedData). if (chainMode == nsIX509Cert::CMS_CHAIN_MODE_CertChain || chainMode == nsIX509Cert::CMS_CHAIN_MODE_CertChainWithRoot) { - ScopedCERTCertificate issuerCert( - CERT_FindCertIssuer(mCert.get(), PR_Now(), certUsageAnyCA)); + UniqueCERTCertificate issuerCert( + CERT_FindCertIssuer(mCert.get(), PR_Now(), certUsageAnyCA)); // the issuerCert of a self signed root is the cert itself, // so make sure we're not adding duplicates, again - if (issuerCert && issuerCert != mCert.get()) { + if (issuerCert && issuerCert != mCert) { bool includeRoot = (chainMode == nsIX509Cert::CMS_CHAIN_MODE_CertChainWithRoot); - ScopedCERTCertificateList certChain( - CERT_CertChainFromCert(issuerCert, certUsageAnyCA, includeRoot)); + UniqueCERTCertificateList certChain( + CERT_CertChainFromCert(issuerCert.get(), certUsageAnyCA, includeRoot)); if (certChain) { - if (NSS_CMSSignedData_AddCertList(sigd.get(), certChain) == SECSuccess) { - certChain.forget(); + if (NSS_CMSSignedData_AddCertList(sigd.get(), certChain.get()) + == SECSuccess) { + Unused << certChain.release(); } else { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, @@ -1212,9 +1198,9 @@ nsNSSCertificate::ExportAsCMS(uint32_t chainMode, } else { // try to add the issuerCert, at least - if (NSS_CMSSignedData_AddCertificate(sigd.get(), issuerCert) - == SECSuccess) { - issuerCert.forget(); + if (NSS_CMSSignedData_AddCertificate(sigd.get(), issuerCert.get()) + == SECSuccess) { + Unused << issuerCert.release(); } else { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, @@ -1236,7 +1222,7 @@ nsNSSCertificate::ExportAsCMS(uint32_t chainMode, return NS_ERROR_FAILURE; } - ScopedPLArenaPool arena(PORT_NewArena(1024)); + UniquePLArenaPool arena(PORT_NewArena(1024)); if (!arena) { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsNSSCertificate::ExportAsCMS - out of memory\n")); @@ -1245,7 +1231,7 @@ nsNSSCertificate::ExportAsCMS(uint32_t chainMode, SECItem certP7 = { siBuffer, nullptr, 0 }; NSSCMSEncoderContext* ecx = NSS_CMSEncoder_Start(cmsg.get(), nullptr, nullptr, - &certP7, arena, nullptr, + &certP7, arena.get(), nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); if (!ecx) { @@ -1394,7 +1380,7 @@ nsNSSCertificate::Equals(nsIX509Cert* other, bool* result) NS_ENSURE_ARG(other); NS_ENSURE_ARG(result); - ScopedCERTCertificate cert(other->GetCert()); + UniqueCERTCertificate cert(other->GetCert()); *result = (mCert.get() == cert.get()); return NS_OK; } @@ -1505,17 +1491,17 @@ ConstructCERTCertListFromReversedDERArray( size_t numCerts = certArray.GetLength(); for (size_t i = 0; i < numCerts; ++i) { SECItem certDER(UnsafeMapInputToSECItem(*certArray.GetDER(i))); - ScopedCERTCertificate cert(CERT_NewTempCertificate(certDB, &certDER, + UniqueCERTCertificate cert(CERT_NewTempCertificate(certDB, &certDER, nullptr, false, true)); if (!cert) { return SECFailure; } // certArray is ordered with the root first, but we want the resulting // certList to have the root last. - if (CERT_AddCertToListHead(certList, cert) != SECSuccess) { + if (CERT_AddCertToListHead(certList.get(), cert.get()) != SECSuccess) { return SECFailure; } - cert.forget(); // cert is now owned by certList. + Unused << cert.release(); // cert is now owned by certList. } return SECSuccess; diff --git a/security/manager/ssl/nsNSSCertificate.h b/security/manager/ssl/nsNSSCertificate.h index 519f263278..c31c636b34 100644 --- a/security/manager/ssl/nsNSSCertificate.h +++ b/security/manager/ssl/nsNSSCertificate.h @@ -55,12 +55,13 @@ public: // This is a separate static method so nsNSSComponent can use it during NSS // initialization. Other code should probably not use it. - static nsresult GetDbKey(CERTCertificate* cert, nsACString& aDbKey); + static nsresult GetDbKey(const mozilla::UniqueCERTCertificate& cert, + nsACString& aDbKey); private: virtual ~nsNSSCertificate(); - mozilla::ScopedCERTCertificate mCert; + mozilla::UniqueCERTCertificate mCert; bool mPermDelete; uint32_t mCertType; nsresult CreateASN1Struct(nsIASN1Object** aRetVal); diff --git a/security/manager/ssl/nsNSSCertificateDB.cpp b/security/manager/ssl/nsNSSCertificateDB.cpp index 1317d2011a..c5e609f65f 100644 --- a/security/manager/ssl/nsNSSCertificateDB.cpp +++ b/security/manager/ssl/nsNSSCertificateDB.cpp @@ -15,6 +15,7 @@ #include "SharedSSLState.h" #include "mozilla/Base64.h" #include "mozilla/unused.h" +#include "nsArray.h" #include "nsArrayUtils.h" #include "nsCOMPtr.h" #include "nsCRT.h" @@ -106,14 +107,13 @@ nsNSSCertificateDB::FindCertByNickname(const nsAString& nickname, if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } - ScopedCERTCertificate cert; char *asciiname = nullptr; NS_ConvertUTF16toUTF8 aUtf8Nickname(nickname); asciiname = const_cast(aUtf8Nickname.get()); MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Getting \"%s\"\n", asciiname)); - cert = PK11_FindCertFromNickname(asciiname, nullptr); + UniqueCERTCertificate cert(PK11_FindCertFromNickname(asciiname, nullptr)); if (!cert) { - cert = CERT_FindCertByNickname(CERT_GetDefaultCertDB(), asciiname); + cert.reset(CERT_FindCertByNickname(CERT_GetDefaultCertDB(), asciiname)); } if (cert) { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("got it\n")); @@ -348,15 +348,14 @@ nsNSSCertificateDB::handleCACertDownload(nsIArray *x509Certs, MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Creating temp cert\n")); CERTCertDBHandle *certdb = CERT_GetDefaultCertDB(); - ScopedCERTCertificate tmpCert(CERT_FindCertByDERCert(certdb, &der)); + UniqueCERTCertificate tmpCert(CERT_FindCertByDERCert(certdb, &der)); if (!tmpCert) { - tmpCert = CERT_NewTempCertificate(certdb, &der, - nullptr, false, true); + tmpCert.reset(CERT_NewTempCertificate(certdb, &der, nullptr, false, true)); } free(der.data); der.data = nullptr; der.len = 0; - + if (!tmpCert) { NS_ERROR("Couldn't create cert from DER blob"); return NS_ERROR_FAILURE; @@ -463,10 +462,9 @@ nsNSSCertificateDB::ImportCertificates(uint8_t* data, uint32_t length, return NS_ERROR_FAILURE; } - nsresult rv; - nsCOMPtr array = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); - if (NS_FAILED(rv)) { - return rv; + nsCOMPtr array = nsArrayBase::Create(); + if (!array) { + return NS_ERROR_FAILURE; } // Now let's create some certs to work with @@ -478,7 +476,7 @@ nsNSSCertificateDB::ImportCertificates(uint8_t* data, uint32_t length, if (!cert) { return NS_ERROR_FAILURE; } - rv = array->AppendElement(cert, false); + nsresult rv = array->AppendElement(cert, false); if (NS_FAILED(rv)) { return rv; } @@ -824,7 +822,7 @@ nsNSSCertificateDB::DeleteCertificate(nsIX509Cert *aCert) if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } - ScopedCERTCertificate cert(aCert->GetCert()); + UniqueCERTCertificate cert(aCert->GetCert()); if (!cert) { return NS_ERROR_FAILURE; } @@ -865,7 +863,7 @@ nsNSSCertificateDB::SetCertTrust(nsIX509Cert *cert, } nsNSSCertTrust trust; nsresult rv; - ScopedCERTCertificate nsscert(cert->GetCert()); + UniqueCERTCertificate nsscert(cert->GetCert()); rv = attemptToLogInWithDefaultPassword(); if (NS_WARN_IF(rv != NS_OK)) { @@ -917,7 +915,7 @@ nsNSSCertificateDB::IsCertTrusted(nsIX509Cert *cert, return NS_ERROR_NOT_AVAILABLE; } SECStatus srv; - ScopedCERTCertificate nsscert(cert->GetCert()); + UniqueCERTCertificate nsscert(cert->GetCert()); CERTCertTrust nsstrust; srv = CERT_GetCertTrust(nsscert.get(), &nsstrust); if (srv != SECSuccess) @@ -1084,7 +1082,7 @@ nsNSSCertificateDB::FindEmailEncryptionCert(const nsAString& aNickname, asciiname = const_cast(aUtf8Nickname.get()); /* Find a good cert in the user's database */ - ScopedCERTCertificate cert(CERT_FindUserCertByUsage(CERT_GetDefaultCertDB(), + UniqueCERTCertificate cert(CERT_FindUserCertByUsage(CERT_GetDefaultCertDB(), asciiname, certUsageEmailRecipient, true, ctx)); @@ -1121,7 +1119,7 @@ nsNSSCertificateDB::FindEmailSigningCert(const nsAString& aNickname, asciiname = const_cast(aUtf8Nickname.get()); /* Find a good cert in the user's database */ - ScopedCERTCertificate cert(CERT_FindUserCertByUsage(CERT_GetDefaultCertDB(), + UniqueCERTCertificate cert(CERT_FindUserCertByUsage(CERT_GetDefaultCertDB(), asciiname, certUsageEmailSigner, true, ctx)); @@ -1248,7 +1246,7 @@ nsNSSCertificateDB::ConstructX509(const char* certDER, secitem_cert.data = (unsigned char*)certDER; secitem_cert.len = lengthDER; - ScopedCERTCertificate cert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), + UniqueCERTCertificate cert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &secitem_cert, nullptr, false, true)); if (!cert) @@ -1282,19 +1280,15 @@ nsNSSCertificateDB::get_default_nickname(CERTCertificate *cert, return; nsAutoCString username; - char *temp_un = CERT_GetCommonName(&cert->subject); - if (temp_un) { - username = temp_un; - PORT_Free(temp_un); - temp_un = nullptr; + UniquePORTString tempCN(CERT_GetCommonName(&cert->subject)); + if (tempCN) { + username = tempCN.get(); } nsAutoCString caname; - char *temp_ca = CERT_GetOrgName(&cert->issuer); - if (temp_ca) { - caname = temp_ca; - PORT_Free(temp_ca); - temp_ca = nullptr; + UniquePORTString tempIssuerOrg(CERT_GetOrgName(&cert->issuer)); + if (tempIssuerOrg) { + caname = tempIssuerOrg.get(); } nsAutoString tmpNickFmt; @@ -1346,18 +1340,17 @@ nsNSSCertificateDB::get_default_nickname(CERTCertificate *cert, PR_smprintf_free(tmp); } - ScopedCERTCertificate dummycert; + UniqueCERTCertificate dummycert; if (PK11_IsInternal(slot)) { /* look up the nickname to make sure it isn't in use already */ - dummycert = CERT_FindCertByNickname(defaultcertdb, nickname.get()); - + dummycert.reset(CERT_FindCertByNickname(defaultcertdb, nickname.get())); } else { /* * Check the cert against others that already live on the smart * card. */ - dummycert = PK11_FindCertFromNickname(nickname.get(), ctx); + dummycert.reset(PK11_FindCertFromNickname(nickname.get(), ctx)); if (dummycert) { /* * Make sure the subject names are different. @@ -1409,10 +1402,10 @@ NS_IMETHODIMP nsNSSCertificateDB::AddCertFromBase64(const char* aBase64, MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Creating temp cert\n")); CERTCertDBHandle *certdb = CERT_GetDefaultCertDB(); - ScopedCERTCertificate tmpCert(CERT_FindCertByDERCert(certdb, &der)); - if (!tmpCert) - tmpCert = CERT_NewTempCertificate(certdb, &der, - nullptr, false, true); + UniqueCERTCertificate tmpCert(CERT_FindCertByDERCert(certdb, &der)); + if (!tmpCert) { + tmpCert.reset(CERT_NewTempCertificate(certdb, &der, nullptr, false, true)); + } free(der.data); der.data = nullptr; der.len = 0; @@ -1466,7 +1459,7 @@ nsNSSCertificateDB::SetCertTrustFromString(nsIX509Cert* cert, if (srv != SECSuccess) { return MapSECStatus(SECFailure); } - ScopedCERTCertificate nssCert(cert->GetCert()); + UniqueCERTCertificate nssCert(cert->GetCert()); nsresult rv = attemptToLogInWithDefaultPassword(); if (NS_WARN_IF(rv != NS_OK)) { @@ -1521,7 +1514,7 @@ VerifyCertAtTime(nsIX509Cert* aCert, EnsureIdentityInfoLoaded(); #endif - ScopedCERTCertificate nssCert(aCert->GetCert()); + UniqueCERTCertificate nssCert(aCert->GetCert()); if (!nssCert) { return NS_ERROR_INVALID_ARG; } @@ -1544,7 +1537,7 @@ VerifyCertAtTime(nsIX509Cert* aCert, aFlags, &evOidPolicy); } else { - srv = certVerifier->VerifyCert(nssCert, aUsage, aTime, + srv = certVerifier->VerifyCert(nssCert.get(), aUsage, aTime, nullptr, // Assume no context aHostname, resultChain, diff --git a/security/manager/ssl/nsNSSComponent.cpp b/security/manager/ssl/nsNSSComponent.cpp index 7e844b7b75..972ea1084d 100644 --- a/security/manager/ssl/nsNSSComponent.cpp +++ b/security/manager/ssl/nsNSSComponent.cpp @@ -10,6 +10,7 @@ #include "ExtendedValidation.h" #include "NSSCertDBTrustDomain.h" +#include "ScopedNSSTypes.h" #include "SharedSSLState.h" #include "cert.h" #include "certdb.h" @@ -18,7 +19,6 @@ #include "mozilla/Services.h" #include "mozilla/StaticPtr.h" #include "mozilla/SyncRunnable.h" -#include "mozilla/UniquePtr.h" #include "nsAppDirectoryServiceDefs.h" #include "nsCRT.h" #include "nsCertVerificationThread.h" @@ -59,9 +59,9 @@ #include "nsILocalFileWin.h" #include "windows.h" // this needs to be before the following includes -#include "Lmcons.h" -#include "Sddl.h" -#include "Wincrypt.h" +#include "lmcons.h" +#include "sddl.h" +#include "wincrypt.h" #include "nsIWindowsRegKey.h" #endif @@ -260,7 +260,6 @@ nsNSSComponent::nsNSSComponent() NS_ASSERTION( (0 == mInstanceCount), "nsNSSComponent is a singleton, but instantiated multiple times!"); ++mInstanceCount; - mShutdownObjectList = nsNSSShutDownList::construct(); } void @@ -304,7 +303,7 @@ nsNSSComponent::~nsNSSComponent() SharedSSLState::GlobalCleanup(); RememberCertErrorsTable::Cleanup(); --mInstanceCount; - delete mShutdownObjectList; + nsNSSShutDownList::shutdown(); // We are being freed, drop the haveLoaded flag to re-enable // potential nss initialization later. @@ -684,8 +683,7 @@ MaybeImportFamilySafetyRoot(PCCERT_CONTEXT certificate, return NS_ERROR_FAILURE; } // Looking for a certificate with the common name 'Microsoft Family Safety' - UniquePtr subjectName( - CERT_GetCommonName(&nssCertificate->subject), PORT_Free); + UniquePORTString subjectName(CERT_GetCommonName(&nssCertificate->subject)); MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("subject name is '%s'", subjectName.get())); if (nsCRT::strcmp(subjectName.get(), "Microsoft Family Safety") == 0) { @@ -703,7 +701,7 @@ MaybeImportFamilySafetyRoot(PCCERT_CONTEXT certificate, return NS_ERROR_FAILURE; } nsAutoCString dbKey; - nsresult rv = nsNSSCertificate::GetDbKey(nssCertificate.get(), dbKey); + nsresult rv = nsNSSCertificate::GetDbKey(nssCertificate, dbKey); if (NS_FAILED(rv)) { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("GetDbKey failed")); return rv; @@ -1375,6 +1373,21 @@ void nsNSSComponent::setValidationOptions(bool isInitialSetting, break; default: sha1Mode = CertVerifier::SHA1Mode::Allowed; + break; + } + + BRNameMatchingPolicy::Mode nameMatchingMode = + static_cast + (Preferences::GetInt("security.pki.name_matching_mode", + static_cast(BRNameMatchingPolicy::Mode::DoNotEnforce))); + switch (nameMatchingMode) { + case BRNameMatchingPolicy::Mode::Enforce: + case BRNameMatchingPolicy::Mode::EnforceAfter23August2016: + case BRNameMatchingPolicy::Mode::DoNotEnforce: + break; + default: + nameMatchingMode = BRNameMatchingPolicy::Mode::DoNotEnforce; + break; } CertVerifier::OcspDownloadConfig odc; @@ -1386,7 +1399,8 @@ void nsNSSComponent::setValidationOptions(bool isInitialSetting, lock); mDefaultCertVerifier = new SharedCertVerifier(odc, osc, ogc, certShortLifetimeInDays, - pinningMode, sha1Mode); + pinningMode, sha1Mode, + nameMatchingMode); } // Enable the TLS versions given in the prefs, defaulting to TLS 1.0 (min) and @@ -1626,6 +1640,14 @@ nsNSSComponent::InitializeNSS() return NS_ERROR_FAILURE; } + // Initialize the cert override service + nsCOMPtr coService = + do_GetService(NS_CERTOVERRIDE_CONTRACTID); + if (!coService) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Cannot initialize cert override service\n")); + return NS_ERROR_FAILURE; + } + if (PK11_IsFIPS()) { Telemetry::Accumulate(Telemetry::FIPS_ENABLED, true); } @@ -1659,7 +1681,7 @@ nsNSSComponent::ShutdownNSS() CleanupIdentityInfo(); #endif MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("evaporating psm resources\n")); - mShutdownObjectList->evaporateAllNSSResources(); + nsNSSShutDownList::evaporateAllNSSResources(); EnsureNSSInitialized(nssShutdown); if (SECSuccess != ::NSS_Shutdown()) { MOZ_LOG(gPIPNSSLog, LogLevel::Error, ("NSS SHUTDOWN FAILURE\n")); @@ -1680,12 +1702,6 @@ nsNSSComponent::Init() MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Beginning NSS initialization\n")); - if (!mShutdownObjectList) - { - MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("NSS init, out of memory in constructor\n")); - return NS_ERROR_OUT_OF_MEMORY; - } - rv = InitializePIPNSSBundle(); if (NS_FAILED(rv)) { MOZ_LOG(gPIPNSSLog, LogLevel::Error, ("Unable to create pipnss bundle.\n")); @@ -1825,7 +1841,8 @@ nsNSSComponent::Observe(nsISupports* aSubject, const char* aTopic, prefName.EqualsLiteral("security.ssl.enable_ocsp_stapling") || prefName.EqualsLiteral("security.ssl.enable_ocsp_must_staple") || prefName.EqualsLiteral("security.cert_pinning.enforcement_level") || - prefName.EqualsLiteral("security.pki.sha1_enforcement_level")) { + prefName.EqualsLiteral("security.pki.sha1_enforcement_level") || + prefName.EqualsLiteral("security.pki.name_matching_mode")) { MutexAutoLock lock(mutex); setValidationOptions(false, lock); #ifdef DEBUG @@ -1835,6 +1852,10 @@ nsNSSComponent::Observe(nsISupports* aSubject, const char* aTopic, #endif // DEBUG } else if (prefName.Equals(kFamilySafetyModePref)) { MaybeEnableFamilySafetyCompatibility(); + } else if (prefName.EqualsLiteral("security.content.signature.root_hash")) { + MutexAutoLock lock(mutex); + mContentSigningRootHash = + Preferences::GetString("security.content.signature.root_hash"); } else { clearSessionCache = false; } @@ -1904,7 +1925,7 @@ nsresult nsNSSComponent::LogoutAuthenticatedPK11() nsClientAuthRememberService::ClearAllRememberedDecisions(); - return mShutdownObjectList->doPK11Logout(); + return nsNSSShutDownList::doPK11Logout(); } nsresult @@ -1987,6 +2008,32 @@ nsNSSComponent::IsCertTestBuiltInRoot(CERTCertificate* cert, bool& result) } #endif // DEBUG +NS_IMETHODIMP +nsNSSComponent::IsCertContentSigningRoot(CERTCertificate* cert, bool& result) +{ + MutexAutoLock lock(mutex); + MOZ_ASSERT(mNSSInitialized); + + result = false; + + if (mContentSigningRootHash.IsEmpty()) { + return NS_OK; + } + + RefPtr nsc = nsNSSCertificate::Create(cert); + if (!nsc) { + return NS_ERROR_FAILURE; + } + nsAutoString certHash; + nsresult rv = nsc->GetSha256Fingerprint(certHash); + if (NS_FAILED(rv)) { + return rv; + } + + result = mContentSigningRootHash.Equals(certHash); + return NS_OK; +} + SharedCertVerifier::~SharedCertVerifier() { } already_AddRefed diff --git a/security/manager/ssl/nsNSSComponent.h b/security/manager/ssl/nsNSSComponent.h index d3fe05a24f..d129ed43a7 100644 --- a/security/manager/ssl/nsNSSComponent.h +++ b/security/manager/ssl/nsNSSComponent.h @@ -86,6 +86,8 @@ public: NS_IMETHOD IsCertTestBuiltInRoot(CERTCertificate* cert, bool& result) = 0; #endif + NS_IMETHOD IsCertContentSigningRoot(CERTCertificate* cert, bool& result) = 0; + virtual ::already_AddRefed GetDefaultCertVerifier() = 0; }; @@ -140,6 +142,8 @@ public: NS_IMETHOD IsCertTestBuiltInRoot(CERTCertificate* cert, bool& result) override; #endif + NS_IMETHOD IsCertContentSigningRoot(CERTCertificate* cert, bool& result) override; + ::already_AddRefed GetDefaultCertVerifier() override; @@ -176,7 +180,6 @@ private: nsCOMPtr mNSSErrorsBundle; bool mNSSInitialized; static int mInstanceCount; - nsNSSShutDownList* mShutdownObjectList; #ifndef MOZ_NO_SMART_CARDS SmartCardThreadList* mThreadList; #endif @@ -184,6 +187,7 @@ private: #ifdef DEBUG nsAutoString mTestBuiltInRootHash; #endif + nsString mContentSigningRootHash; void deleteBackgroundThreads(); void createBackgroundThreads(); diff --git a/security/manager/ssl/nsNSSIOLayer.cpp b/security/manager/ssl/nsNSSIOLayer.cpp index 373c9d828a..03aecf775d 100644 --- a/security/manager/ssl/nsNSSIOLayer.cpp +++ b/security/manager/ssl/nsNSSIOLayer.cpp @@ -6,47 +6,41 @@ #include "nsNSSIOLayer.h" -#include "pkix/pkixtypes.h" -#include "nsNSSComponent.h" -#include "mozilla/BinarySearch.h" -#include "mozilla/Casting.h" -#include "mozilla/DebugOnly.h" -#include "mozilla/UniquePtr.h" - -#include "mozilla/Logging.h" -#include "prmem.h" -#include "prnetdb.h" -#include "nsIPrefService.h" -#include "nsIClientAuthDialogs.h" -#include "nsIWebProgressListener.h" -#include "nsClientAuthRemember.h" -#include "nsServiceManagerUtils.h" - -#include "nsISocketProvider.h" -#include "nsPrintfCString.h" -#include "SSLServerCertVerification.h" -#include "nsNSSCertHelper.h" - -#include "nsCharSeparatedTokenizer.h" -#include "nsIConsoleService.h" -#include "PSMRunnable.h" -#include "ScopedNSSTypes.h" -#include "SharedSSLState.h" -#include "mozilla/Preferences.h" -#include "nsContentUtils.h" -#include "NSSCertDBTrustDomain.h" -#include "NSSErrorsService.h" - -#include "ssl.h" -#include "sslproto.h" -#include "secerr.h" -#include "sslerr.h" -#include "secder.h" -#include "keyhi.h" - #include -#include "IntolerantFallbackList.inc" +#include "NSSCertDBTrustDomain.h" +#include "NSSErrorsService.h" +#include "PSMRunnable.h" +#include "SSLServerCertVerification.h" +#include "ScopedNSSTypes.h" +#include "SharedSSLState.h" +#include "keyhi.h" +#include "mozilla/Casting.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/Logging.h" +#include "mozilla/Move.h" +#include "mozilla/Preferences.h" +#include "mozilla/Telemetry.h" +#include "nsCharSeparatedTokenizer.h" +#include "nsClientAuthRemember.h" +#include "nsContentUtils.h" +#include "nsIClientAuthDialogs.h" +#include "nsIConsoleService.h" +#include "nsIPrefService.h" +#include "nsISocketProvider.h" +#include "nsIWebProgressListener.h" +#include "nsNSSCertHelper.h" +#include "nsNSSComponent.h" +#include "nsPrintfCString.h" +#include "nsServiceManagerUtils.h" +#include "pkix/pkixtypes.h" +#include "prmem.h" +#include "prnetdb.h" +#include "secder.h" +#include "secerr.h" +#include "ssl.h" +#include "sslerr.h" +#include "sslproto.h" using namespace mozilla; using namespace mozilla::psm; @@ -74,9 +68,6 @@ getSiteKey(const nsACString& hostName, uint16_t port, key.AppendInt(port); } -// SSM_UserCertChoice: enum for cert choice info -typedef enum {ASK, AUTO} SSM_UserCertChoice; - // Historically, we have required that the server negotiate ALPN or NPN in // order to false start, as a compatibility hack to work around // implementations that just stop responding during false start. However, now @@ -386,14 +377,14 @@ nsNSSSocketInfo::IsAcceptableForHost(const nsACString& hostname, bool* _retval) // Ensure that the server certificate covers the hostname that would // like to join this connection - ScopedCERTCertificate nssCert; + UniqueCERTCertificate nssCert; nsCOMPtr cert; if (NS_FAILED(SSLStatus()->GetServerCert(getter_AddRefs(cert)))) { return NS_OK; } if (cert) { - nssCert = cert->GetCert(); + nssCert.reset(cert->GetCert()); } if (!nssCert) { @@ -1092,7 +1083,10 @@ retryDueToTLSIntolerance(PRErrorCode err, nsNSSSocketInfo* socketInfo) nsIWebProgressListener::STATE_USES_SSL_3); } - if (err == SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT) { + // NSS will return SSL_ERROR_RX_MALFORMED_SERVER_HELLO if anti-downgrade + // detected the downgrade. + if (err == SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT || + err == SSL_ERROR_RX_MALFORMED_SERVER_HELLO) { // This is a clear signal that we've fallen back too many versions. Treat // this as a hard failure, but forget any intolerance so that later attempts // don't use this version (i.e., range.max) and trigger the error again. @@ -1288,7 +1282,6 @@ nsSSLIOLayerHelpers::nsSSLIOLayerHelpers() : mTreatUnsafeNegotiationAsBroken(false) , mTLSIntoleranceInfo() , mFalseStartRequireNPN(false) - , mUseStaticFallbackList(true) , mUnrestrictedRC4Fallback(true) , mVersionFallbackLimit(SSL_LIBRARY_VERSION_TLS_1_0) , mutex("nsSSLIOLayerHelpers.mutex") @@ -1511,9 +1504,6 @@ PrefObserver::Observe(nsISupports* aSubject, const char* aTopic, 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); } else if (prefName.EqualsLiteral("security.tls.unrestricted_rc4_fallback")) { mOwner->mUnrestrictedRC4Fallback = Preferences::GetBool("security.tls.unrestricted_rc4_fallback", true); @@ -1612,8 +1602,6 @@ nsSSLIOLayerHelpers::Init() FALSE_START_REQUIRE_NPN_DEFAULT); loadVersionFallbackLimit(); initInsecureFallbackSites(); - mUseStaticFallbackList = - Preferences::GetBool("security.tls.insecure_fallback_hosts.use_static_list", true); mUnrestrictedRC4Fallback = Preferences::GetBool("security.tls.unrestricted_rc4_fallback", true); @@ -1770,48 +1758,9 @@ nsSSLIOLayerHelpers::removeInsecureFallbackSite(const nsACString& hostname, } } -struct FallbackListComparator -{ - explicit FallbackListComparator(const char* aTarget) - : mTarget(aTarget) - {} - - int operator()(const char* aVal) const { - return strcmp(mTarget, aVal); - } - -private: - const char* mTarget; -}; - -static const char* const kFallbackWildcardList[] = -{ - ".kuronekoyamato.co.jp", // bug 1128366 - ".userstorage.mega.co.nz", // bug 1133496 - ".wildcard.test", -}; - bool nsSSLIOLayerHelpers::isInsecureFallbackSite(const nsACString& hostname) { - size_t match; - if (mUseStaticFallbackList) { - const char* host = PromiseFlatCString(hostname).get(); - if (BinarySearchIf(kIntolerantFallbackList, 0, - ArrayLength(kIntolerantFallbackList), - FallbackListComparator(host), &match)) { - return true; - } - for (size_t i = 0; i < ArrayLength(kFallbackWildcardList); ++i) { - size_t hostLen = hostname.Length(); - const char* target = kFallbackWildcardList[i]; - size_t targetLen = strlen(target); - if (hostLen > targetLen && - !memcmp(host + hostLen - targetLen, target, targetLen)) { - return true; - } - } - } MutexAutoLock lock(mutex); return mInsecureFallbackSites.Contains(hostname); } @@ -1864,9 +1813,17 @@ nsSSLIOLayerNewSocket(int32_t family, // // Note: copied in its entirety from Nova code static SECStatus -nsConvertCANamesToStrings(PLArenaPool* arena, char** caNameStrings, +nsConvertCANamesToStrings(const UniquePLArenaPool& arena, char** caNameStrings, CERTDistNames* caNames) { + MOZ_ASSERT(arena.get()); + MOZ_ASSERT(caNameStrings); + MOZ_ASSERT(caNames); + if (!arena.get() || !caNameStrings || !caNames) { + PR_SetError(SEC_ERROR_INVALID_ARGS, 0); + return SECFailure; + } + SECItem* dername; SECStatus rv; int headerlen; @@ -1926,7 +1883,7 @@ nsConvertCANamesToStrings(PLArenaPool* arena, char** caNameStrings, // XXX - keep going until we fail to convert the name caNameStrings[n] = const_cast(""); } else { - caNameStrings[n] = PORT_ArenaStrdup(arena, namestring); + caNameStrings[n] = PORT_ArenaStrdup(arena.get(), namestring); PR_Free(namestring); if (!caNameStrings[n]) { goto loser; @@ -1946,43 +1903,32 @@ loser: return SECFailure; } -// Sets certChoice by reading the preference -// -// If done properly, this function will read the identifier strings for ASK and -// AUTO modes read the selected strings from the preference, compare the -// strings, and determine in which mode it is in. We currently use ASK mode for -// UI apps and AUTO mode for UI-less apps without really asking for -// preferences. -nsresult -nsGetUserCertChoice(SSM_UserCertChoice* certChoice) +// Possible behaviors for choosing a cert for client auth. +enum class UserCertChoice { + // Ask the user to choose a cert. + Ask = 0, + // Automatically choose a cert. + Auto = 1, +}; + +// Returns the most appropriate user cert choice based on the value of the +// security.default_personal_cert preference. +UserCertChoice +nsGetUserCertChoice() { - char* mode = nullptr; - nsresult ret; - - NS_ENSURE_ARG_POINTER(certChoice); - - nsCOMPtr pref = do_GetService(NS_PREFSERVICE_CONTRACTID); - - ret = pref->GetCharPref("security.default_personal_cert", &mode); - if (NS_FAILED(ret)) { - goto loser; + nsAutoCString value; + nsresult rv = Preferences::GetCString("security.default_personal_cert", &value); + if (NS_FAILED(rv)) { + return UserCertChoice::Ask; } - if (PL_strcmp(mode, "Select Automatically") == 0) { - *certChoice = AUTO; - } else if (PL_strcmp(mode, "Ask Every Time") == 0) { - *certChoice = ASK; - } else { - // Most likely we see a nickname from a migrated cert. - // We do not currently support that, ask the user which cert to use. - *certChoice = ASK; - } - -loser: - if (mode) { - free(mode); - } - return ret; + // There are three cases for what the preference could be set to: + // 1. "Select Automatically" -> Auto. + // 2. "Ask Every Time" -> Ask. + // 3. Something else -> Ask. This might be a nickname from a migrated cert, + // but we no longer support this case. + return value.EqualsLiteral("Select Automatically") ? UserCertChoice::Auto + : UserCertChoice::Ask; } static bool @@ -2013,14 +1959,14 @@ public: CERTCertificate** pRetCert, SECKEYPrivateKey** pRetKey, nsNSSSocketInfo* info, - CERTCertificate* serverCert) + const UniqueCERTCertificate& serverCert) : mRV(SECFailure) , mErrorCodeToReport(SEC_ERROR_NO_MEMORY) , mPRetCert(pRetCert) , mPRetKey(pRetKey) , mCANames(caNames) , mSocketInfo(info) - , mServerCert(serverCert) + , mServerCert(serverCert.get()) { } @@ -2061,7 +2007,7 @@ nsNSS_SSLGetClientAuthData(void* arg, PRFileDesc* socket, RefPtr info( reinterpret_cast(socket->higher->secret)); - CERTCertificate* serverCert = SSL_PeerCertificate(socket); + UniqueCERTCertificate serverCert(SSL_PeerCertificate(socket)); if (!serverCert) { NS_NOTREACHED("Missing server certificate should have been detected during " "server cert authentication."); @@ -2103,15 +2049,18 @@ nsNSS_SSLGetClientAuthData(void* arg, PRFileDesc* socket, void ClientAuthDataRunnable::RunOnTargetThread() { - PLArenaPool* arena = nullptr; + // We check the value of a pref in this runnable, so this runnable should only + // be run on the main thread. + MOZ_ASSERT(NS_IsMainThread()); + + UniquePLArenaPool arena; char** caNameStrings; - ScopedCERTCertificate cert; - ScopedSECKEYPrivateKey privKey; + UniqueCERTCertificate cert; + UniqueSECKEYPrivateKey privKey; ScopedCERTCertList certList; CERTCertListNode* node; UniqueCERTCertNicknames nicknames; int keyError = 0; // used for private key retrieval error - SSM_UserCertChoice certChoice; int32_t NumberOfCerts = 0; void* wincx = mSocketInfo; nsresult rv; @@ -2122,31 +2071,31 @@ ClientAuthDataRunnable::RunOnTargetThread() // If a client cert preference was set on the socket info, use that and skip // the client cert UI and/or search of the user's past cert decisions. if (socketClientCert) { - cert = socketClientCert->GetCert(); + cert.reset(socketClientCert->GetCert()); if (!cert) { goto loser; } // Get the private key - privKey = PK11_FindKeyByAnyCert(cert.get(), wincx); + privKey.reset(PK11_FindKeyByAnyCert(cert.get(), wincx)); if (!privKey) { goto loser; } - *mPRetCert = cert.forget(); - *mPRetKey = privKey.forget(); + *mPRetCert = cert.release(); + *mPRetKey = privKey.release(); mRV = SECSuccess; return; } // create caNameStrings - arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + arena.reset(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); if (!arena) { goto loser; } - caNameStrings = (char**) PORT_ArenaAlloc(arena, - sizeof(char*) * (mCANames->nnames)); + caNameStrings = static_cast( + PORT_ArenaAlloc(arena.get(), sizeof(char*) * mCANames->nnames)); if (!caNameStrings) { goto loser; } @@ -2156,13 +2105,8 @@ ClientAuthDataRunnable::RunOnTargetThread() goto loser; } - // get the preference - if (NS_FAILED(nsGetUserCertChoice(&certChoice))) { - goto loser; - } - // find valid user cert and key pair - if (certChoice == AUTO) { + if (nsGetUserCertChoice() == UserCertChoice::Auto) { // automatically find the right cert // find all user certs that are valid and for SSL @@ -2186,23 +2130,23 @@ ClientAuthDataRunnable::RunOnTargetThread() goto noCert; } - ScopedCERTCertificate low_prio_nonrep_cert; + UniqueCERTCertificate lowPrioNonrepCert; // loop through the list until we find a cert with a key while (!CERT_LIST_END(node, certList)) { // if the certificate has restriction and we do not satisfy it we do not // use it - privKey = PK11_FindKeyByAnyCert(node->cert, wincx); + privKey.reset(PK11_FindKeyByAnyCert(node->cert, wincx)); if (privKey) { if (hasExplicitKeyUsageNonRepudiation(node->cert)) { privKey = nullptr; - // Not a prefered cert - if (!low_prio_nonrep_cert) { // did not yet find a low prio cert - low_prio_nonrep_cert = CERT_DupCertificate(node->cert); + // Not a preferred cert + if (!lowPrioNonrepCert) { // did not yet find a low prio cert + lowPrioNonrepCert.reset(CERT_DupCertificate(node->cert)); } } else { // this is a good cert to present - cert = CERT_DupCertificate(node->cert); + cert.reset(CERT_DupCertificate(node->cert)); break; } } @@ -2215,9 +2159,9 @@ ClientAuthDataRunnable::RunOnTargetThread() node = CERT_LIST_NEXT(node); } - if (!cert && low_prio_nonrep_cert) { - cert = low_prio_nonrep_cert.forget(); - privKey = PK11_FindKeyByAnyCert(cert.get(), wincx); + if (!cert && lowPrioNonrepCert) { + cert = Move(lowPrioNonrepCert); + privKey.reset(PK11_FindKeyByAnyCert(cert.get(), wincx)); } if (!cert) { @@ -2260,7 +2204,7 @@ ClientAuthDataRunnable::RunOnTargetThread() nsNSSCertificate* obj_cert = reinterpret_cast(found_cert.get()); if (obj_cert) { - cert = obj_cert->GetCert(); + cert.reset(obj_cert->GetCert()); } } @@ -2322,8 +2266,7 @@ ClientAuthDataRunnable::RunOnTargetThread() NS_ASSERTION(nicknames->numnicknames == NumberOfCerts, "nicknames->numnicknames != NumberOfCerts"); // Get CN and O of the subject and O of the issuer - UniquePtr - ccn(CERT_GetCommonName(&mServerCert->subject), PORT_Free); + UniquePORTString ccn(CERT_GetCommonName(&mServerCert->subject)); NS_ConvertUTF8toUTF16 cn(ccn.get()); int32_t port; @@ -2342,13 +2285,11 @@ ClientAuthDataRunnable::RunOnTargetThread() cn_host_port.Append(')'); } - char* corg = CERT_GetOrgName(&mServerCert->subject); - NS_ConvertUTF8toUTF16 org(corg); - if (corg) PORT_Free(corg); + UniquePORTString corg(CERT_GetOrgName(&mServerCert->subject)); + NS_ConvertUTF8toUTF16 org(corg.get()); - char* cissuer = CERT_GetOrgName(&mServerCert->issuer); - NS_ConvertUTF8toUTF16 issuer(cissuer); - if (cissuer) PORT_Free(cissuer); + UniquePORTString cissuer(CERT_GetOrgName(&mServerCert->issuer)); + NS_ConvertUTF8toUTF16 issuer(cissuer.get()); certNicknameList = (char16_t**)moz_xmalloc(sizeof(char16_t*)* nicknames->numnicknames); @@ -2423,7 +2364,7 @@ ClientAuthDataRunnable::RunOnTargetThread() ++i, node = CERT_LIST_NEXT(node)) { if (i == selectedIndex) { - cert = CERT_DupCertificate(node->cert); + cert.reset(CERT_DupCertificate(node->cert)); break; } } @@ -2441,7 +2382,7 @@ ClientAuthDataRunnable::RunOnTargetThread() } // go get the private key - privKey = PK11_FindKeyByAnyCert(cert.get(), wincx); + privKey.reset(PK11_FindKeyByAnyCert(cert.get(), wincx)); if (!privKey) { keyError = PR_GetError(); if (keyError == SEC_ERROR_BAD_PASSWORD) { @@ -2462,12 +2403,8 @@ loser: done: int error = PR_GetError(); - if (arena) { - PORT_FreeArena(arena, false); - } - - *mPRetCert = cert.forget(); - *mPRetKey = privKey.forget(); + *mPRetCert = cert.release(); + *mPRetKey = privKey.release(); if (mRV == SECFailure) { mErrorCodeToReport = error; @@ -2586,6 +2523,10 @@ nsSSLIOLayerSetOptions(PRFileDesc* fd, bool forSTARTTLS, return NS_ERROR_FAILURE; } } + // tell NSS the max enabled version to make anti-downgrade effective + if (SECSuccess != SSL_SetDowngradeCheckVersion(fd, maxEnabledVersion)) { + return NS_ERROR_FAILURE; + } } bool enabled = infoObject->SharedState().IsOCSPStaplingEnabled(); diff --git a/security/manager/ssl/nsNSSIOLayer.h b/security/manager/ssl/nsNSSIOLayer.h index 0116827933..2be417653f 100644 --- a/security/manager/ssl/nsNSSIOLayer.h +++ b/security/manager/ssl/nsNSSIOLayer.h @@ -236,10 +236,6 @@ public: bool isInsecureFallbackSite(const nsACString& hostname); bool mFalseStartRequireNPN; - // Use the static list of sites that require insecure fallback - // to TLS 1.0 if true, set by the pref - // security.tls.insecure_fallback_hosts.use_static_list. - bool mUseStaticFallbackList; bool mUnrestrictedRC4Fallback; uint16_t mVersionFallbackLimit; private: diff --git a/security/manager/ssl/nsNSSModule.cpp b/security/manager/ssl/nsNSSModule.cpp index 3663128dbd..ab5b0fdeb2 100644 --- a/security/manager/ssl/nsNSSModule.cpp +++ b/security/manager/ssl/nsNSSModule.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "CertBlocklist.h" +#include "ContentSignatureVerifier.h" #include "nsCertOverrideService.h" #include "nsCertPicker.h" #include "nsCrypto.h" @@ -23,6 +24,7 @@ #include "nsNSSCertificateFakeTransport.h" #include "nsNSSComponent.h" #include "NSSErrorsService.h" +#include "nsNSSU2FToken.h" #include "nsNSSVersion.h" #include "nsNTLMAuthModule.h" #include "nsPK11TokenDB.h" @@ -207,7 +209,9 @@ NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsureChromeOrContent, nsCryptoHMAC) NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsureChromeOrContent, nsKeyObject) NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsureChromeOrContent, nsKeyObjectFactory) NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsDataSignatureVerifier) +NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, ContentSignatureVerifier) NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsureChromeOrContent, nsRandomGenerator) +NS_NSS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nssEnsure, nsNSSU2FToken, Init) NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsureOnChromeOnly, nsSSLStatus) NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsureOnChromeOnly, TransportSecurityInfo) @@ -243,8 +247,10 @@ NS_DEFINE_NAMED_CID(NS_NTLMAUTHMODULE_CID); NS_DEFINE_NAMED_CID(NS_KEYMODULEOBJECT_CID); NS_DEFINE_NAMED_CID(NS_KEYMODULEOBJECTFACTORY_CID); NS_DEFINE_NAMED_CID(NS_DATASIGNATUREVERIFIER_CID); +NS_DEFINE_NAMED_CID(NS_CONTENTSIGNATUREVERIFIER_CID); NS_DEFINE_NAMED_CID(NS_CERTOVERRIDE_CID); NS_DEFINE_NAMED_CID(NS_RANDOMGENERATOR_CID); +NS_DEFINE_NAMED_CID(NS_NSSU2FTOKEN_CID); NS_DEFINE_NAMED_CID(NS_SSLSTATUS_CID); NS_DEFINE_NAMED_CID(TRANSPORTSECURITYINFO_CID); NS_DEFINE_NAMED_CID(NS_NSSERRORSSERVICE_CID); @@ -278,8 +284,10 @@ static const mozilla::Module::CIDEntry kNSSCIDs[] = { { &kNS_KEYMODULEOBJECT_CID, false, nullptr, nsKeyObjectConstructor }, { &kNS_KEYMODULEOBJECTFACTORY_CID, false, nullptr, nsKeyObjectFactoryConstructor }, { &kNS_DATASIGNATUREVERIFIER_CID, false, nullptr, nsDataSignatureVerifierConstructor }, + { &kNS_CONTENTSIGNATUREVERIFIER_CID, false, nullptr, ContentSignatureVerifierConstructor }, { &kNS_CERTOVERRIDE_CID, false, nullptr, nsCertOverrideServiceConstructor }, { &kNS_RANDOMGENERATOR_CID, false, nullptr, nsRandomGeneratorConstructor }, + { &kNS_NSSU2FTOKEN_CID, false, nullptr, nsNSSU2FTokenConstructor }, { &kNS_SSLSTATUS_CID, false, nullptr, nsSSLStatusConstructor }, { &kTRANSPORTSECURITYINFO_CID, false, nullptr, TransportSecurityInfoConstructor }, { &kNS_NSSERRORSSERVICE_CID, false, nullptr, NSSErrorsServiceConstructor }, @@ -318,8 +326,10 @@ static const mozilla::Module::ContractIDEntry kNSSContracts[] = { { NS_KEYMODULEOBJECT_CONTRACTID, &kNS_KEYMODULEOBJECT_CID }, { NS_KEYMODULEOBJECTFACTORY_CONTRACTID, &kNS_KEYMODULEOBJECTFACTORY_CID }, { NS_DATASIGNATUREVERIFIER_CONTRACTID, &kNS_DATASIGNATUREVERIFIER_CID }, + { NS_CONTENTSIGNATUREVERIFIER_CONTRACTID, &kNS_CONTENTSIGNATUREVERIFIER_CID }, { NS_CERTOVERRIDE_CONTRACTID, &kNS_CERTOVERRIDE_CID }, { NS_RANDOMGENERATOR_CONTRACTID, &kNS_RANDOMGENERATOR_CID }, + { NS_NSSU2FTOKEN_CONTRACTID, &kNS_NSSU2FTOKEN_CID }, { NS_ENTROPYCOLLECTOR_CONTRACTID, &kNS_ENTROPYCOLLECTOR_CID }, { NS_SECURE_BROWSER_UI_CONTRACTID, &kNS_SECURE_BROWSER_UI_CID }, { NS_SSSERVICE_CONTRACTID, &kNS_SITE_SECURITY_SERVICE_CID }, diff --git a/security/manager/ssl/nsNSSShutDown.cpp b/security/manager/ssl/nsNSSShutDown.cpp index f6b0db17a7..27f8d0bc12 100644 --- a/security/manager/ssl/nsNSSShutDown.cpp +++ b/security/manager/ssl/nsNSSShutDown.cpp @@ -35,11 +35,12 @@ static const PLDHashTableOps gSetOps = { ObjectSetInitEntry }; -nsNSSShutDownList *nsNSSShutDownList::singleton = nullptr; +StaticMutex sListLock; +Atomic sInShutdown(false); +nsNSSShutDownList *singleton = nullptr; nsNSSShutDownList::nsNSSShutDownList() - : mListLock("nsNSSShutDownList.mListLock") - , mObjects(&gSetOps, sizeof(ObjectHashEntry)) + : mObjects(&gSetOps, sizeof(ObjectHashEntry)) , mPK11LogoutCancelObjects(&gSetOps, sizeof(ObjectHashEntry)) { } @@ -52,54 +53,66 @@ nsNSSShutDownList::~nsNSSShutDownList() void nsNSSShutDownList::remember(nsNSSShutDownObject *o) { - if (!singleton) + StaticMutexAutoLock lock(sListLock); + if (!nsNSSShutDownList::construct(lock)) { return; - + } + PR_ASSERT(o); - MutexAutoLock lock(singleton->mListLock); singleton->mObjects.Add(o, fallible); } void nsNSSShutDownList::forget(nsNSSShutDownObject *o) { - if (!singleton) + StaticMutexAutoLock lock(sListLock); + if (!singleton) { return; - + } + PR_ASSERT(o); - MutexAutoLock lock(singleton->mListLock); singleton->mObjects.Remove(o); } void nsNSSShutDownList::remember(nsOnPK11LogoutCancelObject *o) { - if (!singleton) + StaticMutexAutoLock lock(sListLock); + if (!nsNSSShutDownList::construct(lock)) { return; - + } + PR_ASSERT(o); - MutexAutoLock lock(singleton->mListLock); singleton->mPK11LogoutCancelObjects.Add(o, fallible); } void nsNSSShutDownList::forget(nsOnPK11LogoutCancelObject *o) { - if (!singleton) + StaticMutexAutoLock lock(sListLock); + if (!singleton) { return; - + } + PR_ASSERT(o); - MutexAutoLock lock(singleton->mListLock); singleton->mPK11LogoutCancelObjects.Remove(o); } nsresult nsNSSShutDownList::doPK11Logout() { - MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("canceling all open SSL sockets to disallow future IO\n")); + StaticMutexAutoLock lock(sListLock); + if (!singleton) { + return NS_OK; + } + + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("canceling all open SSL sockets to disallow future IO\n")); + // During our iteration we will set a bunch of PRBools to true. // Nobody else ever modifies that bool, only we do. // We only must ensure that our objects do not go away. // This is guaranteed by holding the list lock. - MutexAutoLock lock(singleton->mListLock); - for (auto iter = mPK11LogoutCancelObjects.Iter(); !iter.Done(); iter.Next()) { + for (auto iter = singleton->mPK11LogoutCancelObjects.Iter(); + !iter.Done(); + iter.Next()) { auto entry = static_cast(iter.Get()); nsOnPK11LogoutCancelObject *pklco = reinterpret_cast(entry->obj); @@ -113,7 +126,13 @@ nsresult nsNSSShutDownList::doPK11Logout() nsresult nsNSSShutDownList::evaporateAllNSSResources() { - if (PR_SUCCESS != mActivityState.restrictActivityToCurrentThread()) { + StaticMutexAutoLock lock(sListLock); + if (!singleton) { + return NS_OK; + } + + PRStatus rv = singleton->mActivityState.restrictActivityToCurrentThread(); + if (rv != PR_SUCCESS) { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("failed to restrict activity to current thread\n")); return NS_ERROR_FAILURE; } @@ -123,33 +142,60 @@ nsresult nsNSSShutDownList::evaporateAllNSSResources() // Never free more than one entry, because other threads might be calling // us and remove themselves while we are iterating over the list, // and the behaviour of changing the list while iterating is undefined. - while (true) { - MutexAutoLock lock(mListLock); - auto iter = mObjects.Iter(); + while (singleton) { + auto iter = singleton->mObjects.Iter(); if (iter.Done()) { break; } auto entry = static_cast(iter.Get()); { - MutexAutoUnlock unlock(singleton->mListLock); + StaticMutexAutoUnlock unlock(sListLock); entry->obj->shutdown(nsNSSShutDownObject::calledFromList); } iter.Remove(); } - mActivityState.releaseCurrentThreadActivityRestriction(); + if (!singleton) { + return NS_ERROR_FAILURE; + } + + singleton->mActivityState.releaseCurrentThreadActivityRestriction(); return NS_OK; } -nsNSSShutDownList *nsNSSShutDownList::construct() +void nsNSSShutDownList::enterActivityState() { + StaticMutexAutoLock lock(sListLock); + if (nsNSSShutDownList::construct(lock)) { + singleton->mActivityState.enter(); + } +} + +void nsNSSShutDownList::leaveActivityState() +{ + StaticMutexAutoLock lock(sListLock); if (singleton) { - // we should never ever be called twice - return nullptr; + singleton->mActivityState.leave(); + } +} + +bool nsNSSShutDownList::construct(const StaticMutexAutoLock& /*proofOfLock*/) +{ + if (!singleton && !sInShutdown && XRE_IsParentProcess()) { + singleton = new nsNSSShutDownList(); } - singleton = new nsNSSShutDownList(); - return singleton; + return !!singleton; +} + +void nsNSSShutDownList::shutdown() +{ + StaticMutexAutoLock lock(sListLock); + sInShutdown = true; + + if (singleton) { + delete singleton; + } } nsNSSActivityState::nsNSSActivityState() @@ -209,18 +255,10 @@ void nsNSSActivityState::releaseCurrentThreadActivityRestriction() nsNSSShutDownPreventionLock::nsNSSShutDownPreventionLock() { - nsNSSActivityState *state = nsNSSShutDownList::getActivityState(); - if (!state) - return; - - state->enter(); + nsNSSShutDownList::enterActivityState(); } nsNSSShutDownPreventionLock::~nsNSSShutDownPreventionLock() { - nsNSSActivityState *state = nsNSSShutDownList::getActivityState(); - if (!state) - return; - - state->leave(); + nsNSSShutDownList::leaveActivityState(); } diff --git a/security/manager/ssl/nsNSSShutDown.h b/security/manager/ssl/nsNSSShutDown.h index fa6a0add12..8cd7d4bad9 100644 --- a/security/manager/ssl/nsNSSShutDown.h +++ b/security/manager/ssl/nsNSSShutDown.h @@ -10,11 +10,12 @@ #include "PLDHashTable.h" #include "mozilla/CondVar.h" #include "mozilla/Mutex.h" +#include "mozilla/StaticMutex.h" class nsNSSShutDownObject; class nsOnPK11LogoutCancelObject; -// Singleton, owner by nsNSSShutDownList +// Singleton, owned by nsNSSShutDownList class nsNSSActivityState { public: @@ -62,10 +63,8 @@ public: class nsNSSShutDownList { public: - ~nsNSSShutDownList(); + static void shutdown(); - static nsNSSShutDownList *construct(); - // track instances that support early cleanup static void remember(nsNSSShutDownObject *o); static void forget(nsNSSShutDownObject *o); @@ -76,23 +75,23 @@ public: static void forget(nsOnPK11LogoutCancelObject *o); // Do the "early cleanup", if possible. - nsresult evaporateAllNSSResources(); + static nsresult evaporateAllNSSResources(); // PSM has been asked to log out of a token. // Notify all registered instances that want to react to that event. - nsresult doPK11Logout(); - - static nsNSSActivityState *getActivityState() - { - return singleton ? &singleton->mActivityState : nullptr; - } - + static nsresult doPK11Logout(); + + // Signal entering/leaving a scope where shutting down NSS is prohibited. + static void enterActivityState(); + static void leaveActivityState(); + private: + static bool construct(const mozilla::StaticMutexAutoLock& /*proofOfLock*/); + nsNSSShutDownList(); + ~nsNSSShutDownList(); protected: - mozilla::Mutex mListLock; - static nsNSSShutDownList *singleton; PLDHashTable mObjects; PLDHashTable mPK11LogoutCancelObjects; nsNSSActivityState mActivityState; diff --git a/security/manager/ssl/nsNSSU2FToken.cpp b/security/manager/ssl/nsNSSU2FToken.cpp new file mode 100644 index 0000000000..63c1eaf85d --- /dev/null +++ b/security/manager/ssl/nsNSSU2FToken.cpp @@ -0,0 +1,707 @@ +/* -*- 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 "nsNSSU2FToken.h" + +#include "CryptoBuffer.h" +#include "nsNSSComponent.h" +#include "pk11pub.h" +#include "prerror.h" +#include "secerr.h" +#include "WebCryptoCommon.h" + +using mozilla::dom::CreateECParamsForCurve; + +NS_IMPL_ISUPPORTS(nsNSSU2FToken, nsINSSU2FToken) + +// Not named "security.webauth.u2f_softtoken_counter" because setting that +// name causes the window.u2f object to disappear until preferences get +// reloaded, as its' pref is a substring! +#define PREF_U2F_NSSTOKEN_COUNTER "security.webauth.softtoken_counter" + +const nsCString nsNSSU2FToken::mSecretNickname = + NS_LITERAL_CSTRING("U2F_NSSTOKEN"); +const nsString nsNSSU2FToken::mVersion = + NS_LITERAL_STRING("U2F_V2"); +NS_NAMED_LITERAL_CSTRING(kAttestCertSubjectName, "CN=Firefox U2F Soft Token"); + +// This U2F-compatible soft token uses FIDO U2F-compatible ECDSA keypairs +// on the SEC_OID_SECG_EC_SECP256R1 curve. When asked to Register, it will +// generate and return a new keypair KP, where the private component is wrapped +// using AES-KW with the 128-bit mWrappingKey to make an opaque "key handle". +// In other words, Register yields { KP_pub, AES-KW(KP_priv, key=mWrappingKey) } +// +// The value mWrappingKey is long-lived; it is persisted as part of the NSS DB +// for the current profile. The attestation certificates that are produced are +// ephemeral to counteract profiling. They have little use for a soft-token +// at any rate, but are required by the specification. + +const uint32_t kParamLen = 32; +const uint32_t kPublicKeyLen = 65; +const uint32_t kWrappedKeyBufLen = 256; +const uint32_t kWrappingKeyByteLen = 128/8; +NS_NAMED_LITERAL_STRING(kEcAlgorithm, WEBCRYPTO_NAMED_CURVE_P256); + +const PRTime kOneDay = PRTime(PR_USEC_PER_SEC) + * PRTime(60) // sec + * PRTime(60) // min + * PRTime(24); // hours +const PRTime kExpirationSlack = kOneDay; // Pre-date for clock skew +const PRTime kExpirationLife = kOneDay; + +static mozilla::LazyLogModule gNSSTokenLog("webauth_u2f"); + +nsNSSU2FToken::nsNSSU2FToken() + : mInitialized(false) +{} + +nsNSSU2FToken::~nsNSSU2FToken() +{ + nsNSSShutDownPreventionLock locker; + + if (isAlreadyShutDown()) { + return; + } + + destructorSafeDestroyNSSReference(); + shutdown(calledFromObject); +} + +void +nsNSSU2FToken::virtualDestroyNSSReference() +{ + destructorSafeDestroyNSSReference(); +} + +void +nsNSSU2FToken::destructorSafeDestroyNSSReference() +{ + mWrappingKey = nullptr; +} + +static PK11SymKey* +GetSymKeyByNickname(PK11SlotInfo* aSlot, + nsCString aNickname, + const nsNSSShutDownPreventionLock&) +{ + MOZ_LOG(gNSSTokenLog, LogLevel::Debug, + ("Searching for a symmetric key named %s", aNickname.get())); + + PK11SymKey* keyList; + keyList = PK11_ListFixedKeysInSlot(aSlot, const_cast(aNickname.get()), + /* wincx */ nullptr); + while (keyList) { + ScopedPK11SymKey freeKey(keyList); + + UniquePtr + freeKeyName(PK11_GetSymKeyNickname(freeKey), PORT_Free); + + if (aNickname == freeKeyName.get()) { + MOZ_LOG(gNSSTokenLog, LogLevel::Debug, ("Symmetric key found!")); + return freeKey.forget(); + } + + keyList = PK11_GetNextSymKey(keyList); + } + + MOZ_LOG(gNSSTokenLog, LogLevel::Debug, ("Symmetric key not found.")); + return nullptr; +} + +static nsresult +GenEcKeypair(PK11SlotInfo* aSlot, ScopedSECKEYPrivateKey& aPrivKey, + ScopedSECKEYPublicKey& aPubKey, const nsNSSShutDownPreventionLock&) +{ + UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); + if (!arena) { + return NS_ERROR_OUT_OF_MEMORY; + } + + // Set the curve parameters; keyParams belongs to the arena memory space + SECItem* keyParams = CreateECParamsForCurve(kEcAlgorithm, arena.get()); + if (!keyParams) { + return NS_ERROR_OUT_OF_MEMORY; + } + + // Generate a key pair + CK_MECHANISM_TYPE mechanism = CKM_EC_KEY_PAIR_GEN; + + SECKEYPublicKey* pubKeyRaw; + aPrivKey = PK11_GenerateKeyPair(aSlot, mechanism, keyParams, &pubKeyRaw, + /* ephemeral */ PR_FALSE, PR_FALSE, + /* wincx */ nullptr); + aPubKey = pubKeyRaw; + if (!aPrivKey.get() || !aPubKey.get()) { + return NS_ERROR_FAILURE; + } + + // Check that the public key has the correct length + if (aPubKey->u.ec.publicValue.len != kPublicKeyLen) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult +nsNSSU2FToken::GetOrCreateWrappingKey(PK11SlotInfo* aSlot, + const nsNSSShutDownPreventionLock& locker) +{ + // Search for an existing wrapping key. If we find it, + // store it for later and mark ourselves initialized. + mWrappingKey = GetSymKeyByNickname(aSlot, mSecretNickname, locker); + if (mWrappingKey) { + MOZ_LOG(gNSSTokenLog, LogLevel::Debug, ("U2F Soft Token Key found.")); + mInitialized = true; + return NS_OK; + } + + MOZ_LOG(gNSSTokenLog, LogLevel::Info, + ("No keys found. Generating new U2F Soft Token wrapping key.")); + + // We did not find an existing wrapping key, so we generate one in the + // persistent database (e.g, Token). + mWrappingKey = PK11_TokenKeyGenWithFlags(aSlot, CKM_AES_KEY_GEN, + /* default params */ nullptr, + kWrappingKeyByteLen, + /* empty keyid */ nullptr, + /* flags */ CKF_WRAP | CKF_UNWRAP, + /* attributes */ PK11_ATTR_TOKEN | + PK11_ATTR_PRIVATE, + /* wincx */ nullptr); + + if (!mWrappingKey) { + MOZ_LOG(gNSSTokenLog, LogLevel::Warning, + ("Failed to store wrapping key, NSS error #%d", PORT_GetError())); + return NS_ERROR_FAILURE; + } + + SECStatus srv = PK11_SetSymKeyNickname(mWrappingKey, mSecretNickname.get()); + if (srv != SECSuccess) { + MOZ_LOG(gNSSTokenLog, LogLevel::Warning, + ("Failed to set nickname, NSS error #%d", PORT_GetError())); + return NS_ERROR_FAILURE; + } + + MOZ_LOG(gNSSTokenLog, LogLevel::Debug, + ("Key stored, nickname set to %s.", mSecretNickname.get())); + + Preferences::SetUint(PREF_U2F_NSSTOKEN_COUNTER, 0); + return NS_OK; +} + +static nsresult +GetAttestationCertificate(PK11SlotInfo* aSlot, + ScopedSECKEYPrivateKey& aAttestPrivKey, + ScopedCERTCertificate& aAttestCert, + const nsNSSShutDownPreventionLock& locker) +{ + ScopedSECKEYPublicKey pubKey; + + // Construct an ephemeral keypair for this Attestation Certificate + nsresult rv = GenEcKeypair(aSlot, aAttestPrivKey, pubKey, locker); + if (NS_FAILED(rv) || !aAttestPrivKey || !pubKey) { + MOZ_LOG(gNSSTokenLog, LogLevel::Warning, + ("Failed to gen keypair, NSS error #%d", PORT_GetError())); + return NS_ERROR_FAILURE; + } + + // Construct the Attestation Certificate itself + ScopedCERTName subjectName(CERT_AsciiToName(kAttestCertSubjectName.get())); + if (!subjectName) { + MOZ_LOG(gNSSTokenLog, LogLevel::Warning, + ("Failed to set subject name, NSS error #%d", PORT_GetError())); + return NS_ERROR_FAILURE; + } + + ScopedCERTSubjectPublicKeyInfo spki( + SECKEY_CreateSubjectPublicKeyInfo(pubKey)); + if (!spki) { + MOZ_LOG(gNSSTokenLog, LogLevel::Warning, + ("Failed to set SPKI, NSS error #%d", PORT_GetError())); + return NS_ERROR_FAILURE; + } + + ScopedCERTCertificateRequest certreq( + CERT_CreateCertificateRequest(subjectName, spki, nullptr)); + if (!certreq) { + MOZ_LOG(gNSSTokenLog, LogLevel::Warning, + ("Failed to gen CSR, NSS error #%d", PORT_GetError())); + return NS_ERROR_FAILURE; + } + + PRTime now = PR_Now(); + PRTime notBefore = now - kExpirationSlack; + PRTime notAfter = now + kExpirationLife; + + ScopedCERTValidity validity(CERT_CreateValidity(notBefore, notAfter)); + if (!validity) { + MOZ_LOG(gNSSTokenLog, LogLevel::Warning, + ("Failed to gen validity, NSS error #%d", PORT_GetError())); + return NS_ERROR_FAILURE; + } + + unsigned long serial; + unsigned char* serialBytes = reinterpret_cast(&serial); + SECStatus srv = PK11_GenerateRandomOnSlot(aSlot, serialBytes, sizeof(serial)); + if (srv != SECSuccess) { + MOZ_LOG(gNSSTokenLog, LogLevel::Warning, + ("Failed to gen serial, NSS error #%d", PORT_GetError())); + return NS_ERROR_FAILURE; + } + // Ensure that the most significant bit isn't set (which would + // indicate a negative number, which isn't valid for serial + // numbers). + serialBytes[0] &= 0x7f; + // Also ensure that the least significant bit on the most + // significant byte is set (to prevent a leading zero byte, + // which also wouldn't be valid). + serialBytes[0] |= 0x01; + + aAttestCert = CERT_CreateCertificate(serial, subjectName, validity, certreq); + if (!aAttestCert) { + MOZ_LOG(gNSSTokenLog, LogLevel::Warning, + ("Failed to gen certificate, NSS error #%d", PORT_GetError())); + return NS_ERROR_FAILURE; + } + + PLArenaPool *arena = aAttestCert->arena; + + srv = SECOID_SetAlgorithmID(arena, &aAttestCert->signature, + SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE, + /* wincx */ nullptr); + if (srv != SECSuccess) { + return NS_ERROR_FAILURE; + } + + // Set version to X509v3. + *(aAttestCert->version.data) = SEC_CERTIFICATE_VERSION_3; + aAttestCert->version.len = 1; + + SECItem innerDER = { siBuffer, nullptr, 0 }; + if (!SEC_ASN1EncodeItem(arena, &innerDER, aAttestCert, + SEC_ASN1_GET(CERT_CertificateTemplate))) { + return NS_ERROR_FAILURE; + } + + SECItem *signedCert = PORT_ArenaZNew(arena, SECItem); + if (!signedCert) { + return NS_ERROR_FAILURE; + } + + srv = SEC_DerSignData(arena, signedCert, innerDER.data, innerDER.len, + aAttestPrivKey, SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE); + if (srv != SECSuccess) { + return NS_ERROR_FAILURE; + } + aAttestCert->derCert = *signedCert; + + MOZ_LOG(gNSSTokenLog, LogLevel::Debug, + ("U2F Soft Token attestation certificate generated.")); + return NS_OK; +} + +// Set up the context for the soft U2F Token. This is called by NSS +// initialization. +NS_IMETHODIMP +nsNSSU2FToken::Init() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mInitialized); + if (mInitialized) { + return NS_ERROR_FAILURE; + } + + nsNSSShutDownPreventionLock locker; + if (isAlreadyShutDown()) { + return NS_ERROR_NOT_AVAILABLE; + } + + ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); + MOZ_ASSERT(slot.get()); + + // Search for an existing wrapping key, or create one. + nsresult rv = GetOrCreateWrappingKey(slot.get(), locker); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mInitialized = true; + MOZ_LOG(gNSSTokenLog, LogLevel::Debug, ("U2F Soft Token initialized.")); + return NS_OK; +} + +// Convert a Private Key object into an opaque key handle, using AES Key Wrap +// and aWrappingKey to convert aPrivKey. +static SECItem* +KeyHandleFromPrivateKey(PK11SlotInfo* aSlot, + PK11SymKey* aWrappingKey, + SECKEYPrivateKey* aPrivKey, + const nsNSSShutDownPreventionLock&) +{ + ScopedSECItem wrappedKey(SECITEM_AllocItem(/* default arena */ nullptr, + /* no buffer */ nullptr, + kWrappedKeyBufLen)); + if (!wrappedKey) { + MOZ_LOG(gNSSTokenLog, LogLevel::Warning, + ("Failed to allocate memory, NSS error #%d", PORT_GetError())); + return nullptr; + } + + ScopedSECItem param(PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP_PAD, + /* default IV */ nullptr )); + + SECStatus srv = PK11_WrapPrivKey(aSlot, aWrappingKey, aPrivKey, + CKM_NSS_AES_KEY_WRAP_PAD, param, + wrappedKey.get(), /* wincx */ nullptr); + if (srv != SECSuccess) { + MOZ_LOG(gNSSTokenLog, LogLevel::Warning, + ("Failed to wrap U2F key, NSS error #%d", PORT_GetError())); + return nullptr; + } + + return wrappedKey.forget(); +} + +// Convert an opaque key handle aKeyHandle back into a Private Key object, using +// aWrappingKey and the AES Key Wrap algorithm. +static SECKEYPrivateKey* +PrivateKeyFromKeyHandle(PK11SlotInfo* aSlot, PK11SymKey* aWrappingKey, + uint8_t* aKeyHandle, uint32_t aKeyHandleLen, + const nsNSSShutDownPreventionLock&) +{ + ScopedAutoSECItem pubKey(kPublicKeyLen); + + ScopedAutoSECItem keyHandleItem(aKeyHandleLen); + memcpy(keyHandleItem.data, aKeyHandle, keyHandleItem.len); + + ScopedSECItem param(PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP_PAD, + /* default IV */ nullptr )); + + CK_ATTRIBUTE_TYPE usages[] = { CKA_SIGN }; + int usageCount = 1; + + SECKEYPrivateKey* unwrappedKey; + unwrappedKey = PK11_UnwrapPrivKey(aSlot, aWrappingKey, + CKM_NSS_AES_KEY_WRAP_PAD, + param, &keyHandleItem, + /* no nickname */ nullptr, + /* discard pubkey */ &pubKey, + /* not permanent */ PR_FALSE, + /* non-exportable */ PR_TRUE, + CKK_EC, usages, usageCount, + /* wincx */ nullptr); + if (!unwrappedKey) { + // Not our key. + MOZ_LOG(gNSSTokenLog, LogLevel::Debug, + ("Could not unwrap key handle, NSS Error #%d", PORT_GetError())); + return nullptr; + } + + return unwrappedKey; +} + +// Return whether the provided version is supported by this token. +NS_IMETHODIMP +nsNSSU2FToken::IsCompatibleVersion(const nsAString& aVersion, bool* aResult) +{ + NS_ENSURE_ARG_POINTER(aResult); + MOZ_ASSERT(mInitialized); + *aResult = (mVersion == aVersion); + return NS_OK; +} + +// IsRegistered determines if the provided key handle is usable by this token. +NS_IMETHODIMP +nsNSSU2FToken::IsRegistered(uint8_t* aKeyHandle, uint32_t aKeyHandleLen, + bool* aResult) +{ + NS_ENSURE_ARG_POINTER(aKeyHandle); + NS_ENSURE_ARG_POINTER(aResult); + + if (!NS_IsMainThread()) { + NS_ERROR("nsNSSU2FToken::IsRegistered called off the main thread"); + return NS_ERROR_NOT_SAME_THREAD; + } + + nsNSSShutDownPreventionLock locker; + if (isAlreadyShutDown()) { + return NS_ERROR_FAILURE; + } + + MOZ_ASSERT(mInitialized); + if (!mInitialized) { + return NS_ERROR_FAILURE; + } + + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + MOZ_ASSERT(slot.get()); + + // Decode the key handle + ScopedSECKEYPrivateKey privKey(PrivateKeyFromKeyHandle(slot.get(), + mWrappingKey.get(), + aKeyHandle, + aKeyHandleLen, + locker)); + *aResult = (privKey.get() != nullptr); + return NS_OK; +} + +// A U2F Register operation causes a new key pair to be generated by the token. +// The token then returns the public key of the key pair, and a handle to the +// private key, which is a fancy way of saying "key wrapped private key", as +// well as the generated attestation certificate and a signature using that +// certificate's private key. +// +// The KeyHandleFromPrivateKey and PrivateKeyFromKeyHandle methods perform +// the actual key wrap/unwrap operations. +// +// The format of the return registration data is as follows: +// +// Bytes Value +// 1 0x05 +// 65 public key +// 1 key handle length +// * key handle +// ASN.1 attestation certificate +// * attestation signature +// +NS_IMETHODIMP +nsNSSU2FToken::Register(uint8_t* aApplication, + uint32_t aApplicationLen, + uint8_t* aChallenge, + uint32_t aChallengeLen, + uint8_t** aRegistration, + uint32_t* aRegistrationLen) +{ + NS_ENSURE_ARG_POINTER(aApplication); + NS_ENSURE_ARG_POINTER(aChallenge); + NS_ENSURE_ARG_POINTER(aRegistration); + NS_ENSURE_ARG_POINTER(aRegistrationLen); + + if (!NS_IsMainThread()) { + NS_ERROR("nsNSSU2FToken::Register called off the main thread"); + return NS_ERROR_NOT_SAME_THREAD; + } + + nsNSSShutDownPreventionLock locker; + if (isAlreadyShutDown()) { + return NS_ERROR_NOT_AVAILABLE; + } + + MOZ_ASSERT(mInitialized); + if (!mInitialized) { + return NS_ERROR_NOT_INITIALIZED; + } + + // We should already have a wrapping key + MOZ_ASSERT(mWrappingKey); + + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + MOZ_ASSERT(slot.get()); + + // Construct a one-time-use Attestation Certificate + ScopedSECKEYPrivateKey attestPrivKey; + ScopedCERTCertificate attestCert; + nsresult rv = GetAttestationCertificate(slot.get(), attestPrivKey, attestCert, + locker); + if (NS_WARN_IF(NS_FAILED(rv))) { + return NS_ERROR_FAILURE; + } + MOZ_ASSERT(attestCert); + MOZ_ASSERT(attestPrivKey); + + // Generate a new keypair; the private will be wrapped into a Key Handle + ScopedSECKEYPrivateKey privKey; + ScopedSECKEYPublicKey pubKey; + rv = GenEcKeypair(slot.get(), privKey, pubKey, locker); + if (NS_WARN_IF(NS_FAILED(rv))) { + return NS_ERROR_FAILURE; + } + + // The key handle will be the result of keywrap(privKey, key=mWrappingKey) + ScopedSECItem keyHandleItem(KeyHandleFromPrivateKey(slot.get(), + mWrappingKey.get(), + privKey.get(), + locker)); + if (!keyHandleItem.get()) { + return NS_ERROR_FAILURE; + } + + // Sign the challenge using the Attestation privkey (from attestCert) + mozilla::dom::CryptoBuffer signedDataBuf; + if (!signedDataBuf.SetCapacity(1 + aApplicationLen + aChallengeLen + + keyHandleItem->len + kPublicKeyLen, + mozilla::fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + // It's OK to ignore the return values here because we're writing into + // pre-allocated space + signedDataBuf.AppendElement(0x00, mozilla::fallible); + signedDataBuf.AppendElements(aApplication, aApplicationLen, mozilla::fallible); + signedDataBuf.AppendElements(aChallenge, aChallengeLen, mozilla::fallible); + signedDataBuf.AppendSECItem(keyHandleItem.get()); + signedDataBuf.AppendSECItem(pubKey->u.ec.publicValue); + + ScopedSECItem signatureItem(::SECITEM_AllocItem(/* default arena */ nullptr, + /* no buffer */ nullptr, 0)); + if (!signatureItem) { + return NS_ERROR_OUT_OF_MEMORY; + } + + SECStatus srv = SEC_SignData(signatureItem.get(), signedDataBuf.Elements(), + signedDataBuf.Length(), attestPrivKey.get(), + SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE); + if (srv != SECSuccess) { + MOZ_LOG(gNSSTokenLog, LogLevel::Warning, + ("Signature failure: %d", PORT_GetError())); + return NS_ERROR_FAILURE; + } + + // Serialize the registration data + mozilla::dom::CryptoBuffer registrationBuf; + if (!registrationBuf.SetCapacity(1 + kPublicKeyLen + 1 + keyHandleItem->len + + attestCert.get()->derCert.len + + signatureItem->len, mozilla::fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + registrationBuf.AppendElement(0x05, mozilla::fallible); + registrationBuf.AppendSECItem(pubKey->u.ec.publicValue); + registrationBuf.AppendElement(keyHandleItem->len, mozilla::fallible); + registrationBuf.AppendSECItem(keyHandleItem.get()); + registrationBuf.AppendSECItem(attestCert.get()->derCert); + registrationBuf.AppendSECItem(signatureItem.get()); + if (!registrationBuf.ToNewUnsignedBuffer(aRegistration, aRegistrationLen)) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +// A U2F Sign operation creates a signature over the "param" arguments (plus +// some other stuff) using the private key indicated in the key handle argument. +// +// The format of the signed data is as follows: +// +// 32 Application parameter +// 1 User presence (0x01) +// 4 Counter +// 32 Challenge parameter +// +// The format of the signature data is as follows: +// +// 1 User presence +// 4 Counter +// * Signature +// +NS_IMETHODIMP +nsNSSU2FToken::Sign(uint8_t* aApplication, uint32_t aApplicationLen, + uint8_t* aChallenge, uint32_t aChallengeLen, + uint8_t* aKeyHandle, uint32_t aKeyHandleLen, + uint8_t** aSignature, uint32_t* aSignatureLen) +{ + NS_ENSURE_ARG_POINTER(aApplication); + NS_ENSURE_ARG_POINTER(aChallenge); + NS_ENSURE_ARG_POINTER(aKeyHandle); + NS_ENSURE_ARG_POINTER(aKeyHandleLen); + NS_ENSURE_ARG_POINTER(aSignature); + NS_ENSURE_ARG_POINTER(aSignatureLen); + + if (!NS_IsMainThread()) { + NS_ERROR("nsNSSU2FToken::Sign called off the main thread"); + return NS_ERROR_NOT_SAME_THREAD; + } + + nsNSSShutDownPreventionLock locker; + if (isAlreadyShutDown()) { + return NS_ERROR_NOT_AVAILABLE; + } + + MOZ_ASSERT(mInitialized); + if (!mInitialized) { + return NS_ERROR_NOT_INITIALIZED; + } + + MOZ_ASSERT(mWrappingKey); + + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + MOZ_ASSERT(slot.get()); + + if ((aChallengeLen != kParamLen) || (aApplicationLen != kParamLen)) { + MOZ_LOG(gNSSTokenLog, LogLevel::Warning, + ("Parameter lengths are wrong! challenge=%d app=%d expected=%d", + aChallengeLen, aApplicationLen, kParamLen)); + + return NS_ERROR_ILLEGAL_VALUE; + } + + // Decode the key handle + ScopedSECKEYPrivateKey privKey(PrivateKeyFromKeyHandle(slot.get(), + mWrappingKey.get(), + aKeyHandle, + aKeyHandleLen, + locker)); + if (!privKey.get()) { + MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Couldn't get the priv key!")); + return NS_ERROR_FAILURE; + } + + // Increment the counter and turn it into a SECItem + uint32_t counter = Preferences::GetUint(PREF_U2F_NSSTOKEN_COUNTER) + 1; + Preferences::SetUint(PREF_U2F_NSSTOKEN_COUNTER, counter); + ScopedAutoSECItem counterItem(4); + counterItem.data[0] = (counter >> 24) & 0xFF; + counterItem.data[1] = (counter >> 16) & 0xFF; + counterItem.data[2] = (counter >> 8) & 0xFF; + counterItem.data[3] = (counter >> 0) & 0xFF; + + // Compute the signature + mozilla::dom::CryptoBuffer signedDataBuf; + if (!signedDataBuf.SetCapacity(1 + 4 + (2 * kParamLen), mozilla::fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + // It's OK to ignore the return values here because we're writing into + // pre-allocated space + signedDataBuf.AppendElements(aApplication, aApplicationLen, mozilla::fallible); + signedDataBuf.AppendElement(0x01, mozilla::fallible); + signedDataBuf.AppendSECItem(counterItem); + signedDataBuf.AppendElements(aChallenge, aChallengeLen, mozilla::fallible); + + ScopedSECItem signatureItem(::SECITEM_AllocItem(/* default arena */ nullptr, + /* no buffer */ nullptr, 0)); + if (!signatureItem) { + return NS_ERROR_OUT_OF_MEMORY; + } + + SECStatus srv = SEC_SignData(signatureItem.get(), signedDataBuf.Elements(), + signedDataBuf.Length(), privKey.get(), + SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE); + if (srv != SECSuccess) { + MOZ_LOG(gNSSTokenLog, LogLevel::Warning, + ("Signature failure: %d", PORT_GetError())); + return NS_ERROR_FAILURE; + } + + // Assmeble the signature data into a buffer for return + mozilla::dom::CryptoBuffer signatureBuf; + if (!signatureBuf.SetCapacity(1 + counterItem.len + signatureItem->len, + mozilla::fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + // It's OK to ignore the return values here because we're writing into + // pre-allocated space + signatureBuf.AppendElement(0x01, mozilla::fallible); + signatureBuf.AppendSECItem(counterItem); + signatureBuf.AppendSECItem(signatureItem); + + if (!signatureBuf.ToNewUnsignedBuffer(aSignature, aSignatureLen)) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} diff --git a/security/manager/ssl/nsNSSU2FToken.h b/security/manager/ssl/nsNSSU2FToken.h new file mode 100644 index 0000000000..d725b0da10 --- /dev/null +++ b/security/manager/ssl/nsNSSU2FToken.h @@ -0,0 +1,43 @@ +/* -*- 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/. */ + +#ifndef nsNSSU2FToken_h +#define nsNSSU2FToken_h + +#include "nsINSSU2FToken.h" + +#include "nsNSSShutDown.h" +#include "ScopedNSSTypes.h" + +#define NS_NSSU2FTOKEN_CID \ + {0x79f95a6c, 0xd0f7, 0x4d7d, {0xae, 0xaa, 0xcd, 0x0a, 0x04, 0xb6, 0x50, 0x89}} + +class nsNSSU2FToken : public nsINSSU2FToken, + public nsNSSShutDownObject +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSINSSU2FTOKEN + + nsNSSU2FToken(); + + // For nsNSSShutDownObject + virtual void virtualDestroyNSSReference() override; + void destructorSafeDestroyNSSReference(); + +private: + bool mInitialized; + mozilla::ScopedPK11SymKey mWrappingKey; + + static const nsCString mSecretNickname; + static const nsString mVersion; + + ~nsNSSU2FToken(); + nsresult GetOrCreateWrappingKey(PK11SlotInfo* aSlot, + const nsNSSShutDownPreventionLock&); +}; + +#endif // nsNSSU2FToken_h diff --git a/security/manager/ssl/nsNTLMAuthModule.cpp b/security/manager/ssl/nsNTLMAuthModule.cpp index 416e4cb9d4..8ef2223711 100644 --- a/security/manager/ssl/nsNTLMAuthModule.cpp +++ b/security/manager/ssl/nsNTLMAuthModule.cpp @@ -12,6 +12,7 @@ #include "mozilla/Endian.h" #include "mozilla/Likely.h" #include "mozilla/Preferences.h" +#include "mozilla/Snprintf.h" #include "nsCOMPtr.h" #include "nsComponentManagerUtils.h" #include "nsICryptoHash.h" @@ -179,23 +180,23 @@ LogBuf(const char *tag, const uint8_t *buf, uint32_t bufLen) for (i=0; iGetCert()); + UniqueCERTCertificate nssCert(cert->GetCert()); if (!nssCert) { rv = NS_ERROR_FAILURE; goto finish; diff --git a/security/manager/ssl/nsSDR.cpp b/security/manager/ssl/nsSDR.cpp index 963cfdb51a..f5f9138fd1 100644 --- a/security/manager/ssl/nsSDR.cpp +++ b/security/manager/ssl/nsSDR.cpp @@ -15,6 +15,7 @@ #include "nsThreadUtils.h" #include "nsIInterfaceRequestor.h" #include "nsIInterfaceRequestorUtils.h" +#include "nsIObserverService.h" #include "nsIServiceManager.h" #include "nsITokenPasswordDialogs.h" @@ -22,6 +23,7 @@ #include "nsCRT.h" #include "nsSDR.h" #include "nsNSSComponent.h" +#include "nsNSSHelper.h" #include "nsNSSShutDown.h" #include "ScopedNSSTypes.h" diff --git a/security/manager/ssl/nsSDR.h b/security/manager/ssl/nsSDR.h index 1f3fd15a68..c4e3094f21 100644 --- a/security/manager/ssl/nsSDR.h +++ b/security/manager/ssl/nsSDR.h @@ -8,6 +8,7 @@ #define _NSSDR_H_ #include "nsISecretDecoderRing.h" +#include "nsNSSShutDown.h" /** * NS_SDR_CONTRACTID - contract id for SDR services. diff --git a/security/manager/ssl/nsSecureBrowserUIImpl.cpp b/security/manager/ssl/nsSecureBrowserUIImpl.cpp index b5ed3c854d..673091ad27 100644 --- a/security/manager/ssl/nsSecureBrowserUIImpl.cpp +++ b/security/manager/ssl/nsSecureBrowserUIImpl.cpp @@ -106,6 +106,7 @@ nsSecureBrowserUIImpl::nsSecureBrowserUIImpl() , mIsViewSource(false) , mSubRequestsBrokenSecurity(0) , mSubRequestsNoSecurity(0) + , mCertUserOverridden(false) , mRestoreSubrequests(false) , mOnLocationChangeSeen(false) #ifdef DEBUG @@ -236,6 +237,10 @@ nsSecureBrowserUIImpl::MapInternalToExternalState(uint32_t* aState, lockIconStat if (ev && (*aState & STATE_IS_SECURE)) *aState |= nsIWebProgressListener::STATE_IDENTITY_EV_TOPLEVEL; + if (mCertUserOverridden && (*aState & STATE_IS_SECURE)) { + *aState |= nsIWebProgressListener::STATE_CERT_USER_OVERRIDDEN; + } + nsCOMPtr docShell = do_QueryReferent(mDocShell); if (!docShell) return NS_OK; @@ -263,6 +268,9 @@ nsSecureBrowserUIImpl::MapInternalToExternalState(uint32_t* aState, lockIconStat if (ev) { *aState |= nsIWebProgressListener::STATE_IDENTITY_EV_TOPLEVEL; } + if (mCertUserOverridden) { + *aState |= nsIWebProgressListener::STATE_CERT_USER_OVERRIDDEN; + } } // * If so, the state should be broken or insecure; overriding the previous // state set by the lock parameter. @@ -796,6 +804,11 @@ nsSecureBrowserUIImpl::OnStateChange(nsIWebProgress* aWebProgress, f -= nsIWebProgressListener::STATE_SECURE_HIGH; info.AppendLiteral("SECURE_HIGH "); } + if (f & nsIWebProgressListener::STATE_CERT_USER_OVERRIDDEN) + { + f -= nsIWebProgressListener::STATE_CERT_USER_OVERRIDDEN; + info.AppendLiteral("STATE_CERT_USER_OVERRIDDEN "); + } if (f & nsIWebProgressListener::STATE_RESTORING) { f -= nsIWebProgressListener::STATE_RESTORING; @@ -1134,6 +1147,9 @@ nsSecureBrowserUIImpl::UpdateSecurityState(nsIRequest* aRequest, newSecurityState = lis_broken_security; } + mCertUserOverridden = + mNewToplevelSecurityState & STATE_CERT_USER_OVERRIDDEN; + MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI:%p: UpdateSecurityState: old-new %d - %d\n", this, mNotifiedSecurityState, newSecurityState)); diff --git a/security/manager/ssl/nsSecureBrowserUIImpl.h b/security/manager/ssl/nsSecureBrowserUIImpl.h index f40319a172..d6e0d9f16a 100644 --- a/security/manager/ssl/nsSecureBrowserUIImpl.h +++ b/security/manager/ssl/nsSecureBrowserUIImpl.h @@ -74,6 +74,7 @@ protected: int32_t mDocumentRequestsInProgress; int32_t mSubRequestsBrokenSecurity; int32_t mSubRequestsNoSecurity; + bool mCertUserOverridden; bool mRestoreSubrequests; bool mOnLocationChangeSeen; #ifdef DEBUG diff --git a/security/manager/ssl/nsSiteSecurityService.cpp b/security/manager/ssl/nsSiteSecurityService.cpp index 3241640fe8..f9282c6812 100644 --- a/security/manager/ssl/nsSiteSecurityService.cpp +++ b/security/manager/ssl/nsSiteSecurityService.cpp @@ -702,7 +702,7 @@ nsSiteSecurityService::ProcessPKPHeader(nsIURI* aSourceURI, rv = aSSLStatus->GetServerCert(getter_AddRefs(cert)); NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(cert, NS_ERROR_FAILURE); - ScopedCERTCertificate nssCert(cert->GetCert()); + UniqueCERTCertificate nssCert(cert->GetCert()); NS_ENSURE_TRUE(nssCert, NS_ERROR_FAILURE); mozilla::pkix::Time now(mozilla::pkix::Now()); diff --git a/security/manager/ssl/tests/compiled/TestIsCertBuiltInRoot.cpp b/security/manager/ssl/tests/compiled/TestIsCertBuiltInRoot.cpp index 0289d5bb4f..cbb5787ae5 100644 --- a/security/manager/ssl/tests/compiled/TestIsCertBuiltInRoot.cpp +++ b/security/manager/ssl/tests/compiled/TestIsCertBuiltInRoot.cpp @@ -130,7 +130,7 @@ AddCertificate(char* pem, char* nickname) return false; } - mozilla::ScopedCERTCertificate cert( + mozilla::UniqueCERTCertificate cert( CERT_NewTempCertificate(CERT_GetDefaultCertDB(), sCertDER, nickname, false, true)); if (!cert) { @@ -142,7 +142,7 @@ AddCertificate(char* pem, char* nickname) trust.sslFlags = 0; trust.emailFlags = 0; trust.objectSigningFlags = 0; - if (CERT_AddTempCertToPerm(cert, nickname, &trust) != SECSuccess) { + if (CERT_AddTempCertToPerm(cert.get(), nickname, &trust) != SECSuccess) { fail("CERT_AddTempCertToPerm failed"); return false; } diff --git a/security/manager/ssl/tests/gtest/OCSPCacheTest.cpp b/security/manager/ssl/tests/gtest/OCSPCacheTest.cpp index 7ea07c1840..c524b185b7 100644 --- a/security/manager/ssl/tests/gtest/OCSPCacheTest.cpp +++ b/security/manager/ssl/tests/gtest/OCSPCacheTest.cpp @@ -7,11 +7,11 @@ #include "CertVerifier.h" #include "OCSPCache.h" #include "gtest/gtest.h" +#include "mozilla/Snprintf.h" #include "nss.h" #include "pkix/pkixtypes.h" #include "pkixtestutil.h" #include "prerr.h" -#include "prprf.h" #include "secerr.h" using namespace mozilla::pkix; @@ -85,7 +85,7 @@ TEST_F(OCSPCacheTest, TestVariousGets) SCOPED_TRACE(""); for (int i = 0; i < MaxCacheEntries; i++) { uint8_t serialBuf[8]; - PR_snprintf(reinterpret_cast(serialBuf), sizeof(serialBuf), "%04d", i); + snprintf(reinterpret_cast(serialBuf), sizeof(serialBuf), "%04d", i); Input fakeSerial; ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4)); Time timeIn(now); @@ -134,7 +134,7 @@ TEST_F(OCSPCacheTest, TestEviction) // we cause the least recently used entry to be evicted. for (int i = 0; i < MaxCacheEntries + 1; i++) { uint8_t serialBuf[8]; - PR_snprintf(reinterpret_cast(serialBuf), sizeof(serialBuf), "%04d", i); + snprintf(reinterpret_cast(serialBuf), sizeof(serialBuf), "%04d", i); Input fakeSerial; ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4)); Time timeIn(now); @@ -159,7 +159,7 @@ TEST_F(OCSPCacheTest, TestNoEvictionForRevokedResponses) // we cause the least recently used entry that isn't revoked to be evicted. for (int i = 1; i < MaxCacheEntries + 1; i++) { uint8_t serialBuf[8]; - PR_snprintf(reinterpret_cast(serialBuf), sizeof(serialBuf), "%04d", i); + snprintf(reinterpret_cast(serialBuf), sizeof(serialBuf), "%04d", i); Input fakeSerial; ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4)); Time timeIn(now); @@ -185,7 +185,7 @@ TEST_F(OCSPCacheTest, TestEverythingIsRevoked) // Fill up the cache with revoked responses. for (int i = 0; i < MaxCacheEntries; i++) { uint8_t serialBuf[8]; - PR_snprintf(reinterpret_cast(serialBuf), sizeof(serialBuf), "%04d", i); + snprintf(reinterpret_cast(serialBuf), sizeof(serialBuf), "%04d", i); Input fakeSerial; ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4)); Time timeIn(now); diff --git a/security/manager/ssl/tests/gtest/TLSIntoleranceTest.cpp b/security/manager/ssl/tests/gtest/TLSIntoleranceTest.cpp index 5f35e2473b..8f20fb2e08 100644 --- a/security/manager/ssl/tests/gtest/TLSIntoleranceTest.cpp +++ b/security/manager/ssl/tests/gtest/TLSIntoleranceTest.cpp @@ -566,67 +566,3 @@ TEST_F(TLSIntoleranceTest, TLS_Per_Site_Fallback_Limit) ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_1)); ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_0)); } - -TEST_F(TLSIntoleranceTest, TLS_Static_Fallback_List) -{ - NS_NAMED_LITERAL_CSTRING(fallback_test, "fallback.test"); - NS_NAMED_LITERAL_CSTRING(no_fallback_test, "no.fallback.test"); - NS_NAMED_LITERAL_CSTRING(wildcard_test, "wildcard.test"); - NS_NAMED_LITERAL_CSTRING(a_wildcard_test, "a.wildcard.test"); - NS_NAMED_LITERAL_CSTRING(long_example_wildcard_test, "long.example.wildcard.test"); - - helpers.mVersionFallbackLimit = SSL_LIBRARY_VERSION_TLS_1_0; - helpers.mUseStaticFallbackList = false; - - ASSERT_FALSE(helpers.fallbackLimitReached(fallback_test, SSL_LIBRARY_VERSION_TLS_1_2)); - ASSERT_FALSE(helpers.fallbackLimitReached(fallback_test, SSL_LIBRARY_VERSION_TLS_1_1)); - ASSERT_TRUE(helpers.fallbackLimitReached(fallback_test, SSL_LIBRARY_VERSION_TLS_1_0)); - ASSERT_FALSE(helpers.fallbackLimitReached(no_fallback_test, SSL_LIBRARY_VERSION_TLS_1_2)); - ASSERT_FALSE(helpers.fallbackLimitReached(no_fallback_test, SSL_LIBRARY_VERSION_TLS_1_1)); - ASSERT_TRUE(helpers.fallbackLimitReached(no_fallback_test, SSL_LIBRARY_VERSION_TLS_1_0)); - ASSERT_FALSE(helpers.fallbackLimitReached(wildcard_test, SSL_LIBRARY_VERSION_TLS_1_2)); - ASSERT_FALSE(helpers.fallbackLimitReached(wildcard_test, SSL_LIBRARY_VERSION_TLS_1_1)); - ASSERT_TRUE(helpers.fallbackLimitReached(wildcard_test, SSL_LIBRARY_VERSION_TLS_1_0)); - ASSERT_FALSE(helpers.fallbackLimitReached(a_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_2)); - ASSERT_FALSE(helpers.fallbackLimitReached(a_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_1)); - ASSERT_TRUE(helpers.fallbackLimitReached(a_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_0)); - ASSERT_FALSE(helpers.fallbackLimitReached(long_example_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_2)); - ASSERT_FALSE(helpers.fallbackLimitReached(long_example_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_1)); - ASSERT_TRUE(helpers.fallbackLimitReached(long_example_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_0)); - - helpers.mVersionFallbackLimit = SSL_LIBRARY_VERSION_TLS_1_2; - - ASSERT_TRUE(helpers.fallbackLimitReached(fallback_test, SSL_LIBRARY_VERSION_TLS_1_2)); - ASSERT_TRUE(helpers.fallbackLimitReached(fallback_test, SSL_LIBRARY_VERSION_TLS_1_1)); - ASSERT_TRUE(helpers.fallbackLimitReached(fallback_test, SSL_LIBRARY_VERSION_TLS_1_0)); - ASSERT_TRUE(helpers.fallbackLimitReached(no_fallback_test, SSL_LIBRARY_VERSION_TLS_1_2)); - ASSERT_TRUE(helpers.fallbackLimitReached(no_fallback_test, SSL_LIBRARY_VERSION_TLS_1_1)); - ASSERT_TRUE(helpers.fallbackLimitReached(no_fallback_test, SSL_LIBRARY_VERSION_TLS_1_0)); - ASSERT_TRUE(helpers.fallbackLimitReached(wildcard_test, SSL_LIBRARY_VERSION_TLS_1_2)); - ASSERT_TRUE(helpers.fallbackLimitReached(wildcard_test, SSL_LIBRARY_VERSION_TLS_1_1)); - ASSERT_TRUE(helpers.fallbackLimitReached(wildcard_test, SSL_LIBRARY_VERSION_TLS_1_0)); - ASSERT_TRUE(helpers.fallbackLimitReached(a_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_2)); - ASSERT_TRUE(helpers.fallbackLimitReached(a_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_1)); - ASSERT_TRUE(helpers.fallbackLimitReached(a_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_0)); - ASSERT_TRUE(helpers.fallbackLimitReached(long_example_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_2)); - ASSERT_TRUE(helpers.fallbackLimitReached(long_example_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_1)); - ASSERT_TRUE(helpers.fallbackLimitReached(long_example_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_0)); - - helpers.mUseStaticFallbackList = true; - - ASSERT_FALSE(helpers.fallbackLimitReached(fallback_test, SSL_LIBRARY_VERSION_TLS_1_2)); - ASSERT_FALSE(helpers.fallbackLimitReached(fallback_test, SSL_LIBRARY_VERSION_TLS_1_1)); - ASSERT_TRUE(helpers.fallbackLimitReached(fallback_test, SSL_LIBRARY_VERSION_TLS_1_0)); - ASSERT_TRUE(helpers.fallbackLimitReached(no_fallback_test, SSL_LIBRARY_VERSION_TLS_1_2)); - ASSERT_TRUE(helpers.fallbackLimitReached(no_fallback_test, SSL_LIBRARY_VERSION_TLS_1_1)); - ASSERT_TRUE(helpers.fallbackLimitReached(no_fallback_test, SSL_LIBRARY_VERSION_TLS_1_0)); - ASSERT_TRUE(helpers.fallbackLimitReached(wildcard_test, SSL_LIBRARY_VERSION_TLS_1_2)); - ASSERT_TRUE(helpers.fallbackLimitReached(wildcard_test, SSL_LIBRARY_VERSION_TLS_1_1)); - ASSERT_TRUE(helpers.fallbackLimitReached(wildcard_test, SSL_LIBRARY_VERSION_TLS_1_0)); - ASSERT_FALSE(helpers.fallbackLimitReached(a_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_2)); - ASSERT_FALSE(helpers.fallbackLimitReached(a_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_1)); - ASSERT_TRUE(helpers.fallbackLimitReached(a_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_0)); - ASSERT_FALSE(helpers.fallbackLimitReached(long_example_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_2)); - ASSERT_FALSE(helpers.fallbackLimitReached(long_example_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_1)); - ASSERT_TRUE(helpers.fallbackLimitReached(long_example_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_0)); -} diff --git a/security/manager/ssl/tests/unit/head_psm.js b/security/manager/ssl/tests/unit/head_psm.js index 5639d1caaa..9184fb4e80 100644 --- a/security/manager/ssl/tests/unit/head_psm.js +++ b/security/manager/ssl/tests/unit/head_psm.js @@ -642,7 +642,13 @@ FakeSSLStatus.prototype = { // Helper function for add_cert_override_test. Probably doesn't need to be // called directly. -function add_cert_override(aHost, aExpectedBits, aSecurityInfo) { +function add_cert_override(aHost, aExpectedBits, aExpectedErrorRegexp, + aSecurityInfo) { + if (aExpectedErrorRegexp) { + do_print(aSecurityInfo.errorMessage); + Assert.ok(aExpectedErrorRegexp.test(aSecurityInfo.errorMessage), + "Actual error message should match expected error regexp"); + } let sslstatus = aSecurityInfo.QueryInterface(Ci.nsISSLStatusProvider) .SSLStatus; let bits = @@ -658,14 +664,21 @@ function add_cert_override(aHost, aExpectedBits, aSecurityInfo) { true); } -// Given a host, expected error bits (see nsICertOverrideService.idl), and -// an expected error code, tests that an initial connection to the host fails +// Given a host, expected error bits (see nsICertOverrideService.idl), an +// expected error code, and optionally a regular expression that the resulting +// error message must match, tests that an initial connection to the host fails // with the expected errors and that adding an override results in a subsequent // connection succeeding. -function add_cert_override_test(aHost, aExpectedBits, aExpectedError) { +function add_cert_override_test(aHost, aExpectedBits, aExpectedError, + aExpectedErrorRegexp = undefined) { add_connection_test(aHost, aExpectedError, null, - add_cert_override.bind(this, aHost, aExpectedBits)); - add_connection_test(aHost, PRErrorCodeSuccess); + add_cert_override.bind(this, aHost, aExpectedBits, + aExpectedErrorRegexp)); + add_connection_test(aHost, PRErrorCodeSuccess, null, aSecurityInfo => { + Assert.ok(aSecurityInfo.securityState & + Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN, + "Cert override flag should be set on the security state"); + }); } // Helper function for add_prevented_cert_override_test. This is much like diff --git a/security/manager/ssl/tests/unit/moz.build b/security/manager/ssl/tests/unit/moz.build index 386bb4f556..d1d73d2ca8 100644 --- a/security/manager/ssl/tests/unit/moz.build +++ b/security/manager/ssl/tests/unit/moz.build @@ -12,6 +12,7 @@ if not CONFIG['MOZ_NO_SMART_CARDS']: TEST_DIRS += [ 'bad_certs', 'ocsp_certs', + 'test_baseline_requirements', 'test_cert_eku', 'test_cert_embedded_null', 'test_cert_keyUsage', diff --git a/security/manager/ssl/tests/unit/pycert.py b/security/manager/ssl/tests/unit/pycert.py index f7aefff0d2..d5584b00f5 100644 --- a/security/manager/ssl/tests/unit/pycert.py +++ b/security/manager/ssl/tests/unit/pycert.py @@ -29,7 +29,7 @@ keyUsage:[digitalSignature,nonRepudiation,keyEncipherment, extKeyUsage:[serverAuth,clientAuth,codeSigning,emailProtection nsSGC, # Netscape Server Gated Crypto OCSPSigning,timeStamping] -subjectAlternativeName:[,...] +subjectAlternativeName:[,...] authorityInformationAccess: certificatePolicies: nameConstraints:{permitted,excluded}:[,...] @@ -531,14 +531,19 @@ class Certificate(object): extKeyUsageExtension.setComponentByPosition(count, self.keyPurposeToOID(keyPurpose)) self.addExtension(rfc2459.id_ce_extKeyUsage, extKeyUsageExtension, critical) - def addSubjectAlternativeName(self, dNSNames, critical): + def addSubjectAlternativeName(self, names, critical): subjectAlternativeName = rfc2459.SubjectAltName() - for count, dNSName in enumerate(dNSNames.split(',')): + for count, name in enumerate(names.split(',')): generalName = rfc2459.GeneralName() - # The string may have things like '\0' (i.e. a slash - # followed by the number zero) that have to be decoded into - # the resulting '\x00' (i.e. a byte with value zero). - generalName.setComponentByName('dNSName', dNSName.decode(encoding='string_escape')) + if '/' in name: + directoryName = stringToDN(name, + tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4)) + generalName.setComponentByName('directoryName', directoryName) + else: + # The string may have things like '\0' (i.e. a slash + # followed by the number zero) that have to be decoded into + # the resulting '\x00' (i.e. a byte with value zero). + generalName.setComponentByName('dNSName', name.decode(encoding='string_escape')) subjectAlternativeName.setComponentByPosition(count, generalName) self.addExtension(rfc2459.id_ce_subjectAltName, subjectAlternativeName, critical) diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/ca.pem b/security/manager/ssl/tests/unit/test_baseline_requirements/ca.pem new file mode 100644 index 0000000000..62ff96cb91 --- /dev/null +++ b/security/manager/ssl/tests/unit/test_baseline_requirements/ca.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICxTCCAa+gAwIBAgIUQy+m6w0ZtMTfbmtELQQz8zwqCAowCwYJKoZIhvcNAQEL +MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTQxMTI3MDAwMDAwWhgPMjAxNzAyMDQwMDAw +MDBaMA0xCzAJBgNVBAMMAmNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptu +Gobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO +7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgf +qDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/yt +HSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcx +uLP+SSP6clHEMdUDrNoYCjXtjQIDAQABox0wGzAMBgNVHRMEBTADAQH/MAsGA1Ud +DwQEAwIBBjALBgkqhkiG9w0BAQsDggEBAJQcekrdR+S6U0I3owUQxVOoUJMzHdTj +u562Ra7cOiJQwe1OQZbvo6rQkQWPrpuDOGpwwr1+HBMGb8mjUqeFo5wIinU003TC +UYYEpDCbPwXOKDkDUukKd1aO4wpJc/v8YIiCz7aCRj9HQ3L5YO5JsgMNSCXKKoUm +ILcz2V+IQZ6lePzFfd2aO3zLMDPwEOyujYYtQnBVZIT4F/x/6nU8E6bkbDSGPjQW +CSVhwa0YQ9lCRSM6e//wGry4i8X8718t1V+Nqh7y6u7UlOrXbNEA4pR6mvJsqPhF +Mj82We4OGNBxXbyuGJObQgLBfmRuwKQT9SNtKWEifiaTw8apT/fBagc= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/ca.pem.certspec b/security/manager/ssl/tests/unit/test_baseline_requirements/ca.pem.certspec new file mode 100644 index 0000000000..6660f5d478 --- /dev/null +++ b/security/manager/ssl/tests/unit/test_baseline_requirements/ca.pem.certspec @@ -0,0 +1,4 @@ +issuer:ca +subject:ca +extension:basicConstraints:cA, +extension:keyUsage:cRLSign,keyCertSign diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/moz.build b/security/manager/ssl/tests/unit/test_baseline_requirements/moz.build new file mode 100644 index 0000000000..a66a115c75 --- /dev/null +++ b/security/manager/ssl/tests/unit/test_baseline_requirements/moz.build @@ -0,0 +1,17 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# Temporarily disabled. See bug 1256495. +#test_certificates = ( +# 'ca.pem', +# 'no-san-old.pem', +# 'no-san-recent.pem', +# 'san-contains-no-hostnames-old.pem', +# 'san-contains-no-hostnames-recent.pem', +#) +# +#for test_certificate in test_certificates: +# GeneratedTestCertificate(test_certificate) diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-old.pem b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-old.pem new file mode 100644 index 0000000000..4a0032f434 --- /dev/null +++ b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-old.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICrzCCAZmgAwIBAgIUS00fexo4Y4FagP1oiKQiGCJKd/swCwYJKoZIhvcNAQEL +MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTYwNzI0MDAwMDAwWhgPMjAxNjA5MjQwMDAw +MDBaMBYxFDASBgNVBAMMC2V4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo +4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDD +SeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFX +kD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUx +owyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/ +Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABMAsGCSqGSIb3DQEBCwOC +AQEAnooFCIG4D5EQxpe6EcB3gXE2Cj5kMBvO5H+OobaVRd4kIxET+MChwsN2nuwy +xooA/P2M+9W/S3KP/K8L1tlyf/8J2xlr349MhULhCKG7XxpEQZl6sVjaeDLwWrEK +2Ts1jPgCTufdfPYBcO3rb4HSdHNfFlhB98dfgSakXqXzUZ9sz2VxmAeL6tLvspG9 +tH5viWFc+lv1J3/Jtzk79hleDsiIdcjTjC/bM/Y49jkNOBxru4Qrw5AZuveoVy/G +2axz89wBpjLYjI0KtVxCf8dutcVW7UwFKQywmo7QDqha61f0f8I6oCYXsDPK9FPf +WLIrK1TT3YxdhN7QIRu6J8ttBQ== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-old.pem.certspec b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-old.pem.certspec new file mode 100644 index 0000000000..7a34d0758f --- /dev/null +++ b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-old.pem.certspec @@ -0,0 +1,3 @@ +issuer:ca +subject:example.com +validity:20160724-20160924 diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-recent.pem b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-recent.pem new file mode 100644 index 0000000000..f901f613bf --- /dev/null +++ b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-recent.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICrzCCAZmgAwIBAgIUWxGwhSb8roUQoLNpJajl0X8jk10wCwYJKoZIhvcNAQEL +MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTYwODI0MDAwMDAwWhgPMjAxNjA5MjQwMDAw +MDBaMBYxFDASBgNVBAMMC2V4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo +4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDD +SeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFX +kD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUx +owyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/ +Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABMAsGCSqGSIb3DQEBCwOC +AQEADs0POeb1wJthEcg1nRYRcPgcNHsV1yVCkHMyfmssA1rgWXsp93PRVaCyZYgI +Dq+8QJtAYpdcChFcEYeGjcjLv49Dh+yiiZPPbRWKJB0y3v+13A74M1q+IQFoQWAh +L5GzAEjApsB1j/csRfDNjIwcP1WApN1iZ2NPxFU+PAIFhcmm+uD2doDtQfpMN9vi +HEg5H1x7JOufOZlN+zbnPK+Ob7N13pFd/P/IO8XhA/X8by4G45oh0deyELf9zVcW +4flslHPYthp4LDEyPvTP52jHn/yTO/m8cxKmCZOuwiw4DWfSVUy6irUs8W5SlfS8 +gI2Ctb+G5YvSffsPYwkUg3Hg7g== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-recent.pem.certspec b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-recent.pem.certspec new file mode 100644 index 0000000000..e38478165c --- /dev/null +++ b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-recent.pem.certspec @@ -0,0 +1,3 @@ +issuer:ca +subject:example.com +validity:20160824-20160924 diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-old.pem b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-old.pem new file mode 100644 index 0000000000..2d6821a7cd --- /dev/null +++ b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-old.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC4TCCAcugAwIBAgIUL/Gibj3iILbh9idTh3u9XTSwzqIwCwYJKoZIhvcNAQEL +MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTYwNzI0MDAwMDAwWhgPMjAxNjA5MjQwMDAw +MDBaMBYxFDASBgNVBAMMC2V4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo +4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDD +SeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFX +kD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUx +owyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/ +Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABozAwLjAsBgNVHREEJTAj +pCEwHzEdMBsGA1UECgwURXhhbXBsZSBPcmdhbml6YXRpb24wCwYJKoZIhvcNAQEL +A4IBAQAtCjzHSdXdAyaC5Qyw77gFwQKECHDAimgze1Nvgkyiv4LJLSuFZ84jnLIL +PM+iRxrxeBOdNy8PNIaDadFb5NoovmdLTG08ZjNjJoXOt5JufIHQrUzrcZy1aP7z +rWXED1QcwyKkoOAOqr5hOZ3pmu67a1vJgjZ8H4dVhfFkmSVGPG/6mTvn9H4N/AEo +K+M7BW1WMnNexsV5mMvlUUdfZP0J3o9oI9msH6uH92xU6jIHpgcm6plXfpOBGQfB +g6EUGD4znDe24ljbaohFATWw5c09kkOQNg/H6DQpb1Vi6k+X62Zgj5UR79zx53e+ +3Wo3Iu+hJUfNwyNk7KVF+r1wsUdA +-----END CERTIFICATE----- \ No newline at end of file diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-old.pem.certspec b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-old.pem.certspec new file mode 100644 index 0000000000..41817bde75 --- /dev/null +++ b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-old.pem.certspec @@ -0,0 +1,4 @@ +issuer:ca +subject:example.com +validity:20160724-20160924 +extension:subjectAlternativeName:/O=Example Organization diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-recent.pem b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-recent.pem new file mode 100644 index 0000000000..fc8b5b48a3 --- /dev/null +++ b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-recent.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC4TCCAcugAwIBAgIUUwz+d++GVy8L6Lxi9GVnzG6yUCEwCwYJKoZIhvcNAQEL +MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTYwODI0MDAwMDAwWhgPMjAxNjA5MjQwMDAw +MDBaMBYxFDASBgNVBAMMC2V4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo +4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDD +SeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFX +kD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUx +owyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/ +Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABozAwLjAsBgNVHREEJTAj +pCEwHzEdMBsGA1UECgwURXhhbXBsZSBPcmdhbml6YXRpb24wCwYJKoZIhvcNAQEL +A4IBAQCdKXA+1XhcpKdyjeJjEEUm9ptoS8tKJt/vdGUPCByzlD71OJGsiXorTcGY +V2sgbGCmxA8dnxG8bPQMFdAZ2hRjWjZ/Hs18SDbMAONjzgiPlwUWRZldb2Th7WX3 +7a+1uMsT1rEsgmip7FuJjqW0qEyuHFRTt47aK0GJRX42VC5kJVMX8ujl8ucqSSNa +PRh6IPQgIxSchu79weP+YIxMz3GDvNuu6z4QWdkzQrnYqSJpLgNGPAdAxFCgsok3 +5rFNhadGNv6RqrG00HmXPBTTq6T8rQEsbQZQZ4eensb0HxdiQyX4XGnMeEpElTwO +LHziGIW2jBA9v5FJVBCC9QQysNby +-----END CERTIFICATE----- \ No newline at end of file diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-recent.pem.certspec b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-recent.pem.certspec new file mode 100644 index 0000000000..140c201434 --- /dev/null +++ b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-recent.pem.certspec @@ -0,0 +1,4 @@ +issuer:ca +subject:example.com +validity:20160824-20160924 +extension:subjectAlternativeName:/O=Example Organization diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements_subject_common_name.js b/security/manager/ssl/tests/unit/test_baseline_requirements_subject_common_name.js new file mode 100644 index 0000000000..d85bce740f --- /dev/null +++ b/security/manager/ssl/tests/unit/test_baseline_requirements_subject_common_name.js @@ -0,0 +1,131 @@ +// -*- 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/. + +// The preference security.pki.name_matching_mode controls whether or not +// mozilla::pkix will fall back to using a certificate's subject common name +// during name matching. If the Baseline Requirements are followed, fallback +// should not be necessary (because any name information in the subject common +// name should be present in the subject alternative name extension). Due to +// compatibility concerns, the platform can be configured to fall back for +// certificates that are valid before 23 August 2016. Note that for certificates +// issued by an imported root, the platform will fall back if necessary, +// regardless of the value of the preference. + +"use strict"; + +do_get_profile(); // must be called before getting nsIX509CertDB +const gCertDB = Cc["@mozilla.org/security/x509certdb;1"] + .getService(Ci.nsIX509CertDB); + +function certFromFile(certName) { + return constructCertFromFile(`test_baseline_requirements/${certName}.pem`); +} + +function loadCertWithTrust(certName, trustString) { + addCertFromFile(gCertDB, `test_baseline_requirements/${certName}.pem`, + trustString); +} + +function checkCertOn25August2016(cert, expectedResult) { + // (new Date("2016-08-25T00:00:00Z")).getTime() / 1000 + const VALIDATION_TIME = 1472083200; + checkCertErrorGenericAtTime(gCertDB, cert, expectedResult, + certificateUsageSSLServer, VALIDATION_TIME, {}, + "example.com"); +} + +function run_test() { + do_register_cleanup(() => { + Services.prefs.clearUserPref("security.pki.name_matching_mode"); + Services.prefs.clearUserPref("security.test.built_in_root_hash"); + }); + + loadCertWithTrust("ca", "CTu,,"); + + // When verifying a certificate, if the trust anchor is not a built-in root, + // name matching will fall back to using the subject common name if necessary + // (i.e. if there is no subject alternative name extension or it does not + // contain any dNSName or iPAddress entries). Thus, since imported roots are + // not in general treated as built-ins, these should all successfully verify + // regardless of the value of the pref. + Services.prefs.setIntPref("security.pki.name_matching_mode", 0); + do_print("current mode: always fall back, root not built-in"); + checkCertOn25August2016(certFromFile("no-san-recent"), + PRErrorCodeSuccess); + checkCertOn25August2016(certFromFile("no-san-old"), + PRErrorCodeSuccess); + checkCertOn25August2016(certFromFile("san-contains-no-hostnames-recent"), + PRErrorCodeSuccess); + checkCertOn25August2016(certFromFile("san-contains-no-hostnames-old"), + PRErrorCodeSuccess); + + Services.prefs.setIntPref("security.pki.name_matching_mode", 1); + do_print("current mode: fall back for notBefore < August 23, 2016, root " + + "not built-in"); + checkCertOn25August2016(certFromFile("no-san-recent"), + PRErrorCodeSuccess); + checkCertOn25August2016(certFromFile("no-san-old"), + PRErrorCodeSuccess); + checkCertOn25August2016(certFromFile("san-contains-no-hostnames-recent"), + PRErrorCodeSuccess); + checkCertOn25August2016(certFromFile("san-contains-no-hostnames-old"), + PRErrorCodeSuccess); + + Services.prefs.setIntPref("security.pki.name_matching_mode", 2); + do_print("current mode: never fall back, root not built-in"); + checkCertOn25August2016(certFromFile("no-san-recent"), + PRErrorCodeSuccess); + checkCertOn25August2016(certFromFile("no-san-old"), + PRErrorCodeSuccess); + checkCertOn25August2016(certFromFile("san-contains-no-hostnames-recent"), + PRErrorCodeSuccess); + checkCertOn25August2016(certFromFile("san-contains-no-hostnames-old"), + PRErrorCodeSuccess); + + // In debug builds, we can treat an imported root as a built-in, and thus we + // can actually test the different values of the pref. + if (isDebugBuild) { + let root = certFromFile("ca"); + Services.prefs.setCharPref("security.test.built_in_root_hash", + root.sha256Fingerprint); + + // Always fall back if necessary. + Services.prefs.setIntPref("security.pki.name_matching_mode", 0); + do_print("current mode: always fall back, root built-in"); + checkCertOn25August2016(certFromFile("no-san-recent"), + PRErrorCodeSuccess); + checkCertOn25August2016(certFromFile("no-san-old"), + PRErrorCodeSuccess); + checkCertOn25August2016(certFromFile("san-contains-no-hostnames-recent"), + PRErrorCodeSuccess); + checkCertOn25August2016(certFromFile("san-contains-no-hostnames-old"), + PRErrorCodeSuccess); + + // Only fall back if notBefore < 23 August 2016 + Services.prefs.setIntPref("security.pki.name_matching_mode", 1); + do_print("current mode: fall back for notBefore < August 23, 2016, root " + + "built-in"); + checkCertOn25August2016(certFromFile("no-san-recent"), + SSL_ERROR_BAD_CERT_DOMAIN); + checkCertOn25August2016(certFromFile("no-san-old"), + PRErrorCodeSuccess); + checkCertOn25August2016(certFromFile("san-contains-no-hostnames-recent"), + SSL_ERROR_BAD_CERT_DOMAIN); + checkCertOn25August2016(certFromFile("san-contains-no-hostnames-old"), + PRErrorCodeSuccess); + + // Never fall back. + Services.prefs.setIntPref("security.pki.name_matching_mode", 2); + do_print("current mode: never fall back, root built-in"); + checkCertOn25August2016(certFromFile("no-san-recent"), + SSL_ERROR_BAD_CERT_DOMAIN); + checkCertOn25August2016(certFromFile("no-san-old"), + SSL_ERROR_BAD_CERT_DOMAIN); + checkCertOn25August2016(certFromFile("san-contains-no-hostnames-recent"), + SSL_ERROR_BAD_CERT_DOMAIN); + checkCertOn25August2016(certFromFile("san-contains-no-hostnames-old"), + SSL_ERROR_BAD_CERT_DOMAIN); + } +} diff --git a/security/manager/ssl/tests/unit/test_cert_overrides.js b/security/manager/ssl/tests/unit/test_cert_overrides.js index 7aabd77ee2..72824cbd4e 100644 --- a/security/manager/ssl/tests/unit/test_cert_overrides.js +++ b/security/manager/ssl/tests/unit/test_cert_overrides.js @@ -153,12 +153,14 @@ function add_simple_tests() { // but not the subject common name. add_cert_override_test("mismatch.example.com", Ci.nsICertOverrideService.ERROR_MISMATCH, - SSL_ERROR_BAD_CERT_DOMAIN); + SSL_ERROR_BAD_CERT_DOMAIN, + /The certificate is only valid for the following names:\s*doesntmatch\.example\.com, \*\.alsodoesntmatch\.example\.com/); // This has name information in the subject common name but not the subject // alternative names extension. add_cert_override_test("mismatch-CN.example.com", Ci.nsICertOverrideService.ERROR_MISMATCH, - SSL_ERROR_BAD_CERT_DOMAIN); + SSL_ERROR_BAD_CERT_DOMAIN, + /The certificate is not valid for the name mismatch-CN\.example\.com/); // A Microsoft IIS utility generates self-signed certificates with // properties similar to the one this "host" will present. @@ -241,7 +243,8 @@ function add_simple_tests() { SSL_ERROR_BAD_CERT_DOMAIN); add_cert_override_test("noValidNames.example.com", Ci.nsICertOverrideService.ERROR_MISMATCH, - SSL_ERROR_BAD_CERT_DOMAIN); + SSL_ERROR_BAD_CERT_DOMAIN, + /The certificate is not valid for the name noValidNames\.example\.com/); add_cert_override_test("badSubjectAltNames.example.com", Ci.nsICertOverrideService.ERROR_MISMATCH, SSL_ERROR_BAD_CERT_DOMAIN); @@ -268,7 +271,8 @@ function add_combo_tests() { add_cert_override_test("mismatch-expired.example.com", Ci.nsICertOverrideService.ERROR_MISMATCH | Ci.nsICertOverrideService.ERROR_TIME, - SSL_ERROR_BAD_CERT_DOMAIN); + SSL_ERROR_BAD_CERT_DOMAIN, + /The certificate is only valid for doesntmatch\.example\.com<\/a>/); add_cert_override_test("mismatch-notYetValid.example.com", Ci.nsICertOverrideService.ERROR_MISMATCH | Ci.nsICertOverrideService.ERROR_TIME, diff --git a/security/manager/ssl/tests/unit/test_ocsp_caching.js b/security/manager/ssl/tests/unit/test_ocsp_caching.js index 564ae6794c..8cb42c069d 100644 --- a/security/manager/ssl/tests/unit/test_ocsp_caching.js +++ b/security/manager/ssl/tests/unit/test_ocsp_caching.js @@ -62,6 +62,7 @@ function run_test() { do_get_profile(); Services.prefs.setBoolPref("security.ssl.enable_ocsp_stapling", true); Services.prefs.setIntPref("security.OCSP.enabled", 1); + Services.prefs.setIntPref("security.pki.sha1_enforcement_level", 3); add_tls_server_setup("OCSPStaplingServer", "ocsp_certs"); let ocspResponder = new HttpServer(); @@ -145,8 +146,6 @@ function add_tests() { respondWithError, respondWithError, respondWithError, - respondWithError, - respondWithError, ], "No stapled response -> a fetch should have been attempted"); diff --git a/security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js b/security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js index 0a84f1b6ae..355488b76c 100644 --- a/security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js +++ b/security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js @@ -28,6 +28,7 @@ function add_ocsp_test(aHost, aExpectedResult, aOCSPResponseToServe) { do_get_profile(); Services.prefs.setBoolPref("security.ssl.enable_ocsp_stapling", true); Services.prefs.setIntPref("security.OCSP.enabled", 1); +Services.prefs.setIntPref("security.pki.sha1_enforcement_level", 3); var args = [["good", "default-ee", "unused"], ["expiredresponse", "default-ee", "unused"], ["oldvalidperiod", "default-ee", "unused"], @@ -46,6 +47,14 @@ var ocspResponseRevoked = ocspResponses[3]; // Fresh signature, certificate is unknown. var ocspResponseUnknown = ocspResponses[4]; +// sometimes we expect a result without re-fetch +var willNotRetry = 1; +// but sometimes, since a bad response is in the cache, OCSP fetch will be +// attempted for each validation - in practice, for these test certs, this +// means 6 requests because various hash algorithm and key size combinations +// are tried. +var willRetry = 6; + function run_test() { let ocspResponder = new HttpServer(); ocspResponder.registerPrefixHandler("/", function(request, response) { diff --git a/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp b/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp index ee4a3a4636..57b5dca0f9 100644 --- a/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp +++ b/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp @@ -83,11 +83,12 @@ DoSNISocketConfigBySubjectCN(PRFileDesc* aFd, const SECItem* aSrvNameArr, uint32_t aSrvNameArrSize) { for (uint32_t i = 0; i < aSrvNameArrSize; i++) { - ScopedPORTString name((char*)PORT_ZAlloc(aSrvNameArr[i].len + 1)); + UniquePORTString name( + static_cast(PORT_ZAlloc(aSrvNameArr[i].len + 1))); if (name) { - PORT_Memcpy(name, aSrvNameArr[i].data, aSrvNameArr[i].len); - if (SECSuccess == ConfigSecureServerWithNamedCert(aFd, name, - nullptr, nullptr)) { + PORT_Memcpy(name.get(), aSrvNameArr[i].data, aSrvNameArr[i].len); + if (ConfigSecureServerWithNamedCert(aFd, name.get(), nullptr, nullptr) + == SECSuccess) { return 0; } } @@ -116,7 +117,7 @@ DoSNISocketConfig(PRFileDesc* aFd, const SECItem* aSrvNameArr, fprintf(stderr, "found pre-defined host '%s'\n", host->mHostName); } - ScopedCERTCertificate cert; + UniqueCERTCertificate cert; SSLKEAType certKEA; if (SECSuccess != ConfigSecureServerWithNamedCert(aFd, host->mCertName, &cert, &certKEA)) { diff --git a/security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp b/security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp index a86ac1c34d..b735a17015 100644 --- a/security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp +++ b/security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp @@ -90,14 +90,14 @@ WriteResponse(const char* filename, const SECItem* item) return false; } - ScopedPRFileDesc outFile(PR_Open(filename, + UniquePRFileDesc outFile(PR_Open(filename, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0644)); if (!outFile) { PrintPRError("cannot open file for writing"); return false; } - int32_t rv = PR_Write(outFile, item->data, item->len); + int32_t rv = PR_Write(outFile.get(), item->data, item->len); if (rv < 0 || (uint32_t) rv != item->len) { PrintPRError("File write failure"); return false; @@ -318,7 +318,7 @@ main(int argc, char* argv[]) PR_fprintf(PR_STDERR, "Failed to initialize NSS\n"); exit(EXIT_FAILURE); } - PLArenaPool* arena = PORT_NewArena(256 * argc); + UniquePLArenaPool arena(PORT_NewArena(256 * argc)); if (!arena) { PrintPRError("PORT_NewArena failed"); exit(EXIT_FAILURE); @@ -337,7 +337,7 @@ main(int argc, char* argv[]) exit(EXIT_FAILURE); } - ScopedCERTCertificate cert(PK11_FindCertFromNickname(certNick, nullptr)); + UniqueCERTCertificate cert(PK11_FindCertFromNickname(certNick, nullptr)); if (!cert) { PrintPRError("PK11_FindCertFromNickname failed"); PR_fprintf(PR_STDERR, "Failed to find certificate with nick '%s'\n", diff --git a/security/manager/ssl/tests/unit/tlsserver/cmd/OCSPStaplingServer.cpp b/security/manager/ssl/tests/unit/tlsserver/cmd/OCSPStaplingServer.cpp index fa7160c323..6bf33a143d 100644 --- a/security/manager/ssl/tests/unit/tlsserver/cmd/OCSPStaplingServer.cpp +++ b/security/manager/ssl/tests/unit/tlsserver/cmd/OCSPStaplingServer.cpp @@ -82,7 +82,7 @@ DoSNISocketConfig(PRFileDesc *aFd, const SECItem *aSrvNameArr, const char *certNickname = host->mServerCertName ? host->mServerCertName : DEFAULT_CERT_NICKNAME; - ScopedCERTCertificate cert; + UniqueCERTCertificate cert; SSLKEAType certKEA; if (SECSuccess != ConfigSecureServerWithNamedCert(aFd, certNickname, &cert, &certKEA)) { @@ -94,7 +94,7 @@ DoSNISocketConfig(PRFileDesc *aFd, const SECItem *aSrvNameArr, return 0; } - PLArenaPool *arena = PORT_NewArena(1024); + UniquePLArenaPool arena(PORT_NewArena(1024)); if (!arena) { PrintPRError("PORT_NewArena failed"); return SSL_SNI_SEND_ALERT; @@ -104,13 +104,11 @@ DoSNISocketConfig(PRFileDesc *aFd, const SECItem *aSrvNameArr, SECItemArray *response = GetOCSPResponseForType(host->mORT, cert, arena, host->mAdditionalCertName); if (!response) { - PORT_FreeArena(arena, PR_FALSE); return SSL_SNI_SEND_ALERT; } // SSL_SetStapledOCSPResponses makes a deep copy of response SECStatus st = SSL_SetStapledOCSPResponses(aFd, response, certKEA); - PORT_FreeArena(arena, PR_FALSE); if (st != SECSuccess) { PrintPRError("SSL_SetStapledOCSPResponses failed"); return SSL_SNI_SEND_ALERT; diff --git a/security/manager/ssl/tests/unit/tlsserver/lib/OCSPCommon.cpp b/security/manager/ssl/tests/unit/tlsserver/lib/OCSPCommon.cpp index 50931658d4..5ede59b5c3 100644 --- a/security/manager/ssl/tests/unit/tlsserver/lib/OCSPCommon.cpp +++ b/security/manager/ssl/tests/unit/tlsserver/lib/OCSPCommon.cpp @@ -27,23 +27,29 @@ using namespace mozilla::pkix::test; using namespace mozilla::test; static TestKeyPair* -CreateTestKeyPairFromCert(CERTCertificate& cert) +CreateTestKeyPairFromCert(const UniqueCERTCertificate& cert) { - ScopedSECKEYPrivateKey privateKey(PK11_FindKeyByAnyCert(&cert, nullptr)); + UniqueSECKEYPrivateKey privateKey(PK11_FindKeyByAnyCert(cert.get(), nullptr)); if (!privateKey) { return nullptr; } - ScopedSECKEYPublicKey publicKey(CERT_ExtractPublicKey(&cert)); + ScopedSECKEYPublicKey publicKey(CERT_ExtractPublicKey(cert.get())); if (!publicKey) { return nullptr; } - return CreateTestKeyPair(RSA_PKCS1(), *publicKey, privateKey.forget()); + return CreateTestKeyPair(RSA_PKCS1(), *publicKey, privateKey.release()); } -SECItemArray * -GetOCSPResponseForType(OCSPResponseType aORT, CERTCertificate *aCert, - PLArenaPool *aArena, const char *aAdditionalCertName) +SECItemArray* +GetOCSPResponseForType(OCSPResponseType aORT, const UniqueCERTCertificate& aCert, + const UniquePLArenaPool& aArena, + const char* aAdditionalCertName) { + MOZ_ASSERT(aArena); + MOZ_ASSERT(aCert); + // Note: |aAdditionalCertName| may or may not need to be non-null depending + // on the |aORT| value given. + if (aORT == ORTNone) { if (gDebugLevel >= DEBUG_WARNINGS) { fprintf(stderr, "GetOCSPResponseForType called with type ORTNone, " @@ -53,7 +59,7 @@ GetOCSPResponseForType(OCSPResponseType aORT, CERTCertificate *aCert, } if (aORT == ORTEmpty) { - SECItemArray* arr = SECITEM_AllocArray(aArena, nullptr, 1); + SECItemArray* arr = SECITEM_AllocArray(aArena.get(), nullptr, 1); arr->items[0].data = nullptr; arr->items[0].len = 0; return arr; @@ -62,18 +68,18 @@ GetOCSPResponseForType(OCSPResponseType aORT, CERTCertificate *aCert, time_t now = time(nullptr); time_t oldNow = now - (8 * Time::ONE_DAY_IN_SECONDS); - mozilla::ScopedCERTCertificate cert(CERT_DupCertificate(aCert)); + mozilla::UniqueCERTCertificate cert(CERT_DupCertificate(aCert.get())); if (aORT == ORTGoodOtherCert) { - cert = PK11_FindCertFromNickname(aAdditionalCertName, nullptr); + cert.reset(PK11_FindCertFromNickname(aAdditionalCertName, nullptr)); if (!cert) { PrintPRError("PK11_FindCertFromNickname failed"); return nullptr; } } // XXX CERT_FindCertIssuer uses the old, deprecated path-building logic - mozilla::ScopedCERTCertificate - issuerCert(CERT_FindCertIssuer(aCert, PR_Now(), certUsageSSLCA)); + mozilla::UniqueCERTCertificate + issuerCert(CERT_FindCertIssuer(aCert.get(), PR_Now(), certUsageSSLCA)); if (!issuerCert) { PrintPRError("CERT_FindCertIssuer failed"); return nullptr; @@ -95,11 +101,11 @@ GetOCSPResponseForType(OCSPResponseType aORT, CERTCertificate *aCert, CertID certID(issuer, issuerPublicKey, serialNumber); OCSPResponseContext context(certID, now); - mozilla::ScopedCERTCertificate signerCert; + mozilla::UniqueCERTCertificate signerCert; if (aORT == ORTGoodOtherCA || aORT == ORTDelegatedIncluded || aORT == ORTDelegatedIncludedLast || aORT == ORTDelegatedMissing || aORT == ORTDelegatedMissingMultiple) { - signerCert = PK11_FindCertFromNickname(aAdditionalCertName, nullptr); + signerCert.reset(PK11_FindCertFromNickname(aAdditionalCertName, nullptr)); if (!signerCert) { PrintPRError("PK11_FindCertFromNickname failed"); return nullptr; @@ -186,9 +192,9 @@ GetOCSPResponseForType(OCSPResponseType aORT, CERTCertificate *aCert, } if (!signerCert) { - signerCert = CERT_DupCertificate(issuerCert.get()); + signerCert.reset(CERT_DupCertificate(issuerCert.get())); } - context.signerKeyPair.reset(CreateTestKeyPairFromCert(*signerCert)); + context.signerKeyPair.reset(CreateTestKeyPairFromCert(signerCert)); if (!context.signerKeyPair) { PrintPRError("PK11_FindKeyByAnyCert failed"); return nullptr; @@ -206,5 +212,5 @@ GetOCSPResponseForType(OCSPResponseType aORT, CERTCertificate *aCert, static_cast(response.length()) }; SECItemArray arr = { &item, 1 }; - return SECITEM_DupArray(aArena, &arr); + return SECITEM_DupArray(aArena.get(), &arr); } diff --git a/security/manager/ssl/tests/unit/tlsserver/lib/OCSPCommon.h b/security/manager/ssl/tests/unit/tlsserver/lib/OCSPCommon.h index 8121a3ec26..5a88d6e525 100644 --- a/security/manager/ssl/tests/unit/tlsserver/lib/OCSPCommon.h +++ b/security/manager/ssl/tests/unit/tlsserver/lib/OCSPCommon.h @@ -8,6 +8,7 @@ #ifndef OCSPCommon_h #define OCSPCommon_h +#include "ScopedNSSTypes.h" #include "certt.h" #include "seccomon.h" @@ -51,8 +52,10 @@ struct OCSPHost const char *mServerCertName; }; -SECItemArray * -GetOCSPResponseForType(OCSPResponseType aORT, CERTCertificate *aCert, - PLArenaPool *aArena, const char *aAdditionalCertName); +SECItemArray* +GetOCSPResponseForType(OCSPResponseType aORT, + const mozilla::UniqueCERTCertificate& aCert, + const mozilla::UniquePLArenaPool& aArena, + const char* aAdditionalCertName); #endif // OCSPCommon_h diff --git a/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.cpp b/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.cpp index 3bc28b44cf..5816eed38e 100644 --- a/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.cpp +++ b/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.cpp @@ -10,6 +10,8 @@ #include "ScopedNSSTypes.h" #include "base64.h" +#include "mozilla/Move.h" +#include "mozilla/Snprintf.h" #include "nspr.h" #include "nss.h" #include "plarenas.h" @@ -69,16 +71,16 @@ SECStatus ReadFileToBuffer(const char* basePath, const char* filename, char (&buf)[N]) { static_assert(N > 0, "input buffer too small for ReadFileToBuffer"); - if (PR_snprintf(buf, N - 1, "%s/%s", basePath, filename) == 0) { - PrintPRError("PR_snprintf failed"); + if (snprintf(buf, N - 1, "%s/%s", basePath, filename) == 0) { + PrintPRError("snprintf failed"); return SECFailure; } - ScopedPRFileDesc fd(PR_OpenFile(buf, PR_RDONLY, 0)); + UniquePRFileDesc fd(PR_OpenFile(buf, PR_RDONLY, 0)); if (!fd) { PrintPRError("PR_Open failed"); return SECFailure; } - int32_t fileSize = PR_Available(fd); + int32_t fileSize = PR_Available(fd.get()); if (fileSize < 0) { PrintPRError("PR_Available failed"); return SECFailure; @@ -87,7 +89,7 @@ ReadFileToBuffer(const char* basePath, const char* filename, char (&buf)[N]) PR_fprintf(PR_STDERR, "file too large - not reading\n"); return SECFailure; } - int32_t bytesRead = PR_Read(fd, buf, fileSize); + int32_t bytesRead = PR_Read(fd.get(), buf, fileSize); if (bytesRead != fileSize) { PrintPRError("PR_Read failed"); return SECFailure; @@ -127,7 +129,8 @@ AddKeyFromFile(const char* basePath, const char* filename) } unsigned int binLength; - ScopedPORTString bin((char*)ATOB_AsciiToData(base64, &binLength)); + UniquePORTString bin( + reinterpret_cast(ATOB_AsciiToData(base64, &binLength))); if (!bin || binLength == 0) { PrintPRError("ATOB_AsciiToData failed"); return SECFailure; @@ -137,7 +140,7 @@ AddKeyFromFile(const char* basePath, const char* filename) PrintPRError("SECITEM_AllocItem failed"); return SECFailure; } - PORT_Memcpy(secitem->data, bin, binLength); + PORT_Memcpy(secitem->data, bin.get(), binLength); ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); if (!slot) { PrintPRError("PK11_GetInternalKeySlot failed"); @@ -187,7 +190,7 @@ AddCertificateFromFile(const char* basePath, const char* filename) PrintPRError("CERT_DecodeCertPackage failed"); return rv; } - ScopedCERTCertificate cert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), + UniqueCERTCertificate cert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &certDER, nullptr, false, true)); PORT_Free(certDER.data); @@ -202,7 +205,8 @@ AddCertificateFromFile(const char* basePath, const char* filename) } // The nickname is the filename without '.pem'. std::string nickname(filename, strlen(filename) - 4); - rv = PK11_ImportCert(slot, cert, CK_INVALID_HANDLE, nickname.c_str(), false); + rv = PK11_ImportCert(slot.get(), cert.get(), CK_INVALID_HANDLE, + nickname.c_str(), false); if (rv != SECSuccess) { PrintPRError("PK11_ImportCert failed"); return rv; @@ -219,7 +223,7 @@ LoadCertificatesAndKeys(const char* basePath) basePath = basePath + 4; } - ScopedPRDir fdDir(PR_OpenDir(basePath)); + UniquePRDir fdDir(PR_OpenDir(basePath)); if (!fdDir) { PrintPRError("PR_OpenDir failed"); return SECFailure; @@ -235,8 +239,8 @@ LoadCertificatesAndKeys(const char* basePath) // AddKeyFromFile.) std::vector certificates; std::vector keys; - for (PRDirEntry* dirEntry = PR_ReadDir(fdDir, PR_SKIP_BOTH); dirEntry; - dirEntry = PR_ReadDir(fdDir, PR_SKIP_BOTH)) { + for (PRDirEntry* dirEntry = PR_ReadDir(fdDir.get(), PR_SKIP_BOTH); dirEntry; + dirEntry = PR_ReadDir(fdDir.get(), PR_SKIP_BOTH)) { size_t nameLength = strlen(dirEntry->name); if (nameLength > 4) { if (strncmp(dirEntry->name + nameLength - 4, ".pem", 4) == 0) { @@ -351,10 +355,10 @@ ReadRequest(Connection *aConn) } void -HandleConnection(PRFileDesc *aSocket, PRFileDesc *aModelSocket) +HandleConnection(PRFileDesc* aSocket, const UniquePRFileDesc& aModelSocket) { Connection conn(aSocket); - nsresult rv = SetupTLS(&conn, aModelSocket); + nsresult rv = SetupTLS(&conn, aModelSocket.get()); if (NS_FAILED(rv)) { PR_SetError(PR_INVALID_STATE_ERROR, 0); PrintPRError("PR_Recv failed"); @@ -376,7 +380,7 @@ HandleConnection(PRFileDesc *aSocket, PRFileDesc *aModelSocket) int DoCallback() { - ScopedPRFileDesc socket(PR_NewTCPSocket()); + UniquePRFileDesc socket(PR_NewTCPSocket()); if (!socket) { PrintPRError("PR_NewTCPSocket failed"); return 1; @@ -384,16 +388,16 @@ DoCallback() PRNetAddr addr; PR_InitializeNetAddr(PR_IpAddrLoopback, gCallbackPort, &addr); - if (PR_Connect(socket, &addr, PR_INTERVAL_NO_TIMEOUT) != PR_SUCCESS) { + if (PR_Connect(socket.get(), &addr, PR_INTERVAL_NO_TIMEOUT) != PR_SUCCESS) { PrintPRError("PR_Connect failed"); return 1; } const char *request = "GET / HTTP/1.0\r\n\r\n"; - SendAll(socket, request, strlen(request)); + SendAll(socket.get(), request, strlen(request)); char buf[4096]; memset(buf, 0, sizeof(buf)); - int32_t bytesRead = PR_Recv(socket, buf, sizeof(buf) - 1, 0, + int32_t bytesRead = PR_Recv(socket.get(), buf, sizeof(buf) - 1, 0, PR_INTERVAL_NO_TIMEOUT); if (bytesRead < 0) { PrintPRError("PR_Recv failed 1"); @@ -408,11 +412,11 @@ DoCallback() } SECStatus -ConfigSecureServerWithNamedCert(PRFileDesc *fd, const char *certName, - /*optional*/ ScopedCERTCertificate *certOut, - /*optional*/ SSLKEAType *keaOut) +ConfigSecureServerWithNamedCert(PRFileDesc* fd, const char* certName, + /*optional*/ UniqueCERTCertificate* certOut, + /*optional*/ SSLKEAType* keaOut) { - ScopedCERTCertificate cert(PK11_FindCertFromNickname(certName, nullptr)); + UniqueCERTCertificate cert(PK11_FindCertFromNickname(certName, nullptr)); if (!cert) { PrintPRError("PK11_FindCertFromNickname failed"); return SECFailure; @@ -421,8 +425,8 @@ ConfigSecureServerWithNamedCert(PRFileDesc *fd, const char *certName, // directly by a trust anchor), we want to send it along in the handshake so // we don't encounter unknown issuer errors when that's not what we're // testing. - ScopedCERTCertificateList certList; - ScopedCERTCertificate issuerCert( + UniqueCERTCertificateList certList; + UniqueCERTCertificate issuerCert( CERT_FindCertByName(CERT_GetDefaultCertDB(), &cert->derIssuer)); // If we can't find the issuer cert, continue without it. if (issuerCert) { @@ -430,18 +434,18 @@ ConfigSecureServerWithNamedCert(PRFileDesc *fd, const char *certName, // utility function, so we must create it ourselves. This consists // of creating an arena, allocating space for the CERTCertificateList, // and then transferring ownership of the arena to that list. - ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); + UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); if (!arena) { PrintPRError("PORT_NewArena failed"); return SECFailure; } - certList = reinterpret_cast( - PORT_ArenaAlloc(arena, sizeof(CERTCertificateList))); + certList.reset(static_cast( + PORT_ArenaAlloc(arena.get(), sizeof(CERTCertificateList)))); if (!certList) { PrintPRError("PORT_ArenaAlloc failed"); return SECFailure; } - certList->arena = arena.forget(); + certList->arena = arena.release(); // We also have to manually copy the certificates we care about to the // list, because there aren't any utility functions for that either. certList->certs = reinterpret_cast( @@ -460,22 +464,23 @@ ConfigSecureServerWithNamedCert(PRFileDesc *fd, const char *certName, } ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); - ScopedSECKEYPrivateKey key(PK11_FindKeyByDERCert(slot, cert, nullptr)); + UniqueSECKEYPrivateKey key( + PK11_FindKeyByDERCert(slot.get(), cert.get(), nullptr)); if (!key) { PrintPRError("PK11_FindKeyByDERCert failed"); return SECFailure; } - SSLKEAType certKEA = NSS_FindCertKEAType(cert); + SSLKEAType certKEA = NSS_FindCertKEAType(cert.get()); - if (SSL_ConfigSecureServerWithCertChain(fd, cert, certList, key, certKEA) - != SECSuccess) { + if (SSL_ConfigSecureServerWithCertChain(fd, cert.get(), certList.get(), + key.get(), certKEA) != SECSuccess) { PrintPRError("SSL_ConfigSecureServer failed"); return SECFailure; } if (certOut) { - *certOut = cert.forget(); + *certOut = Move(cert); } if (keaOut) { @@ -522,7 +527,7 @@ StartServer(const char *nssCertDBDir, SSLSNISocketConfig sniSocketConfig, return 1; } - ScopedPRFileDesc serverSocket(PR_NewTCPSocket()); + UniquePRFileDesc serverSocket(PR_NewTCPSocket()); if (!serverSocket) { PrintPRError("PR_NewTCPSocket failed"); return 1; @@ -531,34 +536,34 @@ StartServer(const char *nssCertDBDir, SSLSNISocketConfig sniSocketConfig, PRSocketOptionData socketOption; socketOption.option = PR_SockOpt_Reuseaddr; socketOption.value.reuse_addr = true; - PR_SetSocketOption(serverSocket, &socketOption); + PR_SetSocketOption(serverSocket.get(), &socketOption); PRNetAddr serverAddr; PR_InitializeNetAddr(PR_IpAddrLoopback, LISTEN_PORT, &serverAddr); - if (PR_Bind(serverSocket, &serverAddr) != PR_SUCCESS) { + if (PR_Bind(serverSocket.get(), &serverAddr) != PR_SUCCESS) { PrintPRError("PR_Bind failed"); return 1; } - if (PR_Listen(serverSocket, 1) != PR_SUCCESS) { + if (PR_Listen(serverSocket.get(), 1) != PR_SUCCESS) { PrintPRError("PR_Listen failed"); return 1; } - ScopedPRFileDesc rawModelSocket(PR_NewTCPSocket()); + UniquePRFileDesc rawModelSocket(PR_NewTCPSocket()); if (!rawModelSocket) { PrintPRError("PR_NewTCPSocket failed for rawModelSocket"); return 1; } - ScopedPRFileDesc modelSocket(SSL_ImportFD(nullptr, rawModelSocket.forget())); + UniquePRFileDesc modelSocket(SSL_ImportFD(nullptr, rawModelSocket.release())); if (!modelSocket) { PrintPRError("SSL_ImportFD of rawModelSocket failed"); return 1; } - if (SECSuccess != SSL_SNISocketConfigHook(modelSocket, sniSocketConfig, - sniSocketConfigArg)) { + if (SSL_SNISocketConfigHook(modelSocket.get(), sniSocketConfig, + sniSocketConfigArg) != SECSuccess) { PrintPRError("SSL_SNISocketConfigHook failed"); return 1; } @@ -566,9 +571,8 @@ StartServer(const char *nssCertDBDir, SSLSNISocketConfig sniSocketConfig, // We have to configure the server with a certificate, but it's not one // we're actually going to end up using. In the SNI callback, we pick // the right certificate for the connection. - if (SECSuccess != ConfigSecureServerWithNamedCert(modelSocket, - DEFAULT_CERT_NICKNAME, - nullptr, nullptr)) { + if (ConfigSecureServerWithNamedCert(modelSocket.get(), DEFAULT_CERT_NICKNAME, + nullptr, nullptr) != SECSuccess) { return 1; } @@ -580,7 +584,7 @@ StartServer(const char *nssCertDBDir, SSLSNISocketConfig sniSocketConfig, while (true) { PRNetAddr clientAddr; - PRFileDesc *clientSocket = PR_Accept(serverSocket, &clientAddr, + PRFileDesc* clientSocket = PR_Accept(serverSocket.get(), &clientAddr, PR_INTERVAL_NO_TIMEOUT); HandleConnection(clientSocket, modelSocket); } diff --git a/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.h b/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.h index 8dab6fd938..eff86ca38b 100644 --- a/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.h +++ b/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.h @@ -2,8 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef mozilla_test__TLSServer_h -#define mozilla_test__TLSServer_h +#ifndef TLSServer_h +#define TLSServer_h // This is a standalone server for testing SSL features of Gecko. // The client is expected to connect and initiate an SSL handshake (with SNI @@ -21,8 +21,7 @@ namespace mozilla { -MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRDir, PRDir, PR_CloseDir); -MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPORTString, char, PORT_Free); +MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePRDir, PRDir, PR_CloseDir); } // namespace mozilla @@ -45,9 +44,9 @@ extern const char DEFAULT_CERT_NICKNAME[]; // Pass DEFAULT_CERT_NICKNAME as certName unless you need a specific // certificate. SECStatus -ConfigSecureServerWithNamedCert(PRFileDesc *fd, const char *certName, - /*optional*/ ScopedCERTCertificate *cert, - /*optional*/ SSLKEAType *kea); +ConfigSecureServerWithNamedCert(PRFileDesc* fd, const char* certName, + /*optional*/ UniqueCERTCertificate* cert, + /*optional*/ SSLKEAType* kea); SECStatus InitializeNSS(const char* nssCertDBDir); @@ -85,4 +84,4 @@ GetHostForSNI(const SECItem *aSrvNameArr, uint32_t aSrvNameArrSize, } } // namespace mozilla::test -#endif // mozilla_test__TLSServer_h +#endif // TLSServer_h diff --git a/security/manager/ssl/tests/unit/xpcshell.ini b/security/manager/ssl/tests/unit/xpcshell.ini index 2f2d97df3d..77242d5fca 100644 --- a/security/manager/ssl/tests/unit/xpcshell.ini +++ b/security/manager/ssl/tests/unit/xpcshell.ini @@ -4,6 +4,7 @@ tail = tags = psm support-files = ocsp_common/** + test_baseline_requirements/** test_cert_eku/** test_cert_embedded_null/** test_cert_keyUsage/** @@ -28,6 +29,7 @@ support-files = tlsserver/** [test_add_preexisting_cert.js] +[test_baseline_requirements_subject_common_name.js] [test_cert_blocklist.js] skip-if = buildapp == "b2g" tags = addons psm diff --git a/security/manager/tools/genIntolerantFallbackList.js b/security/manager/tools/genIntolerantFallbackList.js deleted file mode 100644 index 20cabfea1e..0000000000 --- a/security/manager/tools/genIntolerantFallbackList.js +++ /dev/null @@ -1,264 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// How to run this file: -// 1. [obtain firefox source code] -// 2. [build/obtain firefox binaries] -// 3. run `[path to]/run-mozilla.sh [path to]/xpcshell \ -// [path to]/genIntolerantFallbackList.js \ -// [absolute path to]/IntolerantFallbackList.inc - -const { 'classes': Cc, 'interfaces': Ci, 'utils': Cu, 'results': Cr } = Components; - -const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {}); -const { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {}); -const { Services } = Cu.import("resource://gre/modules/Services.jsm", {}); -const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {}); - -const SEC_ERROR_UNKNOWN_ISSUER = 0x805a1ff3; - -const OUTPUT = "IntolerantFallbackList.inc"; -const ERROR_OUTPUT = "IntolerantFallbackList.errors"; -const MAX_CONCURRENT_REQUESTS = 5; -const MAX_RETRIES = 3; -const REQUEST_TIMEOUT = 30 * 1000; -const TEST_DOMAINS = { - "fallback.test": true, -}; - -const FILE_HEADER = `/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/////////////////////////////////////////////////////////////////////////////// -// This is an automatically generated file. If you're not -// nsNSSIOLayer.cpp, you shouldn't be #including it. -/////////////////////////////////////////////////////////////////////////////// - -static const char* const kIntolerantFallbackList[] = -{ -`; - -const FILE_FOOTER = "};\n"; - -var errorTable = {}; -var errorWithoutFallbacks = {}; - -try { - - if (arguments.length != 1) { - throw "Usage: genIntolerantFallbackList.js " + - " "; - } - - // initialize the error message table - for (let name in Cr) { - errorTable[Cr[name]] = name; - } - - // disable the current fallback list so it won't interfere with requests we make - Services.prefs.setBoolPref("security.tls.insecure_fallback_hosts.use_static_list", false); - - // get the current preload list - let gIntolerantFallbackList = readCurrentList(arguments[0]); - - // get the fallback status of each host without whitelist - let fallbackStatuses = []; - getFallbackStatuses(gIntolerantFallbackList, fallbackStatuses); - - // reenable the current fallback list - Services.prefs.clearUserPref("security.tls.insecure_fallback_hosts.use_static_list"); - - // get the fallback status of each host with whitelist - for (let entry of fallbackStatuses) { - entry.errorWithoutFallback = entry.error; - delete entry.error; - entry.retries = MAX_RETRIES; - } - gIntolerantFallbackList = fallbackStatuses; - fallbackStatuses = []; - getFallbackStatuses(gIntolerantFallbackList, fallbackStatuses); - - // sort the hosts alphabetically - fallbackStatuses.sort(function(a, b) { - return (a.name > b.name ? 1 : (a.name < b.name ? -1 : 0)); - }); - - // write the results to a file (this is where we filter out hosts that we - // either could connect to without list, or couldn't connect to with list) - output(fallbackStatuses); - -} catch (e) { - dump([e, e.stack].join("\n")); - throw e; -} - -function readCurrentList(filename) { - let currentHosts = []; - let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); - file.initWithPath(filename); - let fis = Cc["@mozilla.org/network/file-input-stream;1"] - .createInstance(Ci.nsILineInputStream); - fis.init(file, -1, -1, Ci.nsIFileInputStream.CLOSE_ON_EOF); - let line = {}; - let entryRegex = / "([^"]*)",(?: \/\/ (.*))?/; - while (fis.readLine(line)) { - let match = entryRegex.exec(line.value); - if (match) { - currentHosts.push({ - name: match[1], - comment: match[2], - retries: MAX_RETRIES} - ); - } - } - return currentHosts; -} - -function getFallbackStatuses(inHosts, outStatuses) { - const expectedOutputLength = inHosts.length; - let tmpOutput = []; - function handleOneHost() { - let host = inHosts.shift(); - dump("spinning off request to '" + host.name + "' (remaining retries: " + - host.retries + ")\n"); - getFallbackStatus(host, tmpOutput); - } - - for (let i = 0; i < MAX_CONCURRENT_REQUESTS && inHosts.length > 0; i++) { - handleOneHost(); - } - while (outStatuses.length != expectedOutputLength) { - waitForAResponse(tmpOutput); - let response = tmpOutput.shift(); - dump("request to '" + response.name + "' finished: " + - errorToString(response.error) + "\n"); - outStatuses.push(response); - - if (inHosts.length > 0) { - handleOneHost(); - } - } -} - -function getFallbackStatus(host, resultList) { - if (TEST_DOMAINS[host.name]) { - host.error = Cr.NS_OK; - host.retries--; - resultList.push(host); - return; - } - let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"] - .createInstance(Ci.nsIXMLHttpRequest); - let inResultList = false; - let uri = "https://" + host.name + "/"; - req.open("GET", uri, true); - req.timeout = REQUEST_TIMEOUT; - req.channel.notificationCallbacks = new RedirectAndAuthStopper(); - req.onreadystatechange = function(event) { - if (!inResultList && req.readyState == 4) { - inResultList = true; - // If the url is changed, a redirect happened that means - // a successfull TLS handshake. Treat it as success rather than - // using a status from the redirect target. - host.error = uri == req.responseURL ? req.channel.status : Cr.NS_OK; - host.retries--; - resultList.push(host); - } - }; - - try { - req.send(); - } - catch (e) { - dump("ERROR: exception making request to " + host.name + ": " + e + "\n"); - } -} - -// RedirectAndAuthStopper prevents redirects and HTTP authentication -function RedirectAndAuthStopper() {}; - -RedirectAndAuthStopper.prototype = { - // nsIChannelEventSink - asyncOnChannelRedirect: function(oldChannel, newChannel, flags, callback) { - throw Cr.NS_ERROR_ENTITY_CHANGED; - }, - - // nsIAuthPrompt2 - promptAuth: function(channel, level, authInfo) { - return false; - }, - - asyncPromptAuth: function(channel, callback, context, level, authInfo) { - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - }, - - getInterface: function(iid) { - return this.QueryInterface(iid); - }, - - QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannelEventSink, - Ci.nsIAuthPrompt2]) -}; - -// Since all events are processed on the main thread, and since event -// handlers are not preemptible, there shouldn't be any concurrency issues. -function waitForAResponse(outputList) { - // From - let threadManager = Cc["@mozilla.org/thread-manager;1"] - .getService(Ci.nsIThreadManager); - let mainThread = threadManager.currentThread; - while (outputList.length == 0) { - mainThread.processNextEvent(true); - } -} - -function errorToString(error) { - return error != undefined ? errorTable[error] || ("0x" + error.toString(16)) : error; -} - -function formatComment(comment) { - return comment ? " // " + comment : ""; -} - -function success(error) { - return error == Cr.NS_OK || error == SEC_ERROR_UNKNOWN_ISSUER; -} - -function output(sortedStatuses) { - let file = FileUtils.getFile("CurWorkD", [OUTPUT]); - let errorFile = FileUtils.getFile("CurWorkD", [ERROR_OUTPUT]); - let fos = FileUtils.openSafeFileOutputStream(file); - let eos = FileUtils.openSafeFileOutputStream(errorFile); - writeTo(FILE_HEADER, fos); - for (let status of sortedStatuses) { - - if (!TEST_DOMAINS[status.name] && - success(status.errorWithoutFallback)) { - dump("INFO: " + status.name + " does NOT require fallback\n"); - writeTo(status.name + ": worked (" + - errorToString(status.errorWithoutFallback) + " / " + - errorToString(status.error) + ")" + - formatComment(status.comment) + "\n", eos); - } else { - if (!success(status.error)) { - dump("INFO: " + status.name + " is dead?\n"); - writeTo(status.name + ": failed (" + - errorToString(status.errorWithoutFallback) + " / " + - errorToString(status.error) + ")" + - formatComment(status.comment) + "\n", eos); - } - dump("INFO: " + status.name + " ON the fallback list\n"); - writeTo(" \"" + status.name + "\"," + - formatComment(status.comment) + "\n", fos); - } - } - writeTo(FILE_FOOTER, fos); - FileUtils.closeSafeFileOutputStream(fos); - FileUtils.closeSafeFileOutputStream(eos); -} - -function writeTo(string, fos) { - fos.write(string, string.length); -} diff --git a/security/pkix/include/pkix/pkix.h b/security/pkix/include/pkix/pkix.h index ee6746b0fc..da4f636157 100644 --- a/security/pkix/include/pkix/pkix.h +++ b/security/pkix/include/pkix/pkix.h @@ -116,7 +116,8 @@ Result BuildCertChain(TrustDomain& trustDomain, Input cert, // - IP addresses are out of scope of RFC 6125, but this method accepts them for // backward compatibility (see SearchNames in pkixnames.cpp) // - A wildcard in a DNS-ID may only appear as the entirety of the first label. -Result CheckCertHostname(Input cert, Input hostname); +Result CheckCertHostname(Input cert, Input hostname, + NameMatchingPolicy& nameMatchingPolicy); // Construct an RFC-6960-encoded OCSP request, ready for submission to a // responder, for the provided CertID. The request has no extensions. diff --git a/security/pkix/include/pkix/pkixtypes.h b/security/pkix/include/pkix/pkixtypes.h index 1ea9535a15..3c92b3a8a6 100644 --- a/security/pkix/include/pkix/pkixtypes.h +++ b/security/pkix/include/pkix/pkixtypes.h @@ -349,6 +349,30 @@ protected: void operator=(const TrustDomain&) = delete; }; +enum class FallBackToSearchWithinSubject { No = 0, Yes = 1 }; + +// Applications control the behavior of matching presented name information from +// a certificate against a reference hostname by implementing the +// NameMatchingPolicy interface. Used in concert with CheckCertHostname. +class NameMatchingPolicy +{ +public: + virtual ~NameMatchingPolicy() { } + + // Given that the certificate in question has a notBefore field with the given + // value, should name matching fall back to searching within the subject + // common name field? + virtual Result FallBackToCommonName( + Time notBefore, + /*out*/ FallBackToSearchWithinSubject& fallBackToCommonName) = 0; + +protected: + NameMatchingPolicy() { } + + NameMatchingPolicy(const NameMatchingPolicy&) = delete; + void operator=(const NameMatchingPolicy&) = delete; +}; + } } // namespace mozilla::pkix #endif // mozilla_pkix_pkixtypes_h diff --git a/security/pkix/lib/pkixnames.cpp b/security/pkix/lib/pkixnames.cpp index f073f9bb18..41b30e65ba 100644 --- a/security/pkix/lib/pkixnames.cpp +++ b/security/pkix/lib/pkixnames.cpp @@ -114,8 +114,6 @@ ReadGeneralName(Reader& reader, return Success; } -enum class FallBackToSearchWithinSubject { No = 0, Yes = 1 }; - enum class MatchResult { NoNamesOfGivenType = 0, @@ -219,7 +217,8 @@ MatchPresentedDNSIDWithReferenceDNSID(Input presentedDNSID, // assumed to be a string representation of an IPv4 address, an IPv6 addresss, // or a normalized ASCII (possibly punycode) DNS name. Result -CheckCertHostname(Input endEntityCertDER, Input hostname) +CheckCertHostname(Input endEntityCertDER, Input hostname, + NameMatchingPolicy& nameMatchingPolicy) { BackCert cert(endEntityCertDER, EndEntityOrCA::MustBeEndEntity, nullptr); Result rv = cert.Init(); @@ -227,10 +226,22 @@ CheckCertHostname(Input endEntityCertDER, Input hostname) return rv; } + Time notBefore(Time::uninitialized); + rv = ParseValidity(cert.GetValidity(), ¬Before); + if (rv != Success) { + return rv; + } + FallBackToSearchWithinSubject fallBackToSearchWithinSubject; + rv = nameMatchingPolicy.FallBackToCommonName(notBefore, + fallBackToSearchWithinSubject); + if (rv != Success) { + return rv; + } + const Input* subjectAltName(cert.GetSubjectAltName()); Input subject(cert.GetSubject()); - // For backward compatibility with legacy certificates, we fall back to + // For backward compatibility with legacy certificates, we may fall back to // searching for a name match in the subject common name for DNS names and // IPv4 addresses. We don't do so for IPv6 addresses because we do not think // there are many certificates that would need such fallback, and because @@ -244,13 +255,13 @@ CheckCertHostname(Input endEntityCertDER, Input hostname) uint8_t ipv4[4]; if (IsValidReferenceDNSID(hostname)) { rv = SearchNames(subjectAltName, subject, GeneralNameType::dNSName, - hostname, FallBackToSearchWithinSubject::Yes, match); + hostname, fallBackToSearchWithinSubject, match); } else if (ParseIPv6Address(hostname, ipv6)) { rv = SearchNames(subjectAltName, subject, GeneralNameType::iPAddress, Input(ipv6), FallBackToSearchWithinSubject::No, match); } else if (ParseIPv4Address(hostname, ipv4)) { rv = SearchNames(subjectAltName, subject, GeneralNameType::iPAddress, - Input(ipv4), FallBackToSearchWithinSubject::Yes, match); + Input(ipv4), fallBackToSearchWithinSubject, match); } else { return Result::ERROR_BAD_CERT_DOMAIN; } diff --git a/security/pkix/test/gtest/pkixgtest.h b/security/pkix/test/gtest/pkixgtest.h index a6fb9dfa33..68a0a61c2c 100644 --- a/security/pkix/test/gtest/pkixgtest.h +++ b/security/pkix/test/gtest/pkixgtest.h @@ -216,6 +216,17 @@ class DefaultCryptoTrustDomain : public EverythingFailsByDefaultTrustDomain } }; +class DefaultNameMatchingPolicy : public NameMatchingPolicy +{ +public: + virtual Result FallBackToCommonName( + Time, /*out*/ FallBackToSearchWithinSubject& fallBackToCommonName) override + { + fallBackToCommonName = FallBackToSearchWithinSubject::Yes; + return Success; + } +}; + } } } // namespace mozilla::pkix::test #endif // mozilla_pkix_pkixgtest_h diff --git a/security/pkix/test/gtest/pkixnames_tests.cpp b/security/pkix/test/gtest/pkixnames_tests.cpp index 14af9c68d6..7021de2996 100644 --- a/security/pkix/test/gtest/pkixnames_tests.cpp +++ b/security/pkix/test/gtest/pkixnames_tests.cpp @@ -902,6 +902,8 @@ class pkixnames_MatchPresentedDNSIDWithReferenceDNSID : public ::testing::Test , public ::testing::WithParamInterface { +public: + DefaultNameMatchingPolicy mNameMatchingPolicy; }; TEST_P(pkixnames_MatchPresentedDNSIDWithReferenceDNSID, @@ -937,6 +939,8 @@ class pkixnames_Turkish_I_Comparison : public ::testing::Test , public ::testing::WithParamInterface { +public: + DefaultNameMatchingPolicy mNameMatchingPolicy; }; TEST_P(pkixnames_Turkish_I_Comparison, MatchPresentedDNSIDWithReferenceDNSID) @@ -982,6 +986,8 @@ class pkixnames_IsValidReferenceDNSID : public ::testing::Test , public ::testing::WithParamInterface { +public: + DefaultNameMatchingPolicy mNameMatchingPolicy; }; TEST_P(pkixnames_IsValidReferenceDNSID, IsValidReferenceDNSID) @@ -1006,6 +1012,8 @@ class pkixnames_ParseIPv4Address : public ::testing::Test , public ::testing::WithParamInterface> { +public: + DefaultNameMatchingPolicy mNameMatchingPolicy; }; TEST_P(pkixnames_ParseIPv4Address, ParseIPv4Address) @@ -1032,6 +1040,8 @@ class pkixnames_ParseIPv6Address : public ::testing::Test , public ::testing::WithParamInterface> { +public: + DefaultNameMatchingPolicy mNameMatchingPolicy; }; TEST_P(pkixnames_ParseIPv6Address, ParseIPv6Address) @@ -1074,6 +1084,8 @@ class pkixnames_CheckCertHostname : public ::testing::Test , public ::testing::WithParamInterface { +public: + DefaultNameMatchingPolicy mNameMatchingPolicy; }; #define WITH_SAN(r, ps, psan, result) \ @@ -1566,7 +1578,8 @@ TEST_P(pkixnames_CheckCertHostname, CheckCertHostname) ASSERT_EQ(Success, hostnameInput.Init(param.hostname.data(), param.hostname.length())); - ASSERT_EQ(param.result, CheckCertHostname(certInput, hostnameInput)); + ASSERT_EQ(param.result, CheckCertHostname(certInput, hostnameInput, + mNameMatchingPolicy)); } INSTANTIATE_TEST_CASE_P(pkixnames_CheckCertHostname, @@ -1598,13 +1611,15 @@ TEST_F(pkixnames_CheckCertHostname, SANWithoutSequence) static const uint8_t a[] = { 'a' }; ASSERT_EQ(Result::ERROR_EXTENSION_VALUE_INVALID, - CheckCertHostname(certInput, Input(a))); + CheckCertHostname(certInput, Input(a), mNameMatchingPolicy)); } class pkixnames_CheckCertHostname_PresentedMatchesReference : public ::testing::Test , public ::testing::WithParamInterface { +public: + DefaultNameMatchingPolicy mNameMatchingPolicy; }; TEST_P(pkixnames_CheckCertHostname_PresentedMatchesReference, CN_NoSAN) @@ -1624,7 +1639,7 @@ TEST_P(pkixnames_CheckCertHostname_PresentedMatchesReference, CN_NoSAN) param.referenceDNSID.length())); ASSERT_EQ(param.expectedMatches ? Success : Result::ERROR_BAD_CERT_DOMAIN, - CheckCertHostname(certInput, hostnameInput)); + CheckCertHostname(certInput, hostnameInput, mNameMatchingPolicy)); } TEST_P(pkixnames_CheckCertHostname_PresentedMatchesReference, @@ -1648,7 +1663,8 @@ TEST_P(pkixnames_CheckCertHostname_PresentedMatchesReference, = param.expectedResult != Success ? param.expectedResult : param.expectedMatches ? Success : Result::ERROR_BAD_CERT_DOMAIN; - ASSERT_EQ(expectedResult, CheckCertHostname(certInput, hostnameInput)); + ASSERT_EQ(expectedResult, CheckCertHostname(certInput, hostnameInput, + mNameMatchingPolicy)); } INSTANTIATE_TEST_CASE_P(pkixnames_CheckCertHostname_DNSID_MATCH_PARAMS, @@ -1677,8 +1693,10 @@ TEST_P(pkixnames_Turkish_I_Comparison, CheckCertHostname_CN_NoSAN) ? Success : Result::ERROR_BAD_CERT_DOMAIN; - ASSERT_EQ(expectedResult, CheckCertHostname(certInput, UPPERCASE_I)); - ASSERT_EQ(expectedResult, CheckCertHostname(certInput, LOWERCASE_I)); + ASSERT_EQ(expectedResult, CheckCertHostname(certInput, UPPERCASE_I, + mNameMatchingPolicy)); + ASSERT_EQ(expectedResult, CheckCertHostname(certInput, LOWERCASE_I, + mNameMatchingPolicy)); } TEST_P(pkixnames_Turkish_I_Comparison, CheckCertHostname_SAN) @@ -1704,14 +1722,18 @@ TEST_P(pkixnames_Turkish_I_Comparison, CheckCertHostname_SAN) InputsAreEqual(UPPERCASE_I, input)) ? Success : Result::ERROR_BAD_CERT_DOMAIN; - ASSERT_EQ(expectedResult, CheckCertHostname(certInput, UPPERCASE_I)); - ASSERT_EQ(expectedResult, CheckCertHostname(certInput, LOWERCASE_I)); + ASSERT_EQ(expectedResult, CheckCertHostname(certInput, UPPERCASE_I, + mNameMatchingPolicy)); + ASSERT_EQ(expectedResult, CheckCertHostname(certInput, LOWERCASE_I, + mNameMatchingPolicy)); } class pkixnames_CheckCertHostname_IPV4_Addresses : public ::testing::Test , public ::testing::WithParamInterface> { +public: + DefaultNameMatchingPolicy mNameMatchingPolicy; }; TEST_P(pkixnames_CheckCertHostname_IPV4_Addresses, @@ -1733,7 +1755,7 @@ TEST_P(pkixnames_CheckCertHostname_IPV4_Addresses, param.input.length())); ASSERT_EQ(param.isValid ? Success : Result::ERROR_BAD_CERT_DOMAIN, - CheckCertHostname(certInput, hostnameInput)); + CheckCertHostname(certInput, hostnameInput, mNameMatchingPolicy)); } TEST_P(pkixnames_CheckCertHostname_IPV4_Addresses, @@ -1760,7 +1782,8 @@ TEST_P(pkixnames_CheckCertHostname_IPV4_Addresses, ? Success : Result::ERROR_BAD_CERT_DOMAIN; - ASSERT_EQ(expectedResult, CheckCertHostname(certInput, hostnameInput)); + ASSERT_EQ(expectedResult, CheckCertHostname(certInput, hostnameInput, + mNameMatchingPolicy)); } INSTANTIATE_TEST_CASE_P(pkixnames_CheckCertHostname_IPV4_ADDRESSES, @@ -2510,6 +2533,8 @@ class pkixnames_CheckNameConstraints : public ::testing::Test , public ::testing::WithParamInterface { +public: + DefaultNameMatchingPolicy mNameMatchingPolicy; }; TEST_P(pkixnames_CheckNameConstraints, diff --git a/testing/gtest/moz.build b/testing/gtest/moz.build index fb19ada245..89ac42068f 100644 --- a/testing/gtest/moz.build +++ b/testing/gtest/moz.build @@ -20,6 +20,7 @@ if CONFIG['ENABLE_TESTS']: 'gtest/include/gtest/gtest.h', 'gtest/include/gtest/gtest_pred_impl.h', 'gtest/include/gtest/gtest_prod.h', + 'mozilla/MozGTestBench.h', ] # GTest internal are exposed in gtest.h. See comment in gtest.h @@ -60,6 +61,7 @@ if CONFIG['ENABLE_TESTS']: 'gmock/src/gmock-all.cc', 'gtest/src/gtest-all.cc', 'mozilla/GTestRunner.cpp', + 'mozilla/MozGTestBench.cpp', ] Library('gtest') diff --git a/testing/gtest/mozilla/MozGTestBench.cpp b/testing/gtest/mozilla/MozGTestBench.cpp new file mode 100644 index 0000000000..15e5a7ae17 --- /dev/null +++ b/testing/gtest/mozilla/MozGTestBench.cpp @@ -0,0 +1,39 @@ +/* -*- 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 "MozGTestBench.h" +#include "mozilla/TimeStamp.h" +#include + +#define MOZ_GTEST_BENCH_FRAMEWORK "platform_microbench" + +using mozilla::TimeStamp; + +namespace mozilla { +void GTestBench(const char* aSuite, const char* aName, + const mozilla::function& aTest) +{ +#ifdef DEBUG + // Run the test to make sure that it doesn't fail but don't log + // any measurements since it's not an optimized build. + aTest(); +#else + mozilla::TimeStamp start = TimeStamp::Now(); + + aTest(); + + int msDuration = (TimeStamp::Now() - start).ToMicroseconds(); + + // Print the result for each test. Let perfherder aggregate for us + printf("PERFHERDER_DATA: {\"framework\": {\"name\": \"%s\"}, " + "\"suites\": [{\"name\": \"%s\", \"subtests\": " + "[{\"name\": \"%s\", \"value\": %i, \"lowerIsBetter\": true}]" + "}]}\n", + MOZ_GTEST_BENCH_FRAMEWORK, aSuite, aName, msDuration); +#endif +} + +} // mozilla + diff --git a/testing/gtest/mozilla/MozGTestBench.h b/testing/gtest/mozilla/MozGTestBench.h new file mode 100644 index 0000000000..1e9417b293 --- /dev/null +++ b/testing/gtest/mozilla/MozGTestBench.h @@ -0,0 +1,22 @@ +/* -*- 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 GTEST_MOZGTESTBENCH_H +#define GTEST_MOZGTESTBENCH_H + +#include "mozilla/Function.h" + +namespace mozilla { + +void GTestBench(const char* aSuite, const char* aName, const mozilla::function& aTest); + +} //mozilla + +#define MOZ_GTEST_BENCH(suite, test, lambdaOrFunc) \ +TEST(suite, test) { \ + mozilla::GTestBench(#suite, #test, lambdaOrFunc); \ +} + +#endif // GTEST_MOZGTESTBENCH_H diff --git a/testing/mozharness/configs/unittests/win_unittest.py b/testing/mozharness/configs/unittests/win_unittest.py index 00a5890a22..eebea5ca7c 100644 --- a/testing/mozharness/configs/unittests/win_unittest.py +++ b/testing/mozharness/configs/unittests/win_unittest.py @@ -182,8 +182,6 @@ config = { 'tests/reftest/tests/layout/reftests/reftest-sanity/reftest.list'], "reftest-no-accel": ["--setpref=gfx.direct2d.disabled=true", "--setpref=layers.acceleration.disabled=true", "tests/reftest/tests/layout/reftests/reftest.list"], - "reftest-omtc": ["--setpref=layers.offmainthreadcomposition.enabled=true", - "tests/reftest/tests/layout/reftests/reftest.list"], "crashtest-ipc": ['--setpref=browser.tabs.remote=true', '--setpref=browser.tabs.remote.autostart=true', '--setpref=layers.async-pan-zoom.enabled=true', diff --git a/testing/web-platform/meta/MANIFEST.json b/testing/web-platform/meta/MANIFEST.json index a6e0d7c1fe..9bf65000fb 100644 --- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -21528,6 +21528,10 @@ { "path": "webdriver/timeouts/page_load_timeouts_tests.py" }, + { + "path": "web-animations/animation-effect-timing/direction.html", + "url": "/web-animations/animation-effect-timing/direction.html" + }, { "path": "webdriver/user_input/clear_test.py" } @@ -22212,6 +22216,18 @@ "path": "service-workers/service-worker/xhr.https.html", "url": "/service-workers/service-worker/xhr.https.html" } + ], + "compat/webkit-text-fill-color-property-001d.html": [ + { + "path": "compat/webkit-text-fill-color-property-001d.html", + "references": [ + [ + "/compat/webkit-text-fill-color-property-001-ref.html", + "==" + ] + ], + "url": "/compat/webkit-text-fill-color-property-001d.html" + } ] } } diff --git a/testing/web-platform/meta/compat/webkit-text-fill-color-property-001a.html.ini b/testing/web-platform/meta/compat/webkit-text-fill-color-property-001a.html.ini new file mode 100644 index 0000000000..cf71de0596 --- /dev/null +++ b/testing/web-platform/meta/compat/webkit-text-fill-color-property-001a.html.ini @@ -0,0 +1,3 @@ +[webkit-text-fill-color-001a.html] + type: reftest + prefs: [layout.css.prefixes.webkit:true] diff --git a/testing/web-platform/meta/compat/webkit-text-fill-color-property-001b.html.ini b/testing/web-platform/meta/compat/webkit-text-fill-color-property-001b.html.ini new file mode 100644 index 0000000000..66c80e330d --- /dev/null +++ b/testing/web-platform/meta/compat/webkit-text-fill-color-property-001b.html.ini @@ -0,0 +1,3 @@ +[webkit-text-fill-color-001b.html] + type: reftest + prefs: [layout.css.prefixes.webkit:true] diff --git a/testing/web-platform/meta/compat/webkit-text-fill-color-property-001c.html.ini b/testing/web-platform/meta/compat/webkit-text-fill-color-property-001c.html.ini new file mode 100644 index 0000000000..c76d9e1a5e --- /dev/null +++ b/testing/web-platform/meta/compat/webkit-text-fill-color-property-001c.html.ini @@ -0,0 +1,3 @@ +[webkit-text-fill-color-001c.html] + type: reftest + prefs: [layout.css.prefixes.webkit:true] diff --git a/testing/web-platform/meta/compat/webkit-text-fill-color-property-001d.html.ini b/testing/web-platform/meta/compat/webkit-text-fill-color-property-001d.html.ini new file mode 100644 index 0000000000..e867dfaca1 --- /dev/null +++ b/testing/web-platform/meta/compat/webkit-text-fill-color-property-001d.html.ini @@ -0,0 +1,3 @@ +[webkit-text-fill-color-001d.html] + type: reftest + prefs: [layout.css.prefixes.webkit:true] diff --git a/testing/web-platform/tests/compat/webkit-text-fill-color-property-001-ref.html b/testing/web-platform/tests/compat/webkit-text-fill-color-property-001-ref.html new file mode 100644 index 0000000000..8d9b8cc22e --- /dev/null +++ b/testing/web-platform/tests/compat/webkit-text-fill-color-property-001-ref.html @@ -0,0 +1,6 @@ + + +webkit-text-fill-color: untouched + + +
These texts should be green
diff --git a/testing/web-platform/tests/compat/webkit-text-fill-color-property-001a.html b/testing/web-platform/tests/compat/webkit-text-fill-color-property-001a.html new file mode 100644 index 0000000000..363f5608ef --- /dev/null +++ b/testing/web-platform/tests/compat/webkit-text-fill-color-property-001a.html @@ -0,0 +1,9 @@ + + +webkit-text-fill-color: green + + + + + +
These texts should be green
diff --git a/testing/web-platform/tests/compat/webkit-text-fill-color-property-001b.html b/testing/web-platform/tests/compat/webkit-text-fill-color-property-001b.html new file mode 100644 index 0000000000..3b9a68cf77 --- /dev/null +++ b/testing/web-platform/tests/compat/webkit-text-fill-color-property-001b.html @@ -0,0 +1,9 @@ + + +webkit-text-fill-color: green + + + + + +
These texts should be green
diff --git a/testing/web-platform/tests/compat/webkit-text-fill-color-property-001c.html b/testing/web-platform/tests/compat/webkit-text-fill-color-property-001c.html new file mode 100644 index 0000000000..54d6007623 --- /dev/null +++ b/testing/web-platform/tests/compat/webkit-text-fill-color-property-001c.html @@ -0,0 +1,9 @@ + + +webkit-text-fill-color: green + + + + + +
These texts should be green
diff --git a/testing/web-platform/tests/compat/webkit-text-fill-color-property-001d.html b/testing/web-platform/tests/compat/webkit-text-fill-color-property-001d.html new file mode 100644 index 0000000000..a38653944a --- /dev/null +++ b/testing/web-platform/tests/compat/webkit-text-fill-color-property-001d.html @@ -0,0 +1,9 @@ + + +webkit-text-fill-color: green + + + + + +
These texts should be green
diff --git a/toolkit/components/filepicker/nsFileView.cpp b/toolkit/components/filepicker/nsFileView.cpp index 35b652577d..617d4347c8 100644 --- a/toolkit/components/filepicker/nsFileView.cpp +++ b/toolkit/components/filepicker/nsFileView.cpp @@ -15,7 +15,6 @@ #include "nsCRT.h" #include "nsPrintfCString.h" #include "nsIDateTimeFormat.h" -#include "nsDateTimeFormatCID.h" #include "nsQuickSort.h" #include "nsIAtom.h" #include "nsIAutoCompleteResult.h" @@ -299,7 +298,7 @@ nsFileView::~nsFileView() nsresult nsFileView::Init() { - mDateFormatter = do_CreateInstance(NS_DATETIMEFORMAT_CONTRACTID); + mDateFormatter = nsIDateTimeFormat::Create(); if (!mDateFormatter) return NS_ERROR_OUT_OF_MEMORY; diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 891cd69646..e9bb3245e7 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -1440,6 +1440,14 @@ "n_values": 48, "description": "HTTP: Protocol Version Used on Response from nsHttp.h" }, + "HTTP_09_INFO": { + "expires_in_version": "never", + "kind": "enumerated", + "n_values": 4, + "description": "HTTP 09 Response Breakdown: lowbit subresource, high bit nonstd port", + "bug_numbers": [1262572], + "alert_emails": ["necko@mozilla.com"] + }, "SPDY_PARALLEL_STREAMS": { "expires_in_version": "never", "kind": "exponential", @@ -2380,6 +2388,33 @@ "n_buckets": 50, "description": "How many actual predictions (preresolves, preconnects, ...) happen" }, + "PREDICTOR_TOTAL_PREFETCHES": { + "expires_in_version": "never", + "alert_emails": [], + "bug_numbers": [1016628], + "kind": "exponential", + "high": 1000000, + "n_buckets": 50, + "description": "How many actual prefetches happen" + }, + "PREDICTOR_TOTAL_PREFETCHES_USED": { + "expires_in_version": "never", + "alert_emails": [], + "bug_numbers": [1016628], + "kind": "exponential", + "high": 1000000, + "n_buckets": 50, + "description": "How many prefetches are actually used by a channel" + }, + "PREDICTOR_PREFETCH_TIME": { + "expires_in_version": "never", + "alert_emails": [], + "bug_numbers": [1016628], + "kind": "exponential", + "high": 3000, + "n_buckets": 10, + "description": "How long it takes from OnStartRequest to OnStopRequest for a prefetch" + }, "PREDICTOR_TOTAL_PRECONNECTS": { "expires_in_version": "never", "kind": "exponential", @@ -8087,6 +8122,14 @@ "high": 5000, "n_buckets": 10 }, + "CERT_EV_STATUS": { + "expires_in_version": "never", + "alert_emails": ["seceng@mozilla.org"], + "bug_numbers": [1254653], + "kind": "enumerated", + "n_values": 10, + "description": "EV status of a certificate, recorded on each TLS connection. 0=invalid, 1=DV, 2=EV" + }, "CERT_VALIDATION_SUCCESS_BY_CA": { "expires_in_version": "never", "kind": "enumerated", @@ -9822,6 +9865,13 @@ "kind": "count", "description": "Number of times the D3D11 compositor failed to get a texture sync handle." }, + "GFX_CONTENT_FAILED_TO_ACQUIRE_DEVICE": { + "alert_emails": ["gfx-telemetry-alerts@mozilla.com"], + "expires_in_version": "never", + "kind": "enumerated", + "n_values": "6", + "description": "Failed to create a gfx content device. 0=content d3d11, 1=image d3d11, 2=d2d1." + }, "GFX_CRASH": { "expires_in_version": "never", "kind": "enumerated", diff --git a/toolkit/library/moz.build b/toolkit/library/moz.build index e6024fd070..de62eb3245 100644 --- a/toolkit/library/moz.build +++ b/toolkit/library/moz.build @@ -143,7 +143,6 @@ if CONFIG['MOZ_WEBRTC']: if CONFIG['OS_TARGET'] == 'WINNT': OS_LIBS += [ 'secur32', - 'crypt32', 'iphlpapi', 'strmiids', 'dmoguids', @@ -187,6 +186,7 @@ if CONFIG['OS_ARCH'] == 'FreeBSD': if CONFIG['OS_ARCH'] == 'WINNT': OS_LIBS += [ + 'crypt32', 'shell32', 'ole32', 'version', diff --git a/uriloader/base/nsIWebProgressListener.idl b/uriloader/base/nsIWebProgressListener.idl index a055cfb9e8..714b931a3e 100644 --- a/uriloader/base/nsIWebProgressListener.idl +++ b/uriloader/base/nsIWebProgressListener.idl @@ -262,9 +262,13 @@ interface nsIWebProgressListener : nsISupports * * STATE_USES_WEAK_CRYPTO * The topmost document uses a weak cipher suite such as RC4. + * + * STATE_CERT_USER_OVERRIDDEN + * The user has added a security exception for the site. */ const unsigned long STATE_USES_SSL_3 = 0x01000000; const unsigned long STATE_USES_WEAK_CRYPTO = 0x02000000; + const unsigned long STATE_CERT_USER_OVERRIDDEN = 0x04000000; /** * Notification indicating the state has changed for one of the requests diff --git a/widget/PuppetWidget.cpp b/widget/PuppetWidget.cpp index 0fde6ab8c8..5f6d4f0af1 100644 --- a/widget/PuppetWidget.cpp +++ b/widget/PuppetWidget.cpp @@ -1043,7 +1043,11 @@ PuppetWidget::Paint() mTabChild->NotifyPainted(); } } else { - RefPtr ctx = new gfxContext(mDrawTarget); + RefPtr ctx = gfxContext::ForDrawTarget(mDrawTarget); + if (!ctx) { + gfxDevCrash(LogReason::InvalidContext) << "PuppetWidget context problem " << gfx::hexa(mDrawTarget); + return NS_ERROR_FAILURE; + } ctx->Rectangle(gfxRect(0,0,0,0)); ctx->Clip(); AutoLayerManagerSetup setupLayerManager(this, ctx, diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index aefabdc265..c201a5712d 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -3712,9 +3712,14 @@ NSEvent* gLastDragMouseDownEvent = nil; gfx::Factory::CreateDrawTargetForCairoCGContext(aContext, gfx::IntSize(backingSize.width, backingSize.height)); - MOZ_ASSERT(dt); // see implementation + if (!dt || !dt->IsValid()) { + // This used to be an assertion, so keep crashing in nightly+aurora + gfxDevCrash(mozilla::gfx::LogReason::InvalidContext) << "Cannot create target with CreateDrawTargetForCairoCGContext " << backingSize; + return; + } dt->AddUserData(&gfxContext::sDontUseAsSourceKey, dt, nullptr); - RefPtr targetContext = new gfxContext(dt); + RefPtr targetContext = gfxContext::ForDrawTarget(dt); + MOZ_ASSERT(targetContext); // already checked the draw target above // Set up the clip region. targetContext->NewPath(); diff --git a/widget/cocoa/nsCocoaUtils.mm b/widget/cocoa/nsCocoaUtils.mm index 5f24b165fe..2c2a97f9b1 100644 --- a/widget/cocoa/nsCocoaUtils.mm +++ b/widget/cocoa/nsCocoaUtils.mm @@ -495,7 +495,7 @@ nsresult nsCocoaUtils::CreateNSImageFromImageContainer(imgIContainer *aImage, ui return NS_ERROR_FAILURE; } - RefPtr context = new gfxContext(drawTarget); + RefPtr context = gfxContext::ForDrawTarget(drawTarget); if (!context) { NS_ERROR("Failed to create gfxContext"); return NS_ERROR_FAILURE; diff --git a/widget/cocoa/nsNativeThemeCocoa.mm b/widget/cocoa/nsNativeThemeCocoa.mm index 76c28bc687..fa211a3bec 100644 --- a/widget/cocoa/nsNativeThemeCocoa.mm +++ b/widget/cocoa/nsNativeThemeCocoa.mm @@ -956,7 +956,7 @@ static float VerticalAlignFactor(nsIFrame *aFrame) if (!aFrame) return 0.5f; // default: center - const nsStyleCoord& va = aFrame->StyleTextReset()->mVerticalAlign; + const nsStyleCoord& va = aFrame->StyleDisplay()->mVerticalAlign; uint8_t intval = (va.GetUnit() == eStyleUnit_Enumerated) ? va.GetIntValue() : NS_STYLE_VERTICAL_ALIGN_MIDDLE; diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index 0fb9c50b86..bce2814711 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -2231,7 +2231,7 @@ nsWindow::OnExposeEvent(cairo_t *cr) BufferMode layerBuffering = BufferMode::BUFFERED; RefPtr dt = GetDrawTarget(region, &layerBuffering); - if (!dt) { + if (!dt || !dt->IsValid()) { return FALSE; } RefPtr ctx; @@ -2255,12 +2255,16 @@ nsWindow::OnExposeEvent(cairo_t *cr) // channel is used on compositing window managers.) layerBuffering = BufferMode::BUFFER_NONE; RefPtr destDT = dt->CreateSimilarDrawTarget(boundsRect.Size(), SurfaceFormat::B8G8R8A8); - ctx = new gfxContext(destDT, boundsRect.TopLeft()); + if (!destDT || !destDT->IsValid()) { + return FALSE; + } + ctx = gfxContext::ForDrawTarget(destDT, boundsRect.TopLeft()); } else { gfxUtils::ClipToRegion(dt, region.ToUnknownRegion()); - ctx = new gfxContext(dt, offset); + ctx = gfxContext::ForDrawTarget(dt, offset); } + MOZ_ASSERT(ctx); // checked both dt and destDT valid draw target above #if 0 // NOTE: Paint flashing region would be wrong for cairo, since diff --git a/widget/nsBaseDragService.cpp b/widget/nsBaseDragService.cpp index 9a1e5aaf36..5b0ca307d9 100644 --- a/widget/nsBaseDragService.cpp +++ b/widget/nsBaseDragService.cpp @@ -690,10 +690,10 @@ nsBaseDragService::DrawDragForImage(nsIImageLoadingContent* aImageLoader, gfxPlatform::GetPlatform()-> CreateOffscreenContentDrawTarget(destSize, SurfaceFormat::B8G8R8A8); - if (!dt) + if (!dt || !dt->IsValid()) return NS_ERROR_FAILURE; - RefPtr ctx = new gfxContext(dt); + RefPtr ctx = gfxContext::ForDrawTarget(dt); if (!ctx) return NS_ERROR_FAILURE; diff --git a/widget/nsBaseFilePicker.cpp b/widget/nsBaseFilePicker.cpp index 70636ea598..5d11fbcfc7 100644 --- a/widget/nsBaseFilePicker.cpp +++ b/widget/nsBaseFilePicker.cpp @@ -46,8 +46,7 @@ LocalFileToDirectoryOrBlob(nsPIDOMWindow* aWindow, MOZ_ASSERT(isDir); #endif - RefPtr directory = - Directory::Create(aWindow, aFile, Directory::eDOMRootDirectory); + RefPtr directory = Directory::Create(aWindow, aFile); MOZ_ASSERT(directory); directory.forget(aResult); diff --git a/widget/nsFilePickerProxy.cpp b/widget/nsFilePickerProxy.cpp index bbd6ba6ab2..8966ea4fa7 100644 --- a/widget/nsFilePickerProxy.cpp +++ b/widget/nsFilePickerProxy.cpp @@ -175,8 +175,7 @@ nsFilePickerProxy::Recv__delete__(const MaybeInputData& aData, } RefPtr directory = - Directory::Create(mParent, file, - Directory::eDOMRootDirectory); + Directory::Create(mParent, file); MOZ_ASSERT(directory); OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement(); diff --git a/widget/uikit/nsWindow.mm b/widget/uikit/nsWindow.mm index 5412fee505..66ddd4fcba 100644 --- a/widget/uikit/nsWindow.mm +++ b/widget/uikit/nsWindow.mm @@ -359,8 +359,12 @@ private: gfx::Factory::CreateDrawTargetForCairoCGContext(aContext, gfx::IntSize(backingSize.width, backingSize.height)); + if (!dt || !dt->IsValid()) { + gfxDevCrash(mozilla::gfx::LogReason::InvalidContext) << "Window context problem 1 " << backingSize; + return; + } dt->AddUserData(&gfxContext::sDontUseAsSourceKey, dt, nullptr); - targetContext = new gfxContext(dt); + targetContext = gfxContext::ForDrawTarget(dt); } else if (gfxPlatform::GetPlatform()->SupportsAzureContentForType(gfx::BackendType::CAIRO)) { // This is dead code unless you mess with prefs, but keep it around for // debugging. @@ -370,11 +374,16 @@ private: gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(targetSurface, gfx::IntSize(backingSize.width, backingSize.height)); + if (!dt || !dt->IsValid()) { + gfxDevCrash(mozilla::gfx::LogReason::InvalidContext) << "Window context problem 2 " << backingSize; + return; + } dt->AddUserData(&gfxContext::sDontUseAsSourceKey, dt, nullptr); - targetContext = new gfxContext(dt); + targetContext = gfxContext::ForDrawTarget(dt); } else { - MOZ_ASSERT_UNREACHABLE("COREGRAPHICS is the only supported backed"); + MOZ_ASSERT_UNREACHABLE("COREGRAPHICS is the only supported backend"); } + MOZ_ASSERT(targetContext); // already checked for valid draw targets above // Set up the clip region. targetContext->NewPath(); diff --git a/widget/windows/nsWindowGfx.cpp b/widget/windows/nsWindowGfx.cpp index c91735eb07..43564028b5 100644 --- a/widget/windows/nsWindowGfx.cpp +++ b/widget/windows/nsWindowGfx.cpp @@ -381,7 +381,7 @@ bool nsWindow::OnPaint(HDC aDC, uint32_t aNestingLevel) gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(targetSurface, IntSize(paintRect.right - paintRect.left, paintRect.bottom - paintRect.top)); - if (!dt) { + if (!dt || !dt->IsValid()) { gfxWarning() << "nsWindow::OnPaint failed in CreateDrawTargetForSurface"; return false; } @@ -410,7 +410,8 @@ bool nsWindow::OnPaint(HDC aDC, uint32_t aNestingLevel) #endif } - RefPtr thebesContext = new gfxContext(dt); + RefPtr thebesContext = gfxContext::ForDrawTarget(dt); + MOZ_ASSERT(thebesContext); // already checked draw target above { AutoLayerManagerSetup