From e4c3e62beb762f62e08202397f13e6393e414d74 Mon Sep 17 00:00:00 2001 From: roytam1 Date: Tue, 14 Nov 2023 15:08:43 +0800 Subject: [PATCH] import changes from `dev' branch of rmottola/Arctic-Fox: - Bug 1216751 part 1. Restrict value iterators to interfaces that have indexed properties and pair iterators to interfaces that do not have indexed properties. r=qdot (6519f3f8c5) - Bug 1216751 part 2. For value iterators, "entries", "keys", and "values" must just come from Array.prototype. r=qdot (c0859f945c) - Bug 1216751 part 3. For pair iterators, @@iterator should be an alias for "entries". Similarly for maplikes and "entries" and setlikes and "values". r=qdot (bbe7c04782) - Bug 1216751 part 4. Implement forEach for iterable interfaces. r=qdot (8fdba677a4) - Bug 1216751 part 5. Remove the now-unnecessary value iterator infrastructure, since it's entirely handled via the %ArrayPrototype% methods now. r=qdot (88d3911694) - Bug 1231333 - part 1, JS engine: only allow futexWait in workers. r=luke (28e16fd2f9) - Bug 1231333 - part 2, DOM: only allow futexWait in workers. r=khuey (6c4dc98037) - Bug 1148990 - Don't ship bagheeraclient.js or tokenserverclient.js on Android. r=gps (aa9b22699a) - Bug 1216749 - Land the Firefox Kinto.js client (r=rnewman) (ea8c74e2ea) - Bug 1230221 - Convert JS callsites to use asyncOpen2 within services/ (r=sicking) (07ac8751f1) - Bug 1242965 - Make services/common eslintable. r=rnewman (0c84562750) - Bug 1055616 - Skip addons addons without a sourceURI or from a non-secure domain rather than treating them as errors. r=rnewman (7b8b738be0) - Bug 1229986 - get Sync tps tests starting again. r=whimboo (8cd0bf4f7f) - Bug 1003204: Removed CommonUtils.exceptionStr() in services/sync r=makh r=gfritzsche (830c106a29) - Bug 1003204: Removed CommonUtils.exceptionStr() in services/common/ r=gfritzsche (2c7bd4f8b5) - Bug 1234734 - Replace CommonUtils.stackTrace() with Log.stackTrace(). r=markh (3f0e88f192) - Bug 1241715 - get Sync TPS tests working locally by tweaking observers listened for and the authentication setup. r=whimboo (529b2f3d44) - Bug 1203736 - Convert H264::DecodeSPS assert to error return. r=jya (41c8c34c42) - Bug 1186716: Error if SPS NAL parsing failed. r=rillian (6c158be51e) - Bug 1187076 - Warn at end of SPS buffers. r=jya (2a49671261) - fix broken files (a090aad200) - Bug 1218217: avoid buffersize overflow even if codec is unbounded in dimensions r=pkerr (356140c947) - Bug 1218217: bustage fix for static assert r=bustage (e86dc5bf3a) - Bug 1041882 - Remove Froyo-specific support from libcubeb. r=snorp, r=padenot (e1f2d5283f) - Bug 1073319 - Enable AVX2 for libvpx on linux (update.py). r=rillian (934fd0a896) - Bug 1245027 - Move LOCAL_INCLUDES to moz.build in media/libvpx. r=mshal (7e56797d0e) - parts of Bug 1151175 - Update libvpx update.py for 1.4.0. (0e3f4a470f) - bits of 1178215 (bab7592703) - Bug 1218124 - Add vpx_once patch to update script. r=gerald (7b72a43382) - Bug 1225221 - vpx: Allow 8k video in update.sh. r=kinetik (9ec59f7737) - Bug 1224363 - Upstream update patch - r=rillian (4772921a5f) - Bug 1224361 - Upstream update patch - r=rillian (36ad6f1de4) - Bug 1233983 - Make libvpx build with clang-cl; r=rillian (5d98a8d888) - Bug 1224371 - Upstream update patch. r=jya (25164ba856) - Bug 1237848 - Updated update.py patch - r=rillian (69646eb6dc) - Bug 1184226 - Suppressing received packets when disabled, r=ekr (c8dfdb1a56) - Bug 1184226 - Disabling write on shutdown, r=ekr (d5a810dbe5) - Bug 1184226 - Updating transportlayerdtls logging levels, r=ekr (f3bc4a9889) - Bug 1137932: Unwind the stack before starting the DTLS handshake. r=mt (69dce8243a) - Bug 1214269 - read multiple DTLS packets from NSS if present. r=mt rjesup (e57b1628f5) - Bug 1235235 - Fix -Wimplicit-fallthrough warning in media/mtransport/. r=ekr (d56c9d1244) - Bug 1115483 - Accept a match on any a=fingerprint value. r=ekr (4a58378c09) - Bug 1167274 - Do the right thing when accessing the proxyinfo fails for some reason. r=mt (3ea23173ea) - Bug 1125292 - Sending ALPN header field for WebRTC calls, r=bwc (16fda60c39) - Bug 1167443 - Fix verification of end-of-candidates in mochitests. r=mt (8d74546e68) - Bug 1192813 - update the default candidate as new candidates arrive. r=bwc (490ac80af2) - Bug 1206981 - prevent ICE TCP from being turned off under e10s. r=jesup (a38afd56b8) - Bug 1234578 - Assert if PCM is destroyed improperly. r=rjesup (f1aa0d7cbc) - Bug 1164564 - WorkerDebugger.initialize should not return failure when called more than once;r=khuey (c316c83af7) - Bug 1211903 - WorkerDebugger should live on the main thread;r=khuey (5586888e77) - Bug 1164581 - Adding an overload for NS_ProxyRelease that accepts already_AddRefed, and removing all the others. r=bobbyholley (bc70230689) - Bug 1186750 part 1 - Inlinize trivial constructors and destructors of events in DeviceStorageRequestParent. r=dhylands (0fc6b594b1) - Bug 1186750 part 2 - Remove some unused member fields in events in DeviceStorageRequestParent. r=dhylands (d4be7e7031) - Bug 1186750 part 3 - Abstract CancelableFileEvent in DeviceStorageReqeustParent and use already_AddRefed&& for passing DeviceStorageFile parameter. r=dhylands (cea4df4465) - Bug 1186750 part 4 - Clear runnable list in DeviceStorageRequestParent when being destroyed. r=dhylands (a4d6018ce6) - Bug 1196315 - Ensure MIME service is only accessed on the main thread. r=dhylands (20c07f4baf) - Bug 1186750 part 5 - Convert nsDOMDeviceStorage::CheckPermission to take already_AddRefed&&. r=dhylands (7b2d0b415e) - Bug 1186750 part 6 - Remove unused and unimplemented method nsDOMDeviceStorage::StorePermission. r=dhylands (e6772e7b51) - Bug 1186750 part 7 - Convert DispatchToOwningThread and DispatchOrAbandon to take already_AddRefed&&. r=dhylands (5925568a22) - Bug 1186750 part 8 - Convert DeviceStorageUsedSpaceCache::Dispatch to use already_AddRef&&. r=dhylands (660b44eec7) - Bug 1186750 part 9 - Use already_AddRefed&& to initialize mFile of device storage requests. r=dhylands (c94464f412) - Bug 1186750 part 10 - Simplify code in DeviceStorageRequestParent::Dispatch. r=dhylands (debcc219ca) - Bug 1186750 part 11 - Convert all usage of Dispatch/NS_DispatchToMainThread in dom/devicestorage to pass in either already_AddRefed or raw pointer. r=dhylands (753694d0b5) - Bug 1059469: Part 1 - Add a log module for dump() calls. r=bent (d94c677e49) - Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent (1edc485b0f) - Bug 1243881 - patch 1 - unship performance.translateTime, r=bz (5a4afeea67) - Bug 1243881 - patch 2 - unship performance.translateTime, r=bz (5bf9557cd4) - Bug 1165722 - Replace JS_GetPropertyDescriptor usage in Xray code. r=bholley (e277cbcc78) - Bug 1243824. Add support for static functions and attributes on JSXrays. r=bholley (498d6c6034) - Bug 1228456 - SharedWorker should close the MessagePort in case the connecting runnable is not dispatched, r=smaug (c14a3e212f) - Bug 779707 - Add crashtest. (e86caca48e) - Bug 1228456 - add 'override' to the Cancel() method of a nsICancelableRunnable, rs=me (48db3b97e9) - Bug 1131323 - Enable SharedWorker loads to be intercepted through service workers; r=nsm (b2d972c5e3) - Bug 1173002 - Set worker system principal flag correctly when created from chrome, r=bz, a=kwierso. (ac9fc2980d) - bits of 1113429 backout (a862f16bb7) - bug 1206312 - add IndexedDatabaseManager include to IDBKeyRange. r=bz (bd6663f976) - Bug 1247117: De-namespace much of IndexedDB. r=baku (a996e3b443) - Bug 1196841: Update getAll/getAllKeys to match the spec and expose them. r=baku (7365769e04) - Bug 1196840: Make IDBTransaction::ObjectStoreNames const. r=baku (e7af2b0510) - Bug 1176165 - Fix the exception codes returned from functions that modify the IndexedDB schema, r=janv. (efa4e818d0) - Bug 935753 - Firefox displays the "This is a secure Firefox page" indicator on pages served by addons. r=MattN (77dced27ad) - Bug 925681 - Show identity block and reload icon in awesomebar in Australis' customization mode. ui-r=shorlander, r=Gijs (ffd1b2f6a4) - Bug 970382 - Add about:accounts to the list of chrome UIs with a special identity mode r=gavin (6d2817d087) - Bug 1051847 - Add trusted identity block to about:license and about:rights. r=dao (aa8dfe4d1d) - Bug 1094947 - The trusted identity block is not displayed for the about:downloads page. r=jaws (1c51faa077) - Bug 686281 - Implement CSS mask style; r=dbaron. (2f823c4a49) - Bug 686281 - Mask CSS parsing and Mask DOM API. r=dbaron (f9cc291131) - Bug 686281 - Mask CSS rendering; r=mstange (b26ba7ba7e) - Bug 686281 - Mask CSS animation; r=dbaron. (4ce1ba671e) - Bug 686281 - Mask CSS webkit-alias; r=dbaron. (c27f4023d6) - Bug 686281 - Mask mochitest; r=dbaron. (010fcdfd04) - Bug 686281 - Expands will-change of a shorthand prop to longhand ones; r=dbaron. (f8e4a6dcfd) - Bug 686281 - A static assertion to keep value correctness of NS_RULE_NODE_IS_ANIMATION_RULE; r=dbaron. (5ae87b576b) - Bug 686281 - Remove nsStyleSVGReset::mMask; r=dbaron (1e7a0dfb45) - Bug 686281 - mask-composite reftests; r=dbaron (7f769e196a) - Bug 686281 - Rename nsStyleSVGReset::mLayers to nsStyleSVGReset::mMask; Rename nsStyleBackground::mLayers to nsStyleBackground::mImage. r=dbaron (3bd4fc6e3b) - Bug 1241275 - Change the way -moz-window-dragging works. r=heycam,roc (5691f2dbf5) - Bug 1246892 - pass aCTF as a reference instead of value. r=roc (98b0e45063) - Bug 1234800 - Reinstate code that adjusts dirty rects for fixed-position frames in display ports. r=tn (44e55ebacb) - Bug 1234800 - Move this line to the right place. r=tn (1a86a7fc72) - Bug 1216832 - Handle preserve-3d visible regions during display list building by always transforming from the preserve-3d root each time. r=roc (1887af1172) - Bug 1231243 - In nsDisplayBackgroundImage::GetBoundsInternal(), take the union of the image bounds and the viewport bounds if APZ is enabled. r=mstange (87a1fa0ab4) - Bug 1246622 - Handle nested preserve-3d contexts when hit testing. r=roc (6eed51c734) - Bug 1235945 - Fix assertion error in some cases when running szip when debug flags are enabled for host tools. r=froydnj (3a0aa4f728) - Bug 1224798: Do not produce a clip mask if our context is entirely clipped out anyway. r=jrmuizel (3926a4ef7d) - Bug 1223604 - Disentangle nsSVGClipPathFrame::ApplyClipOrPaintClipMask and make the code easier to understand. r=Bas (c8c19a1b0d) - Bug 1204405: Don't access prefs off main thread in testing ProcessLink::Open(). r=khuey (301aa7259d) - Bug 1248896 - don't conditional compile on config ENABLE_TESTS in Nuwa. r=khuey (4f2fd275fd) - Bug 1232458 - use UniquePtr instead of nsAutoArrayPtr in WindowsDllBlocklist.cpp; r=aklotz (292071bdb5) - Bug 1247741 - Additional checks for pointer validity in LdrLoadDLL detour. r=aklotz (8ee48e8cf3) - Bug 1113930 - Move __libc_stack_end related code block from StackWalk.cpp in a non-OSX section. r=froydnj (4f0f9e2e66) - Bug 1113930 - Use the actual stack end address on x86 OSX and Android for the stack walker. r=froydnj (7371d9a508) - missing bit of Bug 1216681 (fdf69e362f) - Bug 1193593 - Test fingerprinting resistance for media queries in picture elements. r=heycam (6155b73c26) - Bug 1232829 - Detach obsolete DocumentTimeline from refresh driver when the document is reset; r=smaug (564680e2a0) - Bug 1075457, part 1 - Implement rendering for |clip-path:polygon()|. r=mstange, r=jwatt (76056caacd) - Bug 1075457, part 2 - Implement circle() and ellipse() for the |clip-path| property. r=mstange, r=jwatt (4b8b39c682) - Bug 1094571 - add unicode-range load tests. r=heycam (3358555411) - Bug 1216695 - Remove the Request.context specific bits from fetch-request-resources.https.html; r=bkelly (2315e50b97) - Bug 1193133 - Disable broken service worker wpt tests. r=bkelly (8f0205d5e7) - Bug 1199831: Fix a bunch of mixed-content violations in imported ServiceWorker WPTs. r=jdm (33f261ce91) - bit of Bug 603201 (325170577f) - Bug 1184798 - same origin, cors and no-cors load tests. r=bkelly (f8549dd0bb) - Bug 1210581: Test controlled worker loads (XHR, fetch, importScripts). r=ehsan (41a436df47) - Bug 1215196 - Fix web-platform-tests iframe scripts to avoid pulling in testharness.js in them; r=bkelly (a2edb0784c) - Bug 1242798 - Don't OSR into Ion on debuggee frames. (r=jandem) (21e17bdd9d) - Bug 1238658 - Allow setElem-accessor optimizations only for native baseHolder objects; r=efaust (12c9766a53) - Bug 1144630 - Follup: Fix review nit. (rs=evilpie) (67b5cc2c7f) - Bug 1182866 - Fix Baseline GETNAME stubs to check for uninitialized lexicals. (r=jandem) (dd47d2025a) - Bug 1189536 - Make fetch-request-xhr.https.html pass; r=bkelly (ce177226bf) - Bug 1188822 - Make service-workers/service-worker/fetch-request-resources.https.html pass. r=bkelly (3a5f3a6660) --- browser/base/content/browser.css | 1 - browser/base/content/browser.js | 9 +- .../content/test/general/browser_bug590206.js | 138 + dom/animation/DocumentTimeline.cpp | 11 +- dom/archivereader/ArchiveEvent.cpp | 12 +- dom/base/Console.cpp | 4 +- dom/base/WebSocket.cpp | 4 +- dom/base/nsContentUtils.cpp | 8 + dom/base/nsContentUtils.h | 11 +- dom/base/nsDOMWindowUtils.cpp | 11 +- dom/base/nsDocument.cpp | 10 +- dom/base/nsGlobalWindow.cpp | 4 +- dom/base/nsGlobalWindow.h | 6 +- dom/base/nsPerformance.cpp | 34 - dom/base/nsPerformance.h | 6 - dom/base/nsScriptLoader.cpp | 17 +- dom/base/test/empty_worker.js | 1 - dom/base/test/mochitest.ini | 2 - dom/base/test/test_performance_translate.html | 75 - dom/bindings/Bindings.conf | 40 +- dom/bindings/Codegen.py | 113 +- dom/bindings/Configuration.py | 7 +- dom/bindings/IterableIterator.h | 235 +- dom/bindings/parser/WebIDL.py | 125 +- .../test_interface_maplikesetlikeiterable.py | 16 +- .../test/TestInterfaceIterableSingle.cpp | 19 +- .../test/TestInterfaceIterableSingle.h | 6 +- .../test/test_bug1123516_maplikesetlike.html | 6 +- dom/bindings/test/test_iterable.html | 51 +- dom/cache/ManagerId.cpp | 5 +- dom/datastore/DataStoreDB.cpp | 16 +- dom/datastore/DataStoreDB.h | 15 +- dom/datastore/DataStoreRevision.cpp | 6 +- dom/datastore/DataStoreRevision.h | 9 +- dom/datastore/DataStoreService.cpp | 10 +- dom/devicestorage/DeviceStorage.h | 12 +- .../DeviceStorageRequestParent.cpp | 395 +- .../DeviceStorageRequestParent.h | 194 +- dom/devicestorage/DeviceStorageStatics.cpp | 10 +- dom/devicestorage/nsDeviceStorage.cpp | 352 +- dom/devicestorage/nsDeviceStorage.h | 24 +- dom/events/Event.cpp | 2 +- dom/events/test/test_eventTimeStamp.html | 5 +- dom/fetch/InternalRequest.cpp | 45 + dom/fetch/InternalRequest.h | 3 + dom/indexedDB/ActorsChild.h | 18 +- dom/indexedDB/FileSnapshot.h | 3 +- dom/indexedDB/IDBCursor.cpp | 4 +- dom/indexedDB/IDBCursor.h | 32 +- dom/indexedDB/IDBDatabase.cpp | 14 +- dom/indexedDB/IDBDatabase.h | 36 +- dom/indexedDB/IDBEvents.cpp | 3 +- dom/indexedDB/IDBEvents.h | 9 +- dom/indexedDB/IDBFactory.cpp | 2 - dom/indexedDB/IDBFactory.h | 17 +- dom/indexedDB/IDBFileHandle.cpp | 4 +- dom/indexedDB/IDBFileHandle.h | 10 +- dom/indexedDB/IDBFileRequest.cpp | 4 +- dom/indexedDB/IDBFileRequest.h | 8 +- dom/indexedDB/IDBIndex.cpp | 4 +- dom/indexedDB/IDBIndex.h | 24 +- dom/indexedDB/IDBKeyRange.cpp | 4 +- dom/indexedDB/IDBKeyRange.h | 26 +- dom/indexedDB/IDBMutableFile.cpp | 3 +- dom/indexedDB/IDBMutableFile.h | 17 +- dom/indexedDB/IDBObjectStore.cpp | 28 +- dom/indexedDB/IDBObjectStore.h | 25 +- dom/indexedDB/IDBRequest.cpp | 3 +- dom/indexedDB/IDBRequest.h | 16 +- dom/indexedDB/IDBTransaction.cpp | 9 +- dom/indexedDB/IDBTransaction.h | 49 +- dom/indexedDB/IDBWrapperCache.cpp | 2 - dom/indexedDB/IDBWrapperCache.h | 8 +- dom/indexedDB/IndexedDatabase.h | 10 +- dom/indexedDB/IndexedDatabaseInlines.h | 2 +- dom/indexedDB/IndexedDatabaseManager.cpp | 5 +- dom/indexedDB/IndexedDatabaseManager.h | 14 +- dom/indexedDB/PBackgroundIDBDatabase.ipdl | 4 +- dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh | 4 +- dom/indexedDB/SerializationHelpers.h | 20 +- dom/indexedDB/moz.build | 9 +- dom/ipc/Blob.cpp | 2 +- dom/ipc/ContentParent.cpp | 2 +- dom/media/tests/mochitest/mochitest.ini | 13 +- dom/media/tests/mochitest/pc.js | 220 +- dom/media/tests/mochitest/sdpUtils.js | 118 + dom/media/tests/mochitest/templates.js | 122 +- ...t_dataChannel_basicAudioVideoNoBundle.html | 14 +- ...peerConnection_addDataChannelNoBundle.html | 5 +- ...nnection_addSecondAudioStreamNoBundle.html | 5 +- ...nnection_addSecondVideoStreamNoBundle.html | 5 +- ...t_peerConnection_basicAudioRequireEOC.html | 35 + ...eerConnection_basicAudioVideoNoBundle.html | 5 +- ...tion_basicAudioVideoNoBundleNoRtcpMux.html | 39 + ...erConnection_basicAudioVideoNoRtcpMux.html | 38 + .../recognition/SpeechStreamListener.cpp | 5 +- dom/webidl/IDBDatabase.webidl | 2 +- dom/webidl/IDBIndex.webidl | 18 +- dom/webidl/IDBKeyRange.webidl | 2 +- dom/webidl/IDBObjectStore.webidl | 15 +- dom/webidl/Performance.webidl | 3 - ...stInterfaceJSMaplikeSetlikeIterable.webidl | 2 + dom/workers/Performance.cpp | 8 +- dom/workers/RuntimeService.cpp | 6 +- dom/workers/ScriptLoader.cpp | 8 +- dom/workers/ServiceWorker.cpp | 7 - dom/workers/ServiceWorker.h | 3 - dom/workers/ServiceWorkerManager.cpp | 2 - dom/workers/ServiceWorkerPrivate.cpp | 13 +- dom/workers/ServiceWorkerPrivate.h | 6 - dom/workers/WorkerDebuggerManager.cpp | 162 +- dom/workers/WorkerDebuggerManager.h | 30 +- dom/workers/WorkerPrivate.cpp | 351 +- dom/workers/WorkerPrivate.h | 84 +- dom/workers/WorkerScope.cpp | 4 +- dom/workers/WorkerScope.h | 10 +- dom/workers/Workers.h | 1 + dom/workers/test/crashtests/1228456.html | 14 + dom/workers/test/crashtests/779707.html | 19 + dom/workers/test/crashtests/crashtests.list | 4 +- extensions/gio/nsGIOProtocolHandler.cpp | 11 +- .../spellcheck/src/mozPersonalDictionary.cpp | 21 +- image/Decoder.cpp | 11 +- image/decoders/icon/mac/nsIconChannelCocoa.mm | 7 +- image/decoders/icon/win/nsIconChannel.cpp | 7 +- ipc/glue/MessageLink.cpp | 11 +- js/src/builtin/AtomicsObject.cpp | 11 + js/src/builtin/AtomicsObject.h | 13 + js/src/jit-test/tests/baseline/bug1182866.js | 17 + js/src/jit-test/tests/debug/bug1242798.js | 14 + js/src/jit/BaselineIC.cpp | 16 +- js/src/jit/BaselineIC.h | 8 +- js/src/jsapi.cpp | 6 + js/src/jsapi.h | 3 + js/src/shell/js.cpp | 3 + js/xpconnect/src/Sandbox.cpp | 4 +- js/xpconnect/tests/chrome/test_xrayToJS.xul | 133 +- js/xpconnect/wrappers/XrayWrapper.cpp | 337 +- layout/base/nsCSSRendering.cpp | 25 +- layout/base/nsCSSRendering.h | 23 +- layout/base/nsDisplayList.cpp | 90 +- layout/base/nsDisplayList.h | 10 +- layout/base/tests/mochitest.ini | 2 +- layout/base/tests/test_bug1246622.html | 45 + layout/generic/nsCanvasFrame.cpp | 2 +- layout/generic/nsCanvasFrame.h | 4 +- layout/generic/nsFrame.cpp | 126 +- layout/inspector/inDOMUtils.cpp | 2 + .../clip-path/clip-path-circle-001-ref.html | 20 + .../clip-path/clip-path-circle-001.html | 21 + .../clip-path/clip-path-circle-002-ref.html | 20 + .../clip-path/clip-path-circle-002.html | 21 + .../clip-path/clip-path-circle-003-ref.html | 20 + .../clip-path/clip-path-circle-003.html | 21 + .../clip-path/clip-path-circle-004-ref.html | 20 + .../clip-path/clip-path-circle-004.html | 21 + .../clip-path/clip-path-circle-005-ref.html | 20 + .../clip-path/clip-path-circle-005.html | 21 + .../clip-path/clip-path-circle-006-ref.html | 20 + .../clip-path/clip-path-circle-006.html | 21 + .../clip-path/clip-path-circle-007-ref.html | 20 + .../clip-path/clip-path-circle-007.html | 21 + .../clip-path/clip-path-circle-008-ref.html | 20 + .../clip-path/clip-path-circle-008.html | 21 + .../clip-path/clip-path-circle-009-ref.html | 20 + .../clip-path/clip-path-circle-009.html | 21 + .../clip-path/clip-path-circle-010-ref.html | 20 + .../clip-path/clip-path-circle-010.html | 21 + .../clip-path/clip-path-circle-011.html | 21 + .../clip-path/clip-path-circle-012.html | 21 + .../clip-path/clip-path-circle-013.html | 21 + .../clip-path/clip-path-circle-014.html | 21 + .../clip-path/clip-path-circle-015.html | 21 + .../clip-path/clip-path-circle-016.html | 21 + .../clip-path/clip-path-circle-017.html | 21 + .../clip-path/clip-path-circle-018.html | 21 + .../clip-path/clip-path-circle-019.html | 21 + .../clip-path/clip-path-circle-020.html | 23 + .../clip-path/clip-path-ellipse-001-ref.html | 20 + .../clip-path/clip-path-ellipse-001.html | 21 + .../clip-path/clip-path-ellipse-002.html | 21 + .../clip-path/clip-path-ellipse-003.html | 21 + .../clip-path/clip-path-ellipse-004.html | 21 + .../clip-path/clip-path-ellipse-005.html | 21 + .../clip-path/clip-path-ellipse-006.html | 21 + .../clip-path/clip-path-ellipse-007.html | 21 + .../clip-path/clip-path-ellipse-008.html | 21 + .../clip-path/clip-path-polygon-001.html | 21 + .../clip-path/clip-path-polygon-002.html | 24 + .../clip-path/clip-path-polygon-003.html | 21 + .../clip-path/clip-path-polygon-004.html | 25 + .../clip-path/clip-path-polygon-005.html | 26 + .../clip-path/clip-path-polygon-006.html | 33 + .../clip-path/clip-path-polygon-007.html | 34 + .../clip-path/clip-path-polygon-008.html | 35 + .../clip-path/clip-path-polygon-009.html | 33 + .../clip-path/clip-path-polygon-010.html | 36 + .../clip-path/clip-path-polygon-011.html | 36 + .../clip-path/clip-path-polygon-012.html | 36 + .../clip-path/clip-path-polygon-013.html | 51 + .../clip-path-rectangle-border-ref.html | 15 + .../clip-path/clip-path-rectangle-ref.html | 15 + .../clip-path/clip-path-square-001-ref.html | 15 + .../clip-path/clip-path-square-002-ref.html | 15 + .../clip-path/clip-path-stripes-001-ref.html | 39 + .../clip-path/clip-path-stripes-002-ref.html | 31 + .../clip-path/clip-path-stripes-003-ref.html | 63 + .../svg-integration/clip-path/reftest.list | 49 + .../reftests/svg/svg-integration/reftest.list | 3 + .../blue-100x50-transparent-100X50.png | Bin 0 -> 237 bytes .../blue-100x50-transparent-100X50.svg | 5 + .../masking/mask-composite-1-ref.html | 33 + .../submitted/masking/mask-composite-1a.html | 51 + .../submitted/masking/mask-composite-1b.html | 51 + .../masking/mask-composite-2-ref.html | 39 + .../submitted/masking/mask-composite-2a.html | 51 + .../submitted/masking/mask-composite-2b.html | 51 + .../w3c-css/submitted/masking/reftest.list | 4 + .../transparent-100x50-blue-100X50.png | Bin 0 -> 233 bytes .../transparent-100x50-blue-100X50.svg | 5 + .../reftests/w3c-css/submitted/reftest.list | 3 + layout/style/Declaration.cpp | 315 +- layout/style/Declaration.h | 5 + layout/style/StyleAnimationValue.cpp | 190 +- layout/style/nsCSSKeywordList.h | 4 + layout/style/nsCSSParser.cpp | 185 +- layout/style/nsCSSPropAliasList.h | 37 + layout/style/nsCSSPropList.h | 98 +- layout/style/nsCSSProps.cpp | 34 + layout/style/nsCSSProps.h | 2 + layout/style/nsComputedDOMStyle.cpp | 257 +- layout/style/nsComputedDOMStyle.h | 18 +- layout/style/nsComputedDOMStylePropertyList.h | 8 + layout/style/nsRuleNode.cpp | 328 +- layout/style/nsStyleConsts.h | 16 +- layout/style/nsStyleStruct.cpp | 223 +- layout/style/nsStyleStruct.h | 86 +- layout/style/test/chrome/bug418986-2.js | 105 +- layout/style/test/chrome/chrome.ini | 2 + layout/style/test/chrome/match.png | Bin 0 -> 1210 bytes layout/style/test/chrome/mismatch.png | Bin 0 -> 1573 bytes layout/style/test/chrome/test_bug418986-2.xul | 7 +- layout/style/test/mochitest.ini | 7 + layout/style/test/property_database.js | 277 +- layout/style/test/test_bug1232829.html | 38 + layout/style/test/test_bug418986-2.html | 6 +- layout/style/test/test_clip-path_polygon.html | 41 + .../test/test_transitions_per_property.html | 13 +- .../test/test_unicode_range_loading.html | 366 ++ layout/style/test/test_value_storage.html | 3 +- layout/svg/moz.build | 1 + layout/svg/nsCSSClipPathInstance.cpp | 241 ++ layout/svg/nsCSSClipPathInstance.h | 57 + layout/svg/nsSVGClipPathFrame.cpp | 311 +- layout/svg/nsSVGClipPathFrame.h | 55 +- layout/svg/nsSVGEffects.cpp | 10 +- layout/svg/nsSVGIntegrationUtils.cpp | 106 +- layout/svg/nsSVGIntegrationUtils.h | 1 + layout/svg/nsSVGUtils.cpp | 10 +- layout/tables/nsTablePainter.cpp | 2 +- media/libcubeb/src/audiotrack_definitions.h | 11 +- media/libcubeb/src/cubeb_audiotrack.c | 96 +- media/libstagefright/binding/H264.cpp | 16 +- media/libvorbis/COPYING | 2 +- media/libvorbis/README | 2 +- media/libvorbis/README_MOZILLA | 4 +- media/libvorbis/bug1117571-r19419.patch | 22 - media/libvorbis/update.sh | 2 +- .../libvpx/1237848-check-lookahead-ctx.patch | 50 + media/libvpx/Makefile.in | 2 - media/libvpx/apple-clang.patch | 29 - .../cast-char-to-uint-before-shift.patch | 28 + media/libvpx/clamp-abs-QIndex.patch | 31 + media/libvpx/clamp_abs_lvl_seg.patch | 38 + media/libvpx/clang-cl.patch | 98 + media/libvpx/moz.build | 14 +- media/libvpx/vpx_once.patch | 145 + media/mtransport/nricectx.h | 11 +- .../test/proxy_tunnel_socket_unittest.cpp | 114 +- media/mtransport/test/transport_unittests.cpp | 79 +- .../third_party/nICEr/src/ice/ice_socket.c | 2 +- .../nICEr/src/net/nr_proxy_tunnel.c | 71 +- .../nICEr/src/net/nr_proxy_tunnel.h | 4 + media/mtransport/transportlayerdtls.cpp | 95 +- media/mtransport/transportlayerdtls.h | 5 +- media/mtransport/transportlayerloopback.cpp | 16 +- media/mtransport/transportlayerloopback.h | 16 +- media/webrtc/.gclient | 1017 +---- media/webrtc/.gclient_entries | 1122 +---- media/webrtc/signaling/src/jsep/JsepSession.h | 3 +- .../signaling/src/jsep/JsepSessionImpl.cpp | 109 +- .../signaling/src/jsep/JsepSessionImpl.h | 15 +- .../src/peerconnection/PeerConnectionImpl.cpp | 29 +- .../src/peerconnection/PeerConnectionImpl.h | 11 +- .../peerconnection/PeerConnectionMedia.cpp | 188 +- .../src/peerconnection/PeerConnectionMedia.h | 24 +- media/webrtc/signaling/src/sdp/SdpHelper.cpp | 102 +- media/webrtc/signaling/src/sdp/SdpHelper.h | 13 + .../signaling/test/jsep_session_unittest.cpp | 13 +- .../common_video/libyuv/webrtc_libyuv.cc | 12 + media/webrtc/webrtc_version.h | 1096 +---- modules/libjar/nsJARChannel.cpp | 2 +- mozglue/build/Nuwa.cpp | 2 +- mozglue/build/Nuwa.h | 2 +- mozglue/build/WindowsDllBlocklist.cpp | 28 +- mozglue/linker/szip.cpp | 6 +- mozglue/misc/StackWalk.cpp | 51 +- netwerk/base/TLSServerSocket.cpp | 8 +- netwerk/base/nsBaseChannel.cpp | 2 +- netwerk/base/nsProtocolProxyService.cpp | 19 +- netwerk/base/nsServerSocket.cpp | 10 +- netwerk/base/nsStreamListenerTee.cpp | 7 +- netwerk/base/nsTransportUtils.cpp | 2 +- netwerk/base/nsUDPSocket.cpp | 6 +- netwerk/cache/nsCacheService.cpp | 2 +- netwerk/cache2/CacheIndex.h | 11 +- netwerk/cache2/CacheStorageService.h | 5 +- netwerk/ipc/RemoteOpenFileChild.cpp | 33 +- netwerk/protocol/file/nsFileChannel.cpp | 4 +- netwerk/protocol/http/HttpBaseChannel.cpp | 2 +- .../rtsp/controller/RtspControllerParent.cpp | 7 +- .../websocket/BaseWebSocketChannel.cpp | 7 +- .../protocol/websocket/WebSocketChannel.cpp | 10 +- .../protocol/wyciwyg/WyciwygChannelChild.cpp | 7 +- netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp | 15 +- netwerk/sctp/datachannel/DataChannel.cpp | 4 +- security/manager/ssl/nsNSSCallbacks.cpp | 3 +- services/common/async.js | 4 - services/common/hawkclient.js | 4 +- services/common/hawkrequest.js | 2 +- .../common/modules-testing/storageserver.js | 10 +- services/common/moz-kinto-client.js | 3609 +++++++++++++++++ services/common/moz.build | 22 +- services/common/observers.js | 4 - services/common/rest.js | 40 +- services/common/tests/unit/head_helpers.js | 2 +- services/common/tests/unit/test_hawkclient.js | 26 +- .../common/tests/unit/test_storage_adapter.js | 183 + .../unit/test_storage_adapter/empty.sqlite | Bin 0 -> 2048 bytes .../tests/unit/test_utils_stackTrace.js | 33 - services/common/tests/unit/xpcshell.ini | 5 +- services/common/tokenserverclient.js | 7 +- services/common/utils.js | 6 +- services/sync/modules/addonsreconciler.js | 5 +- services/sync/modules/addonutils.js | 81 +- services/sync/modules/engines.js | 27 +- services/sync/modules/engines/addons.js | 8 + services/sync/modules/engines/bookmarks.js | 12 +- services/sync/modules/engines/passwords.js | 9 +- services/sync/modules/identity.js | 2 +- services/sync/modules/policies.js | 2 +- services/sync/modules/record.js | 2 +- services/sync/modules/resource.js | 41 +- services/sync/modules/service.js | 23 +- services/sync/modules/stages/enginesync.js | 3 +- services/sync/modules/util.js | 7 +- services/sync/tests/unit/head_http_server.js | 2 +- services/sync/tests/unit/test_addon_utils.js | 48 +- services/sync/tests/unit/test_addons_store.js | 8 +- .../sync/tests/unit/test_bookmark_engine.js | 6 +- .../sync/tests/unit/test_node_reassignment.js | 21 +- .../tests/unit/test_service_sync_locked.js | 12 +- services/sync/tests/unit/test_utils_catch.js | 2 +- .../tps/resource/auth/fxaccounts.jsm | 2 +- .../tps/resource/modules/history.jsm | 2 +- .../sync/tps/extensions/tps/resource/tps.jsm | 61 +- storage/StorageBaseStatementInternal.cpp | 15 +- storage/mozStorageAsyncStatement.cpp | 6 +- storage/mozStorageConnection.cpp | 26 +- storage/mozStorageService.cpp | 19 +- storage/mozStorageStatementData.h | 3 +- testing/tps/.gitignore | 4 + testing/tps/.hgignore | 4 + testing/tps/setup.py | 4 +- testing/tps/tps/testrunner.py | 12 +- .../web-platform/mozilla/meta/MANIFEST.json | 32 +- .../service-worker/fetch-csp.https.html.ini | 5 + ...pond-with-stops-propagation.https.html.ini | 6 + .../service-worker/fetch-event.https.html.ini | 1 + .../fetch-request-css-base-url.https.html.ini | 3 +- .../fetch-request-css-images.https.html.ini | 5 + .../fetch-request-resources.https.html.ini | 5 - .../fetch-request-xhr.https.html.ini | 10 - .../invalid-blobtype.https.html.ini | 5 + .../invalid-header.https.html.ini | 10 - .../service-worker/referer.https.html.ini | 7 +- ...ch-request-resources-iframe.https.html.ini | 3 - .../fetch-request-xhr-iframe.https.html.ini | 3 - .../fetch-response-xhr-iframe.https.html.ini | 3 - .../invalid-blobtype-iframe.https.html.ini | 3 - .../invalid-header-iframe.https.html.ini | 3 - .../service-worker/fetch-csp.https.html | 2 +- .../fetch-request-css-base-url.https.html | 2 +- .../fetch-request-css-images.https.html | 99 + .../fetch-request-resources.https.html | 124 +- .../fetch-request-xhr.https.html | 11 +- .../fetch-response-xhr.https.html | 7 + .../invalid-blobtype.https.html | 2 +- .../service-worker/invalid-header.https.html | 2 +- .../service-worker/referer.https.html | 2 +- .../resources/dummy-worker-interceptor.js | 13 + .../resources/dummy-worker-script.py | 9 + .../resources/fetch-csp-iframe.html | 10 +- .../fetch-request-css-base-url-worker.js | 2 +- .../fetch-request-resources-iframe.https.html | 3 +- .../fetch-request-resources-worker.js | 3 - .../fetch-request-xhr-iframe.https.html | 46 +- .../resources/fetch-request-xhr-worker.js | 1 - .../resources/fetch-request-xhr.https.html | 30 + .../fetch-response-xhr-iframe.https.html | 6 +- .../invalid-blobtype-iframe.https.html | 1 - .../invalid-header-iframe.https.html | 1 - .../service-worker/resources/load_worker.js | 29 + .../resources/referer-iframe.html | 6 +- .../worker-interception-iframe.https.html | 39 + .../resources/worker-load-interceptor.js | 14 + .../worker-interception.https.html | 118 + .../osfile/NativeOSFileInternals.cpp | 12 +- .../components/places/AsyncFaviconHelpers.cpp | 4 +- toolkit/components/places/Helpers.h | 2 +- .../nsUrlClassifierDBService.cpp | 5 +- .../tests/xpcshell/test_Log_stackTrace.js | 30 + toolkit/modules/tests/xpcshell/xpcshell.ini | 2 + widget/gonk/nsScreenManagerGonk.cpp | 2 +- xpcom/base/nsConsoleService.cpp | 2 +- xpcom/base/nsInterfaceRequestorAgg.cpp | 12 +- xpcom/glue/nsProxyRelease.cpp | 65 - xpcom/glue/nsProxyRelease.h | 127 +- xpcom/glue/objs.mozbuild | 1 - xpcom/libxpcomrt/moz.build | 1 - 430 files changed, 13118 insertions(+), 7404 deletions(-) create mode 100644 browser/base/content/test/general/browser_bug590206.js create mode 100644 dom/media/tests/mochitest/sdpUtils.js create mode 100644 dom/media/tests/mochitest/test_peerConnection_basicAudioRequireEOC.html create mode 100644 dom/media/tests/mochitest/test_peerConnection_basicAudioVideoNoBundleNoRtcpMux.html create mode 100644 dom/media/tests/mochitest/test_peerConnection_basicAudioVideoNoRtcpMux.html create mode 100644 dom/workers/test/crashtests/1228456.html create mode 100644 dom/workers/test/crashtests/779707.html create mode 100644 js/src/jit-test/tests/baseline/bug1182866.js create mode 100644 js/src/jit-test/tests/debug/bug1242798.js create mode 100644 layout/base/tests/test_bug1246622.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-001-ref.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-001.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-002-ref.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-002.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-003-ref.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-003.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-004-ref.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-004.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-005-ref.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-005.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-006-ref.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-006.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-007-ref.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-007.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-008-ref.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-008.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-009-ref.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-009.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-010-ref.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-010.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-011.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-012.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-013.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-014.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-015.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-016.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-017.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-018.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-019.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-circle-020.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-001-ref.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-001.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-002.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-003.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-004.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-005.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-006.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-007.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-008.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-001.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-002.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-003.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-004.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-005.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-006.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-007.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-008.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-009.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-010.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-011.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-012.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-013.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-rectangle-border-ref.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-rectangle-ref.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-square-001-ref.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-square-002-ref.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-stripes-001-ref.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-stripes-002-ref.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-stripes-003-ref.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/reftest.list create mode 100644 layout/reftests/w3c-css/submitted/masking/blue-100x50-transparent-100X50.png create mode 100644 layout/reftests/w3c-css/submitted/masking/blue-100x50-transparent-100X50.svg create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-composite-1-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-composite-1a.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-composite-1b.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-composite-2-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-composite-2a.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-composite-2b.html create mode 100644 layout/reftests/w3c-css/submitted/masking/reftest.list create mode 100644 layout/reftests/w3c-css/submitted/masking/transparent-100x50-blue-100X50.png create mode 100644 layout/reftests/w3c-css/submitted/masking/transparent-100x50-blue-100X50.svg create mode 100644 layout/style/test/chrome/match.png create mode 100644 layout/style/test/chrome/mismatch.png create mode 100644 layout/style/test/test_bug1232829.html create mode 100644 layout/style/test/test_clip-path_polygon.html create mode 100644 layout/style/test/test_unicode_range_loading.html create mode 100644 layout/svg/nsCSSClipPathInstance.cpp create mode 100644 layout/svg/nsCSSClipPathInstance.h delete mode 100644 media/libvorbis/bug1117571-r19419.patch create mode 100644 media/libvpx/1237848-check-lookahead-ctx.patch delete mode 100644 media/libvpx/apple-clang.patch create mode 100644 media/libvpx/cast-char-to-uint-before-shift.patch create mode 100644 media/libvpx/clamp-abs-QIndex.patch create mode 100644 media/libvpx/clamp_abs_lvl_seg.patch create mode 100644 media/libvpx/clang-cl.patch create mode 100644 media/libvpx/vpx_once.patch create mode 100644 services/common/moz-kinto-client.js create mode 100644 services/common/tests/unit/test_storage_adapter.js create mode 100644 services/common/tests/unit/test_storage_adapter/empty.sqlite delete mode 100644 services/common/tests/unit/test_utils_stackTrace.js create mode 100644 testing/tps/.gitignore create mode 100644 testing/tps/.hgignore create mode 100644 testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-csp.https.html.ini create mode 100644 testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-event-respond-with-stops-propagation.https.html.ini create mode 100644 testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-css-images.https.html.ini delete mode 100644 testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-resources.https.html.ini delete mode 100644 testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-xhr.https.html.ini create mode 100644 testing/web-platform/mozilla/meta/service-workers/service-worker/invalid-blobtype.https.html.ini delete mode 100644 testing/web-platform/mozilla/meta/service-workers/service-worker/invalid-header.https.html.ini delete mode 100644 testing/web-platform/mozilla/meta/service-workers/service-worker/resources/fetch-request-resources-iframe.https.html.ini delete mode 100644 testing/web-platform/mozilla/meta/service-workers/service-worker/resources/fetch-request-xhr-iframe.https.html.ini delete mode 100644 testing/web-platform/mozilla/meta/service-workers/service-worker/resources/fetch-response-xhr-iframe.https.html.ini delete mode 100644 testing/web-platform/mozilla/meta/service-workers/service-worker/resources/invalid-blobtype-iframe.https.html.ini delete mode 100644 testing/web-platform/mozilla/meta/service-workers/service-worker/resources/invalid-header-iframe.https.html.ini create mode 100644 testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-css-images.https.html create mode 100644 testing/web-platform/mozilla/tests/service-workers/service-worker/resources/dummy-worker-script.py create mode 100644 testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-xhr.https.html create mode 100644 testing/web-platform/mozilla/tests/service-workers/service-worker/resources/load_worker.js create mode 100644 testing/web-platform/mozilla/tests/service-workers/service-worker/resources/worker-interception-iframe.https.html create mode 100644 testing/web-platform/mozilla/tests/service-workers/service-worker/resources/worker-load-interceptor.js create mode 100644 toolkit/modules/tests/xpcshell/test_Log_stackTrace.js delete mode 100644 xpcom/glue/nsProxyRelease.cpp diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css index 820d362d73..51c77bb682 100644 --- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -145,7 +145,6 @@ toolbar[printpreview="true"] { visibility: collapse; } -#wrapper-urlbar-container #urlbar-container > #urlbar > toolbarbutton, #urlbar-container:not([combined]) > #urlbar > toolbarbutton, #urlbar-container[combined] + #reload-button + #stop-button, #urlbar-container[combined] + #reload-button, diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 9228c6cea3..a2893274e8 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -6404,14 +6404,17 @@ var gIdentityHandler = { let nsIWebProgressListener = Ci.nsIWebProgressListener; // For some URIs like data: we can't get a host and so can't do - // anything useful here. Chrome URIs however get special treatment. + // anything useful here. let unknown = false; try { uri.host; } catch (e) { unknown = true; } - if ((uri.scheme == "chrome" || uri.scheme == "about") && - uri.spec !== "about:blank") { + // Chrome URIs however get special treatment. Some chrome URIs are + // whitelisted to provide a positive security signal to the user. + let whitelist = /^about:(accounts|addons|app-manager|config|crashes|customizing|downloads|healthreport|home|license|newaddon|permissions|preferences|privatebrowsing|rights|sessionrestore|support|welcomeback)/i; + let isChromeUI = uri.schemeIs("about") && whitelist.test(uri.spec); + if (isChromeUI) { this.setMode(this.IDENTITY_MODE_CHROMEUI); } else if (unknown) { this.setMode(this.IDENTITY_MODE_UNKNOWN); diff --git a/browser/base/content/test/general/browser_bug590206.js b/browser/base/content/test/general/browser_bug590206.js new file mode 100644 index 0000000000..c0c00b32a9 --- /dev/null +++ b/browser/base/content/test/general/browser_bug590206.js @@ -0,0 +1,138 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +const DUMMY = "browser/browser/base/content/test/general/dummy_page.html"; + +function loadNewTab(aURL, aCallback) { + gBrowser.selectedTab = gBrowser.addTab(); + gBrowser.loadURI(aURL); + + gBrowser.selectedBrowser.addEventListener("load", function() { + if (gBrowser.selectedBrowser.currentURI.spec != aURL) + return; + gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true); + + aCallback(gBrowser.selectedTab); + }, true); +} + +function getIdentityMode() { + return document.getElementById("identity-box").className; +} + +var TESTS = [ +function test_webpage() { + let oldTab = gBrowser.selectedTab; + + loadNewTab("http://example.com/" + DUMMY, function(aNewTab) { + is(getIdentityMode(), "unknownIdentity", "Identity should be unknown"); + + gBrowser.selectedTab = oldTab; + is(getIdentityMode(), "unknownIdentity", "Identity should be unknown"); + + gBrowser.selectedTab = aNewTab; + is(getIdentityMode(), "unknownIdentity", "Identity should be unknown"); + + gBrowser.removeTab(aNewTab); + + runNextTest(); + }); +}, + +function test_blank() { + let oldTab = gBrowser.selectedTab; + + loadNewTab("about:blank", function(aNewTab) { + is(getIdentityMode(), "unknownIdentity", "Identity should be unknown"); + + gBrowser.selectedTab = oldTab; + is(getIdentityMode(), "unknownIdentity", "Identity should be unknown"); + + gBrowser.selectedTab = aNewTab; + is(getIdentityMode(), "unknownIdentity", "Identity should be unknown"); + + gBrowser.removeTab(aNewTab); + + runNextTest(); + }); +}, + +function test_chrome() { + let oldTab = gBrowser.selectedTab; + + // Since users aren't likely to type in full chrome URLs, we won't show + // the positive security indicator on it, but we will show it on about:addons. + loadNewTab("chrome://mozapps/content/extensions/extensions.xul", function(aNewTab) { + is(getIdentityMode(), "unknownIdentity", "Identity should be chrome"); + + gBrowser.selectedTab = oldTab; + is(getIdentityMode(), "unknownIdentity", "Identity should be unknown"); + + gBrowser.selectedTab = aNewTab; + is(getIdentityMode(), "unknownIdentity", "Identity should be chrome"); + + gBrowser.removeTab(aNewTab); + + runNextTest(); + }); +}, + +function test_https() { + let oldTab = gBrowser.selectedTab; + + loadNewTab("https://example.com/" + DUMMY, function(aNewTab) { + is(getIdentityMode(), "verifiedDomain", "Identity should be verified"); + + gBrowser.selectedTab = oldTab; + is(getIdentityMode(), "unknownIdentity", "Identity should be unknown"); + + gBrowser.selectedTab = aNewTab; + is(getIdentityMode(), "verifiedDomain", "Identity should be verified"); + + gBrowser.removeTab(aNewTab); + + runNextTest(); + }); +}, + +function test_addons() { + let oldTab = gBrowser.selectedTab; + + loadNewTab("about:addons", function(aNewTab) { + is(getIdentityMode(), "chromeUI", "Identity should be chrome"); + + gBrowser.selectedTab = oldTab; + is(getIdentityMode(), "unknownIdentity", "Identity should be unknown"); + + gBrowser.selectedTab = aNewTab; + is(getIdentityMode(), "chromeUI", "Identity should be chrome"); + + gBrowser.removeTab(aNewTab); + + runNextTest(); + }); +} +]; + +var gTestStart = null; + +function runNextTest() { + if (gTestStart) + info("Test part took " + (Date.now() - gTestStart) + "ms"); + + if (TESTS.length == 0) { + finish(); + return; + } + + info("Running " + TESTS[0].name); + gTestStart = Date.now(); + TESTS.shift()(); +}; + +function test() { + waitForExplicitFinish(); + + runNextTest(); +} diff --git a/dom/animation/DocumentTimeline.cpp b/dom/animation/DocumentTimeline.cpp index 3d457bab97..dc9f13302b 100644 --- a/dom/animation/DocumentTimeline.cpp +++ b/dom/animation/DocumentTimeline.cpp @@ -110,6 +110,8 @@ void DocumentTimeline::WillRefresh(mozilla::TimeStamp aTime) { MOZ_ASSERT(mIsObservingRefreshDriver); + MOZ_ASSERT(GetRefreshDriver(), + "Should be able to reach refresh driver from within WillRefresh"); bool needsTicks = false; nsTArray animationsToRemove(mAnimations.Count()); @@ -143,10 +145,13 @@ DocumentTimeline::WillRefresh(mozilla::TimeStamp aTime) } if (!needsTicks) { - // If another refresh driver observer destroys the nsPresContext, - // nsRefreshDriver will detect it and we won't be called. + // We already assert that GetRefreshDriver() is non-null at the beginning + // of this function but we check it again here to be sure that ticking + // animations does not have any side effects that cause us to lose the + // connection with the refresh driver, such as triggering the destruction + // of mDocument's PresShell. MOZ_ASSERT(GetRefreshDriver(), - "Refresh driver should still be valid inside WillRefresh"); + "Refresh driver should still be valid at end of WillRefresh"); GetRefreshDriver()->RemoveRefreshObserver(this, Flush_Style); mIsObservingRefreshDriver = false; } diff --git a/dom/archivereader/ArchiveEvent.cpp b/dom/archivereader/ArchiveEvent.cpp index afcc665a0d..164a03eb2d 100644 --- a/dom/archivereader/ArchiveEvent.cpp +++ b/dom/archivereader/ArchiveEvent.cpp @@ -49,17 +49,7 @@ ArchiveReaderEvent::ArchiveReaderEvent(ArchiveReader* aArchiveReader) ArchiveReaderEvent::~ArchiveReaderEvent() { if (!NS_IsMainThread()) { - nsIMIMEService* mimeService; - mMimeService.forget(&mimeService); - - if (mimeService) { - nsCOMPtr mainThread = do_GetMainThread(); - NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread! Leaking!"); - - if (mainThread) { - NS_ProxyRelease(mainThread, mimeService); - } - } + NS_ReleaseOnMainThread(mMimeService.forget()); } MOZ_COUNT_DTOR(ArchiveReaderEvent); diff --git a/dom/base/Console.cpp b/dom/base/Console.cpp index d1ef284004..6aeecb9fb9 100644 --- a/dom/base/Console.cpp +++ b/dom/base/Console.cpp @@ -778,11 +778,11 @@ Console::~Console() if (!NS_IsMainThread()) { if (mStorage) { - NS_ReleaseOnMainThread(mStorage); + NS_ReleaseOnMainThread(mStorage.forget()); } if (mSandbox) { - NS_ReleaseOnMainThread(mSandbox); + NS_ReleaseOnMainThread(mSandbox.forget()); } } diff --git a/dom/base/WebSocket.cpp b/dom/base/WebSocket.cpp index 3b5b4a4819..8b2260fb35 100644 --- a/dom/base/WebSocket.cpp +++ b/dom/base/WebSocket.cpp @@ -636,8 +636,8 @@ WebSocketImpl::Disconnect() // until the end of the method. RefPtr kungfuDeathGrip = this; - NS_ReleaseOnMainThread(mChannel); - NS_ReleaseOnMainThread(static_cast(mService.forget().take())); + NS_ReleaseOnMainThread(mChannel.forget()); + NS_ReleaseOnMainThread(mService.forget()); mWebSocket->DontKeepAliveAnyMore(); mWebSocket->mImpl = nullptr; diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 676a3be42e..5b70906ac5 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -289,6 +289,8 @@ bool nsContentUtils::sFragmentParsingActive = false; bool nsContentUtils::sDOMWindowDumpEnabled; #endif +mozilla::LazyLogModule nsContentUtils::sDOMDumpLog("Dump"); + // Subset of http://www.whatwg.org/specs/web-apps/current-work/#autofill-field-name enum AutocompleteFieldName { @@ -7161,6 +7163,12 @@ nsContentUtils::DOMWindowDumpEnabled() #endif } +mozilla::LogModule* +nsContentUtils::DOMDumpLog() +{ + return sDOMDumpLog; +} + bool nsContentUtils::GetNodeTextContent(nsINode* aNode, bool aDeep, nsAString& aResult, const fallible_t& aFallible) diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index 7b1991ee20..39b801a9e5 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -31,6 +31,7 @@ #include "mozilla/dom/ScriptSettings.h" #include "mozilla/FloatingPoint.h" #include "mozilla/net/ReferrerPolicy.h" +#include "mozilla/Logging.h" #include "nsIContentPolicy.h" #if defined(XP_WIN) @@ -2380,10 +2381,17 @@ public: const nsAString& aVersion); /** - * Return true if the browser.dom.window.dump.enabled pref is set. + * Returns true if the browser.dom.window.dump.enabled pref is set. */ static bool DOMWindowDumpEnabled(); + /** + * Returns a LogModule that dump calls from content script are logged to. + * This can be enabled with the 'Dump' module, and is useful for synchronizing + * content JS to other logging modules. + */ + static mozilla::LogModule* DOMDumpLog(); + /** * Returns whether a content is an insertion point for XBL * bindings or web components ShadowRoot. In web components, @@ -2733,6 +2741,7 @@ private: #if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP)) static bool sDOMWindowDumpEnabled; #endif + static mozilla::LazyLogModule sDOMDumpLog; }; class MOZ_RAII nsAutoScriptBlocker { diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index b64b4ebdf7..9808086e83 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -77,8 +77,8 @@ #include "mozilla/dom/TabChild.h" #include "mozilla/dom/IDBFactoryBinding.h" #include "mozilla/dom/IDBMutableFileBinding.h" -#include "mozilla/dom/indexedDB/IDBMutableFile.h" -#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h" +#include "mozilla/dom/IDBMutableFile.h" +#include "mozilla/dom/IndexedDatabaseManager.h" #include "mozilla/dom/PermissionMessageUtils.h" #include "mozilla/dom/quota/PersistenceType.h" #include "mozilla/dom/quota/QuotaManager.h" @@ -2989,7 +2989,7 @@ nsDOMWindowUtils::GetFileId(JS::Handle aFile, JSContext* aCx, JSObject* obj = aFile.toObjectOrNull(); - indexedDB::IDBMutableFile* mutableFile = nullptr; + IDBMutableFile* mutableFile = nullptr; if (NS_SUCCEEDED(UNWRAP_OBJECT(IDBMutableFile, obj, mutableFile))) { *_retval = mutableFile->GetFileId(); return NS_OK; @@ -3061,8 +3061,7 @@ nsDOMWindowUtils::GetFileReferences(const nsAString& aDatabaseName, int64_t aId, quota::PersistenceType persistenceType = quota::PersistenceTypeFromStorage(options.mStorage); - RefPtr mgr = - indexedDB::IndexedDatabaseManager::Get(); + RefPtr mgr = IndexedDatabaseManager::Get(); if (mgr) { rv = mgr->BlockAndGetFileReferences(persistenceType, origin, aDatabaseName, @@ -3083,8 +3082,6 @@ nsDOMWindowUtils::FlushPendingFileDeletions() { MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); - using mozilla::dom::indexedDB::IndexedDatabaseManager; - RefPtr mgr = IndexedDatabaseManager::Get(); if (mgr) { nsresult rv = mgr->FlushPendingFileDeletions(); diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 79db58ec91..31cb13be12 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -2133,7 +2133,15 @@ nsDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) // Note that, since mTiming does not change during a reset, the // navigationStart time remains unchanged and therefore any future new // timeline will have the same global clock time as the old one. - mDocumentTimeline = nullptr; + if (mDocumentTimeline) { + nsRefreshDriver* rd = mPresShell && mPresShell->GetPresContext() ? + mPresShell->GetPresContext()->RefreshDriver() : + nullptr; + if (rd) { + mDocumentTimeline->NotifyRefreshDriverDestroying(rd); + } + mDocumentTimeline = nullptr; + } nsCOMPtr bag = do_QueryInterface(aChannel); if (bag) { diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index e4409591e3..965cd62097 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -183,8 +183,8 @@ #include "prenv.h" #include "prprf.h" +#include "mozilla/dom/IDBFactory.h" #include "mozilla/dom/MessageChannel.h" -#include "mozilla/dom/indexedDB/IDBFactory.h" #include "mozilla/dom/Promise.h" #ifdef MOZ_GAMEPAD @@ -266,7 +266,6 @@ using mozilla::OriginAttributes; using mozilla::TimeStamp; using mozilla::TimeDuration; using mozilla::dom::cache::CacheStorage; -using mozilla::dom::indexedDB::IDBFactory; static LazyLogModule gDOMLeakPRLog("DOMLeak"); @@ -6481,6 +6480,7 @@ nsGlobalWindow::Dump(const nsAString& aStr) #endif if (cstr) { + MOZ_LOG(nsContentUtils::DOMDumpLog(), LogLevel::Debug, ("[Window.Dump] %s", cstr)); #ifdef XP_WIN PrintToDebugger(cstr); #endif diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index f70f24f860..1a6ea304a1 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -124,9 +124,7 @@ class WindowOrientationObserver; namespace cache { class CacheStorage; } // namespace cache -namespace indexedDB { class IDBFactory; -} // namespace indexedDB } // namespace dom namespace gfx { class VRDeviceProxy; @@ -953,7 +951,7 @@ public: mozilla::dom::DOMStorage* GetLocalStorage(mozilla::ErrorResult& aError); mozilla::dom::Selection* GetSelectionOuter(); mozilla::dom::Selection* GetSelection(mozilla::ErrorResult& aError); - mozilla::dom::indexedDB::IDBFactory* GetIndexedDB(mozilla::ErrorResult& aError); + mozilla::dom::IDBFactory* GetIndexedDB(mozilla::ErrorResult& aError); already_AddRefed GetComputedStyle(mozilla::dom::Element& aElt, const nsAString& aPseudoElt, mozilla::ErrorResult& aError); @@ -1789,7 +1787,7 @@ protected: // unsuspending it. nsCOMPtr mSuspendedDoc; - RefPtr mIndexedDB; + RefPtr mIndexedDB; // This counts the number of windows that have been opened in rapid succession // (i.e. within dom.successive_dialog_time_limit of each other). It is reset diff --git a/dom/base/nsPerformance.cpp b/dom/base/nsPerformance.cpp index d74fbdaf6a..cade636d8f 100644 --- a/dom/base/nsPerformance.cpp +++ b/dom/base/nsPerformance.cpp @@ -11,7 +11,6 @@ #include "nsDOMNavigationTiming.h" #include "nsContentUtils.h" #include "nsIScriptSecurityManager.h" -#include "nsGlobalWindow.h" #include "nsIDOMWindow.h" #include "nsILoadInfo.h" #include "nsIURI.h" @@ -30,8 +29,6 @@ #include "mozilla/Preferences.h" #include "mozilla/IntegerPrintfMacros.h" #include "mozilla/TimeStamp.h" -#include "SharedWorker.h" -#include "ServiceWorker.h" #include "js/HeapAPI.h" #include "GeckoProfiler.h" #include "WorkerPrivate.h" @@ -923,37 +920,6 @@ PerformanceBase::ClearResourceTimings() mResourceEntries.Clear(); } -DOMHighResTimeStamp -PerformanceBase::TranslateTime(DOMHighResTimeStamp aTime, - const WindowOrWorkerOrSharedWorkerOrServiceWorker& aTimeSource, - ErrorResult& aRv) -{ - TimeStamp otherCreationTimeStamp; - - if (aTimeSource.IsWindow()) { - RefPtr performance = aTimeSource.GetAsWindow().GetPerformance(); - if (NS_WARN_IF(!performance)) { - aRv.Throw(NS_ERROR_FAILURE); - } - otherCreationTimeStamp = performance->CreationTimeStamp(); - } else if (aTimeSource.IsWorker()) { - otherCreationTimeStamp = aTimeSource.GetAsWorker().CreationTimeStamp(); - } else if (aTimeSource.IsSharedWorker()) { - SharedWorker& sharedWorker = aTimeSource.GetAsSharedWorker(); - WorkerPrivate* workerPrivate = sharedWorker.GetWorkerPrivate(); - otherCreationTimeStamp = workerPrivate->CreationTimeStamp(); - } else if (aTimeSource.IsServiceWorker()) { - ServiceWorker& serviceWorker = aTimeSource.GetAsServiceWorker(); - WorkerPrivate* workerPrivate = serviceWorker.GetWorkerPrivate(); - otherCreationTimeStamp = workerPrivate->CreationTimeStamp(); - } else { - MOZ_CRASH("This should not be possible."); - } - - return RoundTime( - aTime + (otherCreationTimeStamp - CreationTimeStamp()).ToMilliseconds()); -} - DOMHighResTimeStamp PerformanceBase::RoundTime(double aTime) const { diff --git a/dom/base/nsPerformance.h b/dom/base/nsPerformance.h index 6b1336d9b0..0b2ae4a782 100644 --- a/dom/base/nsPerformance.h +++ b/dom/base/nsPerformance.h @@ -30,7 +30,6 @@ namespace dom { class PerformanceEntry; class PerformanceObserver; -class WindowOrWorkerOrSharedWorkerOrServiceWorker; } // namespace dom } // namespace mozilla @@ -318,11 +317,6 @@ public: virtual DOMHighResTimeStamp Now() const = 0; - DOMHighResTimeStamp - TranslateTime(DOMHighResTimeStamp aTime, - const mozilla::dom::WindowOrWorkerOrSharedWorkerOrServiceWorker& aTimeSource, - mozilla::ErrorResult& aRv); - void Mark(const nsAString& aName, mozilla::ErrorResult& aRv); void ClearMarks(const mozilla::dom::Optional& aName); void Measure(const nsAString& aName, diff --git a/dom/base/nsScriptLoader.cpp b/dom/base/nsScriptLoader.cpp index 2f412f3abe..14208be43e 100644 --- a/dom/base/nsScriptLoader.cpp +++ b/dom/base/nsScriptLoader.cpp @@ -297,7 +297,9 @@ nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType, aRequest->mCORSMode == CORS_NONE ? nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL : nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS; - if (aRequest->mCORSMode == CORS_USE_CREDENTIALS) { + if (aRequest->mCORSMode == CORS_ANONYMOUS) { + securityFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN; + } else if (aRequest->mCORSMode == CORS_USE_CREDENTIALS) { securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE; } securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME; @@ -803,17 +805,8 @@ nsScriptLoader::ProcessOffThreadRequest(nsScriptLoadRequest* aRequest) NotifyOffThreadScriptLoadCompletedRunnable::~NotifyOffThreadScriptLoadCompletedRunnable() { if (MOZ_UNLIKELY(mRequest || mLoader) && !NS_IsMainThread()) { - nsCOMPtr mainThread; - NS_GetMainThread(getter_AddRefs(mainThread)); - if (mainThread) { - NS_ProxyRelease(mainThread, mRequest); - NS_ProxyRelease(mainThread, mLoader); - } else { - MOZ_ASSERT(false, "We really shouldn't leak!"); - // Better to leak than crash. - Unused << mRequest.forget(); - Unused << mLoader.forget(); - } + NS_ReleaseOnMainThread(mRequest.forget()); + NS_ReleaseOnMainThread(mLoader.forget()); } } diff --git a/dom/base/test/empty_worker.js b/dom/base/test/empty_worker.js index 3053583c76..e69de29bb2 100644 --- a/dom/base/test/empty_worker.js +++ b/dom/base/test/empty_worker.js @@ -1 +0,0 @@ -/* nothing here */ diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index faa3af10e3..32a7ffc350 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -256,7 +256,6 @@ support-files = file_explicit_user_agent.sjs referrer_change_server.sjs file_change_policy_redirect.html - empty_worker.js file_bug1198095.js [test_anonymousContent_api.html] @@ -847,6 +846,5 @@ skip-if = e10s || os != 'linux' || buildapp != 'browser' [test_change_policy.html] skip-if = buildapp == 'b2g' #no ssl support [test_document.all_iteration.html] -[test_performance_translate.html] [test_bug1198095.html] [test_bug1187157.html] diff --git a/dom/base/test/test_performance_translate.html b/dom/base/test/test_performance_translate.html index a2e14495fc..e69de29bb2 100644 --- a/dom/base/test/test_performance_translate.html +++ b/dom/base/test/test_performance_translate.html @@ -1,75 +0,0 @@ - - - - Test for performance.translate() - - - - - -
-      
-    
- - diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index d55d5f0170..ded224765a 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -612,7 +612,6 @@ DOMInterfaces = { }, 'IDBCursor': { - 'nativeType': 'mozilla::dom::indexedDB::IDBCursor', 'implicitJSContext': [ 'delete' ], 'binaryNames': { 'direction': 'getDirection' @@ -620,27 +619,10 @@ DOMInterfaces = { }, 'IDBCursorWithValue': { - 'nativeType': 'mozilla::dom::indexedDB::IDBCursor', -}, - -'IDBDatabase': { - 'nativeType': 'mozilla::dom::indexedDB::IDBDatabase', -}, - -'IDBFactory': { - 'nativeType': 'mozilla::dom::indexedDB::IDBFactory', -}, - -'IDBFileHandle': { - 'nativeType': 'mozilla::dom::indexedDB::IDBFileHandle', -}, - -'IDBFileRequest': { - 'nativeType': 'mozilla::dom::indexedDB::IDBFileRequest', + 'nativeType': 'mozilla::dom::IDBCursor', }, 'IDBIndex': { - 'nativeType': 'mozilla::dom::indexedDB::IDBIndex', 'binaryNames': { 'mozGetAll': 'getAll', 'mozGetAllKeys': 'getAllKeys', @@ -648,43 +630,25 @@ DOMInterfaces = { }, 'IDBKeyRange': { - 'nativeType': 'mozilla::dom::indexedDB::IDBKeyRange', - 'headerFile': 'mozilla/dom/indexedDB/IDBKeyRange.h', 'wrapperCache': False, }, 'IDBLocaleAwareKeyRange': { - 'nativeType': 'mozilla::dom::indexedDB::IDBLocaleAwareKeyRange', - 'headerFile': 'mozilla/dom/indexedDB/IDBKeyRange.h', + 'headerFile': 'IDBKeyRange.h', 'wrapperCache': False, }, -'IDBMutableFile': { - 'nativeType': 'mozilla::dom::indexedDB::IDBMutableFile', -}, - 'IDBObjectStore': { - 'nativeType': 'mozilla::dom::indexedDB::IDBObjectStore', 'binaryNames': { 'mozGetAll': 'getAll' } }, 'IDBOpenDBRequest': { - 'nativeType': 'mozilla::dom::indexedDB::IDBOpenDBRequest', 'headerFile': 'IDBRequest.h' }, -'IDBRequest': { - 'nativeType': 'mozilla::dom::indexedDB::IDBRequest', -}, - -'IDBTransaction': { - 'nativeType': 'mozilla::dom::indexedDB::IDBTransaction', -}, - 'IDBVersionChangeEvent': { - 'nativeType': 'mozilla::dom::indexedDB::IDBVersionChangeEvent', 'headerFile': 'IDBEvents.h', }, diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 94f0df0c4d..ff4ae5dfa2 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -1138,11 +1138,13 @@ class CGHeaders(CGWrapper): if desc.interface.maplikeOrSetlikeOrIterable: # We need ToJSValue.h for maplike/setlike type conversions bindingHeaders.add("mozilla/dom/ToJSValue.h") - # Add headers for the key and value types of the maplike, since - # they'll be needed for convenience functions - addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.keyType, - desc, None)) - if desc.interface.maplikeOrSetlikeOrIterable.valueType: + # Add headers for the key and value types of the + # maplike/setlike/iterable, since they'll be needed for + # convenience functions + if desc.interface.maplikeOrSetlikeOrIterable.hasKeyType(): + addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.keyType, + desc, None)) + if desc.interface.maplikeOrSetlikeOrIterable.hasValueType(): addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.valueType, desc, None)) @@ -2271,34 +2273,49 @@ class MethodDefiner(PropertyDefiner): "condition": MemberCondition() }) - # Generate the maplike/setlike iterator, if one wasn't already - # generated by a method. If we already have an @@iterator symbol, fail. - if descriptor.interface.maplikeOrSetlikeOrIterable: - if hasIterator(methods, self.regular): - raise TypeError("Cannot have maplike/setlike/iterable interface with " - "other members that generate @@iterator " - "on interface %s, such as indexed getters " - "or aliased functions." % - self.descriptor.interface.identifier.name) - for m in methods: - if (m.isMaplikeOrSetlikeOrIterableMethod() and - (((m.maplikeOrSetlikeOrIterable.isMaplike() or - (m.maplikeOrSetlikeOrIterable.isIterable() and - m.maplikeOrSetlikeOrIterable.hasValueType())) and - m.identifier.name == "entries") or - (((m.maplikeOrSetlikeOrIterable.isSetlike() or - (m.maplikeOrSetlikeOrIterable.isIterable() and - not m.maplikeOrSetlikeOrIterable.hasValueType()))) and - m.identifier.name == "values"))): - self.regular.append({ - "name": "@@iterator", - "methodName": m.identifier.name, - "length": methodLength(m), - "flags": "0", - "condition": PropertyDefiner.getControllingCondition(m, - descriptor), - }) - break + # Generate the keys/values/entries aliases for value iterables. + maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable + if (not static and + maplikeOrSetlikeOrIterable and + maplikeOrSetlikeOrIterable.isIterable() and + maplikeOrSetlikeOrIterable.isValueIterator()): + # Add our keys/values/entries/forEach + self.regular.append({ + "name": "keys", + "methodInfo": False, + "selfHostedName": "ArrayKeys", + "length": 0, + "flags": "JSPROP_ENUMERATE", + "condition": PropertyDefiner.getControllingCondition(m, + descriptor) + }) + self.regular.append({ + "name": "values", + "methodInfo": False, + "selfHostedName": "ArrayValues", + "length": 0, + "flags": "JSPROP_ENUMERATE", + "condition": PropertyDefiner.getControllingCondition(m, + descriptor) + }) + self.regular.append({ + "name": "entries", + "methodInfo": False, + "selfHostedName": "ArrayEntries", + "length": 0, + "flags": "JSPROP_ENUMERATE", + "condition": PropertyDefiner.getControllingCondition(m, + descriptor) + }) + self.regular.append({ + "name": "forEach", + "methodInfo": False, + "selfHostedName": "ArrayForEach", + "length": 0, + "flags": "JSPROP_ENUMERATE", + "condition": PropertyDefiner.getControllingCondition(m, + descriptor) + }) if not static: stringifier = descriptor.operations['Stringifier'] @@ -13079,8 +13096,9 @@ class CGForwardDeclarations(CGWrapper): # arguments to helper functions, and they'll need to be forward # declared in the header. if d.interface.maplikeOrSetlikeOrIterable: - builder.forwardDeclareForType(d.interface.maplikeOrSetlikeOrIterable.keyType, - config) + if d.interface.maplikeOrSetlikeOrIterable.hasKeyType(): + builder.forwardDeclareForType(d.interface.maplikeOrSetlikeOrIterable.keyType, + config) if d.interface.maplikeOrSetlikeOrIterable.hasValueType(): builder.forwardDeclareForType(d.interface.maplikeOrSetlikeOrIterable.valueType, config) @@ -15808,6 +15826,31 @@ class CGIterableMethodGenerator(CGGeneric): using CGCallGenerator. """ def __init__(self, descriptor, iterable, methodName): + if methodName == "forEach": + CGGeneric.__init__(self, fill( + """ + if (!JS::IsCallable(arg0)) { + ThrowErrorMessage(cx, MSG_NOT_CALLABLE, "Argument 1 of ${ifaceName}.forEach"); + return false; + } + JS::AutoValueArray<3> callArgs(cx); + callArgs[2].setObject(*obj); + JS::Rooted ignoredReturnVal(cx); + for (size_t i = 0; i < self->GetIterableLength(); ++i) { + if (!ToJSValue(cx, self->GetValueAtIndex(i), callArgs[0])) { + return false; + } + if (!ToJSValue(cx, self->GetKeyAtIndex(i), callArgs[1])) { + return false; + } + if (!JS::Call(cx, arg1, arg0, JS::HandleValueArray(callArgs), + &ignoredReturnVal)) { + return false; + } + } + """, + ifaceName=descriptor.interface.identifier.name)) + return CGGeneric.__init__(self, fill( """ typedef ${iterClass} itrType; diff --git a/dom/bindings/Configuration.py b/dom/bindings/Configuration.py index 22a3599360..1b2e9134a7 100644 --- a/dom/bindings/Configuration.py +++ b/dom/bindings/Configuration.py @@ -900,8 +900,5 @@ def getAllTypes(descriptors, dictionaries, callbacks): def iteratorNativeType(descriptor): assert descriptor.interface.isIterable() iterableDecl = descriptor.interface.maplikeOrSetlikeOrIterable - if iterableDecl.valueType is None: - iterClass = "OneTypeIterableIterator" - else: - iterClass = "TwoTypeIterableIterator" - return "mozilla::dom::%s<%s>" % (iterClass, descriptor.nativeType) + assert iterableDecl.isPairIterator() + return "mozilla::dom::IterableIterator<%s>" % descriptor.nativeType diff --git a/dom/bindings/IterableIterator.h b/dom/bindings/IterableIterator.h index 64eaef70e7..1a56cec33f 100644 --- a/dom/bindings/IterableIterator.h +++ b/dom/bindings/IterableIterator.h @@ -6,19 +6,17 @@ /** * The IterableIterator class is used for WebIDL interfaces that have a - * iterable<> member defined. It handles the ES6 Iterator-like functions that - * are generated for the iterable interface. + * iterable<> member defined with two types (so a pair iterator). It handles + * the ES6 Iterator-like functions that are generated for the iterable + * interface. * - * For iterable interfaces, the implementation class will need to - * implement these two functions: + * For iterable interfaces with a pair iterator, the implementation class will + * need to implement these two functions: * * - size_t GetIterableLength() * - Returns the number of elements available to iterate over * - [type] GetValueAtIndex(size_t index) * - Returns the value at the requested index. - * - * If this is a two-type iterator, then the implementation class will also need to implement: - * * - [type] GetKeyAtIndex(size_t index) * - Returns the key at the requested index * @@ -60,13 +58,77 @@ protected: }; template -class IterableIterator : public IterableIteratorBase +class IterableIterator final : public IterableIteratorBase { public: - explicit IterableIterator(T* aIterableObj) + typedef bool (*WrapFunc)(JSContext* aCx, + IterableIterator* aObject, + JS::Handle aGivenProto, + JS::MutableHandle aReflector); + + explicit IterableIterator(T* aIterableObj, + IterableIteratorType aIteratorType, + WrapFunc aWrapFunc) : mIterableObj(aIterableObj) + , mIteratorType(aIteratorType) + , mWrapFunc(aWrapFunc) + , mIndex(0) { MOZ_ASSERT(mIterableObj); + MOZ_ASSERT(mWrapFunc); + } + + void + Next(JSContext* aCx, JS::MutableHandle aResult, ErrorResult& aRv) + { + JS::Rooted value(aCx, JS::UndefinedValue()); + if (mIndex >= this->mIterableObj->GetIterableLength()) { + DictReturn(aCx, aResult, true, value, aRv); + return; + } + switch (mIteratorType) { + case IterableIteratorType::Keys: + { + if (!ToJSValue(aCx, this->mIterableObj->GetKeyAtIndex(mIndex), &value)) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + DictReturn(aCx, aResult, false, value, aRv); + break; + } + case IterableIteratorType::Values: + { + if (!ToJSValue(aCx, this->mIterableObj->GetValueAtIndex(mIndex), &value)) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + DictReturn(aCx, aResult, false, value, aRv); + break; + } + case IterableIteratorType::Entries: + { + JS::Rooted key(aCx); + if (!ToJSValue(aCx, this->mIterableObj->GetKeyAtIndex(mIndex), &key)) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + if (!ToJSValue(aCx, this->mIterableObj->GetValueAtIndex(mIndex), &value)) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + KeyAndValueReturn(aCx, key, value, aResult, aRv); + break; + } + default: + MOZ_CRASH("Invalid iterator type!"); + } + ++mIndex; + } + + bool + WrapObject(JSContext* aCx, JS::Handle aGivenProto, JS::MutableHandle aObj) + { + return (*mWrapFunc)(aCx, this, aGivenProto, aObj); } protected: @@ -128,161 +190,6 @@ protected: // Binding Implementation object that we're iterating over. RefPtr mIterableObj; -}; - -template -class OneTypeIterableIterator final : public IterableIterator -{ -public: - typedef typename IterableIterator::IterableIteratorType IterableIteratorType; - using IterableIterator::DictReturn; - using IterableIterator::KeyAndValueReturn; - typedef bool (*WrapFunc)(JSContext* aCx, - OneTypeIterableIterator* aObject, - JS::Handle aGivenProto, - JS::MutableHandle aReflector); - - OneTypeIterableIterator(T* aIterableObj, - IterableIteratorType aIteratorType, - WrapFunc aWrapFunc) - : IterableIterator(aIterableObj) - , mIteratorType(aIteratorType) - , mWrapFunc(aWrapFunc) - , mIndex(0) - { - MOZ_ASSERT(mWrapFunc); - } - - void - Next(JSContext* aCx, JS::MutableHandle aResult, ErrorResult& aRv) - { - JS::Rooted value(aCx, JS::UndefinedValue()); - if (mIndex >= this->mIterableObj->GetIterableLength()) { - DictReturn(aCx, aResult, true, value, aRv); - return; - } - - switch (mIteratorType) { - case IterableIteratorType::Keys: - case IterableIteratorType::Values: - { - if (!ToJSValue(aCx, this->mIterableObj->GetValueAtIndex(mIndex), &value)) { - aRv.Throw(NS_ERROR_FAILURE); - return; - } - DictReturn(aCx, aResult, false, value, aRv); - break; - } - case IterableIteratorType::Entries: - { - if (!ToJSValue(aCx, this->mIterableObj->GetValueAtIndex(mIndex), &value)) { - aRv.Throw(NS_ERROR_FAILURE); - return; - } - KeyAndValueReturn(aCx, value, value, aResult, aRv); - break; - } - default: - MOZ_CRASH("Invalid iterator type!"); - } - ++mIndex; - } - - bool - WrapObject(JSContext* aCx, JS::Handle aGivenProto, JS::MutableHandle aObj) - { - return (*mWrapFunc)(aCx, this, aGivenProto, aObj); - } - -protected: - virtual ~OneTypeIterableIterator() {} - - // Tells whether this is a key, value, or entries iterator. - IterableIteratorType mIteratorType; - // Function pointer to binding-type-specific Wrap() call for this iterator. - WrapFunc mWrapFunc; - // Current index of iteration. - uint32_t mIndex; -}; - -template -class TwoTypeIterableIterator final : public IterableIterator -{ -public: - typedef typename IterableIterator::IterableIteratorType IterableIteratorType; - using IterableIterator::DictReturn; - using IterableIterator::KeyAndValueReturn; - typedef bool (*WrapFunc)(JSContext* aCx, - TwoTypeIterableIterator* aObject, - JS::Handle aGivenProto, - JS::MutableHandle aReflector); - - TwoTypeIterableIterator(T* aIterableObj, IterableIteratorType aIteratorType, - WrapFunc aWrapFunc) - : IterableIterator(aIterableObj) - , mIteratorType(aIteratorType) - , mWrapFunc(aWrapFunc) - , mIndex(0) - { - MOZ_ASSERT(mWrapFunc); - } - - void - Next(JSContext* aCx, JS::MutableHandle aResult, ErrorResult& aRv) - { - JS::Rooted value(aCx, JS::UndefinedValue()); - if (mIndex >= this->mIterableObj->GetIterableLength()) { - DictReturn(aCx, aResult, true, value, aRv); - return; - } - switch (mIteratorType) { - case IterableIteratorType::Keys: - { - if (!ToJSValue(aCx, this->mIterableObj->GetKeyAtIndex(mIndex), &value)) { - aRv.Throw(NS_ERROR_FAILURE); - return; - } - DictReturn(aCx, aResult, false, value, aRv); - break; - } - case IterableIteratorType::Values: - { - if (!ToJSValue(aCx, this->mIterableObj->GetValueAtIndex(mIndex), &value)) { - aRv.Throw(NS_ERROR_FAILURE); - return; - } - DictReturn(aCx, aResult, false, value, aRv); - break; - } - case IterableIteratorType::Entries: - { - JS::Rooted key(aCx); - if (!ToJSValue(aCx, this->mIterableObj->GetKeyAtIndex(mIndex), &key)) { - aRv.Throw(NS_ERROR_FAILURE); - return; - } - if (!ToJSValue(aCx, this->mIterableObj->GetValueAtIndex(mIndex), &value)) { - aRv.Throw(NS_ERROR_FAILURE); - return; - } - KeyAndValueReturn(aCx, key, value, aResult, aRv); - break; - } - default: - MOZ_CRASH("Invalid iterator type!"); - } - ++mIndex; - } - - bool - WrapObject(JSContext* aCx, JS::Handle aGivenProto, JS::MutableHandle aObj) - { - return (*mWrapFunc)(aCx, this, aGivenProto, aObj); - } - -protected: - virtual ~TwoTypeIterableIterator() {} - // Tells whether this is a key, value, or entries iterator. IterableIteratorType mIteratorType; // Function pointer to binding-type-specific Wrap() call for this iterator. diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py index 3b095e19ef..5c792fa2a7 100644 --- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -1092,7 +1092,7 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins): def validate(self): # We don't support consequential unforgeable interfaces. Need to check - # this here, becaue in finish() an interface might not know yet that + # this here, because in finish() an interface might not know yet that # it's consequential. if self.getExtendedAttribute("Unforgeable") and self.isConsequential(): raise WebIDLError( @@ -1111,6 +1111,8 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins): self.identifier.name, locations) + indexedGetter = None + hasLengthAttribute = False for member in self.members: member.validate() @@ -1121,8 +1123,13 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins): [self.location, member.location]) # Check that PutForwards refers to another attribute and that no - # cycles exist in forwarded assignments. + # cycles exist in forwarded assignments. Also check for a + # integer-typed "length" attribute. if member.isAttr(): + if (member.identifier.name == "length" and + member.type.isInteger()): + hasLengthAttribute = True + iface = self attr = member putForwards = attr.getExtendedAttribute("PutForwards") @@ -1160,8 +1167,11 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins): putForwards = attr.getExtendedAttribute("PutForwards") # Check that the name of an [Alias] doesn't conflict with an - # interface member. + # interface member and whether we support indexed properties. if member.isMethod(): + if member.isGetter() and member.isIndexed(): + indexedGetter = member + for alias in member.aliases: if self.isOnGlobalProtoChain(): raise WebIDLError("[Alias] must not be used on a " @@ -1222,6 +1232,35 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins): "exposed conditionally", [self.location]) + # Value iterators are only allowed on interfaces with indexed getters, + # and pair iterators are only allowed on interfaces without indexed + # getters. + if self.isIterable(): + iterableDecl = self.maplikeOrSetlikeOrIterable + if iterableDecl.isValueIterator(): + if not indexedGetter: + raise WebIDLError("Interface with value iterator does not " + "support indexed properties", + [self.location]) + + if iterableDecl.valueType != indexedGetter.signatures()[0][0]: + raise WebIDLError("Iterable type does not match indexed " + "getter type", + [iterableDecl.location, + indexedGetter.location]) + + if not hasLengthAttribute: + raise WebIDLError('Interface with value iterator does not ' + 'have an integer-typed "length" attribute', + [self.location]) + else: + assert iterableDecl.isPairIterator() + if indexedGetter: + raise WebIDLError("Interface with pair iterator supports " + "indexed properties", + [self.location, iterableDecl.location, + indexedGetter.location]) + def isInterface(self): return True @@ -3420,7 +3459,10 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember): def __init__(self, location, identifier, ifaceType, keyType, valueType, ifaceKind): IDLInterfaceMember.__init__(self, location, identifier, ifaceKind) - assert isinstance(keyType, IDLType) + if keyType is not None: + assert isinstance(keyType, IDLType) + else: + assert valueType is not None assert ifaceType in ['maplike', 'setlike', 'iterable'] if valueType is not None: assert isinstance(valueType, IDLType) @@ -3439,6 +3481,9 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember): def isIterable(self): return self.maplikeOrSetlikeOrIterableType == "iterable" + def hasKeyType(self): + return self.keyType is not None + def hasValueType(self): return self.valueType is not None @@ -3463,7 +3508,8 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember): [self.location, member.location]) def addMethod(self, name, members, allowExistingOperations, returnType, args=[], - chromeOnly=False, isPure=False, affectsNothing=False, newObject=False): + chromeOnly=False, isPure=False, affectsNothing=False, newObject=False, + isIteratorAlias=False): """ Create an IDLMethod based on the parameters passed in. @@ -3523,16 +3569,20 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember): if newObject: method.addExtendedAttributes( [IDLExtendedAttribute(self.location, ("NewObject",))]) + if isIteratorAlias: + method.addExtendedAttributes( + [IDLExtendedAttribute(self.location, ("Alias", "@@iterator"))]) members.append(method) def resolve(self, parentScope): - self.keyType.resolveType(parentScope) + if self.keyType: + self.keyType.resolveType(parentScope) if self.valueType: self.valueType.resolveType(parentScope) def finish(self, scope): IDLInterfaceMember.finish(self, scope) - if not self.keyType.isComplete(): + if self.keyType and not self.keyType.isComplete(): t = self.keyType.complete(scope) assert not isinstance(t, IDLUnresolvedType) @@ -3554,9 +3604,23 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember): IDLInterfaceMember.handleExtendedAttribute(self, attr) def _getDependentObjects(self): + deps = set() + if self.keyType: + deps.add(self.keyType) if self.valueType: - return set([self.keyType, self.valueType]) - return set([self.keyType]) + deps.add(self.valueType) + return deps + + def getForEachArguments(self): + return [IDLArgument(self.location, + IDLUnresolvedIdentifier(BuiltinLocation(""), + "callback"), + BuiltinTypes[IDLBuiltinType.Types.object]), + IDLArgument(self.location, + IDLUnresolvedIdentifier(BuiltinLocation(""), + "thisArg"), + BuiltinTypes[IDLBuiltinType.Types.any], + optional=True)] # Iterable adds ES6 iterator style functions and traits # (keys/values/entries/@@iterator) to an interface. @@ -3577,9 +3641,15 @@ class IDLIterable(IDLMaplikeOrSetlikeOrIterableBase): we generate our functions as if they were part of the interface specification during parsing. """ + # We only need to add entries/keys/values here if we're a pair iterator. + # Value iterators just copy these from %ArrayPrototype% instead. + if not self.isPairIterator(): + return + # object entries() self.addMethod("entries", members, False, self.iteratorType, - affectsNothing=True, newObject=True) + affectsNothing=True, newObject=True, + isIteratorAlias=True) # object keys() self.addMethod("keys", members, False, self.iteratorType, affectsNothing=True, newObject=True) @@ -3587,6 +3657,17 @@ class IDLIterable(IDLMaplikeOrSetlikeOrIterableBase): self.addMethod("values", members, False, self.iteratorType, affectsNothing=True, newObject=True) + # void forEach(callback(valueType, keyType), optional any thisArg) + self.addMethod("forEach", members, False, + BuiltinTypes[IDLBuiltinType.Types.void], + self.getForEachArguments()) + + def isValueIterator(self): + return not self.isPairIterator() + + def isPairIterator(self): + return self.hasKeyType() + # MaplikeOrSetlike adds ES6 map-or-set-like traits to an interface. class IDLMaplikeOrSetlike(IDLMaplikeOrSetlikeOrIterableBase): @@ -3623,26 +3704,17 @@ class IDLMaplikeOrSetlike(IDLMaplikeOrSetlikeOrIterableBase): # object entries() self.addMethod("entries", members, False, BuiltinTypes[IDLBuiltinType.Types.object], - affectsNothing=True) + affectsNothing=True, isIteratorAlias=self.isMaplike()) # object keys() self.addMethod("keys", members, False, BuiltinTypes[IDLBuiltinType.Types.object], affectsNothing=True) # object values() self.addMethod("values", members, False, BuiltinTypes[IDLBuiltinType.Types.object], - affectsNothing=True) + affectsNothing=True, isIteratorAlias=self.isSetlike()) # void forEach(callback(valueType, keyType), thisVal) - foreachArguments = [IDLArgument(self.location, - IDLUnresolvedIdentifier(BuiltinLocation(""), - "callback"), - BuiltinTypes[IDLBuiltinType.Types.object]), - IDLArgument(self.location, - IDLUnresolvedIdentifier(BuiltinLocation(""), - "thisArg"), - BuiltinTypes[IDLBuiltinType.Types.any], - optional=True)] self.addMethod("forEach", members, False, BuiltinTypes[IDLBuiltinType.Types.void], - foreachArguments) + self.getForEachArguments()) def getKeyArg(): return IDLArgument(self.location, @@ -5448,10 +5520,13 @@ class Parser(Tokenizer): location = self.getLocation(p, 2) identifier = IDLUnresolvedIdentifier(location, "__iterable", allowDoubleUnderscore=True) - keyType = p[3] - valueType = None if (len(p) > 6): + keyType = p[3] valueType = p[5] + else: + keyType = None + valueType = p[3] + p[0] = IDLIterable(location, identifier, keyType, valueType, self.globalScope()) def p_Setlike(self, p): @@ -6521,7 +6596,7 @@ class Parser(Tokenizer): if isinstance(m, IDLIterable): iterable = m break - if iterable: + if iterable and iterable.isPairIterator(): def simpleExtendedAttr(str): return IDLExtendedAttribute(iface.location, (str, )) nextMethod = IDLMethod( diff --git a/dom/bindings/parser/tests/test_interface_maplikesetlikeiterable.py b/dom/bindings/parser/tests/test_interface_maplikesetlikeiterable.py index ed6f4c40a4..159b50f84f 100644 --- a/dom/bindings/parser/tests/test_interface_maplikesetlikeiterable.py +++ b/dom/bindings/parser/tests/test_interface_maplikesetlikeiterable.py @@ -45,8 +45,8 @@ def WebIDLTest(parser, harness): prefix + " - Interface failed but not as a WebIDLError exception: %s" % e) iterableMembers = [(x, WebIDL.IDLMethod) for x in ["entries", "keys", - "values"]] - setROMembers = ([(x, WebIDL.IDLMethod) for x in ["has", "forEach"]] + + "values", "forEach"]] + setROMembers = ([(x, WebIDL.IDLMethod) for x in ["has"]] + [("__setlike", WebIDL.IDLMaplikeOrSetlike)] + iterableMembers) setROMembers.extend([("size", WebIDL.IDLAttribute)]) @@ -62,7 +62,7 @@ def WebIDLTest(parser, harness): "__clear", "__delete"]] + setRWMembers) - mapROMembers = ([(x, WebIDL.IDLMethod) for x in ["get", "has", "forEach"]] + + mapROMembers = ([(x, WebIDL.IDLMethod) for x in ["get", "has"]] + [("__maplike", WebIDL.IDLMaplikeOrSetlike)] + iterableMembers) mapROMembers.extend([("size", WebIDL.IDLAttribute)]) @@ -78,6 +78,10 @@ def WebIDLTest(parser, harness): # __iterable to it for the iterable<> case. iterableMembers.append(("__iterable", WebIDL.IDLIterable)) + valueIterableMembers = [("__iterable", WebIDL.IDLIterable)] + valueIterableMembers.append(("__indexedgetter", WebIDL.IDLMethod)) + valueIterableMembers.append(("length", WebIDL.IDLAttribute)) + disallowedIterableNames = ["keys", "entries", "values"] disallowedMemberNames = ["forEach", "has", "size"] + disallowedIterableNames mapDisallowedMemberNames = ["get"] + disallowedMemberNames @@ -93,10 +97,10 @@ def WebIDLTest(parser, harness): """ interface Foo1 { iterable; + readonly attribute unsigned long length; + getter long(unsigned long index); }; - """, iterableMembers, - # numProductions == 2 because of the generated iterator iface, - numProductions=2) + """, valueIterableMembers) shouldPass("Iterable (key and value)", """ diff --git a/dom/bindings/test/TestInterfaceIterableSingle.cpp b/dom/bindings/test/TestInterfaceIterableSingle.cpp index a79f91baf8..797cfebeb9 100644 --- a/dom/bindings/test/TestInterfaceIterableSingle.cpp +++ b/dom/bindings/test/TestInterfaceIterableSingle.cpp @@ -23,7 +23,7 @@ NS_INTERFACE_MAP_END TestInterfaceIterableSingle::TestInterfaceIterableSingle(nsPIDOMWindow* aParent) : mParent(aParent) { - for(int i = 0; i < 3; ++i) { + for (int i = 0; i < 3; ++i) { mValues.AppendElement(i); } } @@ -55,17 +55,22 @@ TestInterfaceIterableSingle::GetParentObject() const return mParent; } -size_t -TestInterfaceIterableSingle::GetIterableLength() const +uint32_t +TestInterfaceIterableSingle::Length() const { return mValues.Length(); } -uint32_t -TestInterfaceIterableSingle::GetValueAtIndex(uint32_t index) const +int32_t +TestInterfaceIterableSingle::IndexedGetter(uint32_t aIndex, bool& aFound) const { - MOZ_ASSERT(index < mValues.Length()); - return mValues.ElementAt(index); + if (aIndex >= mValues.Length()) { + aFound = false; + return 0; + } + + aFound = true; + return mValues[aIndex]; } } // namespace dom diff --git a/dom/bindings/test/TestInterfaceIterableSingle.h b/dom/bindings/test/TestInterfaceIterableSingle.h index 950858c0bc..95a9ba21c9 100644 --- a/dom/bindings/test/TestInterfaceIterableSingle.h +++ b/dom/bindings/test/TestInterfaceIterableSingle.h @@ -36,12 +36,12 @@ public: static already_AddRefed Constructor(const GlobalObject& aGlobal, ErrorResult& rv); - size_t GetIterableLength() const; - uint32_t GetValueAtIndex(uint32_t aIndex) const; + uint32_t Length() const; + int32_t IndexedGetter(uint32_t aIndex, bool& aFound) const; private: virtual ~TestInterfaceIterableSingle() {} nsCOMPtr mParent; - nsTArray mValues; + nsTArray mValues; }; } // namespace dom diff --git a/dom/bindings/test/test_bug1123516_maplikesetlike.html b/dom/bindings/test/test_bug1123516_maplikesetlike.html index 58fcf2fb32..18ede38ace 100644 --- a/dom/bindings/test/test_bug1123516_maplikesetlike.html +++ b/dom/bindings/test/test_bug1123516_maplikesetlike.html @@ -93,7 +93,8 @@ is(e[1], 1, "SimpleMap: iterable second array element should be value"); } is(m[Symbol.iterator].length, 0, "SimpleMap: @@iterator symbol is correct length"); - is(m[Symbol.iterator].name, "[Symbol.iterator]", "SimpleMap: @@iterator symbol has correct name"); + is(m[Symbol.iterator].name, "entries", "SimpleMap: @@iterator symbol has correct name"); + is(m[Symbol.iterator], m.entries, 'SimpleMap: @@iterator is an alias for "entries"'); ok(iterable, "SimpleMap: @@iterator symbol resolved correctly"); for (var k of m.keys()) { is(k, "test", "SimpleMap: first keys element should be 'test'"); @@ -139,7 +140,8 @@ is(e, "test", "SimpleSet: iterable first array element should be key"); } is(m[Symbol.iterator].length, 0, "SimpleSet: @@iterator symbol is correct length"); - is(m[Symbol.iterator].name, "[Symbol.iterator]", "SimpleSet: @@iterator symbol has correct name"); + is(m[Symbol.iterator].name, "values", "SimpleSet: @@iterator symbol has correct name"); + is(m[Symbol.iterator], m.values, 'SimpleSet: @@iterator is an alias for "values"'); ok(iterable, "SimpleSet: @@iterator symbol resolved correctly"); for (var k of m.keys()) { is(k, "test", "SimpleSet: first keys element should be 'test'"); diff --git a/dom/bindings/test/test_iterable.html b/dom/bindings/test/test_iterable.html index fb37c22691..96e3f5468b 100644 --- a/dom/bindings/test/test_iterable.html +++ b/dom/bindings/test/test_iterable.html @@ -14,7 +14,8 @@ base_properties = [["entries", "function", 0], ["keys", "function", 0], - ["values", "function", 0]] + ["values", "function", 0], + ["forEach", "function", 1]] var testExistence = function testExistence(prefix, obj, properties) { for (var [name, type, args] of properties) { // Properties are somewhere up the proto chain, hasOwnProperty won't work @@ -50,6 +51,16 @@ info("IterableSingle: Testing simple iterable creation and functionality"); itr = new TestInterfaceIterableSingle(); testExistence("IterableSingle: ", itr, base_properties); + is(itr[Symbol.iterator], Array.prototype[Symbol.iterator], + "IterableSingle: Should be using %ArrayIterator% for @@iterator"); + is(itr.keys, Array.prototype.keys, + "IterableSingle: Should be using %ArrayIterator% for 'keys'"); + is(itr.entries, Array.prototype.entries, + "IterableSingle: Should be using %ArrayIterator% for 'entries'"); + is(itr.values, itr[Symbol.iterator], + "IterableSingle: Should be using @@iterator for 'values'"); + is(itr.forEach, Array.prototype.forEach, + "IterableSingle: Should be using %ArrayIterator% for 'forEach'"); var keys = [...itr.keys()]; var values = [...itr.values()]; var entries = [...itr.entries()]; @@ -73,6 +84,23 @@ is(entry.value[1], entries[i][1], "IterableSingle: Entry iterator value 1 should match destructuring " + i); } + + var callsToForEachCallback = 0; + var thisArg = {}; + itr.forEach(function(value, index, obj) { + is(index, callsToForEachCallback, + `IterableSingle: Should have the right index at ${callsToForEachCallback} calls to forEach callback`); + is(value, values[index], + `IterableSingle: Should have the right value at ${callsToForEachCallback} calls to forEach callback`); + is(this, thisArg, + "IterableSingle: Should have the right this value for forEach callback"); + is(obj, itr, + "IterableSingle: Should have the right third arg for forEach callback"); + ++callsToForEachCallback; + }, thisArg); + is(callsToForEachCallback, 3, + "IterableSingle: Should have right total number of calls to forEach callback"); + var key = key_itr.next(); var value = value_itr.next(); var entry = entries_itr.next(); @@ -83,13 +111,15 @@ is(entry.value, undefined, "IterableDouble: Entry iterator value should be undefined"); is(entry.done, true, "IterableSingle: Entry iterator done should be true"); is(Object.prototype.toString.call(Object.getPrototypeOf(key_itr)), - "[object TestInterfaceIterableSingleIteratorPrototype]", + "[object Array Iterator]", "iterator prototype should have the right brand"); // Simple dual type iterable creation and functionality test info("IterableDouble: Testing simple iterable creation and functionality"); itr = new TestInterfaceIterableDouble(); testExistence("IterableDouble: ", itr, base_properties); + is(itr.entries, itr[Symbol.iterator], + "IterableDouble: Should be using @@iterator for 'entries'"); var elements = [["a", "b"], ["c", "d"], ["e", "f"]] var keys = [...itr.keys()]; var values = [...itr.values()]; @@ -114,6 +144,23 @@ is(entry.value[1], entries[i][1], "IterableDouble: Entry iterator value 1 should match destructuring " + i); } + + callsToForEachCallback = 0; + thisArg = {}; + itr.forEach(function(value, key, obj) { + is(key, keys[callsToForEachCallback], + `IterableDouble: Should have the right key at ${callsToForEachCallback} calls to forEach callback`); + is(value, values[callsToForEachCallback], + `IterableDouble: Should have the right value at ${callsToForEachCallback} calls to forEach callback`); + is(this, thisArg, + "IterableDouble: Should have the right this value for forEach callback"); + is(obj, itr, + "IterableSingle: Should have the right third arg for forEach callback"); + ++callsToForEachCallback; + }, thisArg); + is(callsToForEachCallback, 3, + "IterableDouble: Should have right total number of calls to forEach callback"); + var key = key_itr.next(); var value = value_itr.next(); var entry = entries_itr.next() diff --git a/dom/cache/ManagerId.cpp b/dom/cache/ManagerId.cpp index ebac11317f..04ecc031b9 100644 --- a/dom/cache/ManagerId.cpp +++ b/dom/cache/ManagerId.cpp @@ -73,10 +73,7 @@ ManagerId::~ManagerId() // The PBackground worker thread shouldn't be running after the main thread // is stopped. So main thread is guaranteed to exist here. - nsCOMPtr mainThread = do_GetMainThread(); - MOZ_ASSERT(mainThread); - - NS_ProxyRelease(mainThread, mPrincipal.forget().take()); + NS_ReleaseOnMainThread(mPrincipal.forget()); } } // namespace cache diff --git a/dom/datastore/DataStoreDB.cpp b/dom/datastore/DataStoreDB.cpp index 00cb49abc4..e8c9f7a069 100644 --- a/dom/datastore/DataStoreDB.cpp +++ b/dom/datastore/DataStoreDB.cpp @@ -9,15 +9,15 @@ #include "DataStoreCallbacks.h" #include "jsapi.h" #include "mozilla/dom/IDBDatabaseBinding.h" +#include "mozilla/dom/IDBDatabase.h" +#include "mozilla/dom/IDBEvents.h" +#include "mozilla/dom/IDBFactory.h" #include "mozilla/dom/IDBFactoryBinding.h" +#include "mozilla/dom/IDBIndex.h" +#include "mozilla/dom/IDBObjectStore.h" #include "mozilla/dom/IDBObjectStoreBinding.h" -#include "mozilla/dom/indexedDB/IDBDatabase.h" -#include "mozilla/dom/indexedDB/IDBEvents.h" -#include "mozilla/dom/indexedDB/IDBFactory.h" -#include "mozilla/dom/indexedDB/IDBIndex.h" -#include "mozilla/dom/indexedDB/IDBObjectStore.h" -#include "mozilla/dom/indexedDB/IDBRequest.h" -#include "mozilla/dom/indexedDB/IDBTransaction.h" +#include "mozilla/dom/IDBRequest.h" +#include "mozilla/dom/IDBTransaction.h" #include "nsComponentManagerUtils.h" #include "nsContentUtils.h" #include "nsIDOMEvent.h" @@ -354,7 +354,7 @@ DataStoreDB::Delete() return NS_OK; } -indexedDB::IDBTransaction* +IDBTransaction* DataStoreDB::Transaction() const { MOZ_ASSERT(mTransaction); diff --git a/dom/datastore/DataStoreDB.h b/dom/datastore/DataStoreDB.h index b0d3bfc503..fb9380bcf8 100644 --- a/dom/datastore/DataStoreDB.h +++ b/dom/datastore/DataStoreDB.h @@ -18,14 +18,11 @@ namespace mozilla { namespace dom { -namespace indexedDB { +class DataStoreDBCallback; class IDBDatabase; class IDBFactory; class IDBOpenDBRequest; class IDBTransaction; -} // namespace indexedDB - -class DataStoreDBCallback; class DataStoreDB final : public nsIDOMEventListener { @@ -39,7 +36,7 @@ public: nsresult Delete(); - indexedDB::IDBTransaction* Transaction() const; + IDBTransaction* Transaction() const; // nsIDOMEventListener NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) override; @@ -59,10 +56,10 @@ private: nsString mDatabaseName; - RefPtr mFactory; - RefPtr mRequest; - RefPtr mDatabase; - RefPtr mTransaction; + RefPtr mFactory; + RefPtr mRequest; + RefPtr mDatabase; + RefPtr mTransaction; RefPtr mCallback; diff --git a/dom/datastore/DataStoreRevision.cpp b/dom/datastore/DataStoreRevision.cpp index a359371322..6612eb392a 100644 --- a/dom/datastore/DataStoreRevision.cpp +++ b/dom/datastore/DataStoreRevision.cpp @@ -9,16 +9,14 @@ #include "DataStoreCallbacks.h" #include "DataStoreService.h" #include "mozilla/dom/DataStoreBinding.h" +#include "mozilla/dom/IDBObjectStore.h" +#include "mozilla/dom/IDBRequest.h" #include "mozilla/dom/ToJSValue.h" -#include "mozilla/dom/indexedDB/IDBObjectStore.h" -#include "mozilla/dom/indexedDB/IDBRequest.h" #include "nsIDOMEvent.h" namespace mozilla { namespace dom { -using namespace indexedDB; - NS_IMPL_ISUPPORTS(DataStoreRevision, nsIDOMEventListener) // Note: this code in it must not assume anything about the compartment cx is diff --git a/dom/datastore/DataStoreRevision.h b/dom/datastore/DataStoreRevision.h index 28d66f7d2c..bf12d213cb 100644 --- a/dom/datastore/DataStoreRevision.h +++ b/dom/datastore/DataStoreRevision.h @@ -15,12 +15,9 @@ namespace mozilla { namespace dom { -namespace indexedDB { +class DataStoreRevisionCallback; class IDBObjectStore; class IDBRequest; -} // namespace indexedDB - -class DataStoreRevisionCallback; class DataStoreRevision final : public nsIDOMEventListener { @@ -32,7 +29,7 @@ public: }; nsresult AddRevision(JSContext* aCx, - indexedDB::IDBObjectStore* aStore, + IDBObjectStore* aStore, uint32_t aObjectId, RevisionType aRevisionType, DataStoreRevisionCallback* aCallback); @@ -43,7 +40,7 @@ public: private: ~DataStoreRevision() {} RefPtr mCallback; - RefPtr mRequest; + RefPtr mRequest; nsString mRevisionID; }; diff --git a/dom/datastore/DataStoreService.cpp b/dom/datastore/DataStoreService.cpp index 8b58fb6be8..faa6767c25 100644 --- a/dom/datastore/DataStoreService.cpp +++ b/dom/datastore/DataStoreService.cpp @@ -21,10 +21,10 @@ #include "mozilla/dom/ContentChild.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/DOMError.h" -#include "mozilla/dom/indexedDB/IDBCursor.h" -#include "mozilla/dom/indexedDB/IDBObjectStore.h" -#include "mozilla/dom/indexedDB/IDBRequest.h" -#include "mozilla/dom/indexedDB/IDBTransaction.h" +#include "mozilla/dom/IDBCursor.h" +#include "mozilla/dom/IDBObjectStore.h" +#include "mozilla/dom/IDBRequest.h" +#include "mozilla/dom/IDBTransaction.h" #include "mozilla/dom/PermissionMessageUtils.h" #include "mozilla/dom/Promise.h" #include "mozilla/unused.h" @@ -63,8 +63,6 @@ using mozilla::OriginAttributes; namespace mozilla { namespace dom { -using namespace indexedDB; - // This class contains all the information about a DataStore. class DataStoreInfo { diff --git a/dom/devicestorage/DeviceStorage.h b/dom/devicestorage/DeviceStorage.h index 5ab9d11603..05949ca3a2 100644 --- a/dom/devicestorage/DeviceStorage.h +++ b/dom/devicestorage/DeviceStorage.h @@ -87,6 +87,13 @@ public: uint32_t aFileType, uint32_t aFileAttributes); + static already_AddRefed + CreateUnique(const nsAString& aStorageType, + const nsAString& aStorageName, + nsAString& aFileName, + uint32_t aFileType, + uint32_t aFileAttributes); + NS_DECL_THREADSAFE_ISUPPORTS bool IsAvailable(); @@ -300,11 +307,10 @@ public: already_AddRefed CreateAndRejectDOMRequest(const char *aReason, ErrorResult& aRv); - nsresult CheckPermission(DeviceStorageRequest* aRequest); - void StorePermission(DeviceStorageRequest* aRequest, bool aAllow); + nsresult CheckPermission(already_AddRefed&& aRequest); bool IsOwningThread(); - nsresult DispatchToOwningThread(nsIRunnable* aRunnable); + nsresult DispatchToOwningThread(already_AddRefed&& aRunnable); private: ~nsDOMDeviceStorage(); diff --git a/dom/devicestorage/DeviceStorageRequestParent.cpp b/dom/devicestorage/DeviceStorageRequestParent.cpp index e51aac823b..d0ac595e25 100644 --- a/dom/devicestorage/DeviceStorageRequestParent.cpp +++ b/dom/devicestorage/DeviceStorageRequestParent.cpp @@ -36,6 +36,7 @@ DeviceStorageRequestParent::DeviceStorageRequestParent( void DeviceStorageRequestParent::Dispatch() { + RefPtr r; switch (mParams.type()) { case DeviceStorageParams::TDeviceStorageAddParams: { @@ -52,13 +53,8 @@ DeviceStorageRequestParent::Dispatch() blobImpl->GetInternalStream(getter_AddRefs(stream), rv); MOZ_ASSERT(!rv.Failed()); - RefPtr r = new WriteFileEvent(this, dsf, stream, - DEVICE_STORAGE_REQUEST_CREATE); - - nsCOMPtr target - = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); - MOZ_ASSERT(target); - target->Dispatch(r, NS_DISPATCH_NORMAL); + r = new WriteFileEvent(this, dsf.forget(), stream, + DEVICE_STORAGE_REQUEST_CREATE); break; } @@ -77,13 +73,8 @@ DeviceStorageRequestParent::Dispatch() blobImpl->GetInternalStream(getter_AddRefs(stream), rv); MOZ_ASSERT(!rv.Failed()); - RefPtr r = new WriteFileEvent(this, dsf, stream, - DEVICE_STORAGE_REQUEST_APPEND); - - nsCOMPtr target - = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); - MOZ_ASSERT(target); - target->Dispatch(r, NS_DISPATCH_NORMAL); + r = new WriteFileEvent(this, dsf.forget(), stream, + DEVICE_STORAGE_REQUEST_APPEND); break; } @@ -94,12 +85,7 @@ DeviceStorageRequestParent::Dispatch() RefPtr dsf = new DeviceStorageFile(p.type(), p.storageName(), p.relpath()); - RefPtr r = new CreateFdEvent(this, dsf); - - nsCOMPtr target - = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); - MOZ_ASSERT(target); - target->Dispatch(r, NS_DISPATCH_NORMAL); + r = new CreateFdEvent(this, dsf.forget()); break; } @@ -109,12 +95,7 @@ DeviceStorageRequestParent::Dispatch() RefPtr dsf = new DeviceStorageFile(p.type(), p.storageName(), p.rootDir(), p.relpath()); - RefPtr r = new ReadFileEvent(this, dsf); - - nsCOMPtr target - = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); - MOZ_ASSERT(target); - target->Dispatch(r, NS_DISPATCH_NORMAL); + r = new ReadFileEvent(this, dsf.forget()); break; } @@ -124,12 +105,7 @@ DeviceStorageRequestParent::Dispatch() RefPtr dsf = new DeviceStorageFile(p.type(), p.storageName(), p.relpath()); - RefPtr r = new DeleteFileEvent(this, dsf); - - nsCOMPtr target - = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); - MOZ_ASSERT(target); - target->Dispatch(r, NS_DISPATCH_NORMAL); + r = new DeleteFileEvent(this, dsf.forget()); break; } @@ -139,12 +115,7 @@ DeviceStorageRequestParent::Dispatch() RefPtr dsf = new DeviceStorageFile(p.type(), p.storageName()); - RefPtr r = new FreeSpaceFileEvent(this, dsf); - - nsCOMPtr target - = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); - MOZ_ASSERT(target); - target->Dispatch(r, NS_DISPATCH_NORMAL); + r = new FreeSpaceFileEvent(this, dsf.forget()); break; } @@ -158,10 +129,9 @@ DeviceStorageRequestParent::Dispatch() RefPtr dsf = new DeviceStorageFile(p.type(), p.storageName()); - RefPtr r = new UsedSpaceFileEvent(this, dsf); - - usedSpaceCache->Dispatch(r); - break; + usedSpaceCache->Dispatch( + MakeAndAddRef(this, dsf.forget())); + return; } case DeviceStorageParams::TDeviceStorageFormatParams: @@ -170,11 +140,10 @@ DeviceStorageRequestParent::Dispatch() RefPtr dsf = new DeviceStorageFile(p.type(), p.storageName()); - RefPtr r - = new PostFormatResultEvent(this, dsf); - DebugOnly rv = NS_DispatchToMainThread(r); + DebugOnly rv = NS_DispatchToMainThread( + new PostFormatResultEvent(this, dsf.forget())); MOZ_ASSERT(NS_SUCCEEDED(rv)); - break; + return; } case DeviceStorageParams::TDeviceStorageMountParams: @@ -183,11 +152,10 @@ DeviceStorageRequestParent::Dispatch() RefPtr dsf = new DeviceStorageFile(p.type(), p.storageName()); - RefPtr r - = new PostMountResultEvent(this, dsf); - DebugOnly rv = NS_DispatchToMainThread(r); + DebugOnly rv = NS_DispatchToMainThread( + new PostMountResultEvent(this, dsf.forget())); MOZ_ASSERT(NS_SUCCEEDED(rv)); - break; + return; } case DeviceStorageParams::TDeviceStorageUnmountParams: @@ -196,11 +164,10 @@ DeviceStorageRequestParent::Dispatch() RefPtr dsf = new DeviceStorageFile(p.type(), p.storageName()); - RefPtr r - = new PostUnmountResultEvent(this, dsf); - DebugOnly rv = NS_DispatchToMainThread(r); + DebugOnly rv = NS_DispatchToMainThread( + new PostUnmountResultEvent(this, dsf.forget())); MOZ_ASSERT(NS_SUCCEEDED(rv)); - break; + return; } case DeviceStorageParams::TDeviceStorageEnumerationParams: @@ -209,21 +176,22 @@ DeviceStorageRequestParent::Dispatch() RefPtr dsf = new DeviceStorageFile(p.type(), p.storageName(), p.rootdir(), NS_LITERAL_STRING("")); - RefPtr r - = new EnumerateFileEvent(this, dsf, p.since()); - - nsCOMPtr target - = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); - MOZ_ASSERT(target); - target->Dispatch(r, NS_DISPATCH_NORMAL); + r = new EnumerateFileEvent(this, dsf.forget(), p.since()); break; } default: { NS_RUNTIMEABORT("not reached"); - break; + return; } } + + if (r) { + nsCOMPtr target = + do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); + MOZ_ASSERT(target); + target->Dispatch(r.forget(), NS_DISPATCH_NORMAL); + } } bool @@ -393,10 +361,15 @@ DeviceStorageRequestParent::ActorDestroy(ActorDestroyReason) { MutexAutoLock lock(mMutex); mActorDestroyed = true; - int32_t count = mRunnables.Length(); - for (int32_t index = 0; index < count; index++) { - mRunnables[index]->Cancel(); + for (auto& runnable : mRunnables) { + runnable->Cancel(); } + // Ensure we clear all references to the runnables so that there won't + // be leak due to cyclic reference. Note that it is safe to release + // the references here, since if a runnable is not cancelled yet, the + // corresponding thread should still hold a reference to it, and thus + // the runnable will end up being released in that thread, not here. + mRunnables.Clear(); } DeviceStorageRequestParent::PostFreeSpaceResultEvent::PostFreeSpaceResultEvent( @@ -419,19 +392,6 @@ DeviceStorageRequestParent::PostFreeSpaceResultEvent::CancelableRun() { return NS_OK; } -DeviceStorageRequestParent::PostUsedSpaceResultEvent:: - PostUsedSpaceResultEvent(DeviceStorageRequestParent* aParent, - const nsAString& aType, - uint64_t aUsedSpace) - : CancelableRunnable(aParent) - , mType(aType) - , mUsedSpace(aUsedSpace) -{ -} - -DeviceStorageRequestParent::PostUsedSpaceResultEvent:: - ~PostUsedSpaceResultEvent() {} - nsresult DeviceStorageRequestParent::PostUsedSpaceResultEvent::CancelableRun() { MOZ_ASSERT(NS_IsMainThread()); @@ -448,8 +408,6 @@ DeviceStorageRequestParent::PostErrorEvent:: CopyASCIItoUTF16(aError, mError); } -DeviceStorageRequestParent::PostErrorEvent::~PostErrorEvent() {} - nsresult DeviceStorageRequestParent::PostErrorEvent::CancelableRun() { MOZ_ASSERT(NS_IsMainThread()); @@ -459,14 +417,6 @@ DeviceStorageRequestParent::PostErrorEvent::CancelableRun() { return NS_OK; } -DeviceStorageRequestParent::PostSuccessEvent:: - PostSuccessEvent(DeviceStorageRequestParent* aParent) - : CancelableRunnable(aParent) -{ -} - -DeviceStorageRequestParent::PostSuccessEvent::~PostSuccessEvent() {} - nsresult DeviceStorageRequestParent::PostSuccessEvent::CancelableRun() { MOZ_ASSERT(NS_IsMainThread()); @@ -476,22 +426,6 @@ DeviceStorageRequestParent::PostSuccessEvent::CancelableRun() { return NS_OK; } -DeviceStorageRequestParent::PostBlobSuccessEvent:: - PostBlobSuccessEvent(DeviceStorageRequestParent* aParent, - DeviceStorageFile* aFile, - uint32_t aLength, - nsACString& aMimeType, - uint64_t aLastModifiedDate) - : CancelableRunnable(aParent) - , mLength(aLength) - , mLastModificationDate(aLastModifiedDate) - , mFile(aFile) - , mMimeType(aMimeType) -{ -} - -DeviceStorageRequestParent::PostBlobSuccessEvent::~PostBlobSuccessEvent() {} - nsresult DeviceStorageRequestParent::PostBlobSuccessEvent::CancelableRun() { MOZ_ASSERT(NS_IsMainThread()); @@ -520,21 +454,6 @@ DeviceStorageRequestParent::PostBlobSuccessEvent::CancelableRun() { return NS_OK; } -DeviceStorageRequestParent::PostEnumerationSuccessEvent:: - PostEnumerationSuccessEvent(DeviceStorageRequestParent* aParent, - const nsAString& aStorageType, - const nsAString& aRelPath, - InfallibleTArray& aPaths) - : CancelableRunnable(aParent) - , mStorageType(aStorageType) - , mRelPath(aRelPath) - , mPaths(aPaths) -{ -} - -DeviceStorageRequestParent::PostEnumerationSuccessEvent:: - ~PostEnumerationSuccessEvent() {} - nsresult DeviceStorageRequestParent::PostEnumerationSuccessEvent::CancelableRun() { MOZ_ASSERT(NS_IsMainThread()); @@ -544,36 +463,23 @@ DeviceStorageRequestParent::PostEnumerationSuccessEvent::CancelableRun() { return NS_OK; } -DeviceStorageRequestParent::CreateFdEvent:: - CreateFdEvent(DeviceStorageRequestParent* aParent, - DeviceStorageFile* aFile) - : CancelableRunnable(aParent) - , mFile(aFile) -{ -} - -DeviceStorageRequestParent::CreateFdEvent::~CreateFdEvent() -{ -} - nsresult DeviceStorageRequestParent::CreateFdEvent::CancelableRun() { MOZ_ASSERT(!NS_IsMainThread()); - nsCOMPtr r; - if (!mFile->mFile) { - r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN); - return NS_DispatchToMainThread(r); + return NS_DispatchToMainThread( + new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN)); } bool check = false; mFile->mFile->Exists(&check); if (check) { - r = new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_EXISTS); - return NS_DispatchToMainThread(r); + return NS_DispatchToMainThread( + new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_EXISTS)); } + nsCOMPtr r; FileDescriptor fileDescriptor; nsresult rv = mFile->CreateFileDescriptor(fileDescriptor); if (NS_FAILED(rv)) { @@ -585,23 +491,7 @@ DeviceStorageRequestParent::CreateFdEvent::CancelableRun() r = new PostFileDescriptorResultEvent(mParent, fileDescriptor); } - return NS_DispatchToMainThread(r); -} - -DeviceStorageRequestParent::WriteFileEvent:: - WriteFileEvent(DeviceStorageRequestParent* aParent, - DeviceStorageFile* aFile, - nsIInputStream* aInputStream, - int32_t aRequestType) - : CancelableRunnable(aParent) - , mFile(aFile) - , mInputStream(aInputStream) - , mRequestType(aRequestType) -{ -} - -DeviceStorageRequestParent::WriteFileEvent::~WriteFileEvent() -{ + return NS_DispatchToMainThread(r.forget()); } nsresult @@ -609,11 +499,9 @@ DeviceStorageRequestParent::WriteFileEvent::CancelableRun() { MOZ_ASSERT(!NS_IsMainThread()); - nsCOMPtr r; - if (!mInputStream || !mFile->mFile) { - r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN); - return NS_DispatchToMainThread(r); + return NS_DispatchToMainThread( + new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN)); } bool check = false; @@ -622,21 +510,22 @@ DeviceStorageRequestParent::WriteFileEvent::CancelableRun() if (mRequestType == DEVICE_STORAGE_REQUEST_CREATE) { if (check) { - r = new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_EXISTS); - return NS_DispatchToMainThread(r); + return NS_DispatchToMainThread( + new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_EXISTS)); } rv = mFile->Write(mInputStream); } else if (mRequestType == DEVICE_STORAGE_REQUEST_APPEND) { if (!check) { - r = new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_DOES_NOT_EXIST); - return NS_DispatchToMainThread(r); + return NS_DispatchToMainThread( + new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_DOES_NOT_EXIST)); } rv = mFile->Append(mInputStream); } else { - r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN); - return NS_DispatchToMainThread(r); + return NS_DispatchToMainThread( + new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN)); } + nsCOMPtr r; if (NS_FAILED(rv)) { r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN); } @@ -644,18 +533,7 @@ DeviceStorageRequestParent::WriteFileEvent::CancelableRun() r = new PostPathResultEvent(mParent, mFile->mPath); } - return NS_DispatchToMainThread(r); -} - -DeviceStorageRequestParent::DeleteFileEvent:: - DeleteFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile) - : CancelableRunnable(aParent) - , mFile(aFile) -{ -} - -DeviceStorageRequestParent::DeleteFileEvent::~DeleteFileEvent() -{ + return NS_DispatchToMainThread(r.forget()); } nsresult @@ -665,12 +543,11 @@ DeviceStorageRequestParent::DeleteFileEvent::CancelableRun() mFile->Remove(); - nsCOMPtr r; - if (!mFile->mFile) { - r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN); - return NS_DispatchToMainThread(r); + return NS_DispatchToMainThread( + new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN)); } + nsCOMPtr r; bool check = false; mFile->mFile->Exists(&check); if (check) { @@ -680,19 +557,7 @@ DeviceStorageRequestParent::DeleteFileEvent::CancelableRun() r = new PostPathResultEvent(mParent, mFile->mPath); } - return NS_DispatchToMainThread(r); -} - -DeviceStorageRequestParent::FreeSpaceFileEvent:: - FreeSpaceFileEvent(DeviceStorageRequestParent* aParent, - DeviceStorageFile* aFile) - : CancelableRunnable(aParent) - , mFile(aFile) -{ -} - -DeviceStorageRequestParent::FreeSpaceFileEvent::~FreeSpaceFileEvent() -{ + return NS_DispatchToMainThread(r.forget()); } nsresult @@ -705,21 +570,8 @@ DeviceStorageRequestParent::FreeSpaceFileEvent::CancelableRun() mFile->GetStorageFreeSpace(&freeSpace); } - nsCOMPtr r; - r = new PostFreeSpaceResultEvent(mParent, static_cast(freeSpace)); - return NS_DispatchToMainThread(r); -} - -DeviceStorageRequestParent::UsedSpaceFileEvent:: - UsedSpaceFileEvent(DeviceStorageRequestParent* aParent, - DeviceStorageFile* aFile) - : CancelableRunnable(aParent) - , mFile(aFile) -{ -} - -DeviceStorageRequestParent::UsedSpaceFileEvent::~UsedSpaceFileEvent() -{ + return NS_DispatchToMainThread( + new PostFreeSpaceResultEvent(mParent, static_cast(freeSpace))); } nsresult @@ -743,13 +595,13 @@ DeviceStorageRequestParent::UsedSpaceFileEvent::CancelableRun() } else { r = new PostUsedSpaceResultEvent(mParent, mFile->mStorageType, totalUsage); } - return NS_DispatchToMainThread(r); + return NS_DispatchToMainThread(r.forget()); } DeviceStorageRequestParent::ReadFileEvent:: - ReadFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile) - : CancelableRunnable(aParent) - , mFile(aFile) + ReadFileEvent(DeviceStorageRequestParent* aParent, + already_AddRefed&& aFile) + : CancelableFileEvent(aParent, Move(aFile)) { nsCOMPtr mimeService = do_GetService(NS_MIMESERVICE_CONTRACTID); @@ -761,60 +613,41 @@ DeviceStorageRequestParent::ReadFileEvent:: } } -DeviceStorageRequestParent::ReadFileEvent::~ReadFileEvent() -{ -} - nsresult DeviceStorageRequestParent::ReadFileEvent::CancelableRun() { MOZ_ASSERT(!NS_IsMainThread()); - nsCOMPtr r; - if (!mFile->mFile) { - r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN); - return NS_DispatchToMainThread(r); + return NS_DispatchToMainThread( + new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN)); } bool check = false; mFile->mFile->Exists(&check); if (!check) { - r = new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_DOES_NOT_EXIST); - return NS_DispatchToMainThread(r); + return NS_DispatchToMainThread( + new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_DOES_NOT_EXIST)); } int64_t fileSize; nsresult rv = mFile->mFile->GetFileSize(&fileSize); if (NS_FAILED(rv)) { - r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN); - return NS_DispatchToMainThread(r); + return NS_DispatchToMainThread( + new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN)); } PRTime modDate; rv = mFile->mFile->GetLastModifiedTime(&modDate); if (NS_FAILED(rv)) { - r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN); - return NS_DispatchToMainThread(r); + return NS_DispatchToMainThread( + new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN)); } - r = new PostBlobSuccessEvent(mParent, mFile, static_cast(fileSize), - mMimeType, modDate); - return NS_DispatchToMainThread(r); -} - -DeviceStorageRequestParent::EnumerateFileEvent:: - EnumerateFileEvent(DeviceStorageRequestParent* aParent, - DeviceStorageFile* aFile, - uint64_t aSince) - : CancelableRunnable(aParent) - , mFile(aFile) - , mSince(aSince) -{ -} - -DeviceStorageRequestParent::EnumerateFileEvent::~EnumerateFileEvent() -{ + return NS_DispatchToMainThread( + new PostBlobSuccessEvent(mParent, mFile.forget(), + static_cast(fileSize), + mMimeType, modDate)); } nsresult @@ -822,13 +655,12 @@ DeviceStorageRequestParent::EnumerateFileEvent::CancelableRun() { MOZ_ASSERT(!NS_IsMainThread()); - nsCOMPtr r; if (mFile->mFile) { bool check = false; mFile->mFile->Exists(&check); if (!check) { - r = new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_DOES_NOT_EXIST); - return NS_DispatchToMainThread(r); + return NS_DispatchToMainThread( + new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_DOES_NOT_EXIST)); } } @@ -843,22 +675,9 @@ DeviceStorageRequestParent::EnumerateFileEvent::CancelableRun() values.AppendElement(dsvf); } - r = new PostEnumerationSuccessEvent(mParent, mFile->mStorageType, - mFile->mRootDir, values); - return NS_DispatchToMainThread(r); -} - - -DeviceStorageRequestParent::PostPathResultEvent:: - PostPathResultEvent(DeviceStorageRequestParent* aParent, - const nsAString& aPath) - : CancelableRunnable(aParent) - , mPath(aPath) -{ -} - -DeviceStorageRequestParent::PostPathResultEvent::~PostPathResultEvent() -{ + return NS_DispatchToMainThread( + new PostEnumerationSuccessEvent(mParent, mFile->mStorageType, + mFile->mRootDir, values)); } nsresult @@ -871,19 +690,6 @@ DeviceStorageRequestParent::PostPathResultEvent::CancelableRun() return NS_OK; } -DeviceStorageRequestParent::PostFileDescriptorResultEvent:: - PostFileDescriptorResultEvent(DeviceStorageRequestParent* aParent, - const FileDescriptor& aFileDescriptor) - : CancelableRunnable(aParent) - , mFileDescriptor(aFileDescriptor) -{ -} - -DeviceStorageRequestParent::PostFileDescriptorResultEvent:: - ~PostFileDescriptorResultEvent() -{ -} - nsresult DeviceStorageRequestParent::PostFileDescriptorResultEvent::CancelableRun() { @@ -894,19 +700,6 @@ DeviceStorageRequestParent::PostFileDescriptorResultEvent::CancelableRun() return NS_OK; } -DeviceStorageRequestParent::PostFormatResultEvent:: - PostFormatResultEvent(DeviceStorageRequestParent* aParent, - DeviceStorageFile* aFile) - : CancelableRunnable(aParent) - , mFile(aFile) -{ -} - -DeviceStorageRequestParent::PostFormatResultEvent:: - ~PostFormatResultEvent() -{ -} - nsresult DeviceStorageRequestParent::PostFormatResultEvent::CancelableRun() { @@ -922,19 +715,6 @@ DeviceStorageRequestParent::PostFormatResultEvent::CancelableRun() return NS_OK; } -DeviceStorageRequestParent::PostMountResultEvent:: - PostMountResultEvent(DeviceStorageRequestParent* aParent, - DeviceStorageFile* aFile) - : CancelableRunnable(aParent) - , mFile(aFile) -{ -} - -DeviceStorageRequestParent::PostMountResultEvent:: - ~PostMountResultEvent() -{ -} - nsresult DeviceStorageRequestParent::PostMountResultEvent::CancelableRun() { @@ -950,19 +730,6 @@ DeviceStorageRequestParent::PostMountResultEvent::CancelableRun() return NS_OK; } -DeviceStorageRequestParent::PostUnmountResultEvent:: - PostUnmountResultEvent(DeviceStorageRequestParent* aParent, - DeviceStorageFile* aFile) - : CancelableRunnable(aParent) - , mFile(aFile) -{ -} - -DeviceStorageRequestParent::PostUnmountResultEvent:: - ~PostUnmountResultEvent() -{ -} - nsresult DeviceStorageRequestParent::PostUnmountResultEvent::CancelableRun() { diff --git a/dom/devicestorage/DeviceStorageRequestParent.h b/dom/devicestorage/DeviceStorageRequestParent.h index 84901120ce..f24f48ad10 100644 --- a/dom/devicestorage/DeviceStorageRequestParent.h +++ b/dom/devicestorage/DeviceStorageRequestParent.h @@ -74,11 +74,22 @@ private: bool mCanceled; }; + class CancelableFileEvent : public CancelableRunnable + { + protected: + CancelableFileEvent(DeviceStorageRequestParent* aParent, + already_AddRefed&& aFile) + : CancelableRunnable(aParent) + , mFile(Move(aFile)) {} + + RefPtr mFile; + }; + class PostErrorEvent : public CancelableRunnable { public: PostErrorEvent(DeviceStorageRequestParent* aParent, const char* aError); - virtual ~PostErrorEvent(); + virtual ~PostErrorEvent() {} virtual nsresult CancelableRun(); private: nsString mError; @@ -87,21 +98,28 @@ private: class PostSuccessEvent : public CancelableRunnable { public: - explicit PostSuccessEvent(DeviceStorageRequestParent* aParent); - virtual ~PostSuccessEvent(); + explicit PostSuccessEvent(DeviceStorageRequestParent* aParent) + : CancelableRunnable(aParent) {} + virtual ~PostSuccessEvent() {} virtual nsresult CancelableRun(); }; - class PostBlobSuccessEvent : public CancelableRunnable + class PostBlobSuccessEvent : public CancelableFileEvent { public: - PostBlobSuccessEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile, uint32_t aLength, nsACString& aMimeType, uint64_t aLastModifiedDate); - virtual ~PostBlobSuccessEvent(); + PostBlobSuccessEvent(DeviceStorageRequestParent* aParent, + already_AddRefed&& aFile, + uint32_t aLength, nsACString& aMimeType, + uint64_t aLastModifiedDate) + : CancelableFileEvent(aParent, Move(aFile)) + , mLength(aLength) + , mLastModificationDate(aLastModifiedDate) + , mMimeType(aMimeType) {} + virtual ~PostBlobSuccessEvent() {} virtual nsresult CancelableRun(); private: uint32_t mLength; uint64_t mLastModificationDate; - RefPtr mFile; nsCString mMimeType; }; @@ -111,8 +129,12 @@ private: PostEnumerationSuccessEvent(DeviceStorageRequestParent* aParent, const nsAString& aStorageType, const nsAString& aRelPath, - InfallibleTArray& aPaths); - virtual ~PostEnumerationSuccessEvent(); + InfallibleTArray& aPaths) + : CancelableRunnable(aParent) + , mStorageType(aStorageType) + , mRelPath(aRelPath) + , mPaths(aPaths) {} + virtual ~PostEnumerationSuccessEvent() {} virtual nsresult CancelableRun(); private: const nsString mStorageType; @@ -120,89 +142,97 @@ private: InfallibleTArray mPaths; }; - class CreateFdEvent : public CancelableRunnable + class CreateFdEvent : public CancelableFileEvent { public: - CreateFdEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile); - virtual ~CreateFdEvent(); + CreateFdEvent(DeviceStorageRequestParent* aParent, + already_AddRefed&& aFile) + : CancelableFileEvent(aParent, Move(aFile)) {} + virtual ~CreateFdEvent() {} virtual nsresult CancelableRun(); - private: - RefPtr mFile; }; - class WriteFileEvent : public CancelableRunnable + class WriteFileEvent : public CancelableFileEvent { public: - WriteFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile, - nsIInputStream* aInputStream, int32_t aRequestType); - virtual ~WriteFileEvent(); + WriteFileEvent(DeviceStorageRequestParent* aParent, + already_AddRefed&& aFile, + nsIInputStream* aInputStream, int32_t aRequestType) + : CancelableFileEvent(aParent, Move(aFile)) + , mInputStream(aInputStream) + , mRequestType(aRequestType) {} + virtual ~WriteFileEvent() {} virtual nsresult CancelableRun(); private: - RefPtr mFile; nsCOMPtr mInputStream; int32_t mRequestType; }; - class DeleteFileEvent : public CancelableRunnable + class DeleteFileEvent : public CancelableFileEvent { public: - DeleteFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile); - virtual ~DeleteFileEvent(); + DeleteFileEvent(DeviceStorageRequestParent* aParent, + already_AddRefed&& aFile) + : CancelableFileEvent(aParent, Move(aFile)) {} + virtual ~DeleteFileEvent() {} + virtual nsresult CancelableRun(); + }; + + class FreeSpaceFileEvent : public CancelableFileEvent + { + public: + FreeSpaceFileEvent(DeviceStorageRequestParent* aParent, + already_AddRefed&& aFile) + : CancelableFileEvent(aParent, Move(aFile)) {} + virtual ~FreeSpaceFileEvent() {} + virtual nsresult CancelableRun(); + }; + + class UsedSpaceFileEvent : public CancelableFileEvent + { + public: + UsedSpaceFileEvent(DeviceStorageRequestParent* aParent, + already_AddRefed&& aFile) + : CancelableFileEvent(aParent, Move(aFile)) {} + virtual ~UsedSpaceFileEvent() {} + virtual nsresult CancelableRun(); + }; + + class ReadFileEvent : public CancelableFileEvent + { + public: + ReadFileEvent(DeviceStorageRequestParent* aParent, + already_AddRefed&& aFile); + virtual ~ReadFileEvent() {} virtual nsresult CancelableRun(); private: - RefPtr mFile; - }; - - class FreeSpaceFileEvent : public CancelableRunnable - { - public: - FreeSpaceFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile); - virtual ~FreeSpaceFileEvent(); - virtual nsresult CancelableRun(); - private: - RefPtr mFile; - }; - - class UsedSpaceFileEvent : public CancelableRunnable - { - public: - UsedSpaceFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile); - virtual ~UsedSpaceFileEvent(); - virtual nsresult CancelableRun(); - private: - RefPtr mFile; - }; - - class ReadFileEvent : public CancelableRunnable - { - public: - ReadFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile); - virtual ~ReadFileEvent(); - virtual nsresult CancelableRun(); - private: - RefPtr mFile; nsCString mMimeType; }; - class EnumerateFileEvent : public CancelableRunnable + class EnumerateFileEvent : public CancelableFileEvent { public: - EnumerateFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile, uint64_t aSince); - virtual ~EnumerateFileEvent(); + EnumerateFileEvent(DeviceStorageRequestParent* aParent, + already_AddRefed&& aFile, + uint64_t aSince) + : CancelableFileEvent(aParent, Move(aFile)) + , mSince(aSince) {} + virtual ~EnumerateFileEvent() {} virtual nsresult CancelableRun(); private: - RefPtr mFile; uint64_t mSince; }; class PostPathResultEvent : public CancelableRunnable { public: - PostPathResultEvent(DeviceStorageRequestParent* aParent, const nsAString& aPath); - virtual ~PostPathResultEvent(); + PostPathResultEvent(DeviceStorageRequestParent* aParent, + const nsAString& aPath) + : CancelableRunnable(aParent) + , mPath(aPath) {} + virtual ~PostPathResultEvent() {} virtual nsresult CancelableRun(); private: - RefPtr mFile; nsString mPath; }; @@ -210,11 +240,12 @@ private: { public: PostFileDescriptorResultEvent(DeviceStorageRequestParent* aParent, - const FileDescriptor& aFileDescriptor); - virtual ~PostFileDescriptorResultEvent(); + const FileDescriptor& aFileDescriptor) + : CancelableRunnable(aParent) + , mFileDescriptor(aFileDescriptor) {} + virtual ~PostFileDescriptorResultEvent() {} virtual nsresult CancelableRun(); private: - RefPtr mFile; FileDescriptor mFileDescriptor; }; @@ -234,42 +265,45 @@ private: public: PostUsedSpaceResultEvent(DeviceStorageRequestParent* aParent, const nsAString& aType, - uint64_t aUsedSpace); - virtual ~PostUsedSpaceResultEvent(); + uint64_t aUsedSpace) + : CancelableRunnable(aParent) + , mType(aType) + , mUsedSpace(aUsedSpace) {} + virtual ~PostUsedSpaceResultEvent() {} virtual nsresult CancelableRun(); private: nsString mType; uint64_t mUsedSpace; }; - class PostFormatResultEvent : public CancelableRunnable + class PostFormatResultEvent : public CancelableFileEvent { public: - PostFormatResultEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile); - virtual ~PostFormatResultEvent(); + PostFormatResultEvent(DeviceStorageRequestParent* aParent, + already_AddRefed&& aFile) + : CancelableFileEvent(aParent, Move(aFile)) {} + virtual ~PostFormatResultEvent() {} virtual nsresult CancelableRun(); - private: - RefPtr mFile; }; - class PostMountResultEvent : public CancelableRunnable + class PostMountResultEvent : public CancelableFileEvent { public: - PostMountResultEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile); - virtual ~PostMountResultEvent(); + PostMountResultEvent(DeviceStorageRequestParent* aParent, + already_AddRefed&& aFile) + : CancelableFileEvent(aParent, Move(aFile)) {} + virtual ~PostMountResultEvent() {} virtual nsresult CancelableRun(); - private: - RefPtr mFile; }; - class PostUnmountResultEvent : public CancelableRunnable + class PostUnmountResultEvent : public CancelableFileEvent { public: - PostUnmountResultEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile); - virtual ~PostUnmountResultEvent(); + PostUnmountResultEvent(DeviceStorageRequestParent* aParent, + already_AddRefed&& aFile) + : CancelableFileEvent(aParent, Move(aFile)) {} + virtual ~PostUnmountResultEvent() {} virtual nsresult CancelableRun(); - private: - RefPtr mFile; }; protected: diff --git a/dom/devicestorage/DeviceStorageStatics.cpp b/dom/devicestorage/DeviceStorageStatics.cpp index e5fa99fdbc..b94518eee6 100644 --- a/dom/devicestorage/DeviceStorageStatics.cpp +++ b/dom/devicestorage/DeviceStorageStatics.cpp @@ -825,7 +825,7 @@ DeviceStorageStatics::ListenerWrapper::ListenerWrapper(nsDOMDeviceStorage* aList DeviceStorageStatics::ListenerWrapper::~ListenerWrapper() { // Even weak pointers are not thread safe - NS_ProxyRelease(mOwningThread, mListener); + NS_ProxyRelease(mOwningThread, mListener.forget()); } bool @@ -854,7 +854,7 @@ DeviceStorageStatics::ListenerWrapper::OnFileWatcherUpdate(const nsCString& aDat listener->OnFileWatcherUpdate(data, file); } }); - mOwningThread->Dispatch(r, NS_DISPATCH_NORMAL); + mOwningThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL); } void @@ -867,7 +867,7 @@ DeviceStorageStatics::ListenerWrapper::OnDiskSpaceWatcher(bool aLowDiskSpace) listener->OnDiskSpaceWatcher(aLowDiskSpace); } }); - mOwningThread->Dispatch(r, NS_DISPATCH_NORMAL); + mOwningThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL); } void @@ -880,7 +880,7 @@ DeviceStorageStatics::ListenerWrapper::OnWritableNameChanged() listener->OnWritableNameChanged(); } }); - mOwningThread->Dispatch(r, NS_DISPATCH_NORMAL); + mOwningThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL); } #ifdef MOZ_WIDGET_GONK @@ -895,7 +895,7 @@ DeviceStorageStatics::ListenerWrapper::OnVolumeStateChanged(nsIVolume* aVolume) listener->OnVolumeStateChanged(volume); } }); - mOwningThread->Dispatch(r, NS_DISPATCH_NORMAL); + mOwningThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL); } #endif diff --git a/dom/devicestorage/nsDeviceStorage.cpp b/dom/devicestorage/nsDeviceStorage.cpp index fe5f43d0f4..d06f780775 100644 --- a/dom/devicestorage/nsDeviceStorage.cpp +++ b/dom/devicestorage/nsDeviceStorage.cpp @@ -693,8 +693,20 @@ DeviceStorageFile::CreateUnique(nsAString& aFileName, if (storageName.IsEmpty()) { nsDOMDeviceStorage::GetDefaultStorageName(storageType, storageName); } + return CreateUnique(storageType, storageName, storagePath, + aFileType, aFileAttributes); +} + +//static +already_AddRefed +DeviceStorageFile::CreateUnique(const nsAString& aStorageType, + const nsAString& aStorageName, + nsAString& aFileName, + uint32_t aFileType, + uint32_t aFileAttributes) +{ RefPtr dsf = - new DeviceStorageFile(storageType, storageName, storagePath); + new DeviceStorageFile(aStorageType, aStorageName, aFileName); if (!dsf->mFile) { return nullptr; } @@ -824,8 +836,7 @@ DeviceStorageFile::Write(nsIInputStream* aInputStream) return rv; } - nsCOMPtr iocomplete = new IOEventComplete(this, "created"); - rv = NS_DispatchToMainThread(iocomplete); + rv = NS_DispatchToMainThread(new IOEventComplete(this, "created")); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -852,8 +863,7 @@ DeviceStorageFile::Write(InfallibleTArray& aBits) return rv; } - nsCOMPtr iocomplete = new IOEventComplete(this, "created"); - rv = NS_DispatchToMainThread(iocomplete); + rv = NS_DispatchToMainThread(new IOEventComplete(this, "created")); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -869,8 +879,7 @@ DeviceStorageFile::Write(InfallibleTArray& aBits) outputStream->Write((char*) aBits.Elements(), aBits.Length(), &wrote); outputStream->Close(); - iocomplete = new IOEventComplete(this, "modified"); - rv = NS_DispatchToMainThread(iocomplete); + rv = NS_DispatchToMainThread(new IOEventComplete(this, "modified")); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -923,8 +932,7 @@ DeviceStorageFile::Append(nsIInputStream* aInputStream, nsIOutputStream* aOutput bufSize -= wrote; } - nsCOMPtr iocomplete = new IOEventComplete(this, "modified"); - rv = NS_DispatchToMainThread(iocomplete); + rv = NS_DispatchToMainThread(new IOEventComplete(this, "modified")); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -961,8 +969,7 @@ DeviceStorageFile::Remove() return rv; } - nsCOMPtr iocomplete = new IOEventComplete(this, "deleted"); - return NS_DispatchToMainThread(iocomplete); + return NS_DispatchToMainThread(new IOEventComplete(this, "deleted")); } nsresult @@ -1499,6 +1506,7 @@ DeviceStorageRequest::DeviceStorageRequest() : mId(DeviceStorageRequestManager::INVALID_ID) , mAccess(DEVICE_STORAGE_ACCESS_UNDEFINED) , mSendToParent(true) + , mUseMainThread(false) , mUseStreamTransport(false) , mCheckFile(false) , mCheckBlob(false) @@ -1521,7 +1529,7 @@ DeviceStorageRequest::~DeviceStorageRequest() void DeviceStorageRequest::Initialize(DeviceStorageRequestManager* aManager, - DeviceStorageFile* aFile, + already_AddRefed&& aFile, uint32_t aId) { DS_LOG_DEBUG("%p manages %p", aManager, this); @@ -1535,11 +1543,11 @@ DeviceStorageRequest::Initialize(DeviceStorageRequestManager* aManager, void DeviceStorageRequest::Initialize(DeviceStorageRequestManager* aManager, - DeviceStorageFile* aFile, + already_AddRefed&& aFile, uint32_t aRequest, BlobImpl* aBlob) { - Initialize(aManager, aFile, aRequest); + Initialize(aManager, Move(aFile), aRequest); mBlob = aBlob; mCheckBlob = true; mCheckFile = true; @@ -1548,11 +1556,11 @@ DeviceStorageRequest::Initialize(DeviceStorageRequestManager* aManager, void DeviceStorageRequest::Initialize(DeviceStorageRequestManager* aManager, - DeviceStorageFile* aFile, + already_AddRefed&& aFile, uint32_t aRequest, DeviceStorageFileDescriptor* aDSFileDescriptor) { - Initialize(aManager, aFile, aRequest); + Initialize(aManager, Move(aFile), aRequest); mDSFileDescriptor = aDSFileDescriptor; MOZ_ASSERT(mDSFileDescriptor); } @@ -1578,11 +1586,30 @@ DeviceStorageRequest::Cancel() nsresult DeviceStorageRequest::Allow() { + if (mUseMainThread && !NS_IsMainThread()) { + RefPtr self = this; + nsCOMPtr r = NS_NewRunnableFunction([self] () -> void + { + self->Allow(); + }); + return NS_DispatchToMainThread(r.forget()); + } + nsresult rv = AllowInternal(); if (NS_WARN_IF(NS_FAILED(rv))) { - return Reject(rv == NS_ERROR_ILLEGAL_VALUE - ? POST_ERROR_EVENT_ILLEGAL_TYPE - : POST_ERROR_EVENT_UNKNOWN); + const char *reason; + switch (rv) { + case NS_ERROR_ILLEGAL_VALUE: + reason = POST_ERROR_EVENT_ILLEGAL_TYPE; + break; + case NS_ERROR_DOM_SECURITY_ERR: + reason = POST_ERROR_EVENT_PERMISSION_DENIED; + break; + default: + reason = POST_ERROR_EVENT_UNKNOWN; + break; + } + return Reject(reason); } return rv; } @@ -1607,9 +1634,10 @@ DeviceStorageRequest::GetManager() const return mManager; } -void +nsresult DeviceStorageRequest::Prepare() { + return NS_OK; } nsresult @@ -1623,7 +1651,11 @@ nsresult DeviceStorageRequest::AllowInternal() { MOZ_ASSERT(mManager->IsOwningThread() || NS_IsMainThread()); - Prepare(); + + nsresult rv = Prepare(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } DeviceStorageTypeChecker* typeChecker = DeviceStorageTypeChecker::CreateOrGet(); @@ -1649,7 +1681,8 @@ DeviceStorageRequest::AllowInternal() nsCOMPtr target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); MOZ_ASSERT(target); - return target->Dispatch(this, NS_DISPATCH_NORMAL); + nsCOMPtr self = this; + return target->Dispatch(self.forget(), NS_DISPATCH_NORMAL); } DS_LOG_INFO("run %u", mId); @@ -1669,7 +1702,7 @@ DeviceStorageRequest::SendToParentProcess() self->Reject(POST_ERROR_EVENT_UNKNOWN); } }); - return NS_DispatchToMainThread(r); + return NS_DispatchToMainThread(r.forget()); } MOZ_ASSERT(NS_IsMainThread()); @@ -1699,11 +1732,11 @@ DeviceStorageCursorRequest::DeviceStorageCursorRequest() void DeviceStorageCursorRequest::Initialize(DeviceStorageRequestManager* aManager, - DeviceStorageFile* aFile, + already_AddRefed&& aFile, uint32_t aRequest, PRTime aSince) { - Initialize(aManager, aFile, aRequest); + Initialize(aManager, Move(aFile), aRequest); mStorageType = mFile->mStorageType; mSince = aSince; } @@ -1729,7 +1762,7 @@ DeviceStorageCursorRequest::SendContinueToParentProcess() { self->SendContinueToParentProcess(); }); - return NS_DispatchToMainThread(r); + return NS_DispatchToMainThread(r.forget()); } MOZ_ASSERT(NS_IsMainThread()); @@ -1756,7 +1789,7 @@ DeviceStorageCursorRequest::Continue() { self->Continue(); }); - nsresult rv = NS_DispatchToMainThread(r); + nsresult rv = NS_DispatchToMainThread(r.forget()); if (NS_WARN_IF(NS_FAILED(rv))) { return Reject(POST_ERROR_EVENT_UNKNOWN); } @@ -1869,6 +1902,8 @@ class DeviceStorageCreateRequest final : public DeviceStorageRequest { public: + using DeviceStorageRequest::Initialize; + DeviceStorageCreateRequest() { mAccess = DEVICE_STORAGE_ACCESS_CREATE; @@ -1903,7 +1938,62 @@ public: return Resolve(fullPath); } + void Initialize(DeviceStorageRequestManager* aManager, + already_AddRefed&& aFile, + uint32_t aRequest) override + { + DeviceStorageRequest::Initialize(aManager, Move(aFile), aRequest); + mUseMainThread = mFile->mPath.IsEmpty(); + } + protected: + nsresult Prepare() override + { + if (!mFile->mPath.IsEmpty()) { + // Checks have already been performed when request was created + return NS_OK; + } + + MOZ_ASSERT(NS_IsMainThread()); + + nsCOMPtr mimeSvc = do_GetService(NS_MIMESERVICE_CONTRACTID); + if (!mimeSvc) { + return NS_ERROR_FAILURE; + } + + // if mimeType or extension are null, the request will be rejected + // in DeviceStorageRequest::AllowInternal when the type checker + // verifies the file path + nsString mimeType; + mBlob->GetType(mimeType); + + nsCString extension; + mimeSvc->GetPrimaryExtension(NS_LossyConvertUTF16toASCII(mimeType), + EmptyCString(), extension); + + char buffer[32]; + NS_MakeRandomString(buffer, ArrayLength(buffer) - 1); + + nsAutoString path; + path.AssignLiteral(buffer); + path.Append('.'); + path.AppendASCII(extension.get()); + + RefPtr file + = DeviceStorageFile::CreateUnique(mFile->mStorageType, + mFile->mStorageName, path, + nsIFile::NORMAL_FILE_TYPE, 00600); + if (!file) { + return NS_ERROR_FAILURE; + } + if (!file->IsSafePath()) { + return NS_ERROR_DOM_SECURITY_ERR; + } + + mFile = file.forget(); + return NS_OK; + } + nsresult CreateSendParams(DeviceStorageParams& aParams) override { BlobChild* actor @@ -1986,16 +2076,17 @@ public: DeviceStorageOpenRequest() { + mUseMainThread = true; mUseStreamTransport = true; mCheckFile = true; DS_LOG_INFO(""); } void Initialize(DeviceStorageRequestManager* aManager, - DeviceStorageFile* aFile, + already_AddRefed&& aFile, uint32_t aRequest) override { - DeviceStorageRequest::Initialize(aManager, aFile, aRequest); + DeviceStorageRequest::Initialize(aManager, Move(aFile), aRequest); mAccess = mFile->mEditable ? DEVICE_STORAGE_ACCESS_WRITE : DEVICE_STORAGE_ACCESS_READ; } @@ -2019,10 +2110,11 @@ public: } protected: - void Prepare() override + nsresult Prepare() override { MOZ_ASSERT(NS_IsMainThread()); mFile->CalculateMimeType(); + return NS_OK; } nsresult CreateSendParams(DeviceStorageParams& aParams) override @@ -2121,7 +2213,8 @@ public: DeviceStorageUsedSpaceCache* usedSpaceCache = DeviceStorageUsedSpaceCache::CreateOrGet(); MOZ_ASSERT(usedSpaceCache); - usedSpaceCache->Dispatch(this); + nsCOMPtr self = this; + usedSpaceCache->Dispatch(self.forget()); return NS_OK; } @@ -2306,10 +2399,10 @@ public: NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(DeviceStoragePermissionCheck, nsIContentPermissionRequest) - DeviceStoragePermissionCheck(DeviceStorageRequest* aRequest, - uint64_t aWindowID, - const PrincipalInfo &aPrincipalInfo) - : mRequest(aRequest) + DeviceStoragePermissionCheck( + already_AddRefed&& aRequest, + uint64_t aWindowID, const PrincipalInfo &aPrincipalInfo) + : mRequest(Move(aRequest)) , mWindowID(aWindowID) , mPrincipalInfo(aPrincipalInfo) { @@ -2461,37 +2554,31 @@ nsDOMDeviceStorage::nsDOMDeviceStorage(nsPIDOMWindow* aWindow) } nsresult -nsDOMDeviceStorage::CheckPermission(DeviceStorageRequest* aRequest) +nsDOMDeviceStorage::CheckPermission( + already_AddRefed&& aRequest) { MOZ_ASSERT(mManager); - uint32_t cache = mManager->CheckPermission(aRequest->GetAccess()); + RefPtr request(aRequest); + uint32_t cache = mManager->CheckPermission(request->GetAccess()); switch (cache) { case nsIPermissionManager::ALLOW_ACTION: - return aRequest->Allow(); + return request->Allow(); case nsIPermissionManager::DENY_ACTION: - return aRequest->Cancel(); + return request->Cancel(); case nsIPermissionManager::PROMPT_ACTION: default: { nsCOMPtr mainThread; nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread)); if (NS_WARN_IF(NS_FAILED(rv))) { - return aRequest->Reject(POST_ERROR_EVENT_UNKNOWN); + return request->Reject(POST_ERROR_EVENT_UNKNOWN); } - /* We need to do a bit of a song and dance here to release the object - because while we can initially increment the ownership count (no one - else is using it), we cannot safely decrement after dispatching because - it uses cycle collection and requires the main thread to free it. */ - nsCOMPtr r - = new DeviceStoragePermissionCheck(aRequest, mInnerWindowID, - *mPrincipalInfo); - rv = mainThread->Dispatch(r, NS_DISPATCH_NORMAL); - if (NS_WARN_IF(NS_FAILED(rv))) { - rv = aRequest->Reject(POST_ERROR_EVENT_UNKNOWN); - } - NS_ProxyRelease(mainThread, r.forget().take()); - return rv; + return mainThread->Dispatch( + MakeAndAddRef(request.forget(), + mInnerWindowID, + *mPrincipalInfo), + NS_DISPATCH_NORMAL); } } } @@ -2505,9 +2592,10 @@ nsDOMDeviceStorage::IsOwningThread() } nsresult -nsDOMDeviceStorage::DispatchToOwningThread(nsIRunnable* aRunnable) +nsDOMDeviceStorage::DispatchToOwningThread( + already_AddRefed&& aRunnable) { - return mOwningThread->Dispatch(aRunnable, NS_DISPATCH_NORMAL); + return mOwningThread->Dispatch(Move(aRunnable), NS_DISPATCH_NORMAL); } /* virtual */ JSObject* @@ -2910,45 +2998,18 @@ nsDOMDeviceStorage::IsAvailable() already_AddRefed nsDOMDeviceStorage::Add(Blob* aBlob, ErrorResult& aRv) { - if (!aBlob) { - return nullptr; - } - - nsCOMPtr mimeSvc = do_GetService(NS_MIMESERVICE_CONTRACTID); - if (!mimeSvc) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - - // if mimeType isn't set, we will not get a correct - // extension, and AddNamed() will fail. This will post an - // onerror to the requestee. - nsString mimeType; - aBlob->GetType(mimeType); - - nsCString extension; - mimeSvc->GetPrimaryExtension(NS_LossyConvertUTF16toASCII(mimeType), - EmptyCString(), extension); - // if extension is null here, we will ignore it for now. - // AddNamed() will check the file path and fail. This - // will post an onerror to the requestee. - - // possible race here w/ unique filename - char buffer[32]; - NS_MakeRandomString(buffer, ArrayLength(buffer) - 1); - - nsAutoCString path; - path.Assign(nsDependentCString(buffer)); - path.Append('.'); - path.Append(extension); - - return AddNamed(aBlob, NS_ConvertASCIItoUTF16(path), aRv); + nsString path; + return AddOrAppendNamed(aBlob, path, true, aRv); } already_AddRefed nsDOMDeviceStorage::AddNamed(Blob* aBlob, const nsAString& aPath, ErrorResult& aRv) { + if (aPath.IsEmpty()) { + aRv.Throw(NS_ERROR_ILLEGAL_VALUE); + return nullptr; + } return AddOrAppendNamed(aBlob, aPath, true, aRv); } @@ -2956,6 +3017,10 @@ already_AddRefed nsDOMDeviceStorage::AppendNamed(Blob* aBlob, const nsAString& aPath, ErrorResult& aRv) { + if (aPath.IsEmpty()) { + aRv.Throw(NS_ERROR_ILLEGAL_VALUE); + return nullptr; + } return AddOrAppendNamed(aBlob, aPath, false, aRv); } @@ -3001,19 +3066,13 @@ nsDOMDeviceStorage::AddOrAppendNamed(Blob* aBlob, const nsAString& aPath, bool aCreate, ErrorResult& aRv) { MOZ_ASSERT(IsOwningThread()); + MOZ_ASSERT(aCreate || !aPath.IsEmpty()); // if the blob is null here, bail if (!aBlob) { return nullptr; } - DeviceStorageTypeChecker* typeChecker - = DeviceStorageTypeChecker::CreateOrGet(); - if (!typeChecker) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - nsCOMPtr r; if (IsFullPath(aPath)) { @@ -3032,25 +3091,25 @@ nsDOMDeviceStorage::AddOrAppendNamed(Blob* aBlob, const nsAString& aPath, return nullptr; } - RefPtr dsf = new DeviceStorageFile(mStorageType, - mStorageName, - aPath); - if (!dsf->IsSafePath()) { - aRv = mManager->Reject(id, POST_ERROR_EVENT_PERMISSION_DENIED); - } else if (!typeChecker->Check(mStorageType, dsf->mFile) || - !typeChecker->Check(mStorageType, aBlob->Impl())) { - aRv = mManager->Reject(id, POST_ERROR_EVENT_ILLEGAL_TYPE); + RefPtr dsf; + if (aPath.IsEmpty()) { + dsf = new DeviceStorageFile(mStorageType, mStorageName); } else { - RefPtr request; - if (aCreate) { - request = new DeviceStorageCreateRequest(); - } else { - request = new DeviceStorageAppendRequest(); + dsf = new DeviceStorageFile(mStorageType, mStorageName, aPath); + if (!dsf->IsSafePath()) { + aRv = mManager->Reject(id, POST_ERROR_EVENT_PERMISSION_DENIED); + return domRequest.forget(); } - request->Initialize(mManager, dsf, id, aBlob->Impl()); - aRv = CheckPermission(request); } + RefPtr request; + if (aCreate) { + request = new DeviceStorageCreateRequest(); + } else { + request = new DeviceStorageAppendRequest(); + } + request->Initialize(mManager, dsf.forget(), id, aBlob->Impl()); + aRv = CheckPermission(request.forget()); return domRequest.forget(); } @@ -3084,9 +3143,9 @@ nsDOMDeviceStorage::GetInternal(const nsAString& aPath, bool aEditable, } RefPtr request = new DeviceStorageOpenRequest(); - request->Initialize(mManager, dsf, id); + request->Initialize(mManager, dsf.forget(), id); - aRv = CheckPermission(request); + aRv = CheckPermission(request.forget()); return domRequest.forget(); } @@ -3118,9 +3177,9 @@ nsDOMDeviceStorage::Delete(const nsAString& aPath, ErrorResult& aRv) } RefPtr request = new DeviceStorageDeleteRequest(); - request->Initialize(mManager, dsf, id); + request->Initialize(mManager, dsf.forget(), id); - aRv = CheckPermission(request); + aRv = CheckPermission(request.forget()); return domRequest.forget(); } @@ -3139,9 +3198,9 @@ nsDOMDeviceStorage::FreeSpace(ErrorResult& aRv) } RefPtr request = new DeviceStorageFreeSpaceRequest(); - request->Initialize(mManager, dsf, id); + request->Initialize(mManager, dsf.forget(), id); - aRv = CheckPermission(request); + aRv = CheckPermission(request.forget()); return domRequest.forget(); } @@ -3164,9 +3223,9 @@ nsDOMDeviceStorage::UsedSpace(ErrorResult& aRv) } RefPtr request = new DeviceStorageUsedSpaceRequest(); - request->Initialize(mManager, dsf, id); + request->Initialize(mManager, dsf.forget(), id); - aRv = CheckPermission(request); + aRv = CheckPermission(request.forget()); return domRequest.forget(); } @@ -3185,9 +3244,9 @@ nsDOMDeviceStorage::Available(ErrorResult& aRv) } RefPtr request = new DeviceStorageAvailableRequest(); - request->Initialize(mManager, dsf, id); + request->Initialize(mManager, dsf.forget(), id); - aRv = CheckPermission(request); + aRv = CheckPermission(request.forget()); return domRequest.forget(); } @@ -3206,9 +3265,9 @@ nsDOMDeviceStorage::StorageStatus(ErrorResult& aRv) } RefPtr request = new DeviceStorageStatusRequest(); - request->Initialize(mManager, dsf, id); + request->Initialize(mManager, dsf.forget(), id); - aRv = CheckPermission(request); + aRv = CheckPermission(request.forget()); return domRequest.forget(); } @@ -3227,9 +3286,9 @@ nsDOMDeviceStorage::Format(ErrorResult& aRv) } RefPtr request = new DeviceStorageFormatRequest(); - request->Initialize(mManager, dsf, id); + request->Initialize(mManager, dsf.forget(), id); - aRv = CheckPermission(request); + aRv = CheckPermission(request.forget()); return domRequest.forget(); } @@ -3248,9 +3307,9 @@ nsDOMDeviceStorage::Mount(ErrorResult& aRv) } RefPtr request = new DeviceStorageMountRequest(); - request->Initialize(mManager, dsf, id); + request->Initialize(mManager, dsf.forget(), id); - aRv = CheckPermission(request); + aRv = CheckPermission(request.forget()); return domRequest.forget(); } @@ -3270,9 +3329,9 @@ nsDOMDeviceStorage::Unmount(ErrorResult& aRv) RefPtr request = new DeviceStorageUnmountRequest(); - request->Initialize(mManager, dsf, id); + request->Initialize(mManager, dsf.forget(), id); - aRv = CheckPermission(request); + aRv = CheckPermission(request.forget()); return domRequest.forget(); } @@ -3318,9 +3377,9 @@ nsDOMDeviceStorage::CreateFileDescriptor(const nsAString& aPath, } RefPtr request = new DeviceStorageCreateFdRequest(); - request->Initialize(mManager, dsf, id, aDSFileDescriptor); + request->Initialize(mManager, dsf.forget(), id, aDSFileDescriptor); - aRv = CheckPermission(request); + aRv = CheckPermission(request.forget()); return domRequest.forget(); } @@ -3425,8 +3484,8 @@ nsDOMDeviceStorage::EnumerateInternal(const nsAString& aPath, if (!dsf->IsSafePath()) { aRv = mManager->Reject(id, POST_ERROR_EVENT_PERMISSION_DENIED); } else { - request->Initialize(mManager, dsf, id, since); - aRv = CheckPermission(request); + request->Initialize(mManager, dsf.forget(), id, since); + aRv = CheckPermission(request.forget()); } return cursor.forget(); @@ -3628,8 +3687,8 @@ nsDOMDeviceStorage::EventListenerWasAdded(const nsAString& aType, RefPtr dsf = new DeviceStorageFile(mStorageType, mStorageName); RefPtr request = new DeviceStorageWatchRequest(); - request->Initialize(mManager, dsf, id); - aRv = CheckPermission(request); + request->Initialize(mManager, dsf.forget(), id); + aRv = CheckPermission(request.forget()); } Atomic DeviceStorageRequestManager::sLastRequestId(0); @@ -3655,8 +3714,7 @@ DeviceStorageRequestManager::~DeviceStorageRequestManager() while (i > 0) { --i; DS_LOG_ERROR("terminate %u", mPending[i].mId); - NS_ProxyRelease(mOwningThread, - NS_ISUPPORTS_CAST(EventTarget*, mPending[i].mRequest.forget().take())); + NS_ProxyRelease(mOwningThread, mPending[i].mRequest.forget()); } } } @@ -3693,14 +3751,15 @@ DeviceStorageRequestManager::IsOwningThread() } nsresult -DeviceStorageRequestManager::DispatchToOwningThread(nsIRunnable* aRunnable) +DeviceStorageRequestManager::DispatchToOwningThread( + already_AddRefed&& aRunnable) { - return mOwningThread->Dispatch(aRunnable, NS_DISPATCH_NORMAL); + return mOwningThread->Dispatch(Move(aRunnable), NS_DISPATCH_NORMAL); } nsresult -DeviceStorageRequestManager::DispatchOrAbandon(uint32_t aId, - nsIRunnable* aRunnable) +DeviceStorageRequestManager::DispatchOrAbandon( + uint32_t aId, already_AddRefed&& aRunnable) { MutexAutoLock lock(mMutex); if (mShutdown) { @@ -3709,10 +3768,11 @@ DeviceStorageRequestManager::DispatchOrAbandon(uint32_t aId, safe to be freed off the owner thread but the dispatch method does not know that. */ DS_LOG_DEBUG("shutdown %u", aId); + nsCOMPtr runnable(aRunnable); return NS_ERROR_ILLEGAL_DURING_SHUTDOWN; } - nsresult rv = DispatchToOwningThread(aRunnable); + nsresult rv = DispatchToOwningThread(Move(aRunnable)); if (NS_WARN_IF(NS_FAILED(rv))) { DS_LOG_ERROR("abandon %u", aId); } @@ -3779,7 +3839,7 @@ DeviceStorageRequestManager::Resolve(uint32_t aId, bool aForceDispatch) { self->Resolve(aId, false); }); - return DispatchOrAbandon(aId, r); + return DispatchOrAbandon(aId, r.forget()); } DS_LOG_INFO("posted %u", aId); @@ -3811,7 +3871,7 @@ DeviceStorageRequestManager::Resolve(uint32_t aId, const nsString& aResult, { self->Resolve(aId, result, false); }); - return DispatchOrAbandon(aId, r); + return DispatchOrAbandon(aId, r.forget()); } DS_LOG_INFO("posted %u w/ %s", aId, @@ -3856,7 +3916,7 @@ DeviceStorageRequestManager::Resolve(uint32_t aId, uint64_t aValue, { self->Resolve(aId, aValue, false); }); - return DispatchOrAbandon(aId, r); + return DispatchOrAbandon(aId, r.forget()); } DS_LOG_INFO("posted %u w/ %" PRIu64, aId, aValue); @@ -3936,7 +3996,7 @@ DeviceStorageRequestManager::Resolve(uint32_t aId, BlobImpl* aBlobImpl, { self->Resolve(aId, blobImpl, false); }); - return DispatchOrAbandon(aId, r); + return DispatchOrAbandon(aId, r.forget()); } DS_LOG_INFO("posted %u w/ %p", aId, aBlobImpl); @@ -4064,7 +4124,7 @@ DeviceStorageRequestManager::Reject(uint32_t aId, const nsString& aReason) self->RejectInternal(i, reason); }); - return DispatchOrAbandon(aId, r); + return DispatchOrAbandon(aId, r.forget()); } nsresult diff --git a/dom/devicestorage/nsDeviceStorage.h b/dom/devicestorage/nsDeviceStorage.h index c7edeff2c8..fc06d1cf56 100644 --- a/dom/devicestorage/nsDeviceStorage.h +++ b/dom/devicestorage/nsDeviceStorage.h @@ -130,16 +130,16 @@ public: MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(mIOThread); - RefPtr r = new InvalidateRunnable(this, aStorageName); - mIOThread->Dispatch(r, NS_DISPATCH_NORMAL); + mIOThread->Dispatch(new InvalidateRunnable(this, aStorageName), + NS_DISPATCH_NORMAL); } - void Dispatch(nsIRunnable* aRunnable) + void Dispatch(already_AddRefed&& aRunnable) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(mIOThread); - mIOThread->Dispatch(aRunnable, NS_DISPATCH_NORMAL); + mIOThread->Dispatch(mozilla::Move(aRunnable), NS_DISPATCH_NORMAL); } nsresult AccumUsedSizes(const nsAString& aStorageName, @@ -245,7 +245,7 @@ public: DeviceStorageRequestManager(); bool IsOwningThread(); - nsresult DispatchToOwningThread(nsIRunnable* aRunnable); + nsresult DispatchToOwningThread(already_AddRefed&& aRunnable); void StorePermission(size_t aAccess, bool aAllow); uint32_t CheckPermission(size_t aAccess); @@ -290,7 +290,8 @@ private: uint32_t CreateInternal(mozilla::dom::DOMRequest* aRequest, bool aCursor); nsresult ResolveInternal(ListIndex aIndex, JS::HandleValue aResult); nsresult RejectInternal(ListIndex aIndex, const nsString& aReason); - nsresult DispatchOrAbandon(uint32_t aId, nsIRunnable* aRunnable); + nsresult DispatchOrAbandon(uint32_t aId, + already_AddRefed&& aRunnable); ListType::index_type Find(uint32_t aId); nsCOMPtr mOwningThread; @@ -311,16 +312,16 @@ protected: public: virtual void Initialize(DeviceStorageRequestManager* aManager, - DeviceStorageFile* aFile, + already_AddRefed&& aFile, uint32_t aRequest); virtual void Initialize(DeviceStorageRequestManager* aManager, - DeviceStorageFile* aFile, + already_AddRefed&& aFile, uint32_t aRequest, mozilla::dom::BlobImpl* aBlob); virtual void Initialize(DeviceStorageRequestManager* aManager, - DeviceStorageFile* aFile, + already_AddRefed&& aFile, uint32_t aRequest, DeviceStorageFileDescriptor* aDSFileDescriptor); @@ -378,7 +379,7 @@ protected: } virtual ~DeviceStorageRequest(); - virtual void Prepare(); + virtual nsresult Prepare(); virtual nsresult CreateSendParams(mozilla::dom::DeviceStorageParams& aParams); nsresult AllowInternal(); nsresult SendToParentProcess(); @@ -390,6 +391,7 @@ protected: RefPtr mDSFileDescriptor; DeviceStorageAccessType mAccess; bool mSendToParent; + bool mUseMainThread; bool mUseStreamTransport; bool mCheckFile; bool mCheckBlob; @@ -410,7 +412,7 @@ public: using DeviceStorageRequest::Initialize; virtual void Initialize(DeviceStorageRequestManager* aManager, - DeviceStorageFile* aFile, + already_AddRefed&& aFile, uint32_t aRequest, PRTime aSince); diff --git a/dom/events/Event.cpp b/dom/events/Event.cpp index 06a50bf2dc..b5a586d496 100644 --- a/dom/events/Event.cpp +++ b/dom/events/Event.cpp @@ -1128,7 +1128,7 @@ Event::TimeStamp() const MOZ_ASSERT(workerPrivate); TimeDuration duration = - mEvent->timeStamp - workerPrivate->CreationTimeStamp(); + mEvent->timeStamp - workerPrivate->NowBaseTimeStamp(); return duration.ToMilliseconds(); } diff --git a/dom/events/test/test_eventTimeStamp.html b/dom/events/test/test_eventTimeStamp.html index edc2ed05d6..a3d096432a 100644 --- a/dom/events/test/test_eventTimeStamp.html +++ b/dom/events/test/test_eventTimeStamp.html @@ -74,9 +74,8 @@ function testWorkerEvents() { var worker = new Worker(window.URL.createObjectURL(blob)); worker.onmessage = function(evt) { var timeAfterEvent = window.performance.now(); - var time = window.performance.translateTime(evt.data, worker); - ok(time >= timeBeforeEvent && - time <= timeAfterEvent, + ok(evt.data > timeBeforeEvent && + evt.data < timeAfterEvent, "Event timestamp in dedicated worker (" + evt.data + ") is in expected range: (" + timeBeforeEvent + ", " + timeAfterEvent + ")"); diff --git a/dom/fetch/InternalRequest.cpp b/dom/fetch/InternalRequest.cpp index 07c9fc0b13..01fe39f8bf 100644 --- a/dom/fetch/InternalRequest.cpp +++ b/dom/fetch/InternalRequest.cpp @@ -324,5 +324,50 @@ InternalRequest::MapChannelToRequestMode(nsIChannel* aChannel) return static_cast(corsMode); } +// static +RequestCredentials +InternalRequest::MapChannelToRequestCredentials(nsIChannel* aChannel) +{ + MOZ_ASSERT(aChannel); + + nsCOMPtr loadInfo; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aChannel->GetLoadInfo(getter_AddRefs(loadInfo)))); + + uint32_t securityMode; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(loadInfo->GetSecurityMode(&securityMode))); + + // TODO: Remove following code after stylesheet and image support cookie policy + if (securityMode == nsILoadInfo::SEC_NORMAL) { + uint32_t loadFlags; + aChannel->GetLoadFlags(&loadFlags); + + if (loadFlags & nsIRequest::LOAD_ANONYMOUS) { + return RequestCredentials::Omit; + } else { + bool includeCrossOrigin; + nsCOMPtr internalChannel = do_QueryInterface(aChannel); + + internalChannel->GetCorsIncludeCredentials(&includeCrossOrigin); + if (includeCrossOrigin) { + return RequestCredentials::Include; + } + } + return RequestCredentials::Same_origin; + } + + uint32_t cookiePolicy = loadInfo->GetCookiePolicy(); + + if (cookiePolicy == nsILoadInfo::SEC_COOKIES_INCLUDE) { + return RequestCredentials::Include; + } else if (cookiePolicy == nsILoadInfo::SEC_COOKIES_OMIT) { + return RequestCredentials::Omit; + } else if (cookiePolicy == nsILoadInfo::SEC_COOKIES_SAME_ORIGIN) { + return RequestCredentials::Same_origin; + } + + MOZ_ASSERT_UNREACHABLE("Unexpected cookie policy!"); + return RequestCredentials::Same_origin; +} + } // namespace dom } // namespace mozilla diff --git a/dom/fetch/InternalRequest.h b/dom/fetch/InternalRequest.h index fa4629babf..df1b8abd2c 100644 --- a/dom/fetch/InternalRequest.h +++ b/dom/fetch/InternalRequest.h @@ -376,6 +376,9 @@ public: static RequestMode MapChannelToRequestMode(nsIChannel* aChannel); + static RequestCredentials + MapChannelToRequestCredentials(nsIChannel* aChannel); + private: // Does not copy mBodyStream. Use fallible Clone() for complete copy. explicit InternalRequest(const InternalRequest& aOther); diff --git a/dom/indexedDB/ActorsChild.h b/dom/indexedDB/ActorsChild.h index 8055a85f8e..e436d97369 100644 --- a/dom/indexedDB/ActorsChild.h +++ b/dom/indexedDB/ActorsChild.h @@ -36,7 +36,6 @@ class BackgroundChildImpl; } // namespace ipc namespace dom { -namespace indexedDB { class IDBCursor; class IDBDatabase; @@ -44,6 +43,9 @@ class IDBFactory; class IDBMutableFile; class IDBOpenDBRequest; class IDBRequest; + +namespace indexedDB { + class Key; class PermissionRequestChild; class PermissionRequestParent; @@ -52,7 +54,7 @@ class SerializedStructuredCloneReadInfo; class ThreadLocal { friend class nsAutoPtr; - friend class IDBFactory; + friend IDBFactory; LoggingInfo mLoggingInfo; IDBTransaction* mCurrentTransaction; @@ -147,7 +149,7 @@ class BackgroundFactoryChild final : public PBackgroundIDBFactoryChild { friend class mozilla::ipc::BackgroundChildImpl; - friend class IDBFactory; + friend IDBFactory; IDBFactory* mFactory; @@ -240,7 +242,7 @@ class BackgroundFactoryRequestChild final { typedef mozilla::dom::quota::PersistenceType PersistenceType; - friend class IDBFactory; + friend IDBFactory; friend class BackgroundFactoryChild; friend class BackgroundDatabaseChild; friend class PermissionRequestChild; @@ -292,7 +294,7 @@ class BackgroundDatabaseChild final { friend class BackgroundFactoryChild; friend class BackgroundFactoryRequestChild; - friend class IDBDatabase; + friend IDBDatabase; nsAutoPtr mSpec; RefPtr mTemporaryStrongDatabase; @@ -424,7 +426,7 @@ class BackgroundDatabaseRequestChild final , public PBackgroundIDBDatabaseRequestChild { friend class BackgroundDatabaseChild; - friend class IDBDatabase; + friend IDBDatabase; RefPtr mDatabase; @@ -504,7 +506,7 @@ class BackgroundTransactionChild final , public PBackgroundIDBTransactionChild { friend class BackgroundDatabaseChild; - friend class IDBDatabase; + friend IDBDatabase; public: #ifdef DEBUG @@ -631,7 +633,7 @@ class BackgroundRequestChild final { friend class BackgroundTransactionChild; friend class BackgroundVersionChangeTransactionChild; - friend class IDBTransaction; + friend IDBTransaction; RefPtr mTransaction; diff --git a/dom/indexedDB/FileSnapshot.h b/dom/indexedDB/FileSnapshot.h index 754e1dac94..d6a3dad001 100644 --- a/dom/indexedDB/FileSnapshot.h +++ b/dom/indexedDB/FileSnapshot.h @@ -29,10 +29,11 @@ NS_DEFINE_STATIC_IID_ACCESSOR(PIBlobImplSnapshot, FILEIMPLSNAPSHOT_IID) namespace mozilla { namespace dom { -namespace indexedDB { class IDBFileHandle; +namespace indexedDB { + class BlobImplSnapshot final : public BlobImpl , public PIBlobImplSnapshot diff --git a/dom/indexedDB/IDBCursor.cpp b/dom/indexedDB/IDBCursor.cpp index e0fdc9c12c..d40b69af52 100644 --- a/dom/indexedDB/IDBCursor.cpp +++ b/dom/indexedDB/IDBCursor.cpp @@ -24,7 +24,8 @@ namespace mozilla { namespace dom { -namespace indexedDB { + +using namespace indexedDB; IDBCursor::IDBCursor(Type aType, BackgroundCursorChild* aBackgroundActor, @@ -896,6 +897,5 @@ IDBCursor::WrapObject(JSContext* aCx, JS::Handle aGivenProto) } } -} // namespace indexedDB } // namespace dom } // namespace mozilla diff --git a/dom/indexedDB/IDBCursor.h b/dom/indexedDB/IDBCursor.h index d54ecade52..1e4d9f2811 100644 --- a/dom/indexedDB/IDBCursor.h +++ b/dom/indexedDB/IDBCursor.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_dom_indexeddb_idbcursor_h__ -#define mozilla_dom_indexeddb_idbcursor_h__ +#ifndef mozilla_dom_idbcursor_h__ +#define mozilla_dom_idbcursor_h__ #include "IndexedDatabase.h" #include "js/RootingAPI.h" @@ -24,21 +24,24 @@ class ErrorResult; namespace dom { -class OwningIDBObjectStoreOrIDBIndex; - -namespace indexedDB { - -class BackgroundCursorChild; class IDBIndex; class IDBObjectStore; class IDBRequest; class IDBTransaction; +class OwningIDBObjectStoreOrIDBIndex; + +namespace indexedDB { +class BackgroundCursorChild; +} class IDBCursor final : public nsISupports , public nsWrapperCache { public: + typedef indexedDB::Key Key; + typedef indexedDB::StructuredCloneReadInfo StructuredCloneReadInfo; + enum Direction { NEXT = 0, @@ -59,7 +62,7 @@ private: Type_IndexKey, }; - BackgroundCursorChild* mBackgroundActor; + indexedDB::BackgroundCursorChild* mBackgroundActor; RefPtr mRequest; RefPtr mSourceObjectStore; @@ -92,23 +95,23 @@ private: public: static already_AddRefed - Create(BackgroundCursorChild* aBackgroundActor, + Create(indexedDB::BackgroundCursorChild* aBackgroundActor, const Key& aKey, StructuredCloneReadInfo&& aCloneInfo); static already_AddRefed - Create(BackgroundCursorChild* aBackgroundActor, + Create(indexedDB::BackgroundCursorChild* aBackgroundActor, const Key& aKey); static already_AddRefed - Create(BackgroundCursorChild* aBackgroundActor, + Create(indexedDB::BackgroundCursorChild* aBackgroundActor, const Key& aKey, const Key& aSortKey, const Key& aPrimaryKey, StructuredCloneReadInfo&& aCloneInfo); static already_AddRefed - Create(BackgroundCursorChild* aBackgroundActor, + Create(indexedDB::BackgroundCursorChild* aBackgroundActor, const Key& aKey, const Key& aSortKey, const Key& aPrimaryKey); @@ -194,7 +197,7 @@ public: private: IDBCursor(Type aType, - BackgroundCursorChild* aBackgroundActor, + indexedDB::BackgroundCursorChild* aBackgroundActor, const Key& aKey); ~IDBCursor(); @@ -212,8 +215,7 @@ private: IsSourceDeleted() const; }; -} // namespace indexedDB } // namespace dom } // namespace mozilla -#endif // mozilla_dom_indexeddb_idbcursor_h__ +#endif // mozilla_dom_idbcursor_h__ diff --git a/dom/indexedDB/IDBDatabase.cpp b/dom/indexedDB/IDBDatabase.cpp index 95c5004d80..57825c2bda 100644 --- a/dom/indexedDB/IDBDatabase.cpp +++ b/dom/indexedDB/IDBDatabase.cpp @@ -56,7 +56,6 @@ namespace mozilla { namespace dom { -namespace indexedDB { using namespace mozilla::dom::quota; using namespace mozilla::ipc; @@ -469,7 +468,6 @@ IDBDatabase::CreateObjectStore( AssertIsOnOwningThread(); IDBTransaction* transaction = IDBTransaction::GetCurrent(); - if (!transaction || transaction->Database() != this || transaction->GetMode() != IDBTransaction::VERSION_CHANGE) { @@ -477,7 +475,10 @@ IDBDatabase::CreateObjectStore( return nullptr; } - MOZ_ASSERT(transaction->IsOpen()); + if (!transaction->IsOpen()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return nullptr; + } KeyPath keyPath(0); if (NS_FAILED(KeyPath::Parse(aOptionalParameters.mKeyPath, &keyPath))) { @@ -545,7 +546,6 @@ IDBDatabase::DeleteObjectStore(const nsAString& aName, ErrorResult& aRv) AssertIsOnOwningThread(); IDBTransaction* transaction = IDBTransaction::GetCurrent(); - if (!transaction || transaction->Database() != this || transaction->GetMode() != IDBTransaction::VERSION_CHANGE) { @@ -553,7 +553,10 @@ IDBDatabase::DeleteObjectStore(const nsAString& aName, ErrorResult& aRv) return; } - MOZ_ASSERT(transaction->IsOpen()); + if (!transaction->IsOpen()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return; + } nsTArray& specArray = mSpec->objectStores(); @@ -1482,6 +1485,5 @@ Observer::Observe(nsISupports* aSubject, return NS_OK; } -} // namespace indexedDB } // namespace dom } // namespace mozilla diff --git a/dom/indexedDB/IDBDatabase.h b/dom/indexedDB/IDBDatabase.h index 6cf20c94f1..f04dc9613d 100644 --- a/dom/indexedDB/IDBDatabase.h +++ b/dom/indexedDB/IDBDatabase.h @@ -4,13 +4,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/. */ -#ifndef mozilla_dom_indexeddb_idbdatabase_h__ -#define mozilla_dom_indexeddb_idbdatabase_h__ +#ifndef mozilla_dom_idbdatabase_h__ +#define mozilla_dom_idbdatabase_h__ #include "mozilla/Attributes.h" #include "mozilla/dom/IDBTransactionBinding.h" #include "mozilla/dom/StorageTypeBinding.h" -#include "mozilla/dom/indexedDB/IDBWrapperCache.h" +#include "mozilla/dom/IDBWrapperCache.h" #include "mozilla/dom/quota/PersistenceType.h" #include "nsAutoPtr.h" #include "nsDataHashtable.h" @@ -30,25 +30,26 @@ namespace dom { class Blob; class DOMStringList; +class IDBFactory; +class IDBMutableFile; +class IDBObjectStore; struct IDBObjectStoreParameters; +class IDBOpenDBRequest; +class IDBRequest; +class IDBTransaction; template class Optional; class StringOrStringSequence; namespace indexedDB { - class BackgroundDatabaseChild; class DatabaseSpec; -class IDBFactory; -class IDBMutableFile; -class IDBObjectStore; -class IDBOpenDBRequest; -class IDBRequest; -class IDBTransaction; class PBackgroundIDBDatabaseFileChild; +} class IDBDatabase final : public IDBWrapperCache { + typedef mozilla::dom::indexedDB::DatabaseSpec DatabaseSpec; typedef mozilla::dom::StorageType StorageType; typedef mozilla::dom::quota::PersistenceType PersistenceType; @@ -68,11 +69,11 @@ class IDBDatabase final // Normally null except during a versionchange transaction. nsAutoPtr mPreviousSpec; - BackgroundDatabaseChild* mBackgroundActor; + indexedDB::BackgroundDatabaseChild* mBackgroundActor; nsTHashtable> mTransactions; - nsDataHashtable + nsDataHashtable mFileActors; nsTHashtable mReceivedBlobs; @@ -90,7 +91,7 @@ public: static already_AddRefed Create(IDBOpenDBRequest* aRequest, IDBFactory* aFactory, - BackgroundDatabaseChild* aActor, + indexedDB::BackgroundDatabaseChild* aActor, DatabaseSpec* aSpec); #ifdef DEBUG @@ -179,11 +180,11 @@ public: void AbortTransactions(bool aShouldWarn); - PBackgroundIDBDatabaseFileChild* + indexedDB::PBackgroundIDBDatabaseFileChild* GetOrCreateFileActorForBlob(Blob* aBlob); void - NoteFinishedFileActor(PBackgroundIDBDatabaseFileChild* aFileActor); + NoteFinishedFileActor(indexedDB::PBackgroundIDBDatabaseFileChild* aFileActor); void NoteReceivedBlob(Blob* aBlob); @@ -285,7 +286,7 @@ public: private: IDBDatabase(IDBOpenDBRequest* aRequest, IDBFactory* aFactory, - BackgroundDatabaseChild* aActor, + indexedDB::BackgroundDatabaseChild* aActor, DatabaseSpec* aSpec); ~IDBDatabase(); @@ -320,8 +321,7 @@ private: uint32_t aColumnNumber); }; -} // namespace indexedDB } // namespace dom } // namespace mozilla -#endif // mozilla_dom_indexeddb_idbdatabase_h__ +#endif // mozilla_dom_idbdatabase_h__ diff --git a/dom/indexedDB/IDBEvents.cpp b/dom/indexedDB/IDBEvents.cpp index 7d26272613..d4529d29e3 100644 --- a/dom/indexedDB/IDBEvents.cpp +++ b/dom/indexedDB/IDBEvents.cpp @@ -44,6 +44,8 @@ CreateGenericEvent(EventTarget* aOwner, return event.forget(); } +} // namespace indexedDB + // static already_AddRefed IDBVersionChangeEvent::CreateInternal(EventTarget* aOwner, @@ -91,6 +93,5 @@ IDBVersionChangeEvent::WrapObjectInternal(JSContext* aCx, JS::Handle return IDBVersionChangeEventBinding::Wrap(aCx, this, aGivenProto); } -} // namespace indexedDB } // namespace dom } // namespace mozilla diff --git a/dom/indexedDB/IDBEvents.h b/dom/indexedDB/IDBEvents.h index e07d9eee56..5b3042a05e 100644 --- a/dom/indexedDB/IDBEvents.h +++ b/dom/indexedDB/IDBEvents.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_dom_indexeddb_idbevents_h__ -#define mozilla_dom_indexeddb_idbevents_h__ +#ifndef mozilla_dom_idbevents_h__ +#define mozilla_dom_idbevents_h__ #include "js/RootingAPI.h" #include "mozilla/dom/BindingDeclarations.h" @@ -54,6 +54,8 @@ CreateGenericEvent(EventTarget* aOwner, Bubbles aBubbles, Cancelable aCancelable); +} // namespace indexedDB + class IDBVersionChangeEvent final : public Event { uint64_t mOldVersion; @@ -125,8 +127,7 @@ private: NS_DEFINE_STATIC_IID_ACCESSOR(IDBVersionChangeEvent, IDBVERSIONCHANGEEVENT_IID) -} // namespace indexedDB } // namespace dom } // namespace mozilla -#endif // mozilla_dom_indexeddb_idbevents_h__ +#endif // mozilla_dom_idbevents_h__ diff --git a/dom/indexedDB/IDBFactory.cpp b/dom/indexedDB/IDBFactory.cpp index 85e3140693..fbbdecc587 100644 --- a/dom/indexedDB/IDBFactory.cpp +++ b/dom/indexedDB/IDBFactory.cpp @@ -43,7 +43,6 @@ namespace mozilla { namespace dom { -namespace indexedDB { using namespace mozilla::dom::quota; using namespace mozilla::ipc; @@ -956,6 +955,5 @@ IDBFactory::BackgroundCreateCallback::ActorFailed() factory->BackgroundActorFailed(); } -} // namespace indexedDB } // namespace dom } // namespace mozilla diff --git a/dom/indexedDB/IDBFactory.h b/dom/indexedDB/IDBFactory.h index f7614de433..461d5fb0b8 100644 --- a/dom/indexedDB/IDBFactory.h +++ b/dom/indexedDB/IDBFactory.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_dom_indexeddb_idbfactory_h__ -#define mozilla_dom_indexeddb_idbfactory_h__ +#ifndef mozilla_dom_idbfactory_h__ +#define mozilla_dom_idbfactory_h__ #include "mozilla/Attributes.h" #include "mozilla/dom/StorageTypeBinding.h" @@ -35,15 +35,15 @@ class PrincipalInfo; namespace dom { struct IDBOpenDBOptions; +class IDBOpenDBRequest; template class Optional; class TabChild; namespace indexedDB { - class BackgroundFactoryChild; class FactoryRequestParams; -class IDBOpenDBRequest; class LoggingInfo; +} class IDBFactory final : public nsISupports @@ -69,7 +69,7 @@ class IDBFactory final nsTArray> mPendingRequests; - BackgroundFactoryChild* mBackgroundActor; + indexedDB::BackgroundFactoryChild* mBackgroundActor; #ifdef DEBUG PRThread* mOwningThread; @@ -240,18 +240,17 @@ private: nsresult BackgroundActorCreated(PBackgroundChild* aBackgroundActor, - const LoggingInfo& aLoggingInfo); + const indexedDB::LoggingInfo& aLoggingInfo); void BackgroundActorFailed(); nsresult InitiateRequest(IDBOpenDBRequest* aRequest, - const FactoryRequestParams& aParams); + const indexedDB::FactoryRequestParams& aParams); }; -} // namespace indexedDB } // namespace dom } // namespace mozilla -#endif // mozilla_dom_indexeddb_idbfactory_h__ +#endif // mozilla_dom_idbfactory_h__ diff --git a/dom/indexedDB/IDBFileHandle.cpp b/dom/indexedDB/IDBFileHandle.cpp index a5b047765a..8b88e1722a 100644 --- a/dom/indexedDB/IDBFileHandle.cpp +++ b/dom/indexedDB/IDBFileHandle.cpp @@ -17,7 +17,8 @@ namespace mozilla { namespace dom { -namespace indexedDB { + +using namespace mozilla::dom::indexedDB; IDBFileHandle::IDBFileHandle(FileMode aMode, IDBMutableFile* aMutableFile) @@ -194,6 +195,5 @@ IDBFileHandle::GenerateFileRequest() /* aWrapAsDOMRequest */ false); } -} // namespace indexedDB } // namespace dom } // namespace mozilla diff --git a/dom/indexedDB/IDBFileHandle.h b/dom/indexedDB/IDBFileHandle.h index 1e0eee0479..e064729e76 100644 --- a/dom/indexedDB/IDBFileHandle.h +++ b/dom/indexedDB/IDBFileHandle.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_dom_indexeddb_idbfilehandle_h__ -#define mozilla_dom_indexeddb_idbfilehandle_h__ +#ifndef mozilla_dom_idbfilehandle_h__ +#define mozilla_dom_idbfilehandle_h__ #include "IDBFileRequest.h" #include "js/TypeDecls.h" @@ -22,9 +22,6 @@ namespace mozilla { namespace dom { struct IDBFileMetadataParameters; - -namespace indexedDB { - class IDBFileRequest; class IDBMutableFile; @@ -146,8 +143,7 @@ private: GenerateFileRequest() override; }; -} // namespace indexedDB } // namespace dom } // namespace mozilla -#endif // mozilla_dom_indexeddb_idbfilehandle_h__ +#endif // mozilla_dom_idbfilehandle_h__ diff --git a/dom/indexedDB/IDBFileRequest.cpp b/dom/indexedDB/IDBFileRequest.cpp index 71000e8d22..9762ea94c6 100644 --- a/dom/indexedDB/IDBFileRequest.cpp +++ b/dom/indexedDB/IDBFileRequest.cpp @@ -21,7 +21,8 @@ namespace mozilla { namespace dom { -namespace indexedDB { + +using namespace mozilla::dom::indexedDB; IDBFileRequest::IDBFileRequest(nsPIDOMWindow* aWindow, IDBFileHandle* aFileHandle, @@ -152,6 +153,5 @@ IDBFileRequest::FireProgressEvent(uint64_t aLoaded, uint64_t aTotal) DispatchTrustedEvent(event); } -} // namespace indexedDB } // namespace dom } // namespace mozilla diff --git a/dom/indexedDB/IDBFileRequest.h b/dom/indexedDB/IDBFileRequest.h index 601395ddee..b91122732e 100644 --- a/dom/indexedDB/IDBFileRequest.h +++ b/dom/indexedDB/IDBFileRequest.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_dom_indexeddb_idbfilerequest_h__ -#define mozilla_dom_indexeddb_idbfilerequest_h__ +#ifndef mozilla_dom_idbfilerequest_h__ +#define mozilla_dom_idbfilerequest_h__ #include "DOMRequest.h" #include "js/TypeDecls.h" @@ -22,7 +22,6 @@ namespace mozilla { class EventChainPreVisitor; namespace dom { -namespace indexedDB { class IDBFileHandle; @@ -90,8 +89,7 @@ private: FireProgressEvent(uint64_t aLoaded, uint64_t aTotal); }; -} // namespace indexedDB } // namespace dom } // namespace mozilla -#endif // mozilla_dom_indexeddb_idbfilerequest_h__ +#endif // mozilla_dom_idbfilerequest_h__ diff --git a/dom/indexedDB/IDBIndex.cpp b/dom/indexedDB/IDBIndex.cpp index abf1eb3f00..25698fae6f 100644 --- a/dom/indexedDB/IDBIndex.cpp +++ b/dom/indexedDB/IDBIndex.cpp @@ -25,7 +25,6 @@ namespace mozilla { namespace dom { -namespace indexedDB { namespace { @@ -177,7 +176,7 @@ IDBIndex::LocaleAware() const return mMetadata->locale().IsEmpty(); } -const KeyPath& +const indexedDB::KeyPath& IDBIndex::GetKeyPath() const { AssertIsOnOwningThread(); @@ -621,6 +620,5 @@ IDBIndex::WrapObject(JSContext* aCx, JS::Handle aGivenProto) return IDBIndexBinding::Wrap(aCx, this, aGivenProto); } -} // namespace indexedDB } // namespace dom } // namespace mozilla diff --git a/dom/indexedDB/IDBIndex.h b/dom/indexedDB/IDBIndex.h index da0893704a..98ecefb69a 100644 --- a/dom/indexedDB/IDBIndex.h +++ b/dom/indexedDB/IDBIndex.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_dom_indexeddb_idbindex_h__ -#define mozilla_dom_indexeddb_idbindex_h__ +#ifndef mozilla_dom_idbindex_h__ +#define mozilla_dom_idbindex_h__ #include "js/RootingAPI.h" #include "mozilla/Attributes.h" @@ -24,15 +24,14 @@ class ErrorResult; namespace dom { +class IDBObjectStore; +class IDBRequest; template class Sequence; namespace indexedDB { - -class IDBObjectStore; -class IDBRequest; class IndexMetadata; -class Key; class KeyPath; +} // namespace indexedDB class IDBIndex final : public nsISupports @@ -46,8 +45,8 @@ class IDBIndex final // object. However, if this index is part of a versionchange transaction and // it gets deleted then the metadata is copied into mDeletedMetadata and // mMetadata is set to point at mDeletedMetadata. - const IndexMetadata* mMetadata; - nsAutoPtr mDeletedMetadata; + const indexedDB::IndexMetadata* mMetadata; + nsAutoPtr mDeletedMetadata; const int64_t mId; bool mRooted; @@ -57,7 +56,7 @@ public: NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBIndex) static already_AddRefed - Create(IDBObjectStore* aObjectStore, const IndexMetadata& aMetadata); + Create(IDBObjectStore* aObjectStore, const indexedDB::IndexMetadata& aMetadata); int64_t Id() const @@ -79,7 +78,7 @@ public: bool LocaleAware() const; - const KeyPath& + const indexedDB::KeyPath& GetKeyPath() const; void @@ -201,7 +200,7 @@ public: WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; private: - IDBIndex(IDBObjectStore* aObjectStore, const IndexMetadata* aMetadata); + IDBIndex(IDBObjectStore* aObjectStore, const indexedDB::IndexMetadata* aMetadata); ~IDBIndex(); @@ -226,8 +225,7 @@ private: ErrorResult& aRv); }; -} // namespace indexedDB } // namespace dom } // namespace mozilla -#endif // mozilla_dom_indexeddb_idbindex_h__ +#endif // mozilla_dom_idbindex_h__ diff --git a/dom/indexedDB/IDBKeyRange.cpp b/dom/indexedDB/IDBKeyRange.cpp index 1880f59123..46a04d3b9d 100644 --- a/dom/indexedDB/IDBKeyRange.cpp +++ b/dom/indexedDB/IDBKeyRange.cpp @@ -14,7 +14,8 @@ namespace mozilla { namespace dom { -namespace indexedDB { + +using namespace mozilla::dom::indexedDB; namespace { @@ -447,6 +448,5 @@ IDBLocaleAwareKeyRange::Bound(const GlobalObject& aGlobal, return keyRange.forget(); } -} // namespace indexedDB } // namespace dom } // namespace mozilla diff --git a/dom/indexedDB/IDBKeyRange.h b/dom/indexedDB/IDBKeyRange.h index 630d13a428..01bd2466a4 100644 --- a/dom/indexedDB/IDBKeyRange.h +++ b/dom/indexedDB/IDBKeyRange.h @@ -4,12 +4,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/. */ -#ifndef mozilla_dom_indexeddb_idbkeyrange_h__ -#define mozilla_dom_indexeddb_idbkeyrange_h__ +#ifndef mozilla_dom_idbkeyrange_h__ +#define mozilla_dom_idbkeyrange_h__ #include "js/RootingAPI.h" #include "js/Value.h" #include "mozilla/Attributes.h" +#include "mozilla/dom/IndexedDatabaseManager.h" #include "mozilla/dom/indexedDB/Key.h" #include "nsCOMPtr.h" #include "nsCycleCollectionParticipant.h" @@ -28,16 +29,16 @@ namespace dom { class GlobalObject; namespace indexedDB { - class SerializedKeyRange; +} // namespace indexedDB class IDBKeyRange : public nsISupports { protected: nsCOMPtr mGlobal; - Key mLower; - Key mUpper; + indexedDB::Key mLower; + indexedDB::Key mUpper; JS::Heap mCachedLowerVal; JS::Heap mCachedUpperVal; @@ -62,7 +63,7 @@ public: IDBKeyRange** aKeyRange); static already_AddRefed - FromSerialized(const SerializedKeyRange& aKeyRange); + FromSerialized(const indexedDB::SerializedKeyRange& aKeyRange); static already_AddRefed Only(const GlobalObject& aGlobal, @@ -98,27 +99,27 @@ public: #endif void - ToSerialized(SerializedKeyRange& aKeyRange) const; + ToSerialized(indexedDB::SerializedKeyRange& aKeyRange) const; - const Key& + const indexedDB::Key& Lower() const { return mLower; } - Key& + indexedDB::Key& Lower() { return mLower; } - const Key& + const indexedDB::Key& Upper() const { return mIsOnly ? mLower : mUpper; } - Key& + indexedDB::Key& Upper() { return mIsOnly ? mLower : mUpper; @@ -205,8 +206,7 @@ public: WrapObject(JSContext* aCx, JS::Handle aGivenProto, JS::MutableHandle aReflector); }; -} // namespace indexedDB } // namespace dom } // namespace mozilla -#endif // mozilla_dom_indexeddb_idbkeyrange_h__ +#endif // mozilla_dom_idbkeyrange_h__ diff --git a/dom/indexedDB/IDBMutableFile.cpp b/dom/indexedDB/IDBMutableFile.cpp index 665a307071..9e9bfc4ee8 100644 --- a/dom/indexedDB/IDBMutableFile.cpp +++ b/dom/indexedDB/IDBMutableFile.cpp @@ -34,8 +34,8 @@ namespace mozilla { namespace dom { -namespace indexedDB { +using namespace mozilla::dom::indexedDB; using namespace mozilla::dom::quota; IDBMutableFile::IDBMutableFile(IDBDatabase* aDatabase, @@ -279,6 +279,5 @@ IDBMutableFile::CreateFileFor(BlobImpl* aBlobImpl, return file.forget(); } -} // namespace indexedDB } // namespace dom } // namespace mozilla diff --git a/dom/indexedDB/IDBMutableFile.h b/dom/indexedDB/IDBMutableFile.h index 10ab2cd697..d57e14a5da 100644 --- a/dom/indexedDB/IDBMutableFile.h +++ b/dom/indexedDB/IDBMutableFile.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_dom_indexeddb_idbmutablefile_h__ -#define mozilla_dom_indexeddb_idbmutablefile_h__ +#ifndef mozilla_dom_idbmutablefile_h__ +#define mozilla_dom_idbmutablefile_h__ #include "js/TypeDecls.h" #include "mozilla/Atomics.h" @@ -29,13 +29,13 @@ namespace dom { class DOMRequest; class File; - -namespace indexedDB { - -class BackgroundMutableFileChild; class IDBDatabase; class IDBFileHandle; +namespace indexedDB { +class BackgroundMutableFileChild; +} + class IDBMutableFile final : public DOMEventTargetHelper , public MutableFileBase @@ -51,7 +51,7 @@ class IDBMutableFile final public: IDBMutableFile(IDBDatabase* aDatabase, - BackgroundMutableFileChild* aActor, + indexedDB::BackgroundMutableFileChild* aActor, const nsAString& aName, const nsAString& aType); @@ -134,8 +134,7 @@ private: ~IDBMutableFile(); }; -} // namespace indexedDB } // namespace dom } // namespace mozilla -#endif // mozilla_dom_indexeddb_idbmutablefile_h__ +#endif // mozilla_dom_idbmutablefile_h__ diff --git a/dom/indexedDB/IDBObjectStore.cpp b/dom/indexedDB/IDBObjectStore.cpp index 5f0e6b4596..299e236397 100644 --- a/dom/indexedDB/IDBObjectStore.cpp +++ b/dom/indexedDB/IDBObjectStore.cpp @@ -54,7 +54,6 @@ namespace mozilla { namespace dom { -namespace indexedDB { using namespace mozilla::dom::quota; using namespace mozilla::dom::workers; @@ -1865,16 +1864,18 @@ IDBObjectStore::CreateIndexInternal( { AssertIsOnOwningThread(); - IDBTransaction* transaction = IDBTransaction::GetCurrent(); - - if (!transaction || - transaction != mTransaction || - mTransaction->GetMode() != IDBTransaction::VERSION_CHANGE || + if (mTransaction->GetMode() != IDBTransaction::VERSION_CHANGE || mDeletedSpec) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); return nullptr; } + IDBTransaction* transaction = IDBTransaction::GetCurrent(); + if (!transaction || transaction != mTransaction) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return nullptr; + } + MOZ_ASSERT(transaction->IsOpen()); auto& indexes = const_cast&>(mSpec->indexes()); @@ -1960,16 +1961,18 @@ IDBObjectStore::DeleteIndex(const nsAString& aName, ErrorResult& aRv) { AssertIsOnOwningThread(); - IDBTransaction* transaction = IDBTransaction::GetCurrent(); - - if (!transaction || - transaction != mTransaction || - mTransaction->GetMode() != IDBTransaction::VERSION_CHANGE || + if (mTransaction->GetMode() != IDBTransaction::VERSION_CHANGE || mDeletedSpec) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); return; } + IDBTransaction* transaction = IDBTransaction::GetCurrent(); + if (!transaction || transaction != mTransaction) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return; + } + MOZ_ASSERT(transaction->IsOpen()); auto& metadataArray = const_cast&>(mSpec->indexes()); @@ -2275,7 +2278,7 @@ IDBObjectStore::AutoIncrement() const return mSpec->metadata().autoIncrement(); } -const KeyPath& +const indexedDB::KeyPath& IDBObjectStore::GetKeyPath() const { AssertIsOnOwningThread(); @@ -2293,6 +2296,5 @@ IDBObjectStore::HasValidKeyPath() const return GetKeyPath().IsValid(); } -} // namespace indexedDB } // namespace dom } // namespace mozilla diff --git a/dom/indexedDB/IDBObjectStore.h b/dom/indexedDB/IDBObjectStore.h index f693dd5a69..43e98eb013 100644 --- a/dom/indexedDB/IDBObjectStore.h +++ b/dom/indexedDB/IDBObjectStore.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_dom_indexeddb_idbobjectstore_h__ -#define mozilla_dom_indexeddb_idbobjectstore_h__ +#ifndef mozilla_dom_idbobjectstore_h__ +#define mozilla_dom_idbobjectstore_h__ #include "js/RootingAPI.h" #include "mozilla/dom/IDBCursorBinding.h" @@ -27,25 +27,31 @@ class ErrorResult; namespace dom { class DOMStringList; -template class Sequence; - -namespace indexedDB { - class IDBCursor; class IDBRequest; class IDBTransaction; -class IndexUpdateInfo; +template class Sequence; + +namespace indexedDB { class Key; class KeyPath; +class IndexUpdateInfo; class ObjectStoreSpec; struct StructuredCloneReadInfo; +} // namespace indexedDB class IDBObjectStore final : public nsISupports , public nsWrapperCache { + typedef indexedDB::IndexUpdateInfo IndexUpdateInfo; + typedef indexedDB::Key Key; + typedef indexedDB::KeyPath KeyPath; + typedef indexedDB::ObjectStoreSpec ObjectStoreSpec; + typedef indexedDB::StructuredCloneReadInfo StructuredCloneReadInfo; + // For AddOrPut() and DeleteInternal(). - friend class IDBCursor; + friend class IDBCursor; static const JSClass sDummyPropJSClass; @@ -339,8 +345,7 @@ private: ErrorResult& aRv); }; -} // namespace indexedDB } // namespace dom } // namespace mozilla -#endif // mozilla_dom_indexeddb_idbobjectstore_h__ +#endif // mozilla_dom_idbobjectstore_h__ diff --git a/dom/indexedDB/IDBRequest.cpp b/dom/indexedDB/IDBRequest.cpp index 2ec29fd42d..5c2b2dab06 100644 --- a/dom/indexedDB/IDBRequest.cpp +++ b/dom/indexedDB/IDBRequest.cpp @@ -38,8 +38,8 @@ namespace mozilla { namespace dom { -namespace indexedDB { +using namespace mozilla::dom::indexedDB; using namespace mozilla::dom::workers; using namespace mozilla::ipc; @@ -660,6 +660,5 @@ WorkerFeature::Notify(JSContext* aCx, Status aStatus) return true; } -} // namespace indexedDB } // namespace dom } // namespace mozilla diff --git a/dom/indexedDB/IDBRequest.h b/dom/indexedDB/IDBRequest.h index 93c8b23419..55e0d6cf68 100644 --- a/dom/indexedDB/IDBRequest.h +++ b/dom/indexedDB/IDBRequest.h @@ -4,14 +4,14 @@ * 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_indexeddb_idbrequest_h__ -#define mozilla_dom_indexeddb_idbrequest_h__ +#ifndef mozilla_dom_idbrequest_h__ +#define mozilla_dom_idbrequest_h__ #include "js/RootingAPI.h" #include "mozilla/Attributes.h" #include "mozilla/EventForwards.h" #include "mozilla/dom/IDBRequestBinding.h" -#include "mozilla/dom/indexedDB/IDBWrapperCache.h" +#include "mozilla/dom/IDBWrapperCache.h" #include "nsAutoPtr.h" #include "nsCycleCollectionParticipant.h" @@ -28,17 +28,14 @@ class ErrorResult; namespace dom { class DOMError; -template struct Nullable; -class OwningIDBObjectStoreOrIDBIndexOrIDBCursor; - -namespace indexedDB { - class IDBCursor; class IDBDatabase; class IDBFactory; class IDBIndex; class IDBObjectStore; class IDBTransaction; +template struct Nullable; +class OwningIDBObjectStoreOrIDBIndexOrIDBCursor; class IDBRequest : public IDBWrapperCache @@ -285,8 +282,7 @@ private: ~IDBOpenDBRequest(); }; -} // namespace indexedDB } // namespace dom } // namespace mozilla -#endif // mozilla_dom_indexeddb_idbrequest_h__ +#endif // mozilla_dom_idbrequest_h__ diff --git a/dom/indexedDB/IDBTransaction.cpp b/dom/indexedDB/IDBTransaction.cpp index db11846d58..1ba974e0a2 100644 --- a/dom/indexedDB/IDBTransaction.cpp +++ b/dom/indexedDB/IDBTransaction.cpp @@ -29,8 +29,8 @@ namespace mozilla { namespace dom { -namespace indexedDB { +using namespace mozilla::dom::indexedDB; using namespace mozilla::dom::workers; using namespace mozilla::ipc; @@ -279,7 +279,7 @@ IDBTransaction::AssertIsOnOwningThread() const #endif // DEBUG void -IDBTransaction::SetBackgroundActor(BackgroundTransactionChild* aBackgroundActor) +IDBTransaction::SetBackgroundActor(indexedDB::BackgroundTransactionChild* aBackgroundActor) { AssertIsOnOwningThread(); MOZ_ASSERT(aBackgroundActor); @@ -570,7 +570,7 @@ IDBTransaction::DeleteObjectStore(int64_t aObjectStoreId) void IDBTransaction::CreateIndex(IDBObjectStore* aObjectStore, - const IndexMetadata& aMetadata) + const indexedDB::IndexMetadata& aMetadata) { AssertIsOnOwningThread(); MOZ_ASSERT(aObjectStore); @@ -859,7 +859,7 @@ IDBTransaction::GetError() const } already_AddRefed -IDBTransaction::ObjectStoreNames() +IDBTransaction::ObjectStoreNames() const { AssertIsOnOwningThread(); @@ -1012,6 +1012,5 @@ WorkerFeature::Notify(JSContext* aCx, Status aStatus) return true; } -} // namespace indexedDB } // namespace dom } // namespace mozilla diff --git a/dom/indexedDB/IDBTransaction.h b/dom/indexedDB/IDBTransaction.h index fa67e088ff..1eacb72a83 100644 --- a/dom/indexedDB/IDBTransaction.h +++ b/dom/indexedDB/IDBTransaction.h @@ -4,12 +4,12 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef mozilla_dom_indexeddb_idbtransaction_h__ -#define mozilla_dom_indexeddb_idbtransaction_h__ +#ifndef mozilla_dom_idbtransaction_h__ +#define mozilla_dom_idbtransaction_h__ #include "mozilla/Attributes.h" #include "mozilla/dom/IDBTransactionBinding.h" -#include "mozilla/dom/indexedDB/IDBWrapperCache.h" +#include "mozilla/dom/IDBWrapperCache.h" #include "nsAutoPtr.h" #include "nsCycleCollectionParticipant.h" #include "nsIRunnable.h" @@ -27,28 +27,28 @@ namespace dom { class DOMError; class DOMStringList; - -namespace indexedDB { - -class BackgroundCursorChild; -class BackgroundRequestChild; -class BackgroundTransactionChild; -class BackgroundVersionChangeTransactionChild; class IDBDatabase; class IDBObjectStore; class IDBOpenDBRequest; class IDBRequest; + +namespace indexedDB { +class BackgroundCursorChild; +class BackgroundRequestChild; +class BackgroundTransactionChild; +class BackgroundVersionChangeTransactionChild; class IndexMetadata; class ObjectStoreSpec; class OpenCursorParams; class RequestParams; +} class IDBTransaction final : public IDBWrapperCache , public nsIRunnable { - friend class BackgroundCursorChild; - friend class BackgroundRequestChild; + friend class indexedDB::BackgroundCursorChild; + friend class indexedDB::BackgroundRequestChild; class WorkerFeature; friend class WorkerFeature; @@ -85,8 +85,8 @@ private: // a BackgroundVersionChangeTransactionChild. Otherwise it will be a // BackgroundTransactionChild. union { - BackgroundTransactionChild* mNormalBackgroundActor; - BackgroundVersionChangeTransactionChild* mVersionChangeBackgroundActor; + indexedDB::BackgroundTransactionChild* mNormalBackgroundActor; + indexedDB::BackgroundVersionChangeTransactionChild* mVersionChangeBackgroundActor; } mBackgroundActor; const int64_t mLoggingSerialNumber; @@ -117,7 +117,7 @@ private: public: static already_AddRefed CreateVersionChange(IDBDatabase* aDatabase, - BackgroundVersionChangeTransactionChild* aActor, + indexedDB::BackgroundVersionChangeTransactionChild* aActor, IDBOpenDBRequest* aOpenRequest, int64_t aNextObjectStoreId, int64_t aNextIndexId); @@ -139,7 +139,7 @@ public: #endif void - SetBackgroundActor(BackgroundTransactionChild* aBackgroundActor); + SetBackgroundActor(indexedDB::BackgroundTransactionChild* aBackgroundActor); void ClearBackgroundActor() @@ -153,12 +153,12 @@ public: } } - BackgroundRequestChild* - StartRequest(IDBRequest* aRequest, const RequestParams& aParams); + indexedDB::BackgroundRequestChild* + StartRequest(IDBRequest* aRequest, const indexedDB::RequestParams& aParams); void - OpenCursor(BackgroundCursorChild* aBackgroundActor, - const OpenCursorParams& aParams); + OpenCursor(indexedDB::BackgroundCursorChild* aBackgroundActor, + const indexedDB::OpenCursorParams& aParams); void RefreshSpec(bool aMayDelete); @@ -238,13 +238,13 @@ public: } already_AddRefed - CreateObjectStore(const ObjectStoreSpec& aSpec); + CreateObjectStore(const indexedDB::ObjectStoreSpec& aSpec); void DeleteObjectStore(int64_t aObjectStoreId); void - CreateIndex(IDBObjectStore* aObjectStore, const IndexMetadata& aMetadata); + CreateIndex(IDBObjectStore* aObjectStore, const indexedDB::IndexMetadata& aMetadata); void DeleteIndex(IDBObjectStore* aObjectStore, int64_t aIndexId); @@ -283,7 +283,7 @@ public: IMPL_EVENT_HANDLER(error) already_AddRefed - ObjectStoreNames(); + ObjectStoreNames() const; void FireCompleteOrAbortEvents(nsresult aResult); @@ -330,8 +330,7 @@ private: OnRequestFinished(bool aActorDestroyedNormally); }; -} // namespace indexedDB } // namespace dom } // namespace mozilla -#endif // mozilla_dom_indexeddb_idbtransaction_h__ +#endif // mozilla_dom_idbtransaction_h__ diff --git a/dom/indexedDB/IDBWrapperCache.cpp b/dom/indexedDB/IDBWrapperCache.cpp index 4770ebb26b..df8ea026b5 100644 --- a/dom/indexedDB/IDBWrapperCache.cpp +++ b/dom/indexedDB/IDBWrapperCache.cpp @@ -13,7 +13,6 @@ namespace mozilla { namespace dom { -namespace indexedDB { NS_IMPL_CYCLE_COLLECTION_CLASS(IDBWrapperCache) @@ -77,6 +76,5 @@ IDBWrapperCache::AssertIsRooted() const } #endif -} // namespace indexedDB } // namespace dom } // namespace mozilla diff --git a/dom/indexedDB/IDBWrapperCache.h b/dom/indexedDB/IDBWrapperCache.h index 9eb10d1ce8..34b0807760 100644 --- a/dom/indexedDB/IDBWrapperCache.h +++ b/dom/indexedDB/IDBWrapperCache.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_dom_indexeddb_idbwrappercache_h__ -#define mozilla_dom_indexeddb_idbwrappercache_h__ +#ifndef mozilla_dom_idbwrappercache_h__ +#define mozilla_dom_idbwrappercache_h__ #include "js/RootingAPI.h" #include "mozilla/DOMEventTargetHelper.h" @@ -16,7 +16,6 @@ class nsPIDOMWindow; namespace mozilla { namespace dom { -namespace indexedDB { class IDBWrapperCache : public DOMEventTargetHelper { @@ -50,8 +49,7 @@ protected: virtual ~IDBWrapperCache(); }; -} // namespace indexedDB } // namespace dom } // namespace mozilla -#endif // mozilla_dom_indexeddb_idbwrappercache_h__ +#endif // mozilla_dom_idbwrappercache_h__ diff --git a/dom/indexedDB/IndexedDatabase.h b/dom/indexedDB/IndexedDatabase.h index ca98cf3e36..ca2900573e 100644 --- a/dom/indexedDB/IndexedDatabase.h +++ b/dom/indexedDB/IndexedDatabase.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_dom_indexeddb_indexeddatabase_h__ -#define mozilla_dom_indexeddb_indexeddatabase_h__ +#ifndef mozilla_dom_indexeddatabase_h__ +#define mozilla_dom_indexeddatabase_h__ #include "js/StructuredClone.h" #include "nsAutoPtr.h" @@ -16,12 +16,12 @@ namespace mozilla { namespace dom { class Blob; +class IDBDatabase; +class IDBMutableFile; namespace indexedDB { class FileInfo; -class IDBDatabase; -class IDBMutableFile; class SerializedStructuredCloneReadInfo; struct StructuredCloneFile @@ -74,4 +74,4 @@ struct StructuredCloneReadInfo } // namespace dom } // namespace mozilla -#endif // mozilla_dom_indexeddb_indexeddatabase_h__ +#endif // mozilla_dom_indexeddatabase_h__ diff --git a/dom/indexedDB/IndexedDatabaseInlines.h b/dom/indexedDB/IndexedDatabaseInlines.h index 31c6101c91..a6d5fd2987 100644 --- a/dom/indexedDB/IndexedDatabaseInlines.h +++ b/dom/indexedDB/IndexedDatabaseInlines.h @@ -7,7 +7,7 @@ #ifndef IndexedDatabaseInlines_h #define IndexedDatabaseInlines_h -#ifndef mozilla_dom_indexeddb_indexeddatabase_h__ +#ifndef mozilla_dom_indexeddatabase_h__ #error Must include IndexedDatabase.h first #endif diff --git a/dom/indexedDB/IndexedDatabaseManager.cpp b/dom/indexedDB/IndexedDatabaseManager.cpp index 301cbcbbe1..12c2f73c65 100644 --- a/dom/indexedDB/IndexedDatabaseManager.cpp +++ b/dom/indexedDB/IndexedDatabaseManager.cpp @@ -118,6 +118,10 @@ private: nsTArray > mDefaultStorageFileManagers; }; +} // namespace indexedDB + +using namespace mozilla::dom::indexedDB; + namespace { NS_DEFINE_IID(kIDBRequestIID, PRIVATE_IDBREQUEST_IID); @@ -1422,6 +1426,5 @@ GetFileReferencesHelper::Run() return NS_OK; } -} // namespace indexedDB } // namespace dom } // namespace mozilla diff --git a/dom/indexedDB/IndexedDatabaseManager.h b/dom/indexedDB/IndexedDatabaseManager.h index 7c6c52fc5e..8566c2faa5 100644 --- a/dom/indexedDB/IndexedDatabaseManager.h +++ b/dom/indexedDB/IndexedDatabaseManager.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_dom_indexeddb_indexeddatabasemanager_h__ -#define mozilla_dom_indexeddb_indexeddatabasemanager_h__ +#ifndef mozilla_dom_indexeddatabasemanager_h__ +#define mozilla_dom_indexeddatabasemanager_h__ #include "nsIObserver.h" @@ -24,17 +24,22 @@ class EventChainPostVisitor; namespace dom { +class IDBFactory; + namespace indexedDB { class FileManager; class FileManagerInfo; -class IDBFactory; + +} // namespace indexedDB class IndexedDatabaseManager final : public nsIObserver , public nsITimerCallback { typedef mozilla::dom::quota::PersistenceType PersistenceType; + typedef mozilla::dom::indexedDB::FileManager FileManager; + typedef mozilla::dom::indexedDB::FileManagerInfo FileManagerInfo; public: enum LoggingMode @@ -217,8 +222,7 @@ private: static mozilla::Atomic sLowDiskSpaceMode; }; -} // namespace indexedDB } // namespace dom } // namespace mozilla -#endif // mozilla_dom_indexeddb_indexeddatabasemanager_h__ +#endif // mozilla_dom_indexeddatabasemanager_h__ diff --git a/dom/indexedDB/PBackgroundIDBDatabase.ipdl b/dom/indexedDB/PBackgroundIDBDatabase.ipdl index dc7d53a83d..a07cd09a45 100644 --- a/dom/indexedDB/PBackgroundIDBDatabase.ipdl +++ b/dom/indexedDB/PBackgroundIDBDatabase.ipdl @@ -18,8 +18,8 @@ include "mozilla/dom/indexedDB/SerializationHelpers.h"; using struct mozilla::null_t from "ipc/IPCMessageUtils.h"; -using mozilla::dom::indexedDB::IDBTransaction::Mode - from "mozilla/dom/indexedDB/IDBTransaction.h"; +using mozilla::dom::IDBTransaction::Mode + from "mozilla/dom/IDBTransaction.h"; namespace mozilla { namespace dom { diff --git a/dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh b/dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh index 4f1cc26304..e9eaff0384 100644 --- a/dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh +++ b/dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh @@ -17,8 +17,8 @@ using struct mozilla::null_t using struct mozilla::void_t from "ipc/IPCMessageUtils.h"; -using mozilla::dom::indexedDB::IDBCursor::Direction - from "mozilla/dom/indexedDB/IDBCursor.h"; +using mozilla::dom::IDBCursor::Direction + from "mozilla/dom/IDBCursor.h"; using class mozilla::dom::indexedDB::Key from "mozilla/dom/indexedDB/Key.h"; diff --git a/dom/indexedDB/SerializationHelpers.h b/dom/indexedDB/SerializationHelpers.h index 8867a7aaab..29e2d84480 100644 --- a/dom/indexedDB/SerializationHelpers.h +++ b/dom/indexedDB/SerializationHelpers.h @@ -11,8 +11,8 @@ #include "mozilla/dom/indexedDB/Key.h" #include "mozilla/dom/indexedDB/KeyPath.h" -#include "mozilla/dom/indexedDB/IDBCursor.h" -#include "mozilla/dom/indexedDB/IDBTransaction.h" +#include "mozilla/dom/IDBCursor.h" +#include "mozilla/dom/IDBTransaction.h" namespace IPC { @@ -68,19 +68,19 @@ struct ParamTraits }; template <> -struct ParamTraits : +struct ParamTraits : public ContiguousEnumSerializer< - mozilla::dom::indexedDB::IDBCursor::Direction, - mozilla::dom::indexedDB::IDBCursor::NEXT, - mozilla::dom::indexedDB::IDBCursor::DIRECTION_INVALID> + mozilla::dom::IDBCursor::Direction, + mozilla::dom::IDBCursor::NEXT, + mozilla::dom::IDBCursor::DIRECTION_INVALID> { }; template <> -struct ParamTraits : +struct ParamTraits : public ContiguousEnumSerializer< - mozilla::dom::indexedDB::IDBTransaction::Mode, - mozilla::dom::indexedDB::IDBTransaction::READ_ONLY, - mozilla::dom::indexedDB::IDBTransaction::MODE_INVALID> + mozilla::dom::IDBTransaction::Mode, + mozilla::dom::IDBTransaction::READ_ONLY, + mozilla::dom::IDBTransaction::MODE_INVALID> { }; } // namespace IPC diff --git a/dom/indexedDB/moz.build b/dom/indexedDB/moz.build index 6bb6518f1b..6ab2321535 100644 --- a/dom/indexedDB/moz.build +++ b/dom/indexedDB/moz.build @@ -20,9 +20,7 @@ XPCSHELL_TESTS_MANIFESTS += [ if CONFIG['ENABLE_INTL_API']: MOCHITEST_MANIFESTS += ['test/mochitest-intl-api.ini'] -EXPORTS.mozilla.dom.indexedDB += [ - 'ActorsParent.h', - 'FileSnapshot.h', +EXPORTS.mozilla.dom += [ 'IDBCursor.h', 'IDBDatabase.h', 'IDBEvents.h', @@ -38,6 +36,11 @@ EXPORTS.mozilla.dom.indexedDB += [ 'IDBWrapperCache.h', 'IndexedDatabase.h', 'IndexedDatabaseManager.h', +] + +EXPORTS.mozilla.dom.indexedDB += [ + 'ActorsParent.h', + 'FileSnapshot.h', 'Key.h', 'KeyPath.h', 'SerializationHelpers.h', diff --git a/dom/ipc/Blob.cpp b/dom/ipc/Blob.cpp index 5e3d4ebb95..1abeaf8c8b 100644 --- a/dom/ipc/Blob.cpp +++ b/dom/ipc/Blob.cpp @@ -24,7 +24,7 @@ #include "mozilla/dom/PBlobStreamChild.h" #include "mozilla/dom/PBlobStreamParent.h" #include "mozilla/dom/indexedDB/FileSnapshot.h" -#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h" +#include "mozilla/dom/IndexedDatabaseManager.h" #include "mozilla/ipc/InputStreamUtils.h" #include "mozilla/ipc/PBackgroundChild.h" #include "mozilla/ipc/PBackgroundParent.h" diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 9eff312e43..184d26d429 100755 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -60,7 +60,7 @@ #include "mozilla/dom/cellbroadcast/CellBroadcastParent.h" #include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h" #include "mozilla/dom/icc/IccParent.h" -#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h" +#include "mozilla/dom/IndexedDatabaseManager.h" #include "mozilla/dom/mobileconnection/MobileConnectionParent.h" #include "mozilla/dom/mobilemessage/SmsParent.h" #include "mozilla/dom/power/PowerManagerService.h" diff --git a/dom/media/tests/mochitest/mochitest.ini b/dom/media/tests/mochitest/mochitest.ini index 2b88706e6c..d24e8dcffb 100644 --- a/dom/media/tests/mochitest/mochitest.ini +++ b/dom/media/tests/mochitest/mochitest.ini @@ -13,6 +13,7 @@ support-files = NetworkPreparationChromeScript.js blacksilence.js turnConfig.js + sdpUtils.js [test_dataChannel_basicAudio.html] skip-if = toolkit == 'gonk' || buildapp == 'mulet' # Bug 962984 for debug, bug 963244 for opt @@ -61,12 +62,18 @@ skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 1021776, too --ing [test_peerConnection_addIceCandidate.html] [test_peerConnection_basicAudio.html] skip-if = toolkit == 'gonk' # B2G emulator is too slow to handle a two-way audio call reliably +[test_peerConnection_basicAudioRequireEOC.html] +skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867) [test_peerConnection_basicAudioVideo.html] -skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) +skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18' && debug) # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator) [test_peerConnection_basicAudioVideoCombined.html] -skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) +skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18' && debug) # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator) [test_peerConnection_basicAudioVideoNoBundle.html] -skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) +skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18' && debug) # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator) +[test_peerConnection_basicAudioVideoNoBundleNoRtcpMux.html] +skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version == '18' # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator) +[test_peerConnection_basicAudioVideoNoRtcpMux.html] +skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version == '18' # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator) [test_peerConnection_basicVideo.html] skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) [test_peerConnection_basicScreenshare.html] diff --git a/dom/media/tests/mochitest/pc.js b/dom/media/tests/mochitest/pc.js index 9fc28d4982..99a337f37a 100644 --- a/dom/media/tests/mochitest/pc.js +++ b/dom/media/tests/mochitest/pc.js @@ -99,19 +99,6 @@ MediaElementChecker.prototype = { } }; -// Also remove mode 0 if it's offered -// Note, we don't bother removing the fmtp lines, which makes a good test -// for some SDP parsing issues. -function removeVP8(sdp) { - var updated_sdp = sdp.replace("a=rtpmap:120 VP8/90000\r\n",""); - updated_sdp = updated_sdp.replace("RTP/SAVPF 120 126 97\r\n","RTP/SAVPF 126 97\r\n"); - updated_sdp = updated_sdp.replace("RTP/SAVPF 120 126\r\n","RTP/SAVPF 126\r\n"); - updated_sdp = updated_sdp.replace("a=rtcp-fb:120 nack\r\n",""); - updated_sdp = updated_sdp.replace("a=rtcp-fb:120 nack pli\r\n",""); - updated_sdp = updated_sdp.replace("a=rtcp-fb:120 ccm fir\r\n",""); - return updated_sdp; -} - var makeDefaultCommands = () => { return [].concat(commandsPeerConnectionInitial, commandsGetUserMedia, @@ -143,6 +130,10 @@ function PeerConnectionTest(options) { options.is_local = "is_local" in options ? options.is_local : true; options.is_remote = "is_remote" in options ? options.is_remote : true; + options.h264 = "h264" in options ? options.h264 : false; + options.bundle = "bundle" in options ? options.bundle : true; + options.rtcpmux = "rtcpmux" in options ? options.rtcpmux : true; + if (typeof turnServers !== "undefined") { if ((!options.turn_disabled_local) && (turnServers.local)) { if (!options.hasOwnProperty("config_local")) { @@ -162,26 +153,24 @@ function PeerConnectionTest(options) { } } - if (options.is_local) - this.pcLocal = new PeerConnectionWrapper('pcLocal', options.config_local, options.h264); - else + if (options.is_local) { + this.pcLocal = new PeerConnectionWrapper('pcLocal', options.config_local); + } else { this.pcLocal = null; + } - if (options.is_remote) - this.pcRemote = new PeerConnectionWrapper('pcRemote', options.config_remote || options.config_local, options.h264); - else + if (options.is_remote) { + this.pcRemote = new PeerConnectionWrapper('pcRemote', options.config_remote || options.config_local); + } else { this.pcRemote = null; + } - this.steeplechase = this.pcLocal === null || this.pcRemote === null; + options.steeplechase = !options.is_local || !options.is_remote; // Create command chain instance and assign default commands this.chain = new CommandChain(this, options.commands); - if (!options.is_local) { - this.chain.filterOut(/^PC_LOCAL/); - } - if (!options.is_remote) { - this.chain.filterOut(/^PC_REMOTE/); - } + + this.testOptions = options; } /** TODO: consider removing this dependency on timeouts */ @@ -406,6 +395,14 @@ function(peer, desc, stateExpected) { peer.setLocalDescDate = new Date(); }); + peer.endOfTrickleSdp = peer.endOfTrickleIce.then(() => { + if (this.testOptions.steeplechase) { + send_message({"type": "end_of_trickle_ice"}); + } + return peer._pc.localDescription; + }) + .catch(e => ok(false, "Sending EOC message failed: " + e)); + return Promise.all([eventFired, stateChanged]); }; @@ -470,10 +467,41 @@ function(peer, desc, stateExpected) { return Promise.all([eventFired, stateChanged]); }; +/** + * Adds and removes steps to/from the execution chain based on the configured + * testOptions. + */ +PeerConnectionTest.prototype.updateChainSteps = function() { + if (this.testOptions.h264) { + this.chain.insertAfterEach( + 'PC_LOCAL_CREATE_OFFER', + [PC_LOCAL_REMOVE_VP8_FROM_OFFER]); + } + if (!this.testOptions.bundle) { + this.chain.insertAfterEach( + 'PC_LOCAL_CREATE_OFFER', + [PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER]); + } + if (!this.testOptions.rtcpmux) { + this.chain.insertAfterEach( + 'PC_LOCAL_CREATE_OFFER', + [PC_LOCAL_REMOVE_RTCPMUX_FROM_OFFER]); + } + if (!this.testOptions.is_local) { + this.chain.filterOut(/^PC_LOCAL/); + } + if (!this.testOptions.is_remote) { + this.chain.filterOut(/^PC_REMOTE/); + } +}; + /** * Start running the tests as assigned to the command chain. */ PeerConnectionTest.prototype.run = function() { + /* We have to modify the chain here to allow tests which modify the default + * test chain instantiating a PeerConnectionTest() */ + this.updateChainSteps(); return this.chain.execute() .then(() => this.close()) .then(() => { @@ -725,7 +753,7 @@ DataChannelWrapper.prototype = { * @param {object} configuration * Configuration for the peer connection instance */ -function PeerConnectionWrapper(label, configuration, h264) { +function PeerConnectionWrapper(label, configuration) { this.configuration = configuration; if (configuration && configuration.label_suffix) { label = label + "_" + configuration.label_suffix; @@ -754,8 +782,6 @@ function PeerConnectionWrapper(label, configuration, h264) { this.iceCheckingRestartExpected = false; - this.h264 = typeof h264 !== "undefined" ? true : false; - info("Creating " + this); this._pc = new RTCPeerConnection(this.configuration); @@ -995,10 +1021,6 @@ PeerConnectionWrapper.prototype = { info("Got offer: " + JSON.stringify(offer)); // note: this might get updated through ICE gathering this._latest_offer = offer; - if (this.h264) { - isnot(offer.sdp.search("H264/90000"), -1, "H.264 should be present in the SDP offer"); - offer.sdp = removeVP8(offer.sdp); - } return offer; }); }, @@ -1292,18 +1314,16 @@ PeerConnectionWrapper.prototype = { this.endOfTrickleIce = new Promise(r => resolveEndOfTrickle = r); this.holdIceCandidates = new Promise(r => this.releaseIceCandidates = r); - this.endOfTrickleIce.then(() => { - this._pc.onicecandidate = () => - ok(false, this.label + " received ICE candidate after end of trickle"); - var localSdp = this._pc.getLocalDescription(); - ok(localSdp.includes("a=end-of-candidates")); - ok(localSdp.includes("a=rtcp:")); - ok(!localSdp.includes("c=IN IP4 0.0.0.0")); - }); - this._pc.onicecandidate = anEvent => { if (!anEvent.candidate) { + this._pc.onicecandidate = () => + ok(false, this.label + " received ICE candidate after end of trickle"); info(this.label + ": received end of trickle ICE event"); + /* Bug 1193731. Accroding to WebRTC spec 4.3.1 the ICE Agent first sets + * the gathering state to completed (step 3.) before sending out the + * null newCandidate in step 4. */ + todo(this._pc.iceGatheringState === 'completed', + "ICE gathering state has reached completed"); resolveEndOfTrickle(this.label); return; } @@ -1312,25 +1332,19 @@ PeerConnectionWrapper.prototype = { ok(anEvent.candidate.candidate.length > 0, "ICE candidate contains candidate"); ok(anEvent.candidate.sdpMid.length > 0, "SDP mid not empty"); + // only check the m-section for the updated default addr that corresponds + // with this candidate. + var mSections = this.localDescription.sdp.split("\r\nm="); + sdputils.checkSdpCLineNotDefault( + mSections[anEvent.candidate.sdpMLineIndex+1], this.label + ); + ok(typeof anEvent.candidate.sdpMLineIndex === 'number', "SDP MLine Index needs to exist"); this._local_ice_candidates.push(anEvent.candidate); candidateHandler(this.label, anEvent.candidate); }; }, - /** - * Counts the amount of audio tracks in a given media constraint. - * - * @param constraints - * The contraint to be examined. - */ - countTracksInConstraint : function(type, constraints) { - if (!Array.isArray(constraints)) { - return 0; - } - return constraints.reduce((sum, c) => sum + (c[type] ? 1 : 0), 0); - }, - checkLocalMediaTracks : function() { var observed = {}; info(this + " Checking local tracks " + JSON.stringify(this.expectedLocalTrackInfoById)); @@ -1379,66 +1393,6 @@ PeerConnectionWrapper.prototype = { "remote"); }, - verifySdp: function(desc, expectedType, offerConstraintsList, offerOptions, isLocal) { - info("Examining this SessionDescription: " + JSON.stringify(desc)); - info("offerConstraintsList: " + JSON.stringify(offerConstraintsList)); - info("offerOptions: " + JSON.stringify(offerOptions)); - ok(desc, "SessionDescription is not null"); - is(desc.type, expectedType, "SessionDescription type is " + expectedType); - ok(desc.sdp.length > 10, "SessionDescription body length is plausible"); - ok(desc.sdp.includes("a=ice-ufrag"), "ICE username is present in SDP"); - ok(desc.sdp.includes("a=ice-pwd"), "ICE password is present in SDP"); - ok(desc.sdp.includes("a=fingerprint"), "ICE fingerprint is present in SDP"); - //TODO: update this for loopback support bug 1027350 - ok(!desc.sdp.includes(LOOPBACK_ADDR), "loopback interface is absent from SDP"); - var requiresTrickleIce = !desc.sdp.includes("a=candidate"); - if (requiresTrickleIce) { - info("at least one ICE candidate is present in SDP"); - } else { - info("No ICE candidate in SDP -> requiring trickle ICE"); - } - if (isLocal) { - this.localRequiresTrickleIce = requiresTrickleIce; - } else { - this.remoteRequiresTrickleIce = requiresTrickleIce; - } - - //TODO: how can we check for absence/presence of m=application? - - var audioTracks = - this.countTracksInConstraint('audio', offerConstraintsList) || - ((offerOptions && offerOptions.offerToReceiveAudio) ? 1 : 0); - - info("expected audio tracks: " + audioTracks); - if (audioTracks == 0) { - ok(!desc.sdp.includes("m=audio"), "audio m-line is absent from SDP"); - } else { - ok(desc.sdp.includes("m=audio"), "audio m-line is present in SDP"); - ok(desc.sdp.includes("a=rtpmap:109 opus/48000/2"), "OPUS codec is present in SDP"); - //TODO: ideally the rtcp-mux should be for the m=audio, and not just - // anywhere in the SDP (JS SDP parser bug 1045429) - ok(desc.sdp.includes("a=rtcp-mux"), "RTCP Mux is offered in SDP"); - } - - var videoTracks = - this.countTracksInConstraint('video', offerConstraintsList) || - ((offerOptions && offerOptions.offerToReceiveVideo) ? 1 : 0); - - info("expected video tracks: " + videoTracks); - if (videoTracks == 0) { - ok(!desc.sdp.includes("m=video"), "video m-line is absent from SDP"); - } else { - ok(desc.sdp.includes("m=video"), "video m-line is present in SDP"); - if (this.h264) { - ok(desc.sdp.includes("a=rtpmap:126 H264/90000"), "H.264 codec is present in SDP"); - } else { - ok(desc.sdp.includes("a=rtpmap:120 VP8/90000"), "VP8 codec is present in SDP"); - } - ok(desc.sdp.includes("a=rtcp-mux"), "RTCP Mux is offered in SDP"); - } - - }, - /** * Check that media flow is present on the given media element by waiting for * it to reach ready state HAVE_ENOUGH_DATA and progress time further than @@ -1763,11 +1717,11 @@ PeerConnectionWrapper.prototype = { * The stats to check for ICE candidate pairs * @param {object} counters * The counters for media and data tracks based on constraints - * @param {object} answer - * The SDP answer to check for SDP bundle support + * @param {object} testOptions + * The test options object from the PeerConnectionTest */ checkStatsIceConnections : function(stats, - offerConstraintsList, offerOptions, answer) { + offerConstraintsList, offerOptions, testOptions) { var numIceConnections = 0; Object.keys(stats).forEach(key => { if ((stats[key].type === "candidatepair") && stats[key].selected) { @@ -1775,24 +1729,35 @@ PeerConnectionWrapper.prototype = { } }); info("ICE connections according to stats: " + numIceConnections); - if (answer.sdp.includes('a=group:BUNDLE')) { - is(numIceConnections, 1, "stats reports exactly 1 ICE connection"); + isnot(numIceConnections, 0, "Number of ICE connections according to stats is not zero"); + if (testOptions.bundle) { + if (testOptions.rtcpmux) { + is(numIceConnections, 1, "stats reports exactly 1 ICE connection"); + } else { + is(numIceConnections, 2, "stats report exactly 2 ICE connections for media and RTCP"); + } } else { // This code assumes that no media sections have been rejected due to // codec mismatch or other unrecoverable negotiation failures. var numAudioTracks = - this.countTracksInConstraint('audio', offerConstraintsList) || + sdputils.countTracksInConstraint('audio', offerConstraintsList) || ((offerOptions && offerOptions.offerToReceiveAudio) ? 1 : 0); var numVideoTracks = - this.countTracksInConstraint('video', offerConstraintsList) || + sdputils.countTracksInConstraint('video', offerConstraintsList) || ((offerOptions && offerOptions.offerToReceiveVideo) ? 1 : 0); - var numDataTracks = this.dataChannels.length; + var numExpectedTransports = numAudioTracks + numVideoTracks; + if (!testOptions.rtcpmux) { + numExpectedTransports *= 2; + } - var numAudioVideoDataTracks = numAudioTracks + numVideoTracks + numDataTracks; - info("expected audio + video + data tracks: " + numAudioVideoDataTracks); - is(numAudioVideoDataTracks, numIceConnections, "stats ICE connections matches expected A/V tracks"); + if (this.dataChannels.length) { + ++numExpectedTransports; + } + + info("expected audio + video + data transports: " + numExpectedTransports); + is(numIceConnections, numExpectedTransports, "stats ICE connections matches expected A/V transports"); } }, @@ -1860,7 +1825,8 @@ var scriptsReady = Promise.all([ "templates.js", "turnConfig.js", "dataChannel.js", - "network.js" + "network.js", + "sdpUtils.js" ].map(script => { var el = document.createElement("script"); if (typeof scriptRelativePath === 'string' && script.charAt(0) !== '/') { diff --git a/dom/media/tests/mochitest/sdpUtils.js b/dom/media/tests/mochitest/sdpUtils.js new file mode 100644 index 0000000000..bf334d6222 --- /dev/null +++ b/dom/media/tests/mochitest/sdpUtils.js @@ -0,0 +1,118 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var sdputils = { + +checkSdpAfterEndOfTrickle: function(sdp, testOptions, label) { + info("EOC-SDP: " + JSON.stringify(sdp)); + + ok(sdp.sdp.includes("a=end-of-candidates"), label + ": SDP contains end-of-candidates"); + sdputils.checkSdpCLineNotDefault(sdp.sdp, label); + + if (testOptions.rtcpmux) { + ok(sdp.sdp.includes("a=rtcp-mux"), label + ": SDP contains rtcp-mux"); + } else { + ok(sdp.sdp.includes("a=rtcp:"), label + ": SDP contains rtcp port"); + } +}, + +// takes sdp in string form (or possibly a fragment, say an m-section), and +// verifies that the default 0.0.0.0 addr is not present. +checkSdpCLineNotDefault: function(sdpStr, label) { + info("CLINE-NO-DEFAULT-ADDR-SDP: " + JSON.stringify(sdpStr)); + ok(!sdpStr.includes("c=IN IP4 0.0.0.0"), label + ": SDP contains non-zero IP c line"); +}, + +// Also remove mode 0 if it's offered +// Note, we don't bother removing the fmtp lines, which makes a good test +// for some SDP parsing issues. +removeVP8: function(sdp) { + var updated_sdp = sdp.replace("a=rtpmap:120 VP8/90000\r\n",""); + updated_sdp = updated_sdp.replace("RTP/SAVPF 120 126 97\r\n","RTP/SAVPF 126 97\r\n"); + updated_sdp = updated_sdp.replace("RTP/SAVPF 120 126\r\n","RTP/SAVPF 126\r\n"); + updated_sdp = updated_sdp.replace("a=rtcp-fb:120 nack\r\n",""); + updated_sdp = updated_sdp.replace("a=rtcp-fb:120 nack pli\r\n",""); + updated_sdp = updated_sdp.replace("a=rtcp-fb:120 ccm fir\r\n",""); + return updated_sdp; +}, + +removeRtcpMux: function(sdp) { + return sdp.replace(/a=rtcp-mux\r\n/g,""); +}, + +removeBundle: function(sdp) { + return sdp.replace(/a=group:BUNDLE .*\r\n/g, ""); +}, + +verifySdp: function(desc, expectedType, offerConstraintsList, offerOptions, + testOptions) { + info("Examining this SessionDescription: " + JSON.stringify(desc)); + info("offerConstraintsList: " + JSON.stringify(offerConstraintsList)); + info("offerOptions: " + JSON.stringify(offerOptions)); + ok(desc, "SessionDescription is not null"); + is(desc.type, expectedType, "SessionDescription type is " + expectedType); + ok(desc.sdp.length > 10, "SessionDescription body length is plausible"); + ok(desc.sdp.includes("a=ice-ufrag"), "ICE username is present in SDP"); + ok(desc.sdp.includes("a=ice-pwd"), "ICE password is present in SDP"); + ok(desc.sdp.includes("a=fingerprint"), "ICE fingerprint is present in SDP"); + //TODO: update this for loopback support bug 1027350 + ok(!desc.sdp.includes(LOOPBACK_ADDR), "loopback interface is absent from SDP"); + var requiresTrickleIce = !desc.sdp.includes("a=candidate"); + if (requiresTrickleIce) { + info("at least one ICE candidate is present in SDP"); + } else { + info("No ICE candidate in SDP -> requiring trickle ICE"); + } + + //TODO: how can we check for absence/presence of m=application? + + var audioTracks = + sdputils.countTracksInConstraint('audio', offerConstraintsList) || + ((offerOptions && offerOptions.offerToReceiveAudio) ? 1 : 0); + + info("expected audio tracks: " + audioTracks); + if (audioTracks == 0) { + ok(!desc.sdp.includes("m=audio"), "audio m-line is absent from SDP"); + } else { + ok(desc.sdp.includes("m=audio"), "audio m-line is present in SDP"); + ok(desc.sdp.includes("a=rtpmap:109 opus/48000/2"), "OPUS codec is present in SDP"); + //TODO: ideally the rtcp-mux should be for the m=audio, and not just + // anywhere in the SDP (JS SDP parser bug 1045429) + is(testOptions.rtcpmux, desc.sdp.includes("a=rtcp-mux"), "RTCP Mux is offered in SDP"); + } + + var videoTracks = + sdputils.countTracksInConstraint('video', offerConstraintsList) || + ((offerOptions && offerOptions.offerToReceiveVideo) ? 1 : 0); + + info("expected video tracks: " + videoTracks); + if (videoTracks == 0) { + ok(!desc.sdp.includes("m=video"), "video m-line is absent from SDP"); + } else { + ok(desc.sdp.includes("m=video"), "video m-line is present in SDP"); + if (testOptions.h264) { + ok(desc.sdp.includes("a=rtpmap:126 H264/90000"), "H.264 codec is present in SDP"); + } else { + ok(desc.sdp.includes("a=rtpmap:120 VP8/90000"), "VP8 codec is present in SDP"); + } + is(testOptions.rtcpmux, desc.sdp.includes("a=rtcp-mux"), "RTCP Mux is offered in SDP"); + } + + return requiresTrickleIce; +}, + +/** + * Counts the amount of audio tracks in a given media constraint. + * + * @param constraints + * The contraint to be examined. + */ +countTracksInConstraint: function(type, constraints) { + if (!Array.isArray(constraints)) { + return 0; + } + return constraints.reduce((sum, c) => sum + (c[type] ? 1 : 0), 0); +}, + +}; diff --git a/dom/media/tests/mochitest/templates.js b/dom/media/tests/mochitest/templates.js index 4e506424b1..abc621b1d8 100644 --- a/dom/media/tests/mochitest/templates.js +++ b/dom/media/tests/mochitest/templates.js @@ -208,20 +208,10 @@ var commandsGetUserMedia = [ var commandsPeerConnectionOfferAnswer = [ function PC_LOCAL_SETUP_ICE_HANDLER(test) { test.pcLocal.setupIceCandidateHandler(test); - if (test.steeplechase) { - test.pcLocal.endOfTrickleIce.then(() => { - send_message({"type": "end_of_trickle_ice"}); - }); - } }, function PC_REMOTE_SETUP_ICE_HANDLER(test) { test.pcRemote.setupIceCandidateHandler(test); - if (test.steeplechase) { - test.pcRemote.endOfTrickleIce.then(() => { - send_message({"type": "end_of_trickle_ice"}); - }); - } }, function PC_LOCAL_STEEPLECHASE_SIGNAL_EXPECTED_LOCAL_TRACKS(test) { @@ -316,15 +306,17 @@ var commandsPeerConnectionOfferAnswer = [ }, function PC_LOCAL_SANE_LOCAL_SDP(test) { - test.pcLocal.verifySdp(test._local_offer, "offer", - test._offer_constraints, test._offer_options, - true); + test.pcLocal.localRequiresTrickleIce = + sdputils.verifySdp(test._local_offer, "offer", + test._offer_constraints, test._offer_options, + test.testOptions); }, function PC_REMOTE_SANE_REMOTE_SDP(test) { - test.pcRemote.verifySdp(test._local_offer, "offer", - test._offer_constraints, test._offer_options, - false); + test.pcRemote.remoteRequiresTrickleIce = + sdputils.verifySdp(test._local_offer, "offer", + test._offer_constraints, test._offer_options, + test.testOptions); }, function PC_REMOTE_CREATE_ANSWER(test) { @@ -342,46 +334,6 @@ var commandsPeerConnectionOfferAnswer = [ }); }, - function PC_REMOTE_CHECK_FOR_DUPLICATED_PORTS_IN_SDP(test) { - var re = /a=candidate.* (UDP|TCP) [\d]+ ([\d\.]+) ([\d]+) typ host/g; - - var _sdpCandidatesIntoArray = sdp => { - var regexArray = []; - var resultArray = []; - while ((regexArray = re.exec(sdp)) !== null) { - info("regexArray: " + regexArray); - if ((regexArray[1] === "TCP") && (regexArray[3] === "9")) { - // As both sides can advertise TCP active connection on port 9 lets - // ignore them all together - info("Ignoring TCP candidate on port 9"); - continue; - } - var triple = regexArray[1] + ":" + regexArray[2] + ":" + regexArray[3]; - info("triple: " + triple); - if (resultArray.indexOf(triple) !== -1) { - dump("SDP: " + sdp.replace(/[\r]/g, '') + "\n"); - ok(false, "This Transport:IP:Port " + triple + " appears twice in the SDP above!"); - } - resultArray.push(triple); - } - return resultArray; - }; - - var offerTriples = _sdpCandidatesIntoArray(test._local_offer.sdp); - info("Offer ICE host candidates: " + JSON.stringify(offerTriples)); - - var answerTriples = _sdpCandidatesIntoArray(test.originalAnswer.sdp); - info("Answer ICE host candidates: " + JSON.stringify(answerTriples)); - - offerTriples.forEach(o => { - if (answerTriples.indexOf(o) !== -1) { - dump("SDP offer: " + test._local_offer.sdp.replace(/[\r]/g, '') + "\n"); - dump("SDP answer: " + test.originalAnswer.sdp.replace(/[\r]/g, '') + "\n"); - ok(false, "This IP:Port " + o + " appears in SDP offer and answer!"); - } - }); - }, - function PC_REMOTE_SET_LOCAL_DESCRIPTION(test) { return test.setLocalDescription(test.pcRemote, test.originalAnswer, STABLE) .then(() => { @@ -412,14 +364,16 @@ var commandsPeerConnectionOfferAnswer = [ }); }, function PC_REMOTE_SANE_LOCAL_SDP(test) { - test.pcRemote.verifySdp(test._remote_answer, "answer", - test._offer_constraints, test._offer_options, - true); + test.pcRemote.localRequiresTrickleIce = + sdputils.verifySdp(test._remote_answer, "answer", + test._offer_constraints, test._offer_options, + test.testOptions); }, function PC_LOCAL_SANE_REMOTE_SDP(test) { - test.pcLocal.verifySdp(test._remote_answer, "answer", - test._offer_constraints, test._offer_options, - false); + test.pcLocal.remoteRequiresTrickleIce = + sdputils.verifySdp(test._remote_answer, "answer", + test._offer_constraints, test._offer_options, + test.testOptions); }, function PC_LOCAL_WAIT_FOR_ICE_CONNECTED(test) { @@ -483,7 +437,7 @@ var commandsPeerConnectionOfferAnswer = [ test.pcLocal.checkStatsIceConnections(stats, test._offer_constraints, test._offer_options, - test._remote_answer); + test.testOptions); }); }, @@ -492,7 +446,7 @@ var commandsPeerConnectionOfferAnswer = [ test.pcRemote.checkStatsIceConnections(stats, test._offer_constraints, test._offer_options, - test.originalAnswer); + test.testOptions); }); }, @@ -509,22 +463,46 @@ var commandsPeerConnectionOfferAnswer = [ function PC_REMOTE_CHECK_STATS(test) { return checkAllTrackStats(test.pcRemote); }, - function PC_LOCAL_WAIT_FOR_END_OF_TRICKLE(test) { - return test.pcLocal.endOfTrickleIce; + function PC_LOCAL_VERIFY_SDP_AFTER_END_OF_TRICKLE(test) { + /* In case the endOfTrickleSdp promise is resolved already it will win the + * race because it gets evaluated first. But if endOfTrickleSdp is still + * pending the rejection will win the race. */ + return Promise.race([ + test.pcLocal.endOfTrickleSdp, + Promise.reject("No SDP") + ]) + .then(sdp => sdputils.checkSdpAfterEndOfTrickle(sdp, test.testOptions, test.pcLocal.label), + () => info("pcLocal: Gathering is not complete yet, skipping post-gathering SDP check")); }, - function PC_REMOTE_WAIT_FOR_END_OF_TRICKLE(test) { - return test.pcRemote.endOfTrickleIce; + function PC_REMOTE_VERIFY_SDP_AFTER_END_OF_TRICKLE(test) { + /* In case the endOfTrickleSdp promise is resolved already it will win the + * race because it gets evaluated first. But if endOfTrickleSdp is still + * pending the rejection will win the race. */ + return Promise.race([ + test.pcRemote.endOfTrickleSdp, + Promise.reject("No SDP") + ]) + .then(sdp => sdputils.checkSdpAfterEndOfTrickle(sdp, test.testOptions, test.pcRemote.label), + () => info("pcRemote: Gathering is not complete yet, skipping post-gathering SDP check")); } ]; +function PC_LOCAL_REMOVE_VP8_FROM_OFFER(test) { + isnot(test.originalOffer.sdp.search("H264/90000"), -1, "H.264 should be present in the SDP offer"); + test.originalOffer.sdp = sdputils.removeVP8(test.originalOffer.sdp); + info("Updated H264 only offer: " + JSON.stringify(test.originalOffer)); +}; + function PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER(test) { - test.originalOffer.sdp = test.originalOffer.sdp.replace( - /a=group:BUNDLE .*\r\n/g, - "" - ); + test.originalOffer.sdp = sdputils.removeBundle(test.originalOffer.sdp); info("Updated no bundle offer: " + JSON.stringify(test.originalOffer)); }; +function PC_LOCAL_REMOVE_RTCPMUX_FROM_OFFER(test) { + test.originalOffer.sdp = sdputils.removeRtcpMux(test.originalOffer.sdp); + info("Updated no RTCP-Mux offer: " + JSON.stringify(test.originalOffer)); +}; + var addRenegotiation = (chain, commands, checks) => { chain.append(commands); chain.append(commandsPeerConnectionOfferAnswer); diff --git a/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoNoBundle.html b/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoNoBundle.html index 7081df0f65..dc8725f5ad 100644 --- a/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoNoBundle.html +++ b/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoNoBundle.html @@ -12,17 +12,11 @@ }); var test; -runNetworkTest(function () { - test = new PeerConnectionTest(); +runNetworkTest(function (options) { + options = options || { }; + options.bundle = false; + test = new PeerConnectionTest(options); addInitialDataChannel(test.chain); - test.chain.insertAfter("PC_LOCAL_CREATE_OFFER", [ - function PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER(test) { - // Just replace a=group:BUNDLE with something that will be ignored. - test.originalOffer.sdp = test.originalOffer.sdp.replace( - "a=group:BUNDLE", - "a=foo:"); - } - ]); test.setMediaConstraints([{audio: true}, {video: true}], [{audio: true}, {video: true}]); test.run(); diff --git a/dom/media/tests/mochitest/test_peerConnection_addDataChannelNoBundle.html b/dom/media/tests/mochitest/test_peerConnection_addDataChannelNoBundle.html index dc91f6976d..5182aa5649 100644 --- a/dom/media/tests/mochitest/test_peerConnection_addDataChannelNoBundle.html +++ b/dom/media/tests/mochitest/test_peerConnection_addDataChannelNoBundle.html @@ -13,6 +13,8 @@ var test; runNetworkTest(function (options) { + options = options || { }; + options.bundle = false; test = new PeerConnectionTest(options); addRenegotiation(test.chain, commandsCreateDataChannel.concat( @@ -27,9 +29,6 @@ ), commandsCheckDataChannel); - test.chain.insertAfterEach('PC_LOCAL_CREATE_OFFER', - PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER); - // Insert before the second PC_LOCAL_CHECK_MEDIA_TRACKS test.chain.insertBefore('PC_LOCAL_CHECK_MEDIA_TRACKS', commandsWaitForDataChannel, diff --git a/dom/media/tests/mochitest/test_peerConnection_addSecondAudioStreamNoBundle.html b/dom/media/tests/mochitest/test_peerConnection_addSecondAudioStreamNoBundle.html index 99fd8e766f..34cf99a2f8 100644 --- a/dom/media/tests/mochitest/test_peerConnection_addSecondAudioStreamNoBundle.html +++ b/dom/media/tests/mochitest/test_peerConnection_addSecondAudioStreamNoBundle.html @@ -13,6 +13,8 @@ var test; runNetworkTest(function (options) { + options = options || { }; + options.bundle = false; test = new PeerConnectionTest(options); addRenegotiation(test.chain, [ @@ -30,9 +32,6 @@ ] ); - test.chain.insertAfterEach('PC_LOCAL_CREATE_OFFER', - PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER); - // TODO(bug 1093835): figure out how to verify if media flows through the new stream test.setMediaConstraints([{audio: true}], [{audio: true}]); test.run(); diff --git a/dom/media/tests/mochitest/test_peerConnection_addSecondVideoStreamNoBundle.html b/dom/media/tests/mochitest/test_peerConnection_addSecondVideoStreamNoBundle.html index 577a125e58..a7dae4045c 100644 --- a/dom/media/tests/mochitest/test_peerConnection_addSecondVideoStreamNoBundle.html +++ b/dom/media/tests/mochitest/test_peerConnection_addSecondVideoStreamNoBundle.html @@ -13,6 +13,8 @@ var test; runNetworkTest(function (options) { + options = options || { }; + options.bundle = false; test = new PeerConnectionTest(options); addRenegotiation(test.chain, [ @@ -30,9 +32,6 @@ ] ); - test.chain.insertAfterEach('PC_LOCAL_CREATE_OFFER', - PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER); - // TODO(bug 1093835): figure out how to verify if media flows through the new stream test.setMediaConstraints([{video: true}], [{video: true}]); test.run(); diff --git a/dom/media/tests/mochitest/test_peerConnection_basicAudioRequireEOC.html b/dom/media/tests/mochitest/test_peerConnection_basicAudioRequireEOC.html new file mode 100644 index 0000000000..e3e44cf1bf --- /dev/null +++ b/dom/media/tests/mochitest/test_peerConnection_basicAudioRequireEOC.html @@ -0,0 +1,35 @@ + + + + + + +
+
+
+ + diff --git a/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoNoBundle.html b/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoNoBundle.html index 5d58bcb90c..c840804a6b 100644 --- a/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoNoBundle.html +++ b/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoNoBundle.html @@ -12,10 +12,9 @@ }); runNetworkTest(options => { + options = options || { }; + options.bundle = false; var test = new PeerConnectionTest(options); - test.chain.insertAfter( - 'PC_LOCAL_CREATE_OFFER', - [PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER]); test.setMediaConstraints([{audio: true}, {video: true}], [{audio: true}, {video: true}]); test.run(); diff --git a/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoNoBundleNoRtcpMux.html b/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoNoBundleNoRtcpMux.html new file mode 100644 index 0000000000..f9e6c81cd4 --- /dev/null +++ b/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoNoBundleNoRtcpMux.html @@ -0,0 +1,39 @@ + + + + + + +
+
+
+ + diff --git a/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoNoRtcpMux.html b/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoNoRtcpMux.html new file mode 100644 index 0000000000..da8dd2d559 --- /dev/null +++ b/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoNoRtcpMux.html @@ -0,0 +1,38 @@ + + + + + + +
+
+
+ + diff --git a/dom/media/webspeech/recognition/SpeechStreamListener.cpp b/dom/media/webspeech/recognition/SpeechStreamListener.cpp index 6e7b76b1f6..773ec27a2c 100644 --- a/dom/media/webspeech/recognition/SpeechStreamListener.cpp +++ b/dom/media/webspeech/recognition/SpeechStreamListener.cpp @@ -22,10 +22,7 @@ SpeechStreamListener::~SpeechStreamListener() nsCOMPtr mainThread; NS_GetMainThread(getter_AddRefs(mainThread)); - SpeechRecognition* forgottenRecognition = nullptr; - mRecognition.swap(forgottenRecognition); - NS_ProxyRelease(mainThread, - static_cast(forgottenRecognition)); + NS_ProxyRelease(mainThread, mRecognition.forget()); } void diff --git a/dom/webidl/IDBDatabase.webidl b/dom/webidl/IDBDatabase.webidl index c51fba05cd..c45db837bd 100644 --- a/dom/webidl/IDBDatabase.webidl +++ b/dom/webidl/IDBDatabase.webidl @@ -35,7 +35,7 @@ interface IDBDatabase : EventTarget { }; partial interface IDBDatabase { - [Func="mozilla::dom::indexedDB::IndexedDatabaseManager::ExperimentalFeaturesEnabled"] + [Func="mozilla::dom::IndexedDatabaseManager::ExperimentalFeaturesEnabled"] readonly attribute StorageType storage; [Exposed=Window, Throws] diff --git a/dom/webidl/IDBIndex.webidl b/dom/webidl/IDBIndex.webidl index df17f27805..ddd5ff181c 100644 --- a/dom/webidl/IDBIndex.webidl +++ b/dom/webidl/IDBIndex.webidl @@ -32,10 +32,10 @@ interface IDBIndex { // : Not locale-aware, uses normal JS sorting. // : Sorted based on the rules of the specified locale. // Note: never returns "auto", only the current locale. - [Func="mozilla::dom::indexedDB::IndexedDatabaseManager::ExperimentalFeaturesEnabled"] + [Func="mozilla::dom::IndexedDatabaseManager::ExperimentalFeaturesEnabled"] readonly attribute DOMString? locale; - [Func="mozilla::dom::indexedDB::IndexedDatabaseManager::ExperimentalFeaturesEnabled"] + [Func="mozilla::dom::IndexedDatabaseManager::ExperimentalFeaturesEnabled"] readonly attribute boolean isAutoLocale; [Throws] @@ -56,16 +56,14 @@ interface IDBIndex { partial interface IDBIndex { [Throws] - IDBRequest mozGetAll (optional any key, optional unsigned long limit); + IDBRequest mozGetAll (optional any key, [EnforceRange] optional unsigned long limit); [Throws] - IDBRequest mozGetAllKeys (optional any key, optional unsigned long limit); + IDBRequest mozGetAllKeys (optional any key, [EnforceRange] optional unsigned long limit); - [Throws, - Func="mozilla::dom::indexedDB::IndexedDatabaseManager::ExperimentalFeaturesEnabled"] - IDBRequest getAll (optional any key, optional unsigned long limit); + [Throws] + IDBRequest getAll (optional any key, [EnforceRange] optional unsigned long limit); - [Throws, - Func="mozilla::dom::indexedDB::IndexedDatabaseManager::ExperimentalFeaturesEnabled"] - IDBRequest getAllKeys (optional any key, optional unsigned long limit); + [Throws] + IDBRequest getAllKeys (optional any key, [EnforceRange] optional unsigned long limit); }; diff --git a/dom/webidl/IDBKeyRange.webidl b/dom/webidl/IDBKeyRange.webidl index 0fca84dae9..ddf9f4469a 100644 --- a/dom/webidl/IDBKeyRange.webidl +++ b/dom/webidl/IDBKeyRange.webidl @@ -30,7 +30,7 @@ interface IDBKeyRange { }; [Exposed=(Window,Worker), - Func="mozilla::dom::indexedDB::IndexedDatabaseManager::ExperimentalFeaturesEnabled"] + Func="mozilla::dom::IndexedDatabaseManager::ExperimentalFeaturesEnabled"] interface IDBLocaleAwareKeyRange : IDBKeyRange { [NewObject, Throws] static IDBLocaleAwareKeyRange bound (any lower, any upper, optional boolean lowerOpen = false, optional boolean upperOpen = false); diff --git a/dom/webidl/IDBObjectStore.webidl b/dom/webidl/IDBObjectStore.webidl index a912aa68bc..7d4d87d7c9 100644 --- a/dom/webidl/IDBObjectStore.webidl +++ b/dom/webidl/IDBObjectStore.webidl @@ -63,17 +63,14 @@ interface IDBObjectStore { partial interface IDBObjectStore { // Success fires IDBTransactionEvent, result == array of values for given keys [Throws] - IDBRequest mozGetAll (optional any key, optional unsigned long limit); + IDBRequest mozGetAll (optional any key, [EnforceRange] optional unsigned long limit); - [Throws, - Func="mozilla::dom::indexedDB::IndexedDatabaseManager::ExperimentalFeaturesEnabled"] - IDBRequest getAll (optional any key, optional unsigned long limit); + [Throws] + IDBRequest getAll (optional any key, [EnforceRange] optional unsigned long limit); - [Throws, - Func="mozilla::dom::indexedDB::IndexedDatabaseManager::ExperimentalFeaturesEnabled"] - IDBRequest getAllKeys (optional any key, optional unsigned long limit); + [Throws] + IDBRequest getAllKeys (optional any key, [EnforceRange] optional unsigned long limit); - [Throws, - Func="mozilla::dom::indexedDB::IndexedDatabaseManager::ExperimentalFeaturesEnabled"] + [Throws] IDBRequest openKeyCursor (optional any range, optional IDBCursorDirection direction = "next"); }; diff --git a/dom/webidl/Performance.webidl b/dom/webidl/Performance.webidl index 6df0dd2ae7..4b06f7e18b 100644 --- a/dom/webidl/Performance.webidl +++ b/dom/webidl/Performance.webidl @@ -17,9 +17,6 @@ typedef sequence PerformanceEntryList; interface Performance { [DependsOn=DeviceState, Affects=Nothing] DOMHighResTimeStamp now(); - - [Throws] - DOMHighResTimeStamp translateTime(DOMHighResTimeStamp time, (Window or Worker or SharedWorker or ServiceWorker) timeSource); }; [Exposed=Window] diff --git a/dom/webidl/TestInterfaceJSMaplikeSetlikeIterable.webidl b/dom/webidl/TestInterfaceJSMaplikeSetlikeIterable.webidl index dc6e3239e7..1a124b016f 100644 --- a/dom/webidl/TestInterfaceJSMaplikeSetlikeIterable.webidl +++ b/dom/webidl/TestInterfaceJSMaplikeSetlikeIterable.webidl @@ -50,6 +50,8 @@ interface TestInterfaceSetlikeNode { Pref="dom.expose_test_interfaces"] interface TestInterfaceIterableSingle { iterable; + getter long(unsigned long index); + readonly attribute unsigned long length; }; [Constructor(), diff --git a/dom/workers/Performance.cpp b/dom/workers/Performance.cpp index cd55b171e1..0b828c3f7f 100644 --- a/dom/workers/Performance.cpp +++ b/dom/workers/Performance.cpp @@ -32,7 +32,7 @@ DOMHighResTimeStamp Performance::Now() const { TimeDuration duration = - TimeStamp::Now() - mWorkerPrivate->CreationTimeStamp(); + TimeStamp::Now() - mWorkerPrivate->NowBaseTimeStamp(); double nowTime = duration.ToMilliseconds(); // Round down to the nearest 20us, because if the timer is too accurate people // can do nasty timing attacks with it. See similar code in the non-worker @@ -57,7 +57,7 @@ Performance::GetPerformanceTimingFromString(const nsAString& aProperty) } if (aProperty.EqualsLiteral("navigationStart")) { - return mWorkerPrivate->CreationTime(); + return mWorkerPrivate->NowBaseTime(); } MOZ_CRASH("IsPerformanceTimingAttribute and GetPerformanceTimingFromString are out of sync"); @@ -82,13 +82,13 @@ Performance::InsertUserEntry(PerformanceEntry* aEntry) TimeStamp Performance::CreationTimeStamp() const { - return mWorkerPrivate->CreationTimeStamp(); + return mWorkerPrivate->NowBaseTimeStamp(); } DOMHighResTimeStamp Performance::CreationTime() const { - return mWorkerPrivate->CreationTime(); + return mWorkerPrivate->NowBaseTime(); } void diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp index 62ee125cc5..1c483d214c 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -38,7 +38,7 @@ #include "mozilla/dom/MessageEventBinding.h" #include "mozilla/dom/WorkerBinding.h" #include "mozilla/dom/ScriptSettings.h" -#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h" +#include "mozilla/dom/IndexedDatabaseManager.h" #include "mozilla/ipc/BackgroundChild.h" #include "mozilla/DebugOnly.h" #include "mozilla/Preferences.h" @@ -82,7 +82,6 @@ USING_WORKERS_NAMESPACE using mozilla::MutexAutoLock; using mozilla::MutexAutoUnlock; using mozilla::Preferences; -using mozilla::dom::indexedDB::IndexedDatabaseManager; // The size of the worker runtime heaps in bytes. May be changed via pref. #define WORKER_DEFAULT_RUNTIME_HEAPSIZE 32 * 1024 * 1024 @@ -900,6 +899,9 @@ public: js::SetPreserveWrapperCallback(rt, PreserveWrapper); JS_InitDestroyPrincipalsCallback(rt, DestroyWorkerPrincipals); JS_SetWrapObjectCallbacks(rt, &WrapObjectCallbacks); + if (aWorkerPrivate->IsDedicatedWorker()) { + JS_SetFutexCanWait(rt); + } } ~WorkerJSRuntime() diff --git a/dom/workers/ScriptLoader.cpp b/dom/workers/ScriptLoader.cpp index 43f564cd87..0f7c835996 100644 --- a/dom/workers/ScriptLoader.cpp +++ b/dom/workers/ScriptLoader.cpp @@ -1071,9 +1071,11 @@ private: // If the load principal is the system principal then the channel // principal must also be the system principal (we do not allow chrome - // code to create workers with non-chrome scripts). Otherwise this channel - // principal must be same origin with the load principal (we check again - // here in case redirects changed the location of the script). + // code to create workers with non-chrome scripts, and if we ever decide + // to change this we need to make sure we don't always set + // mPrincipalIsSystem to true in WorkerPrivate::GetLoadInfo()). Otherwise + // this channel principal must be same origin with the load principal (we + // check again here in case redirects changed the location of the script). if (nsContentUtils::IsSystemPrincipal(loadPrincipal)) { if (!nsContentUtils::IsSystemPrincipal(channelPrincipal)) { // See if this is a resource URI. Since JSMs usually come from diff --git a/dom/workers/ServiceWorker.cpp b/dom/workers/ServiceWorker.cpp index 777d7ef1b3..4542514f62 100644 --- a/dom/workers/ServiceWorker.cpp +++ b/dom/workers/ServiceWorker.cpp @@ -101,13 +101,6 @@ ServiceWorker::PostMessage(JSContext* aCx, JS::Handle aMessage, aRv = workerPrivate->SendMessageEvent(aCx, aMessage, aTransferable, Move(clientInfo)); } -WorkerPrivate* -ServiceWorker::GetWorkerPrivate() const -{ - ServiceWorkerPrivate* workerPrivate = mInfo->WorkerPrivate(); - return workerPrivate->GetWorkerPrivate(); -} - } // namespace workers } // namespace dom } // namespace mozilla diff --git a/dom/workers/ServiceWorker.h b/dom/workers/ServiceWorker.h index 26cf53eb7b..15fc4fbc7a 100644 --- a/dom/workers/ServiceWorker.h +++ b/dom/workers/ServiceWorker.h @@ -67,9 +67,6 @@ public: const Optional>& aTransferable, ErrorResult& aRv); - WorkerPrivate* - GetWorkerPrivate() const; - private: // This class can only be created from the ServiceWorkerManager. ServiceWorker(nsPIDOMWindow* aWindow, ServiceWorkerInfo* aInfo); diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp index 1b3923aa53..38712a2522 100644 --- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -41,8 +41,6 @@ #include "mozilla/dom/DOMError.h" #include "mozilla/dom/ErrorEvent.h" #include "mozilla/dom/Headers.h" -#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h" -#include "mozilla/dom/indexedDB/IDBFactory.h" #include "mozilla/dom/InternalHeaders.h" #include "mozilla/dom/Navigator.h" #include "mozilla/dom/NotificationEvent.h" diff --git a/dom/workers/ServiceWorkerPrivate.cpp b/dom/workers/ServiceWorkerPrivate.cpp index 21a37aa93b..3eb2a58c1b 100644 --- a/dom/workers/ServiceWorkerPrivate.cpp +++ b/dom/workers/ServiceWorkerPrivate.cpp @@ -9,6 +9,7 @@ #include "nsStreamUtils.h" #include "nsStringStream.h" #include "mozilla/dom/FetchUtil.h" +#include "mozilla/dom/IndexedDatabaseManager.h" using namespace mozilla; using namespace mozilla::dom; @@ -1096,15 +1097,7 @@ public: internalChannel->GetRedirectMode(&redirectMode); mRequestRedirect = static_cast(redirectMode); - if (loadFlags & nsIRequest::LOAD_ANONYMOUS) { - mRequestCredentials = RequestCredentials::Omit; - } else { - bool includeCrossOrigin; - internalChannel->GetCorsIncludeCredentials(&includeCrossOrigin); - if (includeCrossOrigin) { - mRequestCredentials = RequestCredentials::Include; - } - } + mRequestCredentials = InternalRequest::MapChannelToRequestCredentials(channel); rv = httpChannel->VisitNonDefaultRequestHeaders(this); NS_ENSURE_SUCCESS(rv, rv); @@ -1431,7 +1424,7 @@ ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy, // TODO(catalinb): Bug 1192138 - Add telemetry for service worker wake-ups. // Ensure that the IndexedDatabaseManager is initialized - NS_WARN_IF(!indexedDB::IndexedDatabaseManager::GetOrCreate()); + NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate()); WorkerLoadInfo info; nsresult rv = NS_NewURI(getter_AddRefs(info.mBaseURI), mInfo->ScriptSpec(), diff --git a/dom/workers/ServiceWorkerPrivate.h b/dom/workers/ServiceWorkerPrivate.h index 43db837c84..fc9ae46f1f 100644 --- a/dom/workers/ServiceWorkerPrivate.h +++ b/dom/workers/ServiceWorkerPrivate.h @@ -126,12 +126,6 @@ public: void NoteStoppedControllingDocuments(); - WorkerPrivate* - GetWorkerPrivate() const - { - return mWorkerPrivate; - } - void Activated(); diff --git a/dom/workers/WorkerDebuggerManager.cpp b/dom/workers/WorkerDebuggerManager.cpp index 8f6ff9f12d..28c0a878df 100644 --- a/dom/workers/WorkerDebuggerManager.cpp +++ b/dom/workers/WorkerDebuggerManager.cpp @@ -12,27 +12,53 @@ USING_WORKERS_NAMESPACE -class RegisterDebuggerRunnable final : public nsRunnable +class RegisterDebuggerMainThreadRunnable final : public nsRunnable { RefPtr mManager; - RefPtr mDebugger; - bool mHasListeners; + WorkerPrivate* mWorkerPrivate; + bool mNotifyListeners; public: - RegisterDebuggerRunnable(WorkerDebuggerManager* aManager, - WorkerDebugger* aDebugger, - bool aHasListeners) - : mManager(aManager), mDebugger(aDebugger), mHasListeners(aHasListeners) + RegisterDebuggerMainThreadRunnable(WorkerDebuggerManager* aManager, + WorkerPrivate* aWorkerPrivate, + bool aNotifyListeners) + : mManager(aManager), + mWorkerPrivate(aWorkerPrivate), + mNotifyListeners(aNotifyListeners) { } private: - ~RegisterDebuggerRunnable() + ~RegisterDebuggerMainThreadRunnable() { } NS_IMETHOD Run() override { - mManager->RegisterDebuggerOnMainThread(mDebugger, mHasListeners); + mManager->RegisterDebuggerMainThread(mWorkerPrivate, mNotifyListeners); + + return NS_OK; + } +}; + +class UnregisterDebuggerMainThreadRunnable final : public nsRunnable +{ + RefPtr mManager; + WorkerPrivate* mWorkerPrivate; + +public: + UnregisterDebuggerMainThreadRunnable(WorkerDebuggerManager* aManager, + WorkerPrivate* aWorkerPrivate) + : mManager(aManager), mWorkerPrivate(aWorkerPrivate) + { } + +private: + ~UnregisterDebuggerMainThreadRunnable() + { } + + NS_IMETHOD + Run() override + { + mManager->UnregisterDebuggerMainThread(mWorkerPrivate); return NS_OK; } @@ -42,14 +68,14 @@ BEGIN_WORKERS_NAMESPACE class WorkerDebuggerEnumerator final : public nsISimpleEnumerator { - nsTArray> mDebuggers; + nsTArray> mDebuggers; uint32_t mIndex; public: - explicit WorkerDebuggerEnumerator(const nsTArray& aDebuggers) - : mIndex(0) + explicit WorkerDebuggerEnumerator( + const nsTArray>& aDebuggers) + : mDebuggers(aDebuggers), mIndex(0) { - mDebuggers.AppendElements(aDebuggers); } NS_DECL_ISUPPORTS @@ -75,8 +101,7 @@ WorkerDebuggerEnumerator::GetNext(nsISupports** aResult) return NS_ERROR_FAILURE; } - nsCOMPtr element = mDebuggers.ElementAt(mIndex++); - element.forget(aResult); + mDebuggers.ElementAt(mIndex++).forget(aResult); return NS_OK; }; @@ -99,8 +124,6 @@ WorkerDebuggerManager::GetWorkerDebuggerEnumerator( { AssertIsOnMainThread(); - MutexAutoLock lock(mMutex); - RefPtr enumerator = new WorkerDebuggerEnumerator(mDebuggers); enumerator.forget(aResult); @@ -149,95 +172,126 @@ WorkerDebuggerManager::ClearListeners() } void -WorkerDebuggerManager::RegisterDebugger(WorkerDebugger* aDebugger) +WorkerDebuggerManager::RegisterDebugger(WorkerPrivate* aWorkerPrivate) { - // May be called on any thread! - - bool hasListeners = false; - - { - MutexAutoLock lock(mMutex); - - hasListeners = !mListeners.IsEmpty(); - } + aWorkerPrivate->AssertIsOnParentThread(); if (NS_IsMainThread()) { - RegisterDebuggerOnMainThread(aDebugger, hasListeners); + // When the parent thread is the main thread, it will always block until all + // register liseners have been called, since it cannot continue until the + // call to RegisterDebuggerMainThread returns. + // + // In this case, it is always safe to notify all listeners on the main + // thread, even if there were no listeners at the time this method was + // called, so we can always pass true for the value of aNotifyListeners. + // This avoids having to lock mMutex to check whether mListeners is empty. + RegisterDebuggerMainThread(aWorkerPrivate, true); } else { + // We guarantee that if any register listeners are called, the worker does + // not start running until all register listeners have been called. To + // guarantee this, the parent thread should block until all register + // listeners have been called. + // + // However, to avoid overhead when the debugger is not being used, the + // parent thread will only block if there were any listeners at the time + // this method was called. As a result, we should not notify any listeners + // on the main thread if there were no listeners at the time this method was + // called, because the parent will not be blocking in that case. + bool hasListeners = false; + { + MutexAutoLock lock(mMutex); + + hasListeners = !mListeners.IsEmpty(); + } + nsCOMPtr runnable = - new RegisterDebuggerRunnable(this, aDebugger, hasListeners); + new RegisterDebuggerMainThreadRunnable(this, aWorkerPrivate, + hasListeners); MOZ_ALWAYS_TRUE(NS_SUCCEEDED( NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))); if (hasListeners) { - aDebugger->WaitIsEnabled(true); + aWorkerPrivate->WaitForIsDebuggerRegistered(true); } } } void -WorkerDebuggerManager::UnregisterDebugger(WorkerDebugger* aDebugger) +WorkerDebuggerManager::UnregisterDebugger(WorkerPrivate* aWorkerPrivate) { - // May be called on any thread! + aWorkerPrivate->AssertIsOnParentThread(); if (NS_IsMainThread()) { - UnregisterDebuggerOnMainThread(aDebugger); + UnregisterDebuggerMainThread(aWorkerPrivate); } else { nsCOMPtr runnable = - NS_NewRunnableMethodWithArg>(this, - &WorkerDebuggerManager::UnregisterDebuggerOnMainThread, aDebugger); + new UnregisterDebuggerMainThreadRunnable(this, aWorkerPrivate); MOZ_ALWAYS_TRUE(NS_SUCCEEDED( NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))); - aDebugger->WaitIsEnabled(false); + aWorkerPrivate->WaitForIsDebuggerRegistered(false); } } void -WorkerDebuggerManager::RegisterDebuggerOnMainThread(WorkerDebugger* aDebugger, - bool aHasListeners) +WorkerDebuggerManager::RegisterDebuggerMainThread(WorkerPrivate* aWorkerPrivate, + bool aNotifyListeners) { AssertIsOnMainThread(); - MOZ_ASSERT(!mDebuggers.Contains(aDebugger)); - mDebuggers.AppendElement(aDebugger); + RefPtr debugger = new WorkerDebugger(aWorkerPrivate); + mDebuggers.AppendElement(debugger); - nsTArray> listeners; - { - MutexAutoLock lock(mMutex); + aWorkerPrivate->SetDebugger(debugger); - listeners.AppendElements(mListeners); - } + if (aNotifyListeners) { + nsTArray> listeners; + { + MutexAutoLock lock(mMutex); + + listeners = mListeners; + } - if (aHasListeners) { for (size_t index = 0; index < listeners.Length(); ++index) { - listeners[index]->OnRegister(aDebugger); + listeners[index]->OnRegister(debugger); } } - aDebugger->Enable(); + aWorkerPrivate->SetIsDebuggerRegistered(true); } void -WorkerDebuggerManager::UnregisterDebuggerOnMainThread(WorkerDebugger* aDebugger) +WorkerDebuggerManager::UnregisterDebuggerMainThread( + WorkerPrivate* aWorkerPrivate) { AssertIsOnMainThread(); - MOZ_ASSERT(mDebuggers.Contains(aDebugger)); - mDebuggers.RemoveElement(aDebugger); + // There is nothing to do here if the debugger was never succesfully + // registered. We need to check this on the main thread because the worker + // does not wait for the registration to complete if there were no listeners + // installed when it started. + if (!aWorkerPrivate->IsDebuggerRegistered()) { + return; + } + + RefPtr debugger = aWorkerPrivate->Debugger(); + mDebuggers.RemoveElement(debugger); + + aWorkerPrivate->SetDebugger(nullptr); nsTArray> listeners; { MutexAutoLock lock(mMutex); - listeners.AppendElements(mListeners); + listeners = mListeners; } for (size_t index = 0; index < listeners.Length(); ++index) { - listeners[index]->OnUnregister(aDebugger); + listeners[index]->OnUnregister(debugger); } - aDebugger->Disable(); + debugger->Close(); + aWorkerPrivate->SetIsDebuggerRegistered(false); } END_WORKERS_NAMESPACE diff --git a/dom/workers/WorkerDebuggerManager.h b/dom/workers/WorkerDebuggerManager.h index a8e5fc9250..2b3d1b5646 100644 --- a/dom/workers/WorkerDebuggerManager.h +++ b/dom/workers/WorkerDebuggerManager.h @@ -20,23 +20,19 @@ #define WORKERDEBUGGERMANAGER_CONTRACTID \ "@mozilla.org/dom/workers/workerdebuggermanager;1" -class RegisterDebuggerRunnable; - BEGIN_WORKERS_NAMESPACE class WorkerDebugger; class WorkerDebuggerManager final : public nsIWorkerDebuggerManager { - friend class ::RegisterDebuggerRunnable; - - mozilla::Mutex mMutex; + Mutex mMutex; // Protected by mMutex. nsTArray> mListeners; // Only touched on the main thread. - nsTArray mDebuggers; + nsTArray> mDebuggers; public: static WorkerDebuggerManager* @@ -54,17 +50,17 @@ public: void ClearListeners(); - void RegisterDebugger(WorkerDebugger* aDebugger); + void RegisterDebugger(WorkerPrivate* aWorkerPrivate); - void UnregisterDebugger(WorkerDebugger* aDebugger); + void UnregisterDebugger(WorkerPrivate* aWorkerPrivate); + + void RegisterDebuggerMainThread(WorkerPrivate* aWorkerPrivate, + bool aNotifyListeners); + + void UnregisterDebuggerMainThread(WorkerPrivate* aWorkerPrivate); private: virtual ~WorkerDebuggerManager(); - - void RegisterDebuggerOnMainThread(WorkerDebugger* aDebugger, - bool aHasListeners); - - void UnregisterDebuggerOnMainThread(WorkerDebugger* aDebugger); }; inline nsresult @@ -81,7 +77,7 @@ ClearWorkerDebuggerManagerListeners() } inline nsresult -RegisterWorkerDebugger(WorkerDebugger* aDebugger) +RegisterWorkerDebugger(WorkerPrivate* aWorkerPrivate) { RefPtr manager = WorkerDebuggerManager::GetOrCreateService(); @@ -89,12 +85,12 @@ RegisterWorkerDebugger(WorkerDebugger* aDebugger) return NS_ERROR_FAILURE; } - manager->RegisterDebugger(aDebugger); + manager->RegisterDebugger(aWorkerPrivate); return NS_OK; } inline nsresult -UnregisterWorkerDebugger(WorkerDebugger* aDebugger) +UnregisterWorkerDebugger(WorkerPrivate* aWorkerPrivate) { RefPtr manager = WorkerDebuggerManager::GetOrCreateService(); @@ -102,7 +98,7 @@ UnregisterWorkerDebugger(WorkerDebugger* aDebugger) return NS_ERROR_FAILURE; } - manager->UnregisterDebugger(aDebugger); + manager->UnregisterDebugger(aWorkerPrivate); return NS_OK; } diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index ad0154fae7..0e54727581 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -63,7 +63,6 @@ #include "mozilla/dom/WorkerBinding.h" #include "mozilla/dom/WorkerDebuggerGlobalScopeBinding.h" #include "mozilla/dom/WorkerGlobalScopeBinding.h" -#include "mozilla/dom/indexedDB/IDBFactory.h" #include "mozilla/Preferences.h" #include "mozilla/TimelineConsumers.h" #include "mozilla/WorkerTimelineMarker.h" @@ -116,11 +115,22 @@ #define PREF_WORKERS_ENABLED "dom.workers.enabled" -#ifdef WORKER_LOGGING -#define LOG(_args) do { printf _args ; fflush(stdout); } while (0) -#else -#define LOG(_args) do { } while (0) -#endif +static mozilla::LazyLogModule sWorkerPrivateLog("WorkerPrivate"); +static mozilla::LazyLogModule sWorkerTimeoutsLog("WorkerTimeouts"); + +mozilla::LogModule* +WorkerLog() +{ + return sWorkerPrivateLog; +} + +mozilla::LogModule* +TimeoutsLog() +{ + return sWorkerTimeoutsLog; +} + +#define LOG(log, _args) MOZ_LOG(log, LogLevel::Debug, _args); using namespace mozilla; using namespace mozilla::dom; @@ -1117,14 +1127,19 @@ private: } }; -class TimerRunnable final : public WorkerRunnable +class TimerRunnable final : public WorkerRunnable, + public nsITimerCallback { public: + NS_DECL_ISUPPORTS_INHERITED + explicit TimerRunnable(WorkerPrivate* aWorkerPrivate) : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) { } private: + ~TimerRunnable() {} + virtual bool PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { @@ -1144,8 +1159,16 @@ private: { return aWorkerPrivate->RunExpiredTimeouts(aCx); } + + NS_IMETHOD + Notify(nsITimer* aTimer) override + { + return Run(); + } }; +NS_IMPL_ISUPPORTS_INHERITED(TimerRunnable, WorkerRunnable, nsITimerCallback) + class DebuggerImmediateRunnable : public WorkerRunnable { RefPtr mHandler; @@ -1597,6 +1620,13 @@ private: { return aWorkerPrivate->ConnectMessagePort(aCx, mPortIdentifier); } + + NS_IMETHOD + Cancel() override + { + MessagePort::ForceClose(mPortIdentifier); + return WorkerRunnable::Cancel(); + } }; class DummyRunnable final @@ -1860,6 +1890,7 @@ InterfaceRequestor::InterfaceRequestor(nsIPrincipal* aPrincipal, callbacks->GetInterface(NS_GET_IID(nsILoadContext), getter_AddRefs(baseContext)); } + mOuterRequestor = callbacks; } mLoadContext = new LoadContext(aPrincipal, baseContext); @@ -1918,6 +1949,13 @@ InterfaceRequestor::GetInterface(const nsIID& aIID, void** aSink) return NS_OK; } + if (aIID.Equals(NS_GET_IID(nsINetworkInterceptController)) && + mOuterRequestor) { + // If asked for the network intercept controller, ask the outer requestor, + // which could be the docshell. + return mOuterRequestor->GetInterface(aIID, aSink); + } + return NS_NOINTERFACE; } @@ -2188,11 +2226,25 @@ WorkerPrivateParent::WorkerPrivateParent( aParent->CopyJSSettings(mJSSettings); MOZ_ASSERT(IsDedicatedWorker()); + mNowBaseTimeStamp = aParent->NowBaseTimeStamp(); + mNowBaseTimeHighRes = aParent->NowBaseTime(); } else { AssertIsOnMainThread(); RuntimeService::GetDefaultJSSettings(mJSSettings); + + if (IsDedicatedWorker() && mLoadInfo.mWindow && + mLoadInfo.mWindow->GetPerformance()) { + mNowBaseTimeStamp = mLoadInfo.mWindow->GetPerformance()->GetDOMTiming()-> + GetNavigationStartTimeStamp(); + mNowBaseTimeHighRes = + mLoadInfo.mWindow->GetPerformance()->GetDOMTiming()-> + GetNavigationStartHighRes(); + } else { + mNowBaseTimeStamp = CreationTimeStamp(); + mNowBaseTimeHighRes = CreationTime(); + } } } @@ -2280,12 +2332,9 @@ WorkerPrivateParent::EnableDebugger() WorkerPrivate* self = ParentAsWorkerPrivate(); - MOZ_ASSERT(!self->mDebugger); - self->mDebugger = new WorkerDebugger(self); - - if (NS_FAILED(RegisterWorkerDebugger(self->mDebugger))) { + if (NS_FAILED(RegisterWorkerDebugger(self))) { NS_WARNING("Failed to register worker debugger!"); - self->mDebugger = nullptr; + return; } } @@ -2297,15 +2346,9 @@ WorkerPrivateParent::DisableDebugger() WorkerPrivate* self = ParentAsWorkerPrivate(); - if (!self->mDebugger) { - return; - } - - if (NS_FAILED(UnregisterWorkerDebugger(self->mDebugger))) { + if (NS_FAILED(UnregisterWorkerDebugger(self))) { NS_WARNING("Failed to unregister worker debugger!"); } - - self->mDebugger = nullptr; } template @@ -3516,17 +3559,43 @@ WorkerPrivateParent::AssertInnerWindowIsCorrect() const #endif -class ReportDebuggerErrorRunnable final : public nsIRunnable +class PostDebuggerMessageRunnable final : public nsRunnable { - RefPtr mDebugger; + WorkerDebugger *mDebugger; + nsString mMessage; + +public: + PostDebuggerMessageRunnable(WorkerDebugger* aDebugger, + const nsAString& aMessage) + : mDebugger(aDebugger), + mMessage(aMessage) + { + } + +private: + ~PostDebuggerMessageRunnable() + { } + + NS_IMETHOD + Run() override + { + mDebugger->PostMessageToDebuggerOnMainThread(mMessage); + + return NS_OK; + } +}; + +class ReportDebuggerErrorRunnable final : public nsRunnable +{ + WorkerDebugger *mDebugger; nsString mFilename; uint32_t mLineno; nsString mMessage; public: ReportDebuggerErrorRunnable(WorkerDebugger* aDebugger, - const nsAString& aFilename, uint32_t aLineno, - const nsAString& aMessage) + const nsAString& aFilename, uint32_t aLineno, + const nsAString& aMessage) : mDebugger(aDebugger), mFilename(aFilename), mLineno(aLineno), @@ -3534,8 +3603,6 @@ public: { } - NS_DECL_THREADSAFE_ISUPPORTS - private: ~ReportDebuggerErrorRunnable() { } @@ -3549,35 +3616,20 @@ private: } }; -NS_IMPL_ISUPPORTS(ReportDebuggerErrorRunnable, nsIRunnable) - WorkerDebugger::WorkerDebugger(WorkerPrivate* aWorkerPrivate) -: mMutex("WorkerDebugger::mMutex"), - mCondVar(mMutex, "WorkerDebugger::mCondVar"), - mWorkerPrivate(aWorkerPrivate), - mIsEnabled(false), +: mWorkerPrivate(aWorkerPrivate), mIsInitialized(false) { - mWorkerPrivate->AssertIsOnParentThread(); + AssertIsOnMainThread(); } WorkerDebugger::~WorkerDebugger() { MOZ_ASSERT(!mWorkerPrivate); - MOZ_ASSERT(!mIsEnabled); if (!NS_IsMainThread()) { - nsCOMPtr mainThread; - if (NS_FAILED(NS_GetMainThread(getter_AddRefs(mainThread)))) { - NS_WARNING("Failed to proxy release of listeners, leaking instead!"); - } - for (size_t index = 0; index < mListeners.Length(); ++index) { - nsIWorkerDebuggerListener* listener = nullptr; - mListeners[index].forget(&listener); - if (NS_FAILED(NS_ProxyRelease(mainThread, listener))) { - NS_WARNING("Failed to proxy release of listener, leaking instead!"); - } + NS_ReleaseOnMainThread(mListeners[index].forget()); } } } @@ -3589,8 +3641,6 @@ WorkerDebugger::GetIsClosed(bool* aResult) { AssertIsOnMainThread(); - MutexAutoLock lock(mMutex); - *aResult = !mWorkerPrivate; return NS_OK; } @@ -3600,8 +3650,6 @@ WorkerDebugger::GetIsChrome(bool* aResult) { AssertIsOnMainThread(); - MutexAutoLock lock(mMutex); - if (!mWorkerPrivate) { return NS_ERROR_UNEXPECTED; } @@ -3615,8 +3663,6 @@ WorkerDebugger::GetIsInitialized(bool* aResult) { AssertIsOnMainThread(); - MutexAutoLock lock(mMutex); - if (!mWorkerPrivate) { return NS_ERROR_UNEXPECTED; } @@ -3630,8 +3676,6 @@ WorkerDebugger::GetParent(nsIWorkerDebugger** aResult) { AssertIsOnMainThread(); - MutexAutoLock lock(mMutex); - if (!mWorkerPrivate) { return NS_ERROR_UNEXPECTED; } @@ -3654,8 +3698,6 @@ WorkerDebugger::GetType(uint32_t* aResult) { AssertIsOnMainThread(); - MutexAutoLock lock(mMutex); - if (!mWorkerPrivate) { return NS_ERROR_UNEXPECTED; } @@ -3669,8 +3711,6 @@ WorkerDebugger::GetUrl(nsAString& aResult) { AssertIsOnMainThread(); - MutexAutoLock lock(mMutex); - if (!mWorkerPrivate) { return NS_ERROR_UNEXPECTED; } @@ -3684,8 +3724,6 @@ WorkerDebugger::GetWindow(nsIDOMWindow** aResult) { AssertIsOnMainThread(); - MutexAutoLock lock(mMutex); - if (!mWorkerPrivate) { return NS_ERROR_UNEXPECTED; } @@ -3735,19 +3773,19 @@ WorkerDebugger::Initialize(const nsAString& aURL, JSContext* aCx) { AssertIsOnMainThread(); - MutexAutoLock lock(mMutex); - - if (!mWorkerPrivate || mIsInitialized) { + if (!mWorkerPrivate) { return NS_ERROR_UNEXPECTED; } - RefPtr runnable = - new CompileDebuggerScriptRunnable(mWorkerPrivate, aURL); - if (!runnable->Dispatch(aCx)) { - return NS_ERROR_FAILURE; - } + if (!mIsInitialized) { + RefPtr runnable = + new CompileDebuggerScriptRunnable(mWorkerPrivate, aURL); + if (!runnable->Dispatch(aCx)) { + return NS_ERROR_FAILURE; + } - mIsInitialized = true; + mIsInitialized = true; + } return NS_OK; } @@ -3757,8 +3795,6 @@ WorkerDebugger::PostMessageMoz(const nsAString& aMessage, JSContext* aCx) { AssertIsOnMainThread(); - MutexAutoLock lock(mMutex); - if (!mWorkerPrivate || !mIsInitialized) { return NS_ERROR_UNEXPECTED; } @@ -3799,57 +3835,15 @@ WorkerDebugger::RemoveListener(nsIWorkerDebuggerListener* aListener) } void -WorkerDebugger::WaitIsEnabled(bool aIsEnabled) +WorkerDebugger::Close() { - MutexAutoLock lock(mMutex); - - while (mIsEnabled != aIsEnabled) { - mCondVar.Wait(); - } -} - -void -WorkerDebugger::NotifyIsEnabled(bool aIsEnabled) -{ - mMutex.AssertCurrentThreadOwns(); - - MOZ_ASSERT(mIsEnabled != aIsEnabled); - mIsEnabled = aIsEnabled; - mCondVar.Notify(); -} - -void -WorkerDebugger::Enable() -{ - AssertIsOnMainThread(); - - MutexAutoLock lock(mMutex); - - MOZ_ASSERT(mWorkerPrivate); - - NotifyIsEnabled(true); -} - -void -WorkerDebugger::Disable() -{ - AssertIsOnMainThread(); - - MutexAutoLock lock(mMutex); - MOZ_ASSERT(mWorkerPrivate); mWorkerPrivate = nullptr; - { - MutexAutoUnlock unlock(mMutex); - - nsTArray> listeners(mListeners); - for (size_t index = 0; index < listeners.Length(); ++index) { + nsTArray> listeners(mListeners); + for (size_t index = 0; index < listeners.Length(); ++index) { listeners[index]->OnClose(); - } } - - NotifyIsEnabled(false); } void @@ -3857,10 +3851,11 @@ WorkerDebugger::PostMessageToDebugger(const nsAString& aMessage) { mWorkerPrivate->AssertIsOnWorkerThread(); - nsCOMPtr runnable = - NS_NewRunnableMethodWithArg(this, - &WorkerDebugger::PostMessageToDebuggerOnMainThread, nsString(aMessage)); - NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL); + RefPtr runnable = + new PostDebuggerMessageRunnable(this, aMessage); + if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) { + NS_WARNING("Failed to post message to debugger on main thread!"); + } } void @@ -3868,14 +3863,7 @@ WorkerDebugger::PostMessageToDebuggerOnMainThread(const nsAString& aMessage) { AssertIsOnMainThread(); - nsTArray> listeners; - - { - MutexAutoLock lock(mMutex); - - listeners.AppendElements(mListeners); - } - + nsTArray> listeners(mListeners); for (size_t index = 0; index < listeners.Length(); ++index) { listeners[index]->OnMessage(aMessage); } @@ -3888,7 +3876,7 @@ WorkerDebugger::ReportErrorToDebugger(const nsAString& aFilename, { mWorkerPrivate->AssertIsOnWorkerThread(); - nsCOMPtr runnable = + RefPtr runnable = new ReportDebuggerErrorRunnable(this, aFilename, aLineno, aMessage); if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) { NS_WARNING("Failed to report error to debugger on main thread!"); @@ -3902,13 +3890,7 @@ WorkerDebugger::ReportErrorToDebuggerOnMainThread(const nsAString& aFilename, { AssertIsOnMainThread(); - nsTArray> listeners; - { - MutexAutoLock lock(mMutex); - - listeners.AppendElements(mListeners); - } - + nsTArray> listeners(mListeners); for (size_t index = 0; index < listeners.Length(); ++index) { listeners[index]->OnError(aFilename, aLineno, aMessage); } @@ -3925,6 +3907,8 @@ WorkerPrivate::WorkerPrivate(JSContext* aCx, : WorkerPrivateParent(aCx, aParent, aScriptURL, aIsChromeWorker, aWorkerType, aWorkerName, aLoadInfo) + , mDebuggerRegistered(false) + , mDebugger(nullptr) , mJSContext(nullptr) , mPRThread(nullptr) , mDebuggerEventLoopLevel(0) @@ -4125,7 +4109,6 @@ WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow, WorkerLoadInfo* aLoadInfo) { using namespace mozilla::dom::workers::scriptloader; - using mozilla::dom::indexedDB::IDBFactory; MOZ_ASSERT(aCx); MOZ_ASSERT_IF(NS_IsMainThread(), aCx == nsContentUtils::GetCurrentJSContext()); @@ -4167,11 +4150,7 @@ WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow, } if (parentStatus > Running) { - nsCOMPtr mainThread; - if (NS_FAILED(NS_GetMainThread(getter_AddRefs(mainThread))) || - NS_FAILED(NS_ProxyRelease(mainThread, loadInfo.mChannel))) { - NS_WARNING("Failed to proxy release of channel, leaking instead!"); - } + NS_ReleaseOnMainThread(loadInfo.mChannel.forget()); return NS_ERROR_FAILURE; } @@ -4186,7 +4165,7 @@ WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow, AssertIsOnMainThread(); // Make sure that the IndexedDatabaseManager is set up - NS_WARN_IF(!indexedDB::IndexedDatabaseManager::GetOrCreate()); + NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate()); nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); MOZ_ASSERT(ssm); @@ -4199,12 +4178,15 @@ WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow, return NS_ERROR_DOM_SECURITY_ERR; } - // Chrome callers (whether ChromeWorker of Worker) always get the system - // principal here as they're allowed to load anything. The script loader may - // change the principal later depending on the script uri. + // Chrome callers (whether creating a ChromeWorker or Worker) always get the + // system principal here as they're allowed to load anything. The script + // loader will refuse to run any script that does not also have the system + // principal. if (isChrome) { rv = ssm->GetSystemPrincipal(getter_AddRefs(loadInfo.mPrincipal)); NS_ENSURE_SUCCESS(rv, rv); + + loadInfo.mPrincipalIsSystem = true; } // See if we're being called from a window. @@ -4623,8 +4605,8 @@ WorkerPrivate::SetGCTimerMode(GCTimerMode aMode) mPeriodicGCTimerRunning = false; mIdleGCTimerRunning = false; - - LOG(("Worker %p canceled GC timer because %s\n", this, + LOG(WorkerLog(), + ("Worker %p canceled GC timer because %s\n", this, aMode == PeriodicTimer ? "periodic" : aMode == IdleTimer ? "idle" : "none")); @@ -4656,11 +4638,11 @@ WorkerPrivate::SetGCTimerMode(GCTimerMode aMode) "dom::workers::DummyCallback(2)"))); if (aMode == PeriodicTimer) { - LOG(("Worker %p scheduled periodic GC timer\n", this)); + LOG(WorkerLog(), ("Worker %p scheduled periodic GC timer\n", this)); mPeriodicGCTimerRunning = true; } else { - LOG(("Worker %p scheduled idle GC timer\n", this)); + LOG(WorkerLog(), ("Worker %p scheduled idle GC timer\n", this)); mIdleGCTimerRunning = true; } } @@ -4675,7 +4657,7 @@ WorkerPrivate::ShutdownGCTimers() // Always make sure the timer is canceled. MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mGCTimer->Cancel())); - LOG(("Worker %p killed the GC timer\n", this)); + LOG(WorkerLog(), ("Worker %p killed the GC timer\n", this)); mGCTimer = nullptr; mPeriodicGCTimerTarget = nullptr; @@ -4915,15 +4897,6 @@ WorkerPrivate::WaitForWorkerEvents(PRIntervalTime aInterval) // The main thread may be waiting so we must notify. mMemoryReportCondVar.Notify(); -#ifdef MOZ_NUWA_PROCESS - { - MOZ_ASSERT(mThread); - - ReentrantMonitorAutoEnter mon(mThread->ThreadStatusMonitor()); - mThread->SetIdle(); - } -#endif // MOZ_NUWA_PROCESS - // Now wait for an actual worker event. mCondVar.Wait(aInterval); @@ -5199,8 +5172,10 @@ WorkerPrivate::CancelAllTimeouts(JSContext* aCx) { AssertIsOnWorkerThread(); + LOG(TimeoutsLog(), ("Worker %p CancelAllTimeouts.\n", this)); + if (mTimerRunning) { - NS_ASSERTION(mTimer, "Huh?!"); + NS_ASSERTION(mTimer && mTimerRunnable, "Huh?!"); NS_ASSERTION(!mTimeouts.IsEmpty(), "Huh?!"); if (NS_FAILED(mTimer->Cancel())) { @@ -5224,6 +5199,7 @@ WorkerPrivate::CancelAllTimeouts(JSContext* aCx) #endif mTimer = nullptr; + mTimerRunnable = nullptr; } already_AddRefed @@ -5895,30 +5871,22 @@ WorkerPrivate::SetTimeout(JSContext* aCx, nsAutoPtr* insertedInfo = mTimeouts.InsertElementSorted(newInfo.forget(), GetAutoPtrComparator(mTimeouts)); + LOG(TimeoutsLog(), ("Worker %p has new timeout: delay=%d interval=%s\n", + this, aTimeout, aIsInterval ? "yes" : "no")); + // If the timeout we just made is set to fire next then we need to update the - // timer. - if (insertedInfo == mTimeouts.Elements()) { + // timer, unless we're currently running timeouts. + if (insertedInfo == mTimeouts.Elements() && !mRunningExpiredTimeouts) { nsresult rv; if (!mTimer) { - nsCOMPtr timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); + mTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); if (NS_FAILED(rv)) { aRv.Throw(rv); return 0; } - RefPtr runnable = new TimerRunnable(this); - - RefPtr target = - new TimerThreadEventTarget(this, runnable); - - rv = timer->SetTarget(target); - if (NS_FAILED(rv)) { - aRv.Throw(rv); - return 0; - } - - timer.swap(mTimer); + mTimerRunnable = new TimerRunnable(this); } if (!mTimerRunning) { @@ -5968,7 +5936,7 @@ WorkerPrivate::RunExpiredTimeouts(JSContext* aCx) return true; } - NS_ASSERTION(mTimer, "Must have a timer!"); + NS_ASSERTION(mTimer && mTimerRunnable, "Must have a timer!"); NS_ASSERTION(!mTimeouts.IsEmpty(), "Should have some work to do!"); bool retval = true; @@ -5978,7 +5946,13 @@ WorkerPrivate::RunExpiredTimeouts(JSContext* aCx) // We want to make sure to run *something*, even if the timer fired a little // early. Fudge the value of now to at least include the first timeout. - const TimeStamp now = std::max(TimeStamp::Now(), mTimeouts[0]->mTargetTime); + const TimeStamp actual_now = TimeStamp::Now(); + const TimeStamp now = std::max(actual_now, mTimeouts[0]->mTargetTime); + + if (now != actual_now) { + LOG(TimeoutsLog(), ("Worker %p fudged timeout by %f ms.\n", this, + (now - actual_now).ToMilliseconds())); + } AutoTArray expiredTimeouts; for (uint32_t index = 0; index < mTimeouts.Length(); index++) { @@ -6000,6 +5974,9 @@ WorkerPrivate::RunExpiredTimeouts(JSContext* aCx) continue; } + LOG(TimeoutsLog(), ("Worker %p executing timeout with original delay %f ms.\n", + this, info->mInterval.ToMilliseconds())); + // Always call JS_ReportPendingException if something fails, and if // JS_ReportPendingException returns false (i.e. uncatchable exception) then // break out of the loop. @@ -6097,16 +6074,26 @@ bool WorkerPrivate::RescheduleTimeoutTimer(JSContext* aCx) { AssertIsOnWorkerThread(); + MOZ_ASSERT(!mRunningExpiredTimeouts); NS_ASSERTION(!mTimeouts.IsEmpty(), "Should have some timeouts!"); - NS_ASSERTION(mTimer, "Should have a timer!"); + NS_ASSERTION(mTimer && mTimerRunnable, "Should have a timer!"); + + // NB: This is important! The timer may have already fired, e.g. if a timeout + // callback itself calls setTimeout for a short duration and then takes longer + // than that to finish executing. If that has happened, it's very important + // that we don't execute the event that is now pending in our event queue, or + // our code in RunExpiredTimeouts to "fudge" the timeout value will unleash an + // early timeout when we execute the event we're about to queue. + mTimer->Cancel(); double delta = (mTimeouts[0]->mTargetTime - TimeStamp::Now()).ToMilliseconds(); uint32_t delay = delta > 0 ? std::min(delta, double(UINT32_MAX)) : 0; - nsresult rv = mTimer->InitWithNamedFuncCallback( - DummyCallback, nullptr, delay, nsITimer::TYPE_ONE_SHOT, - "dom::workers::DummyCallback(3)"); + LOG(TimeoutsLog(), ("Worker %p scheduled timer for %d ms, %d pending timeouts\n", + this, delay, mTimeouts.Length())); + + nsresult rv = mTimer->InitWithCallback(mTimerRunnable, delay, nsITimer::TYPE_ONE_SHOT); if (NS_FAILED(rv)) { JS_ReportError(aCx, "Failed to start timer!"); return false; @@ -6213,17 +6200,17 @@ WorkerPrivate::GarbageCollectInternal(JSContext* aCx, bool aShrinking, JS::GCForReason(rt, GC_SHRINK, JS::gcreason::DOM_WORKER); if (!aCollectChildren) { - LOG(("Worker %p collected idle garbage\n", this)); + LOG(WorkerLog(), ("Worker %p collected idle garbage\n", this)); } } else { JS::GCForReason(rt, GC_NORMAL, JS::gcreason::DOM_WORKER); - LOG(("Worker %p collected garbage\n", this)); + LOG(WorkerLog(), ("Worker %p collected garbage\n", this)); } } else { JS_MaybeGC(aCx); - LOG(("Worker %p collected periodic garbage\n", this)); + LOG(WorkerLog(), ("Worker %p collected periodic garbage\n", this)); } if (aCollectChildren) { diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h index d57f030fc8..c00f4171c3 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -62,6 +62,7 @@ class PrincipalInfo; struct PRThread; class ReportDebuggerErrorRunnable; +class PostDebuggerMessageRunnable; BEGIN_WORKERS_NAMESPACE @@ -191,6 +192,8 @@ private: WorkerType mWorkerType; TimeStamp mCreationTimeStamp; DOMHighResTimeStamp mCreationTimeHighRes; + TimeStamp mNowBaseTimeStamp; + DOMHighResTimeStamp mNowBaseTimeHighRes; protected: // The worker is owned by its thread, which is represented here. This is set @@ -552,6 +555,16 @@ public: return mCreationTimeHighRes; } + TimeStamp NowBaseTimeStamp() const + { + return mNowBaseTimeStamp; + } + + DOMHighResTimeStamp NowBaseTime() const + { + return mNowBaseTimeHighRes; + } + nsIPrincipal* GetPrincipal() const { @@ -803,35 +816,23 @@ public: class WorkerDebugger : public nsIWorkerDebugger { friend class ::ReportDebuggerErrorRunnable; + friend class ::PostDebuggerMessageRunnable; - mozilla::Mutex mMutex; - mozilla::CondVar mCondVar; - - // Protected by mMutex WorkerPrivate* mWorkerPrivate; - bool mIsEnabled; - - // Only touched on the main thread. bool mIsInitialized; nsTArray> mListeners; public: explicit WorkerDebugger(WorkerPrivate* aWorkerPrivate); - NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_ISUPPORTS NS_DECL_NSIWORKERDEBUGGER void AssertIsOnParentThread(); void - WaitIsEnabled(bool aIsEnabled); - - void - Enable(); - - void - Disable(); + Close(); void PostMessageToDebugger(const nsAString& aMessage); @@ -844,9 +845,6 @@ private: virtual ~WorkerDebugger(); - void - NotifyIsEnabled(bool aIsEnabled); - void PostMessageToDebuggerOnMainThread(const nsAString& aMessage); @@ -876,7 +874,8 @@ class WorkerPrivate : public WorkerPrivateParent NoTimer }; - RefPtr mDebugger; + bool mDebuggerRegistered; + WorkerDebugger* mDebugger; Queue mControlQueue; Queue mDebuggerQueue; @@ -914,6 +913,7 @@ class WorkerPrivate : public WorkerPrivateParent nsTArray> mSyncLoopStack; nsCOMPtr mTimer; + nsCOMPtr mTimerRunnable; nsCOMPtr mGCTimer; nsCOMPtr mPeriodicGCTimerTarget; @@ -980,14 +980,60 @@ public: static void OverrideLoadInfoLoadGroup(WorkerLoadInfo& aLoadInfo); + bool + IsDebuggerRegistered() + { + AssertIsOnMainThread(); + + // No need to lock here since this is only ever modified by the same thread. + return mDebuggerRegistered; + } + + void + SetIsDebuggerRegistered(bool aDebuggerRegistered) + { + AssertIsOnMainThread(); + + MutexAutoLock lock(mMutex); + + MOZ_ASSERT(mDebuggerRegistered != aDebuggerRegistered); + mDebuggerRegistered = aDebuggerRegistered; + + mCondVar.Notify(); + } + + void + WaitForIsDebuggerRegistered(bool aDebuggerRegistered) + { + AssertIsOnParentThread(); + + MOZ_ASSERT(!NS_IsMainThread()); + + MutexAutoLock lock(mMutex); + + while (mDebuggerRegistered != aDebuggerRegistered) { + mCondVar.Wait(); + } + } + WorkerDebugger* Debugger() const { AssertIsOnMainThread(); + MOZ_ASSERT(mDebugger); return mDebugger; } + void + SetDebugger(WorkerDebugger* aDebugger) + { + AssertIsOnMainThread(); + + MOZ_ASSERT(mDebugger != aDebugger); + mDebugger = aDebugger; + } + void DoRunLoop(JSContext* aCx); diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp index 8e8961bde3..bb9d189db1 100644 --- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -13,6 +13,7 @@ #include "mozilla/dom/DedicatedWorkerGlobalScopeBinding.h" #include "mozilla/dom/Fetch.h" #include "mozilla/dom/FunctionBinding.h" +#include "mozilla/dom/IDBFactory.h" #include "mozilla/dom/ImageBitmap.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/PromiseWorkerProxy.h" @@ -21,7 +22,6 @@ #include "mozilla/dom/WorkerDebuggerGlobalScopeBinding.h" #include "mozilla/dom/WorkerGlobalScopeBinding.h" #include "mozilla/dom/cache/CacheStorage.h" -#include "mozilla/dom/indexedDB/IDBFactory.h" #include "mozilla/Services.h" #include "nsServiceManagerUtils.h" @@ -53,7 +53,6 @@ using namespace mozilla::dom; USING_WORKERS_NAMESPACE using mozilla::dom::cache::CacheStorage; -using mozilla::dom::indexedDB::IDBFactory; using mozilla::ipc::PrincipalInfo; BEGIN_WORKERS_NAMESPACE @@ -326,6 +325,7 @@ WorkerGlobalScope::Dump(const Optional& aString) const NS_ConvertUTF16toUTF8 str(aString.Value()); + MOZ_LOG(nsContentUtils::DOMDumpLog(), LogLevel::Debug, ("[Worker.Dump] %s", str.get())); #ifdef ANDROID __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", str.get()); #endif diff --git a/dom/workers/WorkerScope.h b/dom/workers/WorkerScope.h index b4a6d1de94..f698b4e3a7 100644 --- a/dom/workers/WorkerScope.h +++ b/dom/workers/WorkerScope.h @@ -19,6 +19,7 @@ namespace dom { class Console; class Function; +class IDBFactory; class Promise; class RequestOrUSVString; class ServiceWorkerRegistrationWorkerThread; @@ -28,13 +29,6 @@ namespace cache { class CacheStorage; } // namespace cache - -namespace indexedDB { - -class IDBFactory; - -} // namespace indexedDB - } // namespace dom } // namespace mozilla @@ -50,7 +44,7 @@ class WorkerGlobalScope : public DOMEventTargetHelper, public nsIGlobalObject, public nsSupportsWeakReference { - typedef mozilla::dom::indexedDB::IDBFactory IDBFactory; + typedef mozilla::dom::IDBFactory IDBFactory; RefPtr mConsole; RefPtr mLocation; diff --git a/dom/workers/Workers.h b/dom/workers/Workers.h index d27129059d..92f3842918 100644 --- a/dom/workers/Workers.h +++ b/dom/workers/Workers.h @@ -240,6 +240,7 @@ struct WorkerLoadInfo already_AddRefed GetAnyLiveTabChild(); nsCOMPtr mLoadContext; + nsCOMPtr mOuterRequestor; // Array of weak references to nsITabChild. We do not want to keep TabChild // actors alive for long after their ActorDestroy() methods are called. diff --git a/dom/workers/test/crashtests/1228456.html b/dom/workers/test/crashtests/1228456.html new file mode 100644 index 0000000000..6d1f0f0a72 --- /dev/null +++ b/dom/workers/test/crashtests/1228456.html @@ -0,0 +1,14 @@ + + + diff --git a/dom/workers/test/crashtests/779707.html b/dom/workers/test/crashtests/779707.html new file mode 100644 index 0000000000..97a8113dab --- /dev/null +++ b/dom/workers/test/crashtests/779707.html @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/dom/workers/test/crashtests/crashtests.list b/dom/workers/test/crashtests/crashtests.list index 3db313da2a..a7518d3c2f 100644 --- a/dom/workers/test/crashtests/crashtests.list +++ b/dom/workers/test/crashtests/crashtests.list @@ -1,3 +1,5 @@ +load 779707.html load 943516.html -load 1158031.html load 1153636.html +load 1158031.html +load 1228456.html diff --git a/extensions/gio/nsGIOProtocolHandler.cpp b/extensions/gio/nsGIOProtocolHandler.cpp index 71db2d5da3..584fc2ef78 100644 --- a/extensions/gio/nsGIOProtocolHandler.cpp +++ b/extensions/gio/nsGIOProtocolHandler.cpp @@ -615,17 +615,10 @@ nsGIOInputStream::Close() mDirListPtr = nullptr; } - if (mChannel) - { - nsresult rv = NS_OK; + if (mChannel) { + NS_ReleaseOnMainThread(dont_AddRef(mChannel)); - nsCOMPtr thread = do_GetMainThread(); - if (thread) - rv = NS_ProxyRelease(thread, mChannel); - - NS_ASSERTION(thread && NS_SUCCEEDED(rv), "leaking channel reference"); mChannel = nullptr; - (void) rv; } mSpec.Truncate(); // free memory diff --git a/extensions/spellcheck/src/mozPersonalDictionary.cpp b/extensions/spellcheck/src/mozPersonalDictionary.cpp index a0e92fa558..f83b97d19a 100644 --- a/extensions/spellcheck/src/mozPersonalDictionary.cpp +++ b/extensions/spellcheck/src/mozPersonalDictionary.cpp @@ -65,16 +65,7 @@ public: mDict->SyncLoad(); // Release the dictionary on the main thread - mozPersonalDictionary *dict; - mDict.forget(&dict); - - nsCOMPtr mainThread = do_GetMainThread(); - if (mainThread) { - NS_ProxyRelease(mainThread, static_cast(dict)); - } else { - // It's better to leak the dictionary than to release it on a wrong thread - NS_WARNING("Cannot get main thread, leaking mozPersonalDictionary."); - } + NS_ReleaseOnMainThread(mDict.forget()); return NS_OK; } @@ -145,16 +136,8 @@ public: } // Release the dictionary on the main thread. - mozPersonalDictionary *dict; - mDict.forget(&dict); + NS_ReleaseOnMainThread(mDict.forget()); - nsCOMPtr mainThread = do_GetMainThread(); - if (mainThread) { - NS_ProxyRelease(mainThread, static_cast(dict)); - } else { - // It's better to leak the dictionary than to release it on a wrong thread. - NS_WARNING("Cannot get main thread, leaking mozPersonalDictionary."); - } return NS_OK; } diff --git a/image/Decoder.cpp b/image/Decoder.cpp index cbe7ce9fb2..c8662f2eae 100644 --- a/image/Decoder.cpp +++ b/image/Decoder.cpp @@ -55,16 +55,7 @@ Decoder::~Decoder() if (mImage && !NS_IsMainThread()) { // Dispatch mImage to main thread to prevent it from being destructed by the // decode thread. - nsCOMPtr mainThread = do_GetMainThread(); - NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread!"); - if (mainThread) { - // Handle ambiguous nsISupports inheritance. - RasterImage* rawImg = nullptr; - mImage.swap(rawImg); - DebugOnly rv = - NS_ProxyRelease(mainThread, NS_ISUPPORTS_CAST(ImageResource*, rawImg)); - MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to proxy release to main thread"); - } + NS_ReleaseOnMainThread(mImage.forget()); } } diff --git a/image/decoders/icon/mac/nsIconChannelCocoa.mm b/image/decoders/icon/mac/nsIconChannelCocoa.mm index e6208d7be7..69968a895f 100644 --- a/image/decoders/icon/mac/nsIconChannelCocoa.mm +++ b/image/decoders/icon/mac/nsIconChannelCocoa.mm @@ -38,12 +38,7 @@ nsIconChannel::nsIconChannel() nsIconChannel::~nsIconChannel() { if (mLoadInfo) { - nsCOMPtr mainThread; - NS_GetMainThread(getter_AddRefs(mainThread)); - - nsILoadInfo* forgetableLoadInfo; - mLoadInfo.forget(&forgetableLoadInfo); - NS_ProxyRelease(mainThread, forgetableLoadInfo, false); + NS_ReleaseOnMainThread(mLoadInfo.forget()); } } diff --git a/image/decoders/icon/win/nsIconChannel.cpp b/image/decoders/icon/win/nsIconChannel.cpp index 9fa2a7750d..9ddcbbc488 100644 --- a/image/decoders/icon/win/nsIconChannel.cpp +++ b/image/decoders/icon/win/nsIconChannel.cpp @@ -76,12 +76,7 @@ nsIconChannel::nsIconChannel() nsIconChannel::~nsIconChannel() { if (mLoadInfo) { - nsCOMPtr mainThread; - NS_GetMainThread(getter_AddRefs(mainThread)); - - nsILoadInfo* forgetableLoadInfo; - mLoadInfo.forget(&forgetableLoadInfo); - NS_ProxyRelease(mainThread, forgetableLoadInfo, false); + NS_ReleaseOnMainThread(mLoadInfo.forget()); } } diff --git a/ipc/glue/MessageLink.cpp b/ipc/glue/MessageLink.cpp index 7b30221912..75e1250008 100644 --- a/ipc/glue/MessageLink.cpp +++ b/ipc/glue/MessageLink.cpp @@ -13,10 +13,10 @@ #ifdef MOZ_NUWA_PROCESS #include "ipc/Nuwa.h" #include "mozilla/Preferences.h" -#include "mozilla/dom/PContent.h" +#include "mozilla/dom/ContentParent.h" #include "mozilla/dom/PNuwa.h" #include "mozilla/hal_sandbox/PHal.h" -#if defined(DEBUG) || defined(ENABLE_TESTS) +#ifdef DEBUG #include "jsprf.h" extern "C" char* PrintJSStack(); #endif @@ -144,7 +144,7 @@ ProcessLink::Open(mozilla::ipc::Transport* aTransport, MessageLoop *aIOLoop, Sid } #ifdef MOZ_NUWA_PROCESS - if (IsNuwaProcess() && + if (IsNuwaProcess() && NS_IsMainThread() && Preferences::GetBool("dom.ipc.processPrelaunch.testMode")) { // The pref value is turned on in a deadlock test against the Nuwa // process. The sleep here makes it easy to trigger the deadlock @@ -202,18 +202,17 @@ ProcessLink::SendMessage(Message *msg) } } +#if defined(DEBUG) // Nuwa to parent: check whether we are currently blocked. if (IsNuwaProcess() && mIsBlocked) { -#if defined(ENABLE_TESTS) || defined(DEBUG) char* jsstack = PrintJSStack(); printf_stderr("Fatal error: sending a message to the chrome process" "with a blocked IPC channel from \n%s", jsstack ? jsstack : ""); JS_smprintf_free(jsstack); MOZ_CRASH(); -#endif } - +#endif #endif mIOLoop->PostTask( diff --git a/js/src/builtin/AtomicsObject.cpp b/js/src/builtin/AtomicsObject.cpp index 1d9b56987c..df0b4ae96a 100644 --- a/js/src/builtin/AtomicsObject.cpp +++ b/js/src/builtin/AtomicsObject.cpp @@ -84,6 +84,13 @@ ReportOutOfRange(JSContext* cx) return false; } +static bool +ReportCannotWait(JSContext* cx) +{ + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_ATOMICS_WAIT_NOT_ALLOWED); + return false; +} + static bool GetSharedTypedArray(JSContext* cx, HandleValue v, MutableHandle viewp) @@ -773,6 +780,9 @@ js::atomics_futexWait(JSContext* cx, unsigned argc, Value* vp) timeout_ms = 0; } + if (!rt->fx.canWait()) + return ReportCannotWait(cx); + // This lock also protects the "waiters" field on SharedArrayRawBuffer, // and it provides the necessary memory fence. AutoLockFutexAPI lock; @@ -1054,6 +1064,7 @@ bool js::FutexRuntime::wait(JSContext* cx, double timeout_ms, AtomicsObject::FutexWaitResult* result) { MOZ_ASSERT(&cx->runtime()->fx == this); + MOZ_ASSERT(cx->runtime()->fx.canWait()); MOZ_ASSERT(lockHolder_ == PR_GetCurrentThread()); MOZ_ASSERT(state_ == Idle || state_ == WaitingInterrupted); diff --git a/js/src/builtin/AtomicsObject.h b/js/src/builtin/AtomicsObject.h index eaf34720af..64745a379c 100644 --- a/js/src/builtin/AtomicsObject.h +++ b/js/src/builtin/AtomicsObject.h @@ -104,6 +104,16 @@ public: bool isWaiting(); + // If canWait() returns false (the default) then futexWait is disabled + // on the runtime to which the FutexRuntime belongs. + bool canWait() { + return canWait_; + } + + void setCanWait(bool flag) { + canWait_ = flag; + } + private: enum FutexState { Idle, // We are not waiting or woken @@ -133,6 +143,9 @@ public: // Null or the thread holding the lock. static mozilla::Atomic lockHolder_; #endif + + // A flag that controls whether waiting is allowed. + bool canWait_; }; JSObject* diff --git a/js/src/jit-test/tests/baseline/bug1182866.js b/js/src/jit-test/tests/baseline/bug1182866.js new file mode 100644 index 0000000000..17a59c6f28 --- /dev/null +++ b/js/src/jit-test/tests/baseline/bug1182866.js @@ -0,0 +1,17 @@ +// |jit-test| error: ReferenceError + +with(7) { + function f() { + if (i == 15) { + g(); + } + const x = 42; + function g() { + return x; + } + return g; + } +} +for (var i = 0; i < 99; i++) { + assertEq(f()(), 42); +} diff --git a/js/src/jit-test/tests/debug/bug1242798.js b/js/src/jit-test/tests/debug/bug1242798.js new file mode 100644 index 0000000000..8e4b74b228 --- /dev/null +++ b/js/src/jit-test/tests/debug/bug1242798.js @@ -0,0 +1,14 @@ + +var g = newGlobal(); +var dbg = new Debugger(g); +g.eval("" + function f(c) { + if (c == 0) + return; + if (c == 2) + debugger; + f(c-1); + for (var i = 0; i < 100; i++) + Debugger += newGlobal('#15: myObj.parseFloat !== parseFloat'); +}); +dbg.onDebuggerStatement = function (frame) {}; +g.eval("f(2)"); diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index e404d521b8..312f1e4446 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -142,7 +142,8 @@ DoWarmUpCounterFallbackOSR(JSContext* cx, BaselineFrame* frame, ICWarmUpCounter_ return false; if (!script->hasIonScript() || script->ionScript()->osrPc() != pc || - script->ionScript()->bailoutExpected()) + script->ionScript()->bailoutExpected() || + frame->isDebuggee()) { return true; } @@ -1403,7 +1404,7 @@ TryAttachNativeGetAccessorElemStub(JSContext* cx, HandleScript script, jsbytecod RootedObject baseHolder(cx); if (!EffectlesslyLookupProperty(cx, obj, id, &baseHolder, &shape)) return false; - if (!baseHolder || baseHolder->isNative()) + if (!baseHolder || !baseHolder->isNative()) return true; HandleNativeObject holder = baseHolder.as(); @@ -2700,6 +2701,11 @@ DoSetElemFallback(JSContext* cx, BaselineFrame* frame, ICSetElem_Fallback* stub_ return false; } + // Don't try to attach stubs that wish to be hidden. We don't know how to + // have different enumerability in the stubs for the moment. + if (op == JSOP_INITHIDDENELEM) + return true; + // Overwrite the object on the stack (pushed for the decompiler) with the rhs. MOZ_ASSERT(stack[2] == objv); stack[2] = rhs; @@ -4228,7 +4234,11 @@ ICGetName_Scope::Compiler::generateStubCode(MacroAssembler& masm) } masm.load32(Address(ICStubReg, ICGetName_Scope::offsetOfOffset()), scratch); - masm.loadValue(BaseIndex(scope, scratch, TimesOne), R0); + + // GETNAME needs to check for uninitialized lexicals. + BaseIndex slot(scope, scratch, TimesOne); + masm.branchTestMagic(Assembler::Equal, slot, &failure); + masm.loadValue(slot, R0); // Enter type monitor IC to type-check result. EmitEnterTypeMonitorIC(masm); diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h index 1491fdbfd5..76a6c29894 100644 --- a/js/src/jit/BaselineIC.h +++ b/js/src/jit/BaselineIC.h @@ -513,7 +513,7 @@ class ICGetElem_Fallback : public ICMonitoredFallbackStub class ICGetElemNativeStub : public ICMonitoredStub { public: - enum AccessType { FixedSlot = 0, DynamicSlot, UnboxedProperty, NativeGetter, ScriptedGetter }; + enum AccessType { FixedSlot = 0, DynamicSlot, UnboxedProperty, NativeGetter, ScriptedGetter, NumAccessTypes }; protected: HeapReceiverGuard receiverGuard_; @@ -522,11 +522,13 @@ class ICGetElemNativeStub : public ICMonitoredStub static const uint16_t NEEDS_ATOMIZE_MASK = 0x1; static const unsigned ACCESSTYPE_SHIFT = 1; - static const uint16_t ACCESSTYPE_MASK = 0x3; + static const uint16_t ACCESSTYPE_MASK = 0x7; - static const unsigned ISSYMBOL_SHIFT = 3; + static const unsigned ISSYMBOL_SHIFT = 4; static const uint16_t ISSYMBOL_MASK = 0x1; + static_assert(ACCESSTYPE_MASK >= NumAccessTypes, "ACCESSTYPE_MASK must cover all possible AccessType values"); + ICGetElemNativeStub(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard, AccessType acctype, bool needsAtomize, bool isSymbol); diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 08be94b487..0f6d634e27 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -510,6 +510,12 @@ JS_SetRuntimePrivate(JSRuntime* rt, void* data) rt->data = data; } +JS_PUBLIC_API(void) +JS_SetFutexCanWait(JSRuntime* rt) +{ + rt->fx.setCanWait(true); +} + static void StartRequest(JSContext* cx) { diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 8ab2eadf1d..2a271d82a2 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1005,6 +1005,9 @@ JS_BeginRequest(JSContext* cx); extern JS_PUBLIC_API(void) JS_EndRequest(JSContext* cx); +extern JS_PUBLIC_API(void) +JS_SetFutexCanWait(JSRuntime* rt); + namespace js { void diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index d55dbbc548..e0b058b420 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -2746,6 +2746,7 @@ WorkerMain(void* arg) sr->isWorker = true; JS_SetRuntimePrivate(rt, sr.get()); + JS_SetFutexCanWait(rt); JS_SetErrorReporter(rt, my_ErrorReporter); SetWorkerRuntimeOptions(rt); @@ -6924,6 +6925,8 @@ main(int argc, char** argv, char** envp) return 1; JS_SetRuntimePrivate(rt, sr.get()); + // Waiting is allowed on the shell's main thread, for now. + JS_SetFutexCanWait(rt); JS_SetErrorReporter(rt, my_ErrorReporter); JS::SetOutOfMemoryCallback(rt, my_OOMCallback, nullptr); if (!SetRuntimeOptions(rt, op)) diff --git a/js/xpconnect/src/Sandbox.cpp b/js/xpconnect/src/Sandbox.cpp index edbfb6e9e0..f8703ae7ca 100644 --- a/js/xpconnect/src/Sandbox.cpp +++ b/js/xpconnect/src/Sandbox.cpp @@ -32,7 +32,7 @@ #include "mozilla/dom/BlobBinding.h" #include "mozilla/dom/cache/CacheStorage.h" #include "mozilla/dom/CSSBinding.h" -#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h" +#include "mozilla/dom/IndexedDatabaseManager.h" #include "mozilla/dom/Fetch.h" #include "mozilla/dom/FileBinding.h" #include "mozilla/dom/PromiseBinding.h" @@ -54,7 +54,7 @@ using namespace JS; using namespace xpc; using mozilla::dom::DestroyProtoAndIfaceCache; -using mozilla::dom::indexedDB::IndexedDatabaseManager; +using mozilla::dom::IndexedDatabaseManager; NS_IMPL_CYCLE_COLLECTION_CLASS(SandboxPrivate) diff --git a/js/xpconnect/tests/chrome/test_xrayToJS.xul b/js/xpconnect/tests/chrome/test_xrayToJS.xul index 169ac1dd3e..692dca729a 100644 --- a/js/xpconnect/tests/chrome/test_xrayToJS.xul +++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul @@ -135,7 +135,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 // We could also test DataView and Iterator here for completeness, but it's // more trouble than it's worth. - SimpleTest.finish(); } @@ -148,6 +147,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 var isNightlyBuild = version.endsWith("a1"); var isReleaseBuild = !version.includes("a"); var gPrototypeProperties = {}; + var gConstructorProperties = {}; + function constructorProps(arr) { + // Some props live on all constructors + return arr.concat(["prototype", "length", "name"]); + } gPrototypeProperties['Date'] = ["getTime", "getTimezoneOffset", "getYear", "getFullYear", "getUTCFullYear", "getMonth", "getUTCMonth", "getDate", "getUTCDate", "getDay", "getUTCDay", @@ -160,30 +164,47 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 "toLocaleDateString", "toLocaleTimeString", "toDateString", "toTimeString", "toISOString", "toJSON", "toSource", "toString", "valueOf", "constructor", "toGMTString", Symbol.toPrimitive]; + gConstructorProperties['Date'] = constructorProps(["UTC", "parse", "now"]); gPrototypeProperties['Object'] = ["constructor", "toSource", "toString", "toLocaleString", "valueOf", "watch", "unwatch", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__", "__proto__"]; + gConstructorProperties['Object'] = + constructorProps(["setPrototypeOf", "getOwnPropertyDescriptor", "keys", + "is", "defineProperty", "defineProperties", "create", + "getOwnPropertyNames", "getOwnPropertySymbols", + "preventExtensions", "freeze", "isFrozen", "seal", + "isSealed", "assign", "getPrototypeOf", "values", + "entries", "isExtensible"]) gPrototypeProperties['Array'] = ["length", "toSource", "toString", "toLocaleString", "join", "reverse", "sort", "push", "pop", "shift", "unshift", "splice", "concat", "slice", "lastIndexOf", "indexOf", "includes", "forEach", "map", "reduce", "reduceRight", "filter", "some", "every", "find", "findIndex", "copyWithin", "fill", Symbol.iterator, "entries", "keys", "constructor"]; + gConstructorProperties['Array'] = + constructorProps(["join", "reverse", "sort", "push", "pop", "shift", + "unshift", "splice", "concat", "slice", "isArray", + "lastIndexOf", "indexOf", "forEach", "map", "filter", + "every", "some", "reduce", "reduceRight", "from", "of"]); for (var c of typedArrayClasses) { gPrototypeProperties[c] = ["constructor", "BYTES_PER_ELEMENT"]; + gConstructorProperties[c] = constructorProps(["BYTES_PER_ELEMENT"]); } gPrototypeProperties['TypedArray'] = ["length", "buffer", "byteLength", "byteOffset", Symbol.iterator, "subarray", "set", "copyWithin", "find", "findIndex", "forEach","indexOf", "lastIndexOf", "includes", "reverse", "join", "every", "some", "reduce", "reduceRight", "entries", "keys", "values", "slice", "map", "filter"]; + // There is no TypedArray constructor, looks like. + is(window.TypedArray, undefined, "If this ever changes, add to this test!"); for (var c of errorObjectClasses) { gPrototypeProperties[c] = ["constructor", "name", // We don't actually resolve these empty data properties // onto the Xray prototypes, but we list them here to make // the test happy. "lineNumber", "columnNumber", "fileName", "message", "stack"]; + gConstructorProperties[c] = constructorProps([]); } // toString and toSource only live on the parent proto (Error.prototype). gPrototypeProperties['Error'].push('toString'); @@ -192,14 +213,17 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 gPrototypeProperties['Function'] = ["constructor", "toSource", "toString", "apply", "call", "bind", "isGenerator", "length", "name", "arguments", "caller"]; - gPrototypeProperties['Function'] = - ["constructor", "toSource", "toString", "apply", "call", "bind", - "isGenerator", "length", "name", "arguments", "caller"]; + gConstructorProperties['Function'] = constructorProps([]) gPrototypeProperties['RegExp'] = ["constructor", "toSource", "toString", "compile", "exec", "test", "flags", "global", "ignoreCase", "multiline", "source", "sticky", "unicode", "lastIndex"]; + gConstructorProperties['RegExp'] = + constructorProps(["input", "multiline", "lastMatch", "lastParen", + "leftContext", "rightContext", "$1", "$2", "$3", "$4", + "$5", "$6", "$7", "$8", "$9", "$_", "$*", "$&", "$+", + "$`", "$'"]) // Sort an array that may contain symbols as well as strings. function sortProperties(arr) { @@ -213,6 +237,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 // again to sort them). for (var c of Object.keys(gPrototypeProperties)) sortProperties(gPrototypeProperties[c]); + for (var c of Object.keys(gConstructorProperties)) + sortProperties(gConstructorProperties[c]); function filterOut(array, props) { return array.filter(p => props.indexOf(p) == -1); @@ -265,7 +291,57 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 } } - function testXray(classname, xray, xray2, propsToSkip) { + function testCtorCallables(ctorCallables, xrayCtor, localCtor) { + for (let name of ctorCallables) { + // Don't try to test Function.prototype, since that is in fact a callable + // but doesn't really do the things we expect callables to do here + // (e.g. it's in the wrong global, since it gets Xrayed itself). + if (name == "prototype" && localCtor.name == "Function") { + continue; + } + info(`Running tests for property: ${localCtor.name}.${name}`); + // Test both methods and getter properties. + function lookupCallable(obj) { + let desc = null; + do { + desc = Object.getOwnPropertyDescriptor(obj, name); + obj = Object.getPrototypeOf(obj); + } while (!desc); + return desc.get || desc.value; + }; + + ok(xrayCtor.hasOwnProperty(name), "ctor should have the property as own"); + let method = lookupCallable(xrayCtor); + is(typeof method, 'function', "Methods from ctor Xrays are functions"); + is(global(method), window, "Methods from ctor Xrays are local"); + ok(method instanceof Function, + "instanceof works on methods from ctor Xrays"); + is(lookupCallable(xrayCtor), method, + "Holder caching works properly on ctors"); + let local = lookupCallable(localCtor); + is(method.length, local.length, + "Function.length identical for method from ctor"); + // Don't try to do the return-value check on Date.now(), since there is + // absolutely no reason it should return the same value each time. + // + // Also don't try to do the return-value check on Regexp.lastMatch and + // Regexp["$&"] (which are aliases), because they get state off the global + // they live in, as far as I can tell, so testing them over Xrays will be + // wrong: on the Xray they will actaully get the lastMatch of _our_ + // global, not the Xrayed one. + if (method.length == 0 && + !(localCtor.name == "Date" && name == "now") && + !(localCtor.name == "RegExp" && (name == "lastMatch" || name == "$&"))) { + is(method.call(xrayCtor) + "", local.call(xrayCtor) + "", + "Xray and local method results stringify identically on constructors"); + is(method.call(xrayCtor) + "", + lookupCallable(xrayCtor.wrappedJSObject).call(xrayCtor.wrappedJSObject) + "", + "Xray and waived method results stringify identically"); + } + } + } + + function testXray(classname, xray, xray2, propsToSkip, ctorPropsToSkip = []) { propsToSkip = propsToSkip || []; let xrayProto = Object.getPrototypeOf(xray); let localProto = window[classname].prototype; @@ -294,13 +370,39 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 protoProps.toSource(), "getOwnPropertyNames works"); is(Object.getOwnPropertySymbols(xrayProto).map(uneval).sort().toSource(), gPrototypeProperties[classname].filter(id => typeof id !== "string").map(uneval).sort().toSource(), - protoProps.toSource(), "getOwnPropertySymbols works"); + "getOwnPropertySymbols works"); is(xrayProto.constructor, iwin[classname], "constructor property works"); xrayProto.expando = 42; is(xray.expando, 42, "Xrayed instances see proto expandos"); is(xray2.expando, 42, "Xrayed instances see proto expandos"); + + // Now test constructors + localCtor = window[classname]; + xrayCtor = xrayProto.constructor; + // We already checked that this is the same as iwin[classname] + + let desiredCtorProps = + Object.getOwnPropertyNames(localCtor).sort(); + is(desiredCtorProps.toSource(), + gConstructorProperties[classname].filter(id => typeof id === "string").toSource(), + "A property on the " + classname + + " constructor has changed! You need a security audit from an XPConnect peer"); + is(Object.getOwnPropertySymbols(localCtor).map(uneval).sort().toSource(), + gConstructorProperties[classname].filter(id => typeof id !== "string").map(uneval).sort().toSource(), + "A symbol-keyed property on the " + classname + + " constructor has been changed! You need a security audit from an XPConnect peer"); + + let ctorProps = filterOut(desiredCtorProps, ctorPropsToSkip); + let ctorCallables = ctorProps.filter(name => propertyIsGetter(localCtor, name, classname) || + typeof localCtor[name] == 'function'); + testCtorCallables(ctorCallables, xrayCtor, localCtor); + is(Object.getOwnPropertyNames(xrayCtor).sort().toSource(), + ctorProps.toSource(), "getOwnPropertyNames works on Xrayed ctors"); + is(Object.getOwnPropertySymbols(xrayCtor).map(uneval).sort().toSource(), + gConstructorProperties[classname].filter(id => typeof id !== "string").map(uneval).sort().toSource(), + "getOwnPropertySymbols works on Xrayed ctors"); } function testDate() { @@ -357,7 +459,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 // |own| data property. So we add it to the ignore list here, and check it // separately. let propsToSkip = ['length']; - testXray('Array', new iwin.Array(20), new iwin.Array(), propsToSkip); + // On the constructor, we want to skip all the non-standard "generic" + // functions. We're trying to remove them anyway; no point doing extra work + // to expose them over Xrays. + let ctorPropsToSkip = ["join", "reverse", "sort", "push", "pop", "shift", + "unshift", "splice", "concat", "slice"]; + testXray('Array', new iwin.Array(20), new iwin.Array(), propsToSkip, + ctorPropsToSkip); let symbolProps = ''; uniqueSymbol = iwin.eval('var uniqueSymbol = Symbol("uniqueSymbol"); uniqueSymbol'); @@ -593,7 +701,16 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 } function testRegExp() { - testXray('RegExp', new iwin.RegExp('foo'), new iwin.RegExp()); + // RegExp statics are very weird, and in particular RegExp has static + // properties that have to do with the last regexp execution in the global. + // Xraying those makes no sense, so we just skip constructor properties for + // RegExp xrays. + let ctorPropsToSkip = ["input", "multiline", "lastMatch", "lastParen", + "leftContext", "rightContext", "$1", "$2", "$3", + "$4", "$5", "$6", "$7", "$8", "$9", "$_", "$*", "$&", + "$+", "$`", "$'"]; + testXray('RegExp', new iwin.RegExp('foo'), new iwin.RegExp(), [], + ctorPropsToSkip); // Test the self-hosted |flags| property, toString, and toSource. for (var flags of ["", "g", "i", "m", "y", "gimy"]) { diff --git a/js/xpconnect/wrappers/XrayWrapper.cpp b/js/xpconnect/wrappers/XrayWrapper.cpp index f627dfcdb6..3dcb1bf51c 100644 --- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -373,6 +373,112 @@ bool JSXrayTraits::getOwnPropertyFromTargetIfSafe(JSContext* cx, return true; } +// Returns true on success (in the JSAPI sense), false on failure. If true is +// returned, desc.object() will indicate whether we actually resolved +// the property. +// +// id is the property id we're looking for. +// holder is the object to define the property on. +// fs is the relevant JSFunctionSpec*. +// ps is the relevant JSPropertySpec*. +// desc is the descriptor we're resolving into. +static bool +TryResolvePropertyFromSpecs(JSContext* cx, HandleId id, HandleObject holder, + const JSFunctionSpec* fs, + const JSPropertySpec* ps, + MutableHandle desc) +{ + // Scan through the functions. + const JSFunctionSpec* fsMatch = nullptr; + for ( ; fs && fs->name; ++fs) { + if (PropertySpecNameEqualsId(fs->name, id)) { + fsMatch = fs; + break; + } + } + if (fsMatch) { + // Generate an Xrayed version of the method. + RootedFunction fun(cx, JS::NewFunctionFromSpec(cx, fsMatch, id)); + if (!fun) + return false; + + // The generic Xray machinery only defines non-own properties of the target on + // the holder. This is broken, and will be fixed at some point, but for now we + // need to cache the value explicitly. See the corresponding call to + // JS_GetOwnPropertyDescriptorById at the top of + // JSXrayTraits::resolveOwnProperty. + RootedObject funObj(cx, JS_GetFunctionObject(fun)); + return JS_DefinePropertyById(cx, holder, id, funObj, 0) && + JS_GetOwnPropertyDescriptorById(cx, holder, id, desc); + } + + // Scan through the properties. + const JSPropertySpec* psMatch = nullptr; + for ( ; ps && ps->name; ++ps) { + if (PropertySpecNameEqualsId(ps->name, id)) { + psMatch = ps; + break; + } + } + if (psMatch) { + desc.value().setUndefined(); + RootedFunction getterObj(cx); + RootedFunction setterObj(cx); + unsigned flags = psMatch->flags; + if (psMatch->isSelfHosted()) { + getterObj = JS::GetSelfHostedFunction(cx, psMatch->getter.selfHosted.funname, id, 0); + if (!getterObj) + return false; + desc.setGetterObject(JS_GetFunctionObject(getterObj)); + if (psMatch->setter.selfHosted.funname) { + MOZ_ASSERT(flags & JSPROP_SETTER); + setterObj = JS::GetSelfHostedFunction(cx, psMatch->setter.selfHosted.funname, id, 0); + if (!setterObj) + return false; + desc.setSetterObject(JS_GetFunctionObject(setterObj)); + } + } else { + desc.setGetter(JS_CAST_NATIVE_TO(psMatch->getter.native.op, + JSGetterOp)); + desc.setSetter(JS_CAST_NATIVE_TO(psMatch->setter.native.op, + JSSetterOp)); + } + desc.setAttributes(flags); + + // The generic Xray machinery only defines non-own properties on the holder. + // This is broken, and will be fixed at some point, but for now we need to + // cache the value explicitly. See the corresponding call to + // JS_GetPropertyById at the top of JSXrayTraits::resolveOwnProperty. + // + // Note also that the public-facing API here doesn't give us a way to + // pass along JITInfo. It's probably ok though, since Xrays are already + // pretty slow. + return JS_DefinePropertyById(cx, holder, id, + UndefinedHandleValue, + // This particular descriptor, unlike most, + // actually stores JSNatives directly, + // since we just set it up. Do NOT pass + // JSPROP_PROPOP_ACCESSORS here! + desc.attributes(), + JS_PROPERTYOP_GETTER(desc.getter()), + JS_PROPERTYOP_SETTER(desc.setter())) && + JS_GetOwnPropertyDescriptorById(cx, holder, id, desc); + } + + return true; +} + +static bool +ShouldResolveStaticProperties(JSProtoKey key) +{ + // Don't try to resolve static properties on RegExp, because they + // have issues. In particular, some of them grab state off the + // global of the RegExp constructor that describes the last regexp + // evaluation in that global, which is not a useful thing to do + // over Xrays. + return key != JSProto_RegExp; +} + bool JSXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper, HandleObject wrapper, HandleObject holder, @@ -385,6 +491,18 @@ JSXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper, if (!ok || desc.object()) return ok; + // The non-HasPrototypes semantics implemented by traditional Xrays are kind + // of broken with respect to |own|-ness and the holder. The common code + // muddles through by only checking the holder for non-|own| lookups, but + // that doesn't work for us. So we do an explicit holder check here, and hope + // that this mess gets fixed up soon. + if (!JS_GetOwnPropertyDescriptorById(cx, holder, id, desc)) + return false; + if (desc.object()) { + desc.object().set(wrapper); + return true; + } + RootedObject target(cx, getTargetObject(wrapper)); JSProtoKey key = getProtoKey(holder); if (!isPrototype(holder)) { @@ -431,22 +549,44 @@ JSXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper, RootedString fname(cx, JS_GetFunctionId(JS_GetObjectFunction(target))); FillPropertyDescriptor(desc, wrapper, JSPROP_PERMANENT | JSPROP_READONLY, fname ? StringValue(fname) : JS_GetEmptyStringValue(cx)); - } else if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_PROTOTYPE)) { - // Handle the 'prototype' property to make xrayedGlobal.StandardClass.prototype work. + } else { + // Look for various static properties/methods and the + // 'prototype' property. JSProtoKey standardConstructor = constructorFor(holder); if (standardConstructor != JSProto_Null) { - RootedObject standardProto(cx); - { - JSAutoCompartment ac(cx, target); - if (!JS_GetClassPrototype(cx, standardConstructor, &standardProto)) + // Handle the 'prototype' property to make + // xrayedGlobal.StandardClass.prototype work. + if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_PROTOTYPE)) { + RootedObject standardProto(cx); + { + JSAutoCompartment ac(cx, target); + if (!JS_GetClassPrototype(cx, standardConstructor, &standardProto)) + return false; + MOZ_ASSERT(standardProto); + } + + if (!JS_WrapObject(cx, &standardProto)) return false; - MOZ_ASSERT(standardProto); + FillPropertyDescriptor(desc, wrapper, JSPROP_PERMANENT | JSPROP_READONLY, + ObjectValue(*standardProto)); + return true; + } + + if (ShouldResolveStaticProperties(standardConstructor)) { + const js::Class* clasp = js::ProtoKeyToClass(standardConstructor); + MOZ_ASSERT(clasp->spec.defined()); + + if (!TryResolvePropertyFromSpecs(cx, id, holder, + clasp->spec.constructorFunctions(), + clasp->spec.constructorProperties(), desc)) { + return false; + } + + if (desc.object()) { + desc.object().set(wrapper); + return true; + } } - if (!JS_WrapObject(cx, &standardProto)) - return false; - FillPropertyDescriptor(desc, wrapper, JSPROP_PERMANENT | JSPROP_READONLY, - ObjectValue(*standardProto)); - return true; } } } else if (IsErrorObjectKey(key)) { @@ -485,18 +625,6 @@ JSXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper, return true; } - // The non-HasPrototypes semantics implemented by traditional Xrays are kind - // of broken with respect to |own|-ness and the holder. The common code - // muddles through by only checking the holder for non-|own| lookups, but - // that doesn't work for us. So we do an explicit holder check here, and hope - // that this mess gets fixed up soon. - if (!JS_GetPropertyDescriptorById(cx, holder, id, desc)) - return false; - if (desc.object()) { - desc.object().set(wrapper); - return true; - } - // Handle the 'constructor' property. if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_CONSTRUCTOR)) { RootedObject constructor(cx); @@ -531,80 +659,17 @@ JSXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper, const js::Class* clasp = js::GetObjectClass(target); MOZ_ASSERT(clasp->spec.defined()); - // Scan through the functions. Indexed array properties are handled above. - const JSFunctionSpec* fsMatch = nullptr; - for (const JSFunctionSpec* fs = clasp->spec.prototypeFunctions(); fs && fs->name; ++fs) { - if (PropertySpecNameEqualsId(fs->name, id)) { - fsMatch = fs; - break; - } - } - if (fsMatch) { - // Generate an Xrayed version of the method. - RootedFunction fun(cx, JS::NewFunctionFromSpec(cx, fsMatch, id)); - if (!fun) - return false; - - // The generic Xray machinery only defines non-own properties on the holder. - // This is broken, and will be fixed at some point, but for now we need to - // cache the value explicitly. See the corresponding call to - // JS_GetPropertyById at the top of this function. - RootedObject funObj(cx, JS_GetFunctionObject(fun)); - return JS_DefinePropertyById(cx, holder, id, funObj, 0) && - JS_GetPropertyDescriptorById(cx, holder, id, desc); + // Indexed array properties are handled above, so we can just work with the + // class spec here. + if (!TryResolvePropertyFromSpecs(cx, id, holder, + clasp->spec.prototypeFunctions(), + clasp->spec.prototypeProperties(), + desc)) { + return false; } - // Scan through the properties. - const JSPropertySpec* psMatch = nullptr; - for (const JSPropertySpec* ps = clasp->spec.prototypeProperties(); ps && ps->name; ++ps) { - if (PropertySpecNameEqualsId(ps->name, id)) { - psMatch = ps; - break; - } - } - if (psMatch) { - desc.value().setUndefined(); - RootedFunction getterObj(cx); - RootedFunction setterObj(cx); - unsigned flags = psMatch->flags; - if (psMatch->isSelfHosted()) { - getterObj = JS::GetSelfHostedFunction(cx, psMatch->getter.selfHosted.funname, id, 0); - if (!getterObj) - return false; - desc.setGetterObject(JS_GetFunctionObject(getterObj)); - if (psMatch->setter.selfHosted.funname) { - MOZ_ASSERT(flags & JSPROP_SETTER); - setterObj = JS::GetSelfHostedFunction(cx, psMatch->setter.selfHosted.funname, id, 0); - if (!setterObj) - return false; - desc.setSetterObject(JS_GetFunctionObject(setterObj)); - } - } else { - desc.setGetter(JS_CAST_NATIVE_TO(psMatch->getter.native.op, - JSGetterOp)); - desc.setSetter(JS_CAST_NATIVE_TO(psMatch->setter.native.op, - JSSetterOp)); - } - desc.setAttributes(flags); - - // The generic Xray machinery only defines non-own properties on the holder. - // This is broken, and will be fixed at some point, but for now we need to - // cache the value explicitly. See the corresponding call to - // JS_GetPropertyById at the top of this function. - // - // Note also that the public-facing API here doesn't give us a way to - // pass along JITInfo. It's probably ok though, since Xrays are already - // pretty slow. - return JS_DefinePropertyById(cx, holder, id, - UndefinedHandleValue, - // This particular descriptor, unlike most, - // actually stores JSNatives directly, - // since we just set it up. Do NOT pass - // JSPROP_PROPOP_ACCESSORS here! - desc.attributes(), - JS_PROPERTYOP_GETTER(desc.getter()), - JS_PROPERTYOP_SETTER(desc.setter())) && - JS_GetPropertyDescriptorById(cx, holder, id, desc); + if (desc.object()) { + desc.object().set(wrapper); } return true; @@ -717,6 +782,33 @@ MaybeAppend(jsid id, unsigned flags, AutoIdVector& props) return props.append(id); } +// Append the names from the given function and property specs to props. +static bool +AppendNamesFromFunctionAndPropertySpecs(JSContext* cx, + const JSFunctionSpec* fs, + const JSPropertySpec* ps, + unsigned flags, + AutoIdVector& props) +{ + // Convert the method and property names to jsids and pass them to the caller. + for ( ; fs && fs->name; ++fs) { + jsid id; + if (!PropertySpecNameToPermanentId(cx, fs->name, &id)) + return false; + if (!MaybeAppend(id, flags, props)) + return false; + } + for ( ; ps && ps->name; ++ps) { + jsid id; + if (!PropertySpecNameToPermanentId(cx, ps->name, &id)) + return false; + if (!MaybeAppend(id, flags, props)) + return false; + } + + return true; +} + bool JSXrayTraits::enumerateNames(JSContext* cx, HandleObject wrapper, unsigned flags, AutoIdVector& props) @@ -764,10 +856,23 @@ JSXrayTraits::enumerateNames(JSContext* cx, HandleObject wrapper, unsigned flags return false; if (!props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_NAME))) return false; - // Handle the .prototype property on standard constructors. - if (constructorFor(holder) != JSProto_Null) { + // Handle the .prototype property and static properties on standard + // constructors. + JSProtoKey standardConstructor = constructorFor(holder); + if (standardConstructor != JSProto_Null) { if (!props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_PROTOTYPE))) return false; + + if (ShouldResolveStaticProperties(standardConstructor)) { + const js::Class* clasp = js::ProtoKeyToClass(standardConstructor); + MOZ_ASSERT(clasp->spec.defined()); + + if (!AppendNamesFromFunctionAndPropertySpecs( + cx, clasp->spec.constructorFunctions(), + clasp->spec.constructorProperties(), flags, props)) { + return false; + } + } } } else if (IsErrorObjectKey(key)) { if (!props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_FILENAME)) || @@ -803,23 +908,9 @@ JSXrayTraits::enumerateNames(JSContext* cx, HandleObject wrapper, unsigned flags const js::Class* clasp = js::GetObjectClass(target); MOZ_ASSERT(clasp->spec.defined()); - // Convert the method and property names to jsids and pass them to the caller. - for (const JSFunctionSpec* fs = clasp->spec.prototypeFunctions(); fs && fs->name; ++fs) { - jsid id; - if (!PropertySpecNameToPermanentId(cx, fs->name, &id)) - return false; - if (!MaybeAppend(id, flags, props)) - return false; - } - for (const JSPropertySpec* ps = clasp->spec.prototypeProperties(); ps && ps->name; ++ps) { - jsid id; - if (!PropertySpecNameToPermanentId(cx, ps->name, &id)) - return false; - if (!MaybeAppend(id, flags, props)) - return false; - } - - return true; + return AppendNamesFromFunctionAndPropertySpecs( + cx, clasp->spec.prototypeFunctions(), + clasp->spec.prototypeProperties(), flags, props); } JSObject* @@ -1290,7 +1381,7 @@ XPCWrappedNativeXrayTraits::resolveNativeProperty(JSContext* cx, HandleObject wr ObjectValue(*JS_GetFunctionObject(toString))); return JS_DefinePropertyById(cx, holder, id, desc) && - JS_GetPropertyDescriptorById(cx, holder, id, desc); + JS_GetOwnPropertyDescriptorById(cx, holder, id, desc); } desc.object().set(holder); @@ -1383,7 +1474,7 @@ XrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper, bool found = false; if (expando) { JSAutoCompartment ac(cx, expando); - if (!JS_GetPropertyDescriptorById(cx, expando, id, desc)) + if (!JS_GetOwnPropertyDescriptorById(cx, expando, id, desc)) return false; found = !!desc.object(); } @@ -1429,7 +1520,7 @@ XrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper, wrappedJSObject_getter)) { return false; } - if (!JS_GetPropertyDescriptorById(cx, holder, id, desc)) + if (!JS_GetOwnPropertyDescriptorById(cx, holder, id, desc)) return false; desc.object().set(wrapper); return true; @@ -1476,7 +1567,7 @@ XPCWrappedNativeXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsW // in the wrapper's compartment here, not the wrappee. MOZ_ASSERT(js::IsObjectInContextCompartment(wrapper, cx)); - return JS_GetPropertyDescriptorById(cx, holder, id, desc); + return JS_GetOwnPropertyDescriptorById(cx, holder, id, desc); } bool @@ -1618,7 +1709,7 @@ DOMXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper, Handl } } - if (!JS_GetPropertyDescriptorById(cx, holder, id, desc)) + if (!JS_GetOwnPropertyDescriptorById(cx, holder, id, desc)) return false; if (desc.object()) { desc.object().set(wrapper); @@ -1636,7 +1727,7 @@ DOMXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper, Handl return true; return JS_DefinePropertyById(cx, holder, id, desc) && - JS_GetPropertyDescriptorById(cx, holder, id, desc); + JS_GetOwnPropertyDescriptorById(cx, holder, id, desc); } bool @@ -1911,7 +2002,7 @@ XrayWrapper::getPropertyDescriptor(JSContext* cx, HandleObject wra return false; // Check the holder. - if (!desc.object() && !JS_GetPropertyDescriptorById(cx, holder, id, desc)) + if (!desc.object() && !JS_GetOwnPropertyDescriptorById(cx, holder, id, desc)) return false; if (desc.object()) { desc.object().set(wrapper); @@ -1953,7 +2044,7 @@ XrayWrapper::getPropertyDescriptor(JSContext* cx, HandleObject wra return true; if (!JS_DefinePropertyById(cx, holder, id, desc) || - !JS_GetPropertyDescriptorById(cx, holder, id, desc)) + !JS_GetOwnPropertyDescriptorById(cx, holder, id, desc)) { return false; } diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index ade9410cd1..6c7be589e4 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -2906,10 +2906,13 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, drawBackgroundImage, drawBackgroundColor); - const nsStyleImageLayers& layers = aBackgroundSC->StyleBackground()->mLayers; + bool paintMask = (aFlags & PAINTBG_MASK_IMAGE); + const nsStyleImageLayers& layers = paintMask ? + aBackgroundSC->StyleSVGReset()->mMask : + aBackgroundSC->StyleBackground()->mImage; // If we're drawing a specific layer, we don't want to draw the // background color. - if (drawBackgroundColor && aLayer >= 0) { + if ((drawBackgroundColor && aLayer >= 0) || paintMask) { drawBackgroundColor = false; } @@ -3032,11 +3035,17 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, } if ((aLayer < 0 || i == (uint32_t)startLayer) && !clipState.mDirtyRectGfx.IsEmpty()) { - nsBackgroundLayerState state = PrepareImageLayer(aPresContext, aForFrame, - aFlags, paintBorderArea, clipState.mBGClipArea, layer); + nsBackgroundLayerState state = + PrepareImageLayer(aPresContext, aForFrame, + aFlags, paintBorderArea, clipState.mBGClipArea, + layer, paintMask); result &= state.mImageRenderer.PrepareResult(); if (!state.mFillArea.IsEmpty()) { - if (state.mCompositionOp != CompositionOp::OP_OVER) { + // Always using OP_OVER mode while drawing the bottom mask layer. + bool isBottomMaskLayer = paintMask ? + (i == (layers.mImageCount - 1)) : false; + if (state.mCompositionOp != CompositionOp::OP_OVER && + !isBottomMaskLayer) { NS_ASSERTION(ctx->CurrentOp() == CompositionOp::OP_OVER, "It is assumed the initial op is OP_OVER, when it is restored later"); ctx->SetOp(state.mCompositionOp); @@ -3290,7 +3299,8 @@ nsCSSRendering::PrepareImageLayer(nsPresContext* aPresContext, uint32_t aFlags, const nsRect& aBorderArea, const nsRect& aBGClipRect, - const nsStyleImageLayers::Layer& aLayer) + const nsStyleImageLayers::Layer& aLayer, + bool aMask) { /* * The properties we need to keep in mind when drawing style image @@ -3483,7 +3493,8 @@ nsCSSRendering::PrepareImageLayer(nsPresContext* aPresContext, state.mFillArea.IntersectRect(state.mFillArea, bgClipRect); - state.mCompositionOp = GetGFXBlendMode(aLayer.mBlendMode); + state.mCompositionOp = aMask ? GetGFXCompositeMode(aLayer.mComposite) : + GetGFXBlendMode(aLayer.mBlendMode); return state; } diff --git a/layout/base/nsCSSRendering.h b/layout/base/nsCSSRendering.h index ce3996ea40..83ecfa2c9f 100644 --- a/layout/base/nsCSSRendering.h +++ b/layout/base/nsCSSRendering.h @@ -549,7 +549,8 @@ struct nsCSSRendering { uint32_t aFlags, const nsRect& aBorderArea, const nsRect& aBGClipRect, - const nsStyleImageLayers::Layer& aLayer); + const nsStyleImageLayers::Layer& aLayer, + bool aMask = false); struct ImageLayerClipState { nsRect mBGClipArea; // Affected by mClippedRadii @@ -576,7 +577,7 @@ struct nsCSSRendering { /** * Render the background for an element using css rendering rules - * for backgrounds. + * for backgrounds or mask. */ enum { /** @@ -592,7 +593,12 @@ struct nsCSSRendering { * When this flag is passed, painting will go to the screen so we can * take advantage of the fact that it will be clipped to the viewport. */ - PAINTBG_TO_WINDOW = 0x04 + PAINTBG_TO_WINDOW = 0x04, + /** + * When this flag is passed, painting will read properties of mask-image + * style, instead of background-image. + */ + PAINTBG_MASK_IMAGE = 0x08 }; static DrawResult PaintBackground(nsPresContext* aPresContext, nsRenderingContext& aRenderingContext, @@ -603,6 +609,7 @@ struct nsCSSRendering { nsRect* aBGClipRect = nullptr, int32_t aLayer = -1); + /** * Same as |PaintBackground|, except using the provided style structs. * This short-circuits the code that ensures that the root element's @@ -805,6 +812,16 @@ struct nsCSSRendering { } } + static CompositionOp GetGFXCompositeMode(uint8_t aCompositeMode) { + switch (aCompositeMode) { + case NS_STYLE_MASK_COMPOSITE_ADD: return CompositionOp::OP_OVER; + case NS_STYLE_MASK_COMPOSITE_SUBSTRACT: return CompositionOp::OP_OUT; + case NS_STYLE_MASK_COMPOSITE_INTERSECT: return CompositionOp::OP_IN; + case NS_STYLE_MASK_COMPOSITE_EXCLUDE: return CompositionOp::OP_XOR; + default: MOZ_ASSERT(false); return CompositionOp::OP_OVER; + } + + } protected: static gfxRect GetTextDecorationRectInternal(const gfxPoint& aPt, const gfxSize& aLineSize, diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index e8119e83ae..af300b3d5a 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -349,7 +349,7 @@ static void AddTransformFunctions(nsCSSValueList* aList, } static TimingFunction -ToTimingFunction(Maybe aCTF) +ToTimingFunction(const Maybe& aCTF) { if (aCTF.isNothing()) { return TimingFunction(null_t()); @@ -750,6 +750,20 @@ void nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame, const nsRect& aDirtyRect) { nsRect dirtyRectRelativeToDirtyFrame = aDirtyRect; + if (nsLayoutUtils::IsFixedPosFrameInDisplayPort(aFrame) && + IsPaintingToWindow()) { + NS_ASSERTION(aDirtyFrame == aFrame->GetParent(), "Dirty frame should be viewport frame"); + // position: fixed items are reflowed into and only drawn inside the + // viewport, or the scroll position clamping scrollport size, if one is + // set. + nsIPresShell* ps = aFrame->PresContext()->PresShell(); + dirtyRectRelativeToDirtyFrame.MoveTo(0, 0); + if (ps->IsScrollPositionClampingScrollPortSizeSet()) { + dirtyRectRelativeToDirtyFrame.SizeTo(ps->GetScrollPositionClampingScrollPortSize()); + } else { + dirtyRectRelativeToDirtyFrame.SizeTo(aDirtyFrame->GetSize()); + } + } nsRect dirty = dirtyRectRelativeToDirtyFrame - aFrame->GetOffsetTo(aDirtyFrame); nsRect overflowRect = aFrame->GetVisualOverflowRect(); @@ -951,12 +965,9 @@ nsDisplayListBuilder::MarkFramesForDisplayList(nsIFrame* aDirtyFrame, * dirty rect for preserve-3d children. * * @param aDirtyFrame is the frame to mark children extending context. - * @param aDirtyRect is the same as the dirty rect of the root of the - * current 3D context, but be translated relative to - * the aDirtyFrame. */ void -nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(nsIFrame* aDirtyFrame, const nsRect& aDirtyRect) +nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(nsIFrame* aDirtyFrame) { AutoTArray childListArray; aDirtyFrame->GetChildLists(&childListArray); @@ -967,10 +978,6 @@ nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(nsIFrame* aDirtyFrame, nsIFrame *child = childFrames.get(); if (child->Combines3DTransformWithAncestors()) { mFramesMarkedForDisplay.AppendElement(child); - nsRect dirty = aDirtyRect - child->GetOffsetTo(aDirtyFrame); - child->Properties().Set(nsDisplayListBuilder::Preserve3DDirtyRectProperty(), - new nsRect(dirty)); - MarkFrameForDisplay(child, aDirtyFrame); } } @@ -1173,6 +1180,13 @@ nsDisplayListBuilder::AdjustWindowDraggingRegion(nsIFrame* aFrame) return; } + const nsStyleUIReset* styleUI = aFrame->StyleUIReset(); + if (styleUI->mWindowDragging == NS_STYLE_WINDOW_DRAGGING_DEFAULT) { + // This frame has the default value and doesn't influence the window + // dragging region. + return; + } + LayoutDeviceToLayoutDeviceMatrix4x4 referenceFrameToRootReferenceFrame; // The const_cast is for nsLayoutUtils::GetTransformToAncestor. @@ -1222,16 +1236,23 @@ nsDisplayListBuilder::AdjustWindowDraggingRegion(nsIFrame* aFrame) transformedDevPixelBorderBox.Round(); LayoutDeviceIntRect transformedDevPixelBorderBoxInt; if (transformedDevPixelBorderBox.ToIntRect(&transformedDevPixelBorderBoxInt)) { - const nsStyleUserInterface* styleUI = aFrame->StyleUserInterface(); if (styleUI->mWindowDragging == NS_STYLE_WINDOW_DRAGGING_DRAG) { mWindowDraggingRegion.OrWith(transformedDevPixelBorderBoxInt); } else { - mWindowDraggingRegion.SubOut(transformedDevPixelBorderBoxInt); + mWindowNoDraggingRegion.OrWith(transformedDevPixelBorderBoxInt); } } } } +LayoutDeviceIntRegion +nsDisplayListBuilder::GetWindowDraggingRegion() const +{ + LayoutDeviceIntRegion result; + result.Sub(mWindowDraggingRegion, mWindowNoDraggingRegion);; + return result; +} + const uint32_t gWillChangeAreaMultiplier = 3; static uint32_t GetWillChangeCost(const nsSize& aSize) { // There's significant overhead for each layer created from Gecko @@ -2262,7 +2283,7 @@ nsDisplayBackgroundImage::GetDestAreaInternal(nsDisplayListBuilder* aBuilder) nsPresContext* presContext = mFrame->PresContext(); uint32_t flags = aBuilder->GetBackgroundPaintFlags(); nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize()); - const nsStyleImageLayers::Layer& layer = mBackgroundStyle->mLayers.mLayers[mLayer]; + const nsStyleImageLayers::Layer& layer = mBackgroundStyle->mImage.mLayers[mLayer]; nsBackgroundLayerState state = nsCSSRendering::PrepareImageLayer(presContext, mFrame, flags, @@ -2409,18 +2430,18 @@ nsDisplayBackgroundImage::AppendBackgroundItemsToTop(nsDisplayListBuilder* aBuil // Passing bg == nullptr in this macro will result in one iteration with // i = 0. - NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, bg->mLayers) { - if (bg->mLayers.mLayers[i].mImage.IsEmpty()) { + NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, bg->mImage) { + if (bg->mImage.mLayers[i].mImage.IsEmpty()) { continue; } - if (bg->mLayers.mLayers[i].mBlendMode != NS_STYLE_BLEND_NORMAL) { + if (bg->mImage.mLayers[i].mBlendMode != NS_STYLE_BLEND_NORMAL) { needBlendContainer = true; } DisplayListClipState::AutoSaveRestore clipState(aBuilder); if (!aBuilder->IsForEventDelivery()) { - const nsStyleImageLayers::Layer& layer = bg->mLayers.mLayers[i]; + const nsStyleImageLayers::Layer& layer = bg->mImage.mLayers[i]; SetBackgroundClipRegion(clipState, aFrame, toRef, layer, willPaintBorder); } @@ -2484,13 +2505,13 @@ nsDisplayBackgroundImage::IsSingleFixedPositionImage(nsDisplayListBuilder* aBuil if (!mBackgroundStyle) return false; - if (mBackgroundStyle->mLayers.mLayers.Length() != 1) + if (mBackgroundStyle->mImage.mLayers.Length() != 1) return false; nsPresContext* presContext = mFrame->PresContext(); uint32_t flags = aBuilder->GetBackgroundPaintFlags(); nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize()); - const nsStyleImageLayers::Layer &layer = mBackgroundStyle->mLayers.mLayers[mLayer]; + const nsStyleImageLayers::Layer &layer = mBackgroundStyle->mImage.mLayers[mLayer]; if (layer.mAttachment != NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED) return false; @@ -2512,8 +2533,8 @@ nsDisplayBackgroundImage::IsSingleFixedPositionImage(nsDisplayListBuilder* aBuil bool nsDisplayBackgroundImage::IsNonEmptyFixedImage() const { - return mBackgroundStyle->mLayers.mLayers[mLayer].mAttachment == NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED && - !mBackgroundStyle->mLayers.mLayers[mLayer].mImage.IsEmpty(); + return mBackgroundStyle->mImage.mLayers[mLayer].mAttachment == NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED && + !mBackgroundStyle->mImage.mLayers[mLayer].mImage.IsEmpty(); } bool @@ -2542,7 +2563,7 @@ nsDisplayBackgroundImage::CanOptimizeToImageLayer(LayerManager* aManager, nsPresContext* presContext = mFrame->PresContext(); uint32_t flags = aBuilder->GetBackgroundPaintFlags(); nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize()); - const nsStyleImageLayers::Layer &layer = mBackgroundStyle->mLayers.mLayers[mLayer]; + const nsStyleImageLayers::Layer &layer = mBackgroundStyle->mImage.mLayers[mLayer]; nsBackgroundLayerState state = nsCSSRendering::PrepareImageLayer(presContext, mFrame, flags, @@ -2621,7 +2642,7 @@ nsDisplayBackgroundImage::ShouldCreateOwnLayer(nsDisplayListBuilder* aBuilder, } if (nsLayoutUtils::AnimatedImageLayersEnabled() && mBackgroundStyle) { - const nsStyleImageLayers::Layer &layer = mBackgroundStyle->mLayers.mLayers[mLayer]; + const nsStyleImageLayers::Layer &layer = mBackgroundStyle->mImage.mLayers[mLayer]; const nsStyleImage* image = &layer.mImage; if (image->GetType() == eStyleImageType_Image) { imgIRequest* imgreq = image->GetImageData(); @@ -2817,7 +2838,7 @@ nsDisplayBackgroundImage::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, if (mFrame->StyleBorder()->mBoxDecorationBreak == NS_STYLE_BOX_DECORATION_BREAK_CLONE || (!mFrame->GetPrevContinuation() && !mFrame->GetNextContinuation())) { - const nsStyleImageLayers::Layer& layer = mBackgroundStyle->mLayers.mLayers[mLayer]; + 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) { @@ -2847,7 +2868,7 @@ nsDisplayBackgroundImage::GetPositioningArea() return nsCSSRendering::ComputeImageLayerPositioningArea( mFrame->PresContext(), mFrame, nsRect(ToReferenceFrame(), mFrame->GetSize()), - mBackgroundStyle->mLayers.mLayers[mLayer], + mBackgroundStyle->mImage.mLayers[mLayer], &attachedToFrame) + ToReferenceFrame(); } @@ -2864,7 +2885,7 @@ nsDisplayBackgroundImage::RenderingMightDependOnPositioningAreaSizeChange() return true; } - const nsStyleImageLayers::Layer &layer = mBackgroundStyle->mLayers.mLayers[mLayer]; + const nsStyleImageLayers::Layer &layer = mBackgroundStyle->mImage.mLayers[mLayer]; if (layer.RenderingMightDependOnPositioningAreaSizeChange()) { return true; } @@ -2940,7 +2961,7 @@ void nsDisplayBackgroundImage::ComputeInvalidationRegion(nsDisplayListBuilder* a return; } if (aBuilder->ShouldSyncDecodeImages()) { - const nsStyleImage& image = mBackgroundStyle->mLayers.mLayers[mLayer].mImage; + const nsStyleImage& image = mBackgroundStyle->mImage.mLayers[mLayer].mImage; if (image.GetType() == eStyleImageType_Image && geometry->ShouldInvalidateToSyncDecodeImages()) { aInvalidRegion->Or(*aInvalidRegion, bounds); @@ -2983,10 +3004,10 @@ nsDisplayBackgroundImage::GetBoundsInternal(nsDisplayListBuilder* aBuilder) { nsIFrame* rootFrame = presContext->PresShell()->GetRootFrame(); nsRect rootRect = rootFrame->GetRectRelativeToSelf(); if (nsLayoutUtils::TransformRect(rootFrame, mFrame, rootRect) == nsLayoutUtils::TRANSFORM_SUCCEEDED) { - clipRect = rootRect + aBuilder->ToReferenceFrame(mFrame); + clipRect = clipRect.Union(rootRect + aBuilder->ToReferenceFrame(mFrame)); } } - const nsStyleImageLayers::Layer& layer = mBackgroundStyle->mLayers.mLayers[mLayer]; + const nsStyleImageLayers::Layer& layer = mBackgroundStyle->mImage.mLayers[mLayer]; return nsCSSRendering::GetBackgroundLayerRect(presContext, mFrame, borderBox, clipRect, layer, aBuilder->GetBackgroundPaintFlags()); @@ -5733,7 +5754,7 @@ nsDisplayTransform::GetAccumulatedPreserved3DTransform(nsDisplayListBuilder* aBu } const nsIFrame* establisher; // Establisher of the 3D rendering context. - for (establisher = nsLayoutUtils::GetCrossDocParentFrame(mFrame); + for (establisher = mFrame; establisher && establisher->Combines3DTransformWithAncestors(); establisher = nsLayoutUtils::GetCrossDocParentFrame(establisher)) { } @@ -6441,8 +6462,10 @@ nsDisplaySVGEffects::PaintAsLayer(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx, LayerManager* aManager) { + nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize()); nsSVGIntegrationUtils::PaintFramesWithEffects(*aCtx->ThebesContext(), mFrame, mVisibleRect, + borderArea, aBuilder, aManager); } @@ -6483,7 +6506,6 @@ nsDisplaySVGEffects::BuildLayer(nsDisplayListBuilder* aBuilder, bool isOK = effectProperties.HasNoFilterOrHasValidFilter(); effectProperties.GetClipPathFrame(&isOK); - effectProperties.GetMaskFrame(&isOK); if (!isOK) { return nullptr; @@ -6594,6 +6616,14 @@ nsDisplaySVGEffects::PrintEffects(nsACString& aTo) aTo += nsPrintfCString("clip(%s)", clipPathFrame->IsTrivial() ? "trivial" : "non-trivial"); first = false; } + const nsStyleSVGReset *style = mFrame->StyleSVGReset(); + if (style->HasClipPath() && !clipPathFrame) { + if (!first) { + aTo += ", "; + } + aTo += "clip(basic-shape)"; + first = false; + } if (effectProperties.HasValidFilter()) { if (!first) { aTo += ", "; diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index 31e491741e..eb7dcff0dc 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -576,7 +576,7 @@ public: * Because these frames include transforms set on their parent, dirty rects * for intermediate frames may be empty, yet child frames could still be visible. */ - void MarkPreserve3DFramesForDisplayList(nsIFrame* aDirtyFrame, const nsRect& aDirtyRect); + void MarkPreserve3DFramesForDisplayList(nsIFrame* aDirtyFrame); const nsTArray& GetThemeGeometries() { return mThemeGeometries; } @@ -615,11 +615,12 @@ public: * Adjusts mWindowDraggingRegion to take into account aFrame. If aFrame's * -moz-window-dragging value is |drag|, its border box is added to the * collected dragging region; if the value is |no-drag|, the border box is - * subtracted from the region. + * subtracted from the region; if the value is |default|, that frame does + * not influence the window dragging region. */ void AdjustWindowDraggingRegion(nsIFrame* aFrame); - const LayoutDeviceIntRegion& GetWindowDraggingRegion() { return mWindowDraggingRegion; } + LayoutDeviceIntRegion GetWindowDraggingRegion() const; /** * Allocate memory in our arena. It will only be freed when this display list @@ -968,8 +969,6 @@ public: aFrame->Properties().Get(OutOfFlowDisplayDataProperty())); } - NS_DECLARE_FRAME_PROPERTY_DELETABLE(Preserve3DDirtyRectProperty, nsRect) - nsPresContext* CurrentPresContext() { return CurrentPresShellState()->mPresShell->GetPresContext(); } @@ -1228,6 +1227,7 @@ private: nsRegion mWindowExcludeGlassRegion; nsRegion mWindowOpaqueRegion; LayoutDeviceIntRegion mWindowDraggingRegion; + LayoutDeviceIntRegion mWindowNoDraggingRegion; // The display item for the Windows window glass background, if any nsDisplayItem* mGlassDisplayItem; // When encountering inactive layers, we need to hoist scroll info items diff --git a/layout/base/tests/mochitest.ini b/layout/base/tests/mochitest.ini index 8f3e4df76b..bf1534d8a4 100644 --- a/layout/base/tests/mochitest.ini +++ b/layout/base/tests/mochitest.ini @@ -261,4 +261,4 @@ support-files = bug1093686_inner.html [test_bug1120705.html] [test_bug1226904.html] support-files = bug1226904.html -skip-if = buildapp == 'android' || buildapp == 'b2g' || buildapp == 'b2g-debug' || os == 'mac' # android and b2g do not have clickable scrollbars, mac does not have scrollbar down and up buttons +[test_bug1246622.html] diff --git a/layout/base/tests/test_bug1246622.html b/layout/base/tests/test_bug1246622.html new file mode 100644 index 0000000000..78d51b145d --- /dev/null +++ b/layout/base/tests/test_bug1246622.html @@ -0,0 +1,45 @@ + + + +Test for Bug 1246622 + + + + + +
+
+
+
+
+ +
+
+
+ + diff --git a/layout/generic/nsCanvasFrame.cpp b/layout/generic/nsCanvasFrame.cpp index 68af05950e..8c170e6740 100644 --- a/layout/generic/nsCanvasFrame.cpp +++ b/layout/generic/nsCanvasFrame.cpp @@ -402,7 +402,7 @@ nsCanvasFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, bool needBlendContainer = false; // Create separate items for each background layer. - const nsStyleImageLayers& layers = bg->mLayers; + const nsStyleImageLayers& layers = bg->mImage; NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, layers) { if (layers.mLayers[i].mImage.IsEmpty()) { continue; diff --git a/layout/generic/nsCanvasFrame.h b/layout/generic/nsCanvasFrame.h index 6a9ed28337..bbf688be9c 100644 --- a/layout/generic/nsCanvasFrame.h +++ b/layout/generic/nsCanvasFrame.h @@ -220,8 +220,8 @@ public: // compositing layer. Since we know their background painting area can't // change (unless the viewport size itself changes), async scrolling // will work well. - return mBackgroundStyle->mLayers.mLayers[mLayer].mAttachment == NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED && - !mBackgroundStyle->mLayers.mLayers[mLayer].mImage.IsEmpty(); + return mBackgroundStyle->mImage.mLayers[mLayer].mAttachment == NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED && + !mBackgroundStyle->mImage.mLayers[mLayer].mImage.IsEmpty(); } // We still need to paint a background color as well as an image for this item, diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 924a698fe9..bac594decf 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -742,6 +742,56 @@ nsFrame::GetOffsets(int32_t &aStart, int32_t &aEnd) const return NS_OK; } +static +void +AddAndRemoveImageAssociations(nsFrame* aFrame, + const nsStyleImageLayers* aOldLayers, + const nsStyleImageLayers* aNewLayers) +{ + ImageLoader* imageLoader = + aFrame->PresContext()->Document()->StyleImageLoader(); + + // If the old context had a background-image image, or mask-image image, + // and new context does not have the same image, clear the image load + // notifier (which keeps the image loading, if it still is) for the frame. + // We want to do this conservatively because some frames paint their + // backgrounds from some other frame's style data, and we don't want + // to clear those notifiers unless we have to. (They'll be reset + // when we paint, although we could miss a notification in that + // interval.) + + if (aOldLayers) { + NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, (*aOldLayers)) { + // If there is an image in oldBG that's not in newBG, drop it. + if (i >= aNewLayers->mImageCount || + !aOldLayers->mLayers[i].mImage.ImageDataEquals( + aNewLayers->mLayers[i].mImage)) { + const nsStyleImage& oldImage = aOldLayers->mLayers[i].mImage; + if (oldImage.GetType() != eStyleImageType_Image) { + continue; + } + + imageLoader->DisassociateRequestFromFrame(oldImage.GetImageData(), + aFrame); + } + } + } + + NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, (*aNewLayers)) { + // If there is an image in newBG that's not in oldBG, add it. + if (!aOldLayers || i >= aOldLayers->mImageCount || + !aNewLayers->mLayers[i].mImage.ImageDataEquals( + aOldLayers->mLayers[i].mImage)) { + const nsStyleImage& newImage = aNewLayers->mLayers[i].mImage; + if (newImage.GetType() != eStyleImageType_Image) { + continue; + } + + imageLoader->AssociateRequestToFrame(newImage.GetImageData(), aFrame); + } + } +} + // Subclass hook for style post processing /* virtual */ void nsFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) @@ -766,50 +816,16 @@ nsFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) } } - ImageLoader* imageLoader = PresContext()->Document()->StyleImageLoader(); + const nsStyleImageLayers *oldLayers = aOldStyleContext ? + &aOldStyleContext->StyleBackground()->mImage : + nullptr; + const nsStyleImageLayers *newLayers = &StyleBackground()->mImage; + AddAndRemoveImageAssociations(this, oldLayers, newLayers); - // If the old context had a background image image and new context - // does not have the same image, clear the image load notifier - // (which keeps the image loading, if it still is) for the frame. - // We want to do this conservatively because some frames paint their - // backgrounds from some other frame's style data, and we don't want - // to clear those notifiers unless we have to. (They'll be reset - // when we paint, although we could miss a notification in that - // interval.) - const nsStyleBackground *oldBG = aOldStyleContext ? - aOldStyleContext->StyleBackground() : - nullptr; - const nsStyleBackground *newBG = StyleBackground(); - const nsStyleImageLayers& oldBGLayers = oldBG->mLayers; - const nsStyleImageLayers& newBGLayers = newBG->mLayers; - if (oldBG) { - NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, oldBGLayers) { - // If there is an image in oldBG that's not in newBG, drop it. - if (i >= newBGLayers.mImageCount || - !oldBGLayers.mLayers[i].mImage.ImageDataEquals(newBGLayers.mLayers[i].mImage)) { - const nsStyleImage& oldImage = oldBGLayers.mLayers[i].mImage; - if (oldImage.GetType() != eStyleImageType_Image) { - continue; - } - - imageLoader->DisassociateRequestFromFrame(oldImage.GetImageData(), - this); - } - } - } - - NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, newBGLayers) { - // If there is an image in newBG that's not in oldBG, add it. - if (!oldBG || i >= oldBGLayers.mImageCount || - !newBGLayers.mLayers[i].mImage.ImageDataEquals(oldBGLayers.mLayers[i].mImage)) { - const nsStyleImage& newImage = newBGLayers.mLayers[i].mImage; - if (newImage.GetType() != eStyleImageType_Image) { - continue; - } - - imageLoader->AssociateRequestToFrame(newImage.GetImageData(), this); - } - } + oldLayers = aOldStyleContext ? &aOldStyleContext->StyleSVGReset()->mMask : + nullptr; + newLayers = &StyleSVGReset()->mMask; + AddAndRemoveImageAssociations(this, oldLayers, newLayers); if (aOldStyleContext) { // If we detect a change on margin, padding or border, we store the old @@ -847,6 +863,7 @@ nsFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) } } + ImageLoader* imageLoader = PresContext()->Document()->StyleImageLoader(); imgIRequest *oldBorderImage = aOldStyleContext ? aOldStyleContext->StyleBorder()->GetBorderImageRequest() : nullptr; @@ -2017,8 +2034,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, nsRect dirtyRectOutsideTransform = dirtyRect; if (isTransformed) { const nsRect overflow = GetVisualOverflowRectRelativeToSelf(); - if (Extend3DContext() || Combines3DTransformWithAncestors() || - nsDisplayTransform::ShouldPrerenderTransformedContent(aBuilder, + if (nsDisplayTransform::ShouldPrerenderTransformedContent(aBuilder, this)) { dirtyRect = overflow; } else { @@ -2026,9 +2042,15 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, return; } + // If we're in preserve-3d then grab the dirty rect that was given to the root + // and transform using the combined transform. + if (Combines3DTransformWithAncestors()) { + dirtyRect = aBuilder->GetPreserves3DDirtyRect(this); + } + nsRect untransformedDirtyRect; if (nsDisplayTransform::UntransformRect(dirtyRect, overflow, this, - nsPoint(0,0), &untransformedDirtyRect, false)) { + nsPoint(0,0), &untransformedDirtyRect, true)) { dirtyRect = untransformedDirtyRect; } else { NS_WARNING("Unable to untransform dirty rect!"); @@ -2100,8 +2122,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, // Extend3DContext() also guarantees that applyAbsPosClipping and usingSVGEffects are false // We only modify the preserve-3d rect if we are the top of a preserve-3d heirarchy if (Extend3DContext()) { - nsRect dirty = aBuilder->GetPreserves3DDirtyRect(this); - aBuilder->MarkPreserve3DFramesForDisplayList(this, dirty); + aBuilder->MarkPreserve3DFramesForDisplayList(this); } if (aBuilder->IsBuildingLayerEventRegions()) { @@ -2430,15 +2451,6 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, } pseudoStackingContext = true; } - if (child->Combines3DTransformWithAncestors()) { - nsRect* savedDirty = static_cast - (child->Properties().Get(nsDisplayListBuilder::Preserve3DDirtyRectProperty())); - if (savedDirty) { - dirty = *savedDirty; - } else { - dirty.SetEmpty(); - } - } NS_ASSERTION(childType != nsGkAtoms::placeholderFrame, "Should have dealt with placeholders already"); diff --git a/layout/inspector/inDOMUtils.cpp b/layout/inspector/inDOMUtils.cpp index 9d71213c55..c1550533a2 100644 --- a/layout/inspector/inDOMUtils.cpp +++ b/layout/inspector/inDOMUtils.cpp @@ -765,6 +765,8 @@ PropertySupportsVariant(nsCSSProperty aPropertyID, uint32_t aVariant) case eCSSProperty_border_bottom_right_radius: case eCSSProperty_background_position: case eCSSProperty_background_size: + case eCSSProperty_mask_position: + case eCSSProperty_mask_size: case eCSSProperty_grid_auto_columns: case eCSSProperty_grid_auto_rows: case eCSSProperty_grid_template_columns: diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-001-ref.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-001-ref.html new file mode 100644 index 0000000000..34def2ff95 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-001-ref.html @@ -0,0 +1,20 @@ + + + + + CSS Masking: Ref test for clip-path's circle function 001 + + + +

The test passes if there is a green circle.

+
+ + + + + + + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-001.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-001.html new file mode 100644 index 0000000000..c506a2b684 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-001.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and circle function with no values + + + + + + + +

The test passes if there is a green circle.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-002-ref.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-002-ref.html new file mode 100644 index 0000000000..007cd80c9d --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-002-ref.html @@ -0,0 +1,20 @@ + + + + + CSS Masking: Ref test for clip-path's circle function 002 + + + +

The test passes if there is a green circle.

+
+ + + + + + + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-002.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-002.html new file mode 100644 index 0000000000..c242dc91b3 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-002.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and circle function with farthest-side + + + + + + + +

The test passes if there is a green circle.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-003-ref.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-003-ref.html new file mode 100644 index 0000000000..4bb8d89ee1 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-003-ref.html @@ -0,0 +1,20 @@ + + + + + CSS Masking: Ref test for clip-path's circle function 003 + + + +

The test passes if there is a green circle.

+
+ + + + + + + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-003.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-003.html new file mode 100644 index 0000000000..ae4d2d5098 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-003.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and circle function with closest-side + + + + + + + +

The test passes if there is a green circle.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-004-ref.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-004-ref.html new file mode 100644 index 0000000000..3885dd5547 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-004-ref.html @@ -0,0 +1,20 @@ + + + + + CSS Masking: Ref test for clip-path's circle function 004 + + + +

The test passes if there is a green circle.

+
+ + + + + + + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-004.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-004.html new file mode 100644 index 0000000000..ec25c044cc --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-004.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and circle function with position 001 + + + + + + + +

The test passes if there is a green circle.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-005-ref.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-005-ref.html new file mode 100644 index 0000000000..1a52c69567 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-005-ref.html @@ -0,0 +1,20 @@ + + + + + CSS Masking: Ref test for clip-path's circle function 005 + + + +

The test passes if there is a green circle.

+
+ + + + + + + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-005.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-005.html new file mode 100644 index 0000000000..a68b33e192 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-005.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and circle function with position 002 + + + + + + + +

The test passes if there is a green circle.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-006-ref.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-006-ref.html new file mode 100644 index 0000000000..a07cbb66cd --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-006-ref.html @@ -0,0 +1,20 @@ + + + + + CSS Masking: Ref test for clip-path's circle function 006 + + + +

The test passes if there is a green circle.

+
+ + + + + + + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-006.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-006.html new file mode 100644 index 0000000000..dea4071f26 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-006.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and circle function with position 003 + + + + + + + +

The test passes if there is a green circle.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-007-ref.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-007-ref.html new file mode 100644 index 0000000000..caba51737b --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-007-ref.html @@ -0,0 +1,20 @@ + + + + + CSS Masking: Ref test for clip-path's circle function 007 + + + +

The test passes if there is a green circle.

+
+ + + + + + + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-007.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-007.html new file mode 100644 index 0000000000..dc4432ee45 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-007.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and circle function with position 004 + + + + + + + +

The test passes if there is a green circle.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-008-ref.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-008-ref.html new file mode 100644 index 0000000000..cdf8da8ede --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-008-ref.html @@ -0,0 +1,20 @@ + + + + + CSS Masking: Ref test for clip-path's circle function 008 + + + +

The test passes if there is a green circle.

+
+ + + + + + + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-008.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-008.html new file mode 100644 index 0000000000..c335f7c3d8 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-008.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and circle function with position 005 + + + + + + + +

The test passes if there is a green circle.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-009-ref.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-009-ref.html new file mode 100644 index 0000000000..7d055c4d7d --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-009-ref.html @@ -0,0 +1,20 @@ + + + + + CSS Masking: Ref test for clip-path's circle function 009 + + + +

The test passes if there is a green circle.

+
+ + + + + + + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-009.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-009.html new file mode 100644 index 0000000000..0b7b69443e --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-009.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and circle function with position 006 + + + + + + + +

The test passes if there is a green circle.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-010-ref.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-010-ref.html new file mode 100644 index 0000000000..7fed7ee45c --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-010-ref.html @@ -0,0 +1,20 @@ + + + + + CSS Masking: Ref test for clip-path's circle function 010 + + + +

The test passes if there is a green circle.

+
+ + + + + + + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-010.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-010.html new file mode 100644 index 0000000000..2e7ea353ff --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-010.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and circle function with position 007 + + + + + + + +

The test passes if there is a green circle.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-011.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-011.html new file mode 100644 index 0000000000..6df5b5228f --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-011.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and circle function with position 008 + + + + + + + +

The test passes if there is a green circle.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-012.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-012.html new file mode 100644 index 0000000000..efded811f8 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-012.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and circle function with position 009 + + + + + + + +

The test passes if there is a green circle.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-013.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-013.html new file mode 100644 index 0000000000..482980af9b --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-013.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and circle function with position 010 + + + + + + + +

The test passes if there is a green circle.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-014.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-014.html new file mode 100644 index 0000000000..3c4b35d7d3 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-014.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and circle function with border-box + + + + + + + +

The test passes if there is a green circle.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-015.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-015.html new file mode 100644 index 0000000000..bc8e161cc6 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-015.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and circle function with padding-box + + + + + + + +

The test passes if there is a green circle.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-016.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-016.html new file mode 100644 index 0000000000..7cea5f2bbb --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-016.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and circle function with content-box + + + + + + + +

The test passes if there is a green circle.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-017.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-017.html new file mode 100644 index 0000000000..4947ac1a27 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-017.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and circle function with margin-box + + + + + + + +

The test passes if there is a green circle.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-018.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-018.html new file mode 100644 index 0000000000..b41d7aecc5 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-018.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and circle function on rectangle 001 + + + + + + + +

The test passes if there is a green circle.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-019.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-019.html new file mode 100644 index 0000000000..395002a2d2 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-019.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and circle function on rectangle 002 + + + + + + + +

The test passes if there is a green circle.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-020.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-020.html new file mode 100644 index 0000000000..82b52ae457 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-circle-020.html @@ -0,0 +1,23 @@ + + + + + CSS Masking: Test clip-path property and circle function on rectangle 003 + + + + + + + +

The test passes if there is a green circle.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-001-ref.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-001-ref.html new file mode 100644 index 0000000000..fc0392da94 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-001-ref.html @@ -0,0 +1,20 @@ + + + + + CSS Masking: Ref test for clip-path's ellipse function 001 + + + +

The test passes if there is a green horizontal ellipse.

+
+ + + + + + + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-001.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-001.html new file mode 100644 index 0000000000..77ff7b9b0e --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-001.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and ellipse function without values + + + + + + + +

The test passes if there is a green horizontal ellipse.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-002.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-002.html new file mode 100644 index 0000000000..6b80585b90 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-002.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and ellipse function with absolute radii + + + + + + + +

The test passes if there is a green horizontal ellipse.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-003.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-003.html new file mode 100644 index 0000000000..9b1051d189 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-003.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and ellipse function percentage radii + + + + + + + +

The test passes if there is a green horizontal ellipse.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-004.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-004.html new file mode 100644 index 0000000000..b1d05cddf5 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-004.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and ellipse function with farthest-side + + + + + + + +

The test passes if there is a green horizontal ellipse.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-005.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-005.html new file mode 100644 index 0000000000..50d97f3f51 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-005.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and ellipse function with closest-side + + + + + + + +

The test passes if there is a green horizontal ellipse.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-006.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-006.html new file mode 100644 index 0000000000..39c07e98e4 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-006.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and ellipse function with position 001 + + + + + + + +

The test passes if there is a green horizontal ellipse.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-007.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-007.html new file mode 100644 index 0000000000..ca23693c2c --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-007.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and ellipse function with position 002 + + + + + + + +

The test passes if there is a green horizontal ellipse.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-008.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-008.html new file mode 100644 index 0000000000..560cfc0761 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-ellipse-008.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and ellipse function with border-box + + + + + + + +

The test passes if there is a green horizontal ellipse.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-001.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-001.html new file mode 100644 index 0000000000..bd3093f15b --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-001.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and polygon function with absolute values + + + + + + + +

The test passes if there is a green rectangle and no red.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-002.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-002.html new file mode 100644 index 0000000000..b34a73228e --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-002.html @@ -0,0 +1,24 @@ + + + + + CSS Masking: Test clip-path property and polygon function with percentage values + + + + + + + +

The test passes if there is a green rectangle and no red.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-003.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-003.html new file mode 100644 index 0000000000..067a3f939a --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-003.html @@ -0,0 +1,21 @@ + + + + + CSS Masking: Test clip-path property and polygon function with absolute and percentage values + + + + + + + +

The test passes if there is a green rectangle and no red.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-004.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-004.html new file mode 100644 index 0000000000..7c937ed3ea --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-004.html @@ -0,0 +1,25 @@ + + + + + CSS Masking: Test clip-path property and polygon function with fill rule evenodd + + + + + + + +

The test passes if there is a green rectangle with a blue border.

+
+
+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-005.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-005.html new file mode 100644 index 0000000000..5b141290fe --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-005.html @@ -0,0 +1,26 @@ + + + + + CSS Masking: Test clip-path property and polygon function with fill rule nonzero + + + + + + + +

The test passes if there is a green rectangle with a blue border.

+
+
+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-006.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-006.html new file mode 100644 index 0000000000..cb345de91d --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-006.html @@ -0,0 +1,33 @@ + + + + + CSS Masking: Test clip-path and polygon with padding-box + + + + + + + + +

The test passes if there is a green square and no red.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-007.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-007.html new file mode 100644 index 0000000000..9d3c2dc9c3 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-007.html @@ -0,0 +1,34 @@ + + + + + CSS Masking: Test clip-path and polygon with border-box + + + + + + + +

The test passes if you see a green vertical stripe next to a lime green vertical stripe, both stripes should be of equal size and there should be no red.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-008.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-008.html new file mode 100644 index 0000000000..04edaf5a5a --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-008.html @@ -0,0 +1,35 @@ + + + + + CSS Masking: Test clip-path and polygon with margin-box + + + + + + + + +

The test passes if you see a green vertical stripe next to a lime green vertical stripe, both stripes should be of equal size and there should be no red.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-009.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-009.html new file mode 100644 index 0000000000..2debdc38e4 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-009.html @@ -0,0 +1,33 @@ + + + + + CSS Masking: Test clip-path and polygon with content-box + + + + + + + + +

The test passes if there is a green square and no red.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-010.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-010.html new file mode 100644 index 0000000000..c5656d134b --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-010.html @@ -0,0 +1,36 @@ + + + + + CSS Masking: Test clip-path and polygon with fill-box + + + + + + + +

The test passes if you see a green vertical stripe next to a lime green vertical stripe, both stripes should be of equal size and there should be no red.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-011.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-011.html new file mode 100644 index 0000000000..f8a80ef2df --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-011.html @@ -0,0 +1,36 @@ + + + + + CSS Masking: Test clip-path and polygon with stroke-box + + + + + + + +

The test passes if you see a green vertical stripe next to a lime green vertical stripe, both stripes should be of equal size and there should be no red.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-012.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-012.html new file mode 100644 index 0000000000..dc48d662e5 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-012.html @@ -0,0 +1,36 @@ + + + + + CSS Masking: Test clip-path and polygon with view-box + + + + + + + +

The test passes if you see a green vertical stripe next to a lime green vertical stripe, both stripes should be of equal size and there should be no red.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-013.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-013.html new file mode 100644 index 0000000000..203c7e1015 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-polygon-013.html @@ -0,0 +1,51 @@ + + + + + CSS Masking: Test clip-path and polygon different units + + + + + + + +

The test passes if you see a multiple green and blue stripe pairs. For each pair, the blue and green stripe must be of same length.

+
+
+ +
+
+ + + +
+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-rectangle-border-ref.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-rectangle-border-ref.html new file mode 100644 index 0000000000..2a9f65f163 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-rectangle-border-ref.html @@ -0,0 +1,15 @@ + + + + + CSS Reftest Reference + + + +

The test passes if there is a green rectangle with a blue border.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-rectangle-ref.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-rectangle-ref.html new file mode 100644 index 0000000000..45bc902c3d --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-rectangle-ref.html @@ -0,0 +1,15 @@ + + + + + CSS Reftest Reference + + + +

The test passes if there is a green rectangle and no red.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-square-001-ref.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-square-001-ref.html new file mode 100644 index 0000000000..f0f26e065c --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-square-001-ref.html @@ -0,0 +1,15 @@ + + + + + CSS Masking: Reftest reference + + + +

The test passes if there is a green square and no red.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-square-002-ref.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-square-002-ref.html new file mode 100644 index 0000000000..1831ecebae --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-square-002-ref.html @@ -0,0 +1,15 @@ + + + + + CSS Masking: Reftest reference + + + +

The test passes if there is a green square and no red.

+
+ + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-stripes-001-ref.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-stripes-001-ref.html new file mode 100644 index 0000000000..6add82da00 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-stripes-001-ref.html @@ -0,0 +1,39 @@ + + + + + CSS Masking: Test clip-path and polygon with border-box + + + + + + + +

The test passes if you see a green vertical stripe next to a lime green vertical stripe, both stripes should be of equal size and there should be no red.

+
+ + + + + + + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-stripes-002-ref.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-stripes-002-ref.html new file mode 100644 index 0000000000..6a0132fb69 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-stripes-002-ref.html @@ -0,0 +1,31 @@ + + + + + CSS Masking: Reftest reference + + + + +

The test passes if you see a green vertical stripe next to a lime green vertical stripe, both stripes should be of equal size and there should be no red.

+
+ + + + + + + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-stripes-003-ref.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-stripes-003-ref.html new file mode 100644 index 0000000000..731ff91533 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-stripes-003-ref.html @@ -0,0 +1,63 @@ + + + + + CSS Masking: Reftest reference + + + +

The test passes if you see a multiple green and blue stripe pairs. For each pair, the blue and green stripe must be of same length.

+
+
+ +
+
+ + + +
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/layout/reftests/svg/svg-integration/clip-path/reftest.list b/layout/reftests/svg/svg-integration/clip-path/reftest.list new file mode 100644 index 0000000000..c7b7cd3ba8 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/reftest.list @@ -0,0 +1,49 @@ +# These tests verify that CSS clip-path behaves properly. +# e.g. clip-path: polygon(nonzero, 3px 3px, 20% 20%) + +default-preferences pref(layout.css.clip-path-shapes.enabled,true) + +# Following tests adapted from W3C csswg-test repo +== clip-path-polygon-001.html clip-path-rectangle-ref.html +== clip-path-polygon-002.html clip-path-rectangle-ref.html +== clip-path-polygon-003.html clip-path-rectangle-ref.html +== clip-path-polygon-004.html clip-path-rectangle-border-ref.html +== clip-path-polygon-005.html clip-path-rectangle-border-ref.html +== clip-path-polygon-006.html clip-path-square-001-ref.html +== clip-path-polygon-007.html clip-path-stripes-001-ref.html +== clip-path-polygon-008.html clip-path-stripes-002-ref.html +== clip-path-polygon-009.html clip-path-square-002-ref.html +== clip-path-polygon-010.html clip-path-stripes-001-ref.html +== clip-path-polygon-011.html clip-path-stripes-001-ref.html +== clip-path-polygon-012.html clip-path-stripes-001-ref.html +== clip-path-polygon-013.html clip-path-stripes-003-ref.html + +== clip-path-circle-001.html clip-path-circle-001-ref.html +== clip-path-circle-002.html clip-path-circle-001-ref.html +== clip-path-circle-003.html clip-path-circle-001-ref.html +== clip-path-circle-004.html clip-path-circle-001-ref.html +== clip-path-circle-005.html clip-path-circle-002-ref.html +== clip-path-circle-006.html clip-path-circle-001-ref.html +== clip-path-circle-007.html clip-path-circle-002-ref.html +== clip-path-circle-008.html clip-path-circle-002-ref.html +== clip-path-circle-009.html clip-path-circle-003-ref.html +== clip-path-circle-010.html clip-path-circle-004-ref.html +== clip-path-circle-011.html clip-path-circle-005-ref.html +== clip-path-circle-012.html clip-path-circle-006-ref.html +== clip-path-circle-013.html clip-path-circle-002-ref.html +== clip-path-circle-014.html clip-path-circle-007-ref.html +== clip-path-circle-015.html clip-path-circle-008-ref.html +== clip-path-circle-016.html clip-path-circle-009-ref.html +== clip-path-circle-017.html clip-path-circle-007-ref.html +== clip-path-circle-018.html clip-path-circle-010-ref.html +== clip-path-circle-019.html clip-path-circle-002-ref.html +== clip-path-circle-020.html clip-path-circle-002-ref.html + +== clip-path-ellipse-001.html clip-path-ellipse-001-ref.html +== clip-path-ellipse-002.html clip-path-ellipse-001-ref.html +== clip-path-ellipse-003.html clip-path-ellipse-001-ref.html +== clip-path-ellipse-004.html clip-path-ellipse-001-ref.html +== clip-path-ellipse-005.html clip-path-ellipse-001-ref.html +== clip-path-ellipse-006.html clip-path-ellipse-001-ref.html +== clip-path-ellipse-007.html clip-path-ellipse-001-ref.html +== clip-path-ellipse-008.html clip-path-ellipse-001-ref.html diff --git a/layout/reftests/svg/svg-integration/reftest.list b/layout/reftests/svg/svg-integration/reftest.list index cff33a98fe..dc87098899 100644 --- a/layout/reftests/svg/svg-integration/reftest.list +++ b/layout/reftests/svg/svg-integration/reftest.list @@ -1,3 +1,6 @@ +# clip-path tests +include clip-path/reftest.list + == clipPath-html-01.xhtml clipPath-html-01-ref.svg == clipPath-html-01-extref.xhtml clipPath-html-01-ref.svg == clipPath-html-02.xhtml clipPath-html-02-ref.svg diff --git a/layout/reftests/w3c-css/submitted/masking/blue-100x50-transparent-100X50.png b/layout/reftests/w3c-css/submitted/masking/blue-100x50-transparent-100X50.png new file mode 100644 index 0000000000000000000000000000000000000000..65b1e88a560e4901b97013529f7afc9a0a82818c GIT binary patch literal 237 zcmeAS@N?(olHy`uVBq!ia0vp^DIm3 + + + + \ No newline at end of file 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 new file mode 100644 index 0000000000..4419f7b7d8 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-composite-1-ref.html @@ -0,0 +1,33 @@ + + + + + CSS mask-composite reference + + + + + +
+
+ + \ No newline at end of file diff --git a/layout/reftests/w3c-css/submitted/masking/mask-composite-1a.html b/layout/reftests/w3c-css/submitted/masking/mask-composite-1a.html new file mode 100644 index 0000000000..86c1e036a2 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-composite-1a.html @@ -0,0 +1,51 @@ + + + + + CSS Masking: mask-composite: compose vector image + + + + + + + + +
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-composite-1b.html b/layout/reftests/w3c-css/submitted/masking/mask-composite-1b.html new file mode 100644 index 0000000000..dd3e1e5e49 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-composite-1b.html @@ -0,0 +1,51 @@ + + + + + CSS Masking: mask-composite: compose raster image + + + + + + + + +
+
+
+
+ + \ No newline at end of file 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 new file mode 100644 index 0000000000..be1fc6d99b --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-composite-2-ref.html @@ -0,0 +1,39 @@ + + + + + CSS mask-composite reference + + + + + +
+
+
+ + \ No newline at end of file diff --git a/layout/reftests/w3c-css/submitted/masking/mask-composite-2a.html b/layout/reftests/w3c-css/submitted/masking/mask-composite-2a.html new file mode 100644 index 0000000000..9c81a43322 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-composite-2a.html @@ -0,0 +1,51 @@ + + + + + CSS Masking: mask-composite: compose vector image + + + + + + + + +
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-composite-2b.html b/layout/reftests/w3c-css/submitted/masking/mask-composite-2b.html new file mode 100644 index 0000000000..9092c1e7f0 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-composite-2b.html @@ -0,0 +1,51 @@ + + + + + CSS Masking: mask-composite: compose raster image + + + + + + + + +
+
+
+
+ + \ No newline at end of file diff --git a/layout/reftests/w3c-css/submitted/masking/reftest.list b/layout/reftests/w3c-css/submitted/masking/reftest.list new file mode 100644 index 0000000000..a4f4ff8863 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/reftest.list @@ -0,0 +1,4 @@ +== mask-composite-1a.html mask-composite-1-ref.html +== mask-composite-1b.html mask-composite-1-ref.html +fails-if(cocoaWidget) == mask-composite-2a.html mask-composite-2-ref.html # bug 1231643 +fails-if(cocoaWidget) == mask-composite-2b.html mask-composite-2-ref.html # bug 1231643 diff --git a/layout/reftests/w3c-css/submitted/masking/transparent-100x50-blue-100X50.png b/layout/reftests/w3c-css/submitted/masking/transparent-100x50-blue-100X50.png new file mode 100644 index 0000000000000000000000000000000000000000..f451746654636fdc56b6fdc307ff2f2d12e09b3c GIT binary patch literal 233 zcmeAS@N?(olHy`uVBq!ia0vp^DIm + + + + \ 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 83bdc1167e..ae1f1b42ca 100644 --- a/layout/reftests/w3c-css/submitted/reftest.list +++ b/layout/reftests/w3c-css/submitted/reftest.list @@ -37,6 +37,9 @@ include images3/reftest.list # Lists and Counters Level 3 include lists-3/reftest.list +# Masking Level 1 +include masking/reftest.list + # Media Queries Level 3 # include mediaqueries3/reftest.list diff --git a/layout/style/Declaration.cpp b/layout/style/Declaration.cpp index 8dc4a02b4f..3ecdc67144 100644 --- a/layout/style/Declaration.cpp +++ b/layout/style/Declaration.cpp @@ -201,6 +201,188 @@ Declaration::GetAuthoredValue(nsCSSProperty aProperty, nsAString& aValue) const GetValue(aProperty, aValue, nsCSSValue::eAuthorSpecified); } +void +Declaration::GetImageLayerValue( + nsCSSCompressedDataBlock *data, + nsAString& aValue, + nsCSSValue::Serialization aSerialization, + const nsCSSProperty aTable[]) const +{ + // We know from our caller that all subproperties were specified. + // However, we still can't represent that in the shorthand unless + // they're all lists of the same length. So if they're different + // lengths, we need to bail out. + // We also need to bail out if an item has background-clip and + // background-origin that are different and not the default + // values. (We omit them if they're both default.) + + // Common CSS properties for both background & mask layer. + const nsCSSValueList *image = + data->ValueFor(aTable[nsStyleImageLayers::image])->GetListValue(); + const nsCSSValuePairList *repeat = + data->ValueFor(aTable[nsStyleImageLayers::repeat])->GetPairListValue(); + const nsCSSValueList *position = + data->ValueFor(aTable[nsStyleImageLayers::position])->GetListValue(); + const nsCSSValueList *clip = + data->ValueFor(aTable[nsStyleImageLayers::clip])->GetListValue(); + const nsCSSValueList *origin = + data->ValueFor(aTable[nsStyleImageLayers::origin])->GetListValue(); + const nsCSSValuePairList *size = + data->ValueFor(aTable[nsStyleImageLayers::size])->GetPairListValue(); + + // Background layer property. + const nsCSSValueList *attachment = + (aTable[nsStyleImageLayers::attachment] == eCSSProperty_UNKNOWN)? + nullptr : + data->ValueFor(aTable[nsStyleImageLayers::attachment])->GetListValue(); + + // Mask layer properties. + const nsCSSValueList *composite = + (aTable[nsStyleImageLayers::composite] == eCSSProperty_UNKNOWN)? + nullptr : + data->ValueFor(aTable[nsStyleImageLayers::composite])->GetListValue(); + const nsCSSValueList *mode = + (aTable[nsStyleImageLayers::maskMode] == eCSSProperty_UNKNOWN)? + nullptr : + data->ValueFor(aTable[nsStyleImageLayers::maskMode])->GetListValue(); + + for (;;) { + // Serialize background-color at the beginning of the last item. + if (!image->mNext) { + if (aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN) { + AppendValueToString(aTable[nsStyleImageLayers::color], aValue, + aSerialization); + aValue.Append(char16_t(' ')); + } + } + + image->mValue.AppendToString(aTable[nsStyleImageLayers::image], aValue, + aSerialization); + + aValue.Append(char16_t(' ')); + repeat->mXValue.AppendToString(aTable[nsStyleImageLayers::repeat], aValue, + aSerialization); + if (repeat->mYValue.GetUnit() != eCSSUnit_Null) { + repeat->mYValue.AppendToString(aTable[nsStyleImageLayers::repeat], aValue, + aSerialization); + } + + if (attachment) { + aValue.Append(char16_t(' ')); + attachment->mValue.AppendToString(aTable[nsStyleImageLayers::attachment], + aValue, aSerialization); + } + + aValue.Append(char16_t(' ')); + position->mValue.AppendToString(aTable[nsStyleImageLayers::position], + aValue, aSerialization); + + if (size->mXValue.GetUnit() != eCSSUnit_Auto || + size->mYValue.GetUnit() != eCSSUnit_Auto) { + aValue.Append(char16_t(' ')); + aValue.Append(char16_t('/')); + aValue.Append(char16_t(' ')); + size->mXValue.AppendToString(aTable[nsStyleImageLayers::size], aValue, + aSerialization); + aValue.Append(char16_t(' ')); + size->mYValue.AppendToString(aTable[nsStyleImageLayers::size], aValue, + aSerialization); + } + + MOZ_ASSERT(clip->mValue.GetUnit() == eCSSUnit_Enumerated && + origin->mValue.GetUnit() == eCSSUnit_Enumerated, + "should not have inherit/initial within list"); + + 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); + 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, + "mask-clip and mask-origin style constants must agree"); + aValue.Append(char16_t(' ')); + origin->mValue.AppendToString(aTable[nsStyleImageLayers::origin], aValue, + aSerialization); + + if (clip->mValue != origin->mValue) { + aValue.Append(char16_t(' ')); + clip->mValue.AppendToString(aTable[nsStyleImageLayers::clip], aValue, + aSerialization); + } + } + + if (composite) { + aValue.Append(char16_t(' ')); + composite->mValue.AppendToString(aTable[nsStyleImageLayers::composite], + aValue, aSerialization); + } + + if (mode) { + aValue.Append(char16_t(' ')); + mode->mValue.AppendToString(aTable[nsStyleImageLayers::maskMode], + aValue, aSerialization); + } + + image = image->mNext; + repeat = repeat->mNext; + position = position->mNext; + clip = clip->mNext; + origin = origin->mNext; + size = size->mNext; + attachment = attachment ? attachment->mNext : nullptr; + composite = composite ? composite->mNext : nullptr; + mode = mode ? mode->mNext : nullptr; + + if (!image) { + // This layer is an background layer + if (aTable == nsStyleImageLayers::kBackgroundLayerTable) { + if (repeat || position || clip || origin || size || attachment) { + // Uneven length lists, so can't be serialized as shorthand. + aValue.Truncate(); + return; + } + // This layer is an mask layer + } else { + MOZ_ASSERT(aTable == nsStyleImageLayers::kMaskLayerTable); + if (repeat || position || clip || origin || size || composite || mode) { + // Uneven length lists, so can't be serialized as shorthand. + aValue.Truncate(); + return; + } + } + break; + } + + // This layer is an background layer + if (aTable == nsStyleImageLayers::kBackgroundLayerTable) { + if (!repeat || !position || !clip || !origin || !size || !attachment) { + // Uneven length lists, so can't be serialized as shorthand. + aValue.Truncate(); + return; + } + // This layer is an mask layer + } else { + MOZ_ASSERT(aTable == nsStyleImageLayers::kMaskLayerTable); + if (!repeat || !position || !clip || !origin || !size || + !composite || !mode) { + // Uneven length lists, so can't be serialized as shorthand. + aValue.Truncate(); + return; + } + } + aValue.Append(char16_t(',')); + aValue.Append(char16_t(' ')); + } +} + void Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue, nsCSSValue::Serialization aSerialization) const @@ -316,10 +498,10 @@ Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue, nsCSSCompressedDataBlock *data = importantCount ? mImportantData : mData; switch (aProperty) { - case eCSSProperty_margin: - case eCSSProperty_padding: - case eCSSProperty_border_color: - case eCSSProperty_border_style: + case eCSSProperty_margin: + case eCSSProperty_padding: + case eCSSProperty_border_color: + case eCSSProperty_border_style: case eCSSProperty_border_width: { const nsCSSProperty* subprops = nsCSSProps::SubpropertyEntryFor(aProperty); @@ -471,124 +653,13 @@ Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue, break; } case eCSSProperty_background: { - // We know from above that all subproperties were specified. - // However, we still can't represent that in the shorthand unless - // they're all lists of the same length. So if they're different - // lengths, we need to bail out. - // We also need to bail out if an item has background-clip and - // background-origin that are different and not the default - // values. (We omit them if they're both default.) - const nsCSSValueList *image = - data->ValueFor(eCSSProperty_background_image)-> - GetListValue(); - const nsCSSValuePairList *repeat = - data->ValueFor(eCSSProperty_background_repeat)-> - GetPairListValue(); - const nsCSSValueList *attachment = - data->ValueFor(eCSSProperty_background_attachment)-> - GetListValue(); - const nsCSSValueList *position = - data->ValueFor(eCSSProperty_background_position)-> - GetListValue(); - const nsCSSValueList *clip = - data->ValueFor(eCSSProperty_background_clip)-> - GetListValue(); - const nsCSSValueList *origin = - data->ValueFor(eCSSProperty_background_origin)-> - GetListValue(); - const nsCSSValuePairList *size = - data->ValueFor(eCSSProperty_background_size)-> - GetPairListValue(); - for (;;) { - // Serialize background-color at the beginning of the last item. - if (!image->mNext) { - AppendValueToString(eCSSProperty_background_color, aValue, - aSerialization); - aValue.Append(char16_t(' ')); - } - - image->mValue.AppendToString(eCSSProperty_background_image, aValue, - aSerialization); - aValue.Append(char16_t(' ')); - repeat->mXValue.AppendToString(eCSSProperty_background_repeat, aValue, - aSerialization); - if (repeat->mYValue.GetUnit() != eCSSUnit_Null) { - repeat->mYValue.AppendToString(eCSSProperty_background_repeat, aValue, - aSerialization); - } - aValue.Append(char16_t(' ')); - attachment->mValue.AppendToString(eCSSProperty_background_attachment, - aValue, aSerialization); - aValue.Append(char16_t(' ')); - position->mValue.AppendToString(eCSSProperty_background_position, - aValue, aSerialization); - - if (size->mXValue.GetUnit() != eCSSUnit_Auto || - size->mYValue.GetUnit() != eCSSUnit_Auto) { - aValue.Append(char16_t(' ')); - aValue.Append(char16_t('/')); - aValue.Append(char16_t(' ')); - size->mXValue.AppendToString(eCSSProperty_background_size, aValue, - aSerialization); - aValue.Append(char16_t(' ')); - size->mYValue.AppendToString(eCSSProperty_background_size, aValue, - aSerialization); - } - - MOZ_ASSERT(clip->mValue.GetUnit() == eCSSUnit_Enumerated && - origin->mValue.GetUnit() == eCSSUnit_Enumerated, - "should not have inherit/initial within list"); - - if (clip->mValue.GetIntValue() != NS_STYLE_IMAGELAYER_CLIP_BORDER || - origin->mValue.GetIntValue() != NS_STYLE_IMAGELAYER_ORIGIN_PADDING) { - MOZ_ASSERT(nsCSSProps::kKeywordTableTable[ - eCSSProperty_background_origin] == - nsCSSProps::kImageLayerOriginKTable); - MOZ_ASSERT(nsCSSProps::kKeywordTableTable[ - eCSSProperty_background_clip] == - nsCSSProps::kImageLayerOriginKTable); - 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"); - aValue.Append(char16_t(' ')); - origin->mValue.AppendToString(eCSSProperty_background_origin, aValue, - aSerialization); - - if (clip->mValue != origin->mValue) { - aValue.Append(char16_t(' ')); - clip->mValue.AppendToString(eCSSProperty_background_clip, aValue, - aSerialization); - } - } - - image = image->mNext; - repeat = repeat->mNext; - attachment = attachment->mNext; - position = position->mNext; - clip = clip->mNext; - origin = origin->mNext; - size = size->mNext; - - if (!image) { - if (repeat || attachment || position || clip || origin || size) { - // Uneven length lists, so can't be serialized as shorthand. - aValue.Truncate(); - return; - } - break; - } - if (!repeat || !attachment || !position || !clip || !origin || !size) { - // Uneven length lists, so can't be serialized as shorthand. - aValue.Truncate(); - return; - } - aValue.Append(char16_t(',')); - aValue.Append(char16_t(' ')); - } + GetImageLayerValue(data, aValue, aSerialization, + nsStyleImageLayers::kBackgroundLayerTable); + break; + } + case eCSSProperty_mask: { + GetImageLayerValue(data, aValue, aSerialization, + nsStyleImageLayers::kMaskLayerTable); break; } case eCSSProperty_font: { diff --git a/layout/style/Declaration.h b/layout/style/Declaration.h index 07e8a706cd..1d4a3e7d46 100644 --- a/layout/style/Declaration.h +++ b/layout/style/Declaration.h @@ -378,6 +378,11 @@ private: void AppendVariableAndValueToString(const nsAString& aName, nsAString& aResult) const; + void GetImageLayerValue(nsCSSCompressedDataBlock *data, + nsAString& aValue, + nsCSSValue::Serialization aSerialization, + const nsCSSProperty aTable[]) const; + public: /** * Returns the property at the given index in the ordered list of diff --git a/layout/style/StyleAnimationValue.cpp b/layout/style/StyleAnimationValue.cpp index e1f091c71a..76f6b9f54f 100644 --- a/layout/style/StyleAnimationValue.cpp +++ b/layout/style/StyleAnimationValue.cpp @@ -2997,6 +2997,98 @@ SubstitutePixelValues(nsStyleContext* aStyleContext, } } +static void +ExtractImageLayerPositionList(const nsStyleImageLayers& aLayer, + StyleAnimationValue& aComputedValue) +{ + MOZ_ASSERT(aLayer.mPositionCount > 0, "unexpected count"); + + nsAutoPtr result; + nsCSSValueList **resultTail = getter_Transfers(result); + for (uint32_t i = 0, i_end = aLayer.mPositionCount; i != i_end; ++i) { + nsCSSValueList *item = new nsCSSValueList; + *resultTail = item; + resultTail = &item->mNext; + SetPositionValue(aLayer.mLayers[i].mPosition, item->mValue); + } + + aComputedValue.SetAndAdoptCSSValueListValue(result.forget(), + StyleAnimationValue::eUnit_BackgroundPosition); +} + +static void +ExtractImageLayerSizePairList(const nsStyleImageLayers& aLayer, + StyleAnimationValue& aComputedValue) +{ + MOZ_ASSERT(aLayer.mSizeCount > 0, "unexpected count"); + + nsAutoPtr result; + nsCSSValuePairList **resultTail = getter_Transfers(result); + for (uint32_t i = 0, i_end = aLayer.mSizeCount; i != i_end; ++i) { + nsCSSValuePairList *item = new nsCSSValuePairList; + *resultTail = item; + resultTail = &item->mNext; + + const nsStyleImageLayers::Size &size = aLayer.mLayers[i].mSize; + switch (size.mWidthType) { + case nsStyleImageLayers::Size::eContain: + case nsStyleImageLayers::Size::eCover: + item->mXValue.SetIntValue(size.mWidthType, + eCSSUnit_Enumerated); + break; + case nsStyleImageLayers::Size::eAuto: + item->mXValue.SetAutoValue(); + break; + case nsStyleImageLayers::Size::eLengthPercentage: + // XXXbz is there a good reason we can't just + // SetCalcValue(&size.mWidth, item->mXValue) here? + if (!size.mWidth.mHasPercent && + // negative values must have come from calc() + size.mWidth.mLength >= 0) { + MOZ_ASSERT(size.mWidth.mPercent == 0.0f, + "Shouldn't have mPercent"); + nscoordToCSSValue(size.mWidth.mLength, item->mXValue); + } else if (size.mWidth.mLength == 0 && + // negative values must have come from calc() + size.mWidth.mPercent >= 0.0f) { + item->mXValue.SetPercentValue(size.mWidth.mPercent); + } else { + SetCalcValue(&size.mWidth, item->mXValue); + } + break; + } + + switch (size.mHeightType) { + case nsStyleImageLayers::Size::eContain: + case nsStyleImageLayers::Size::eCover: + // leave it null + break; + case nsStyleImageLayers::Size::eAuto: + item->mYValue.SetAutoValue(); + break; + case nsStyleImageLayers::Size::eLengthPercentage: + // XXXbz is there a good reason we can't just + // SetCalcValue(&size.mHeight, item->mYValue) here? + if (!size.mHeight.mHasPercent && + // negative values must have come from calc() + size.mHeight.mLength >= 0) { + MOZ_ASSERT(size.mHeight.mPercent == 0.0f, + "Shouldn't have mPercent"); + nscoordToCSSValue(size.mHeight.mLength, item->mYValue); + } else if (size.mHeight.mLength == 0 && + // negative values must have come from calc() + size.mHeight.mPercent >= 0.0f) { + item->mYValue.SetPercentValue(size.mHeight.mPercent); + } else { + SetCalcValue(&size.mHeight, item->mYValue); + } + break; + } + } + + aComputedValue.SetAndAdoptCSSValuePairListValue(result.forget()); +} + bool StyleAnimationValue::ExtractComputedValue(nsCSSProperty aProperty, nsStyleContext* aStyleContext, @@ -3303,92 +3395,30 @@ StyleAnimationValue::ExtractComputedValue(nsCSSProperty aProperty, } case eCSSProperty_background_position: { - const nsStyleBackground *bg = - static_cast(styleStruct); - nsAutoPtr result; - nsCSSValueList **resultTail = getter_Transfers(result); - MOZ_ASSERT(bg->mLayers.mPositionCount > 0, "unexpected count"); - for (uint32_t i = 0, i_end = bg->mLayers.mPositionCount; i != i_end; ++i) { - nsCSSValueList *item = new nsCSSValueList; - *resultTail = item; - resultTail = &item->mNext; - SetPositionValue(bg->mLayers.mLayers[i].mPosition, item->mValue); - } + const nsStyleImageLayers& layers = + static_cast(styleStruct)->mImage; + ExtractImageLayerPositionList(layers, aComputedValue); + break; + } - aComputedValue.SetAndAdoptCSSValueListValue(result.forget(), - eUnit_BackgroundPosition); + case eCSSProperty_mask_position: { + const nsStyleImageLayers& layers = + static_cast(styleStruct)->mMask; + ExtractImageLayerPositionList(layers, aComputedValue); break; } case eCSSProperty_background_size: { - const nsStyleBackground *bg = - static_cast(styleStruct); - nsAutoPtr result; - nsCSSValuePairList **resultTail = getter_Transfers(result); - MOZ_ASSERT(bg->mLayers.mSizeCount > 0, "unexpected count"); - for (uint32_t i = 0, i_end = bg->mLayers.mSizeCount; i != i_end; ++i) { - nsCSSValuePairList *item = new nsCSSValuePairList; - *resultTail = item; - resultTail = &item->mNext; + const nsStyleImageLayers& layers = + static_cast(styleStruct)->mImage; + ExtractImageLayerSizePairList(layers, aComputedValue); + break; + } - const nsStyleImageLayers::Size &size = bg->mLayers.mLayers[i].mSize; - switch (size.mWidthType) { - case nsStyleImageLayers::Size::eContain: - case nsStyleImageLayers::Size::eCover: - item->mXValue.SetIntValue(size.mWidthType, - eCSSUnit_Enumerated); - break; - case nsStyleImageLayers::Size::eAuto: - item->mXValue.SetAutoValue(); - break; - case nsStyleImageLayers::Size::eLengthPercentage: - // XXXbz is there a good reason we can't just - // SetCalcValue(&size.mWidth, item->mXValue) here? - if (!size.mWidth.mHasPercent && - // negative values must have come from calc() - size.mWidth.mLength >= 0) { - MOZ_ASSERT(size.mWidth.mPercent == 0.0f, - "Shouldn't have mPercent"); - nscoordToCSSValue(size.mWidth.mLength, item->mXValue); - } else if (size.mWidth.mLength == 0 && - // negative values must have come from calc() - size.mWidth.mPercent >= 0.0f) { - item->mXValue.SetPercentValue(size.mWidth.mPercent); - } else { - SetCalcValue(&size.mWidth, item->mXValue); - } - break; - } - - switch (size.mHeightType) { - case nsStyleImageLayers::Size::eContain: - case nsStyleImageLayers::Size::eCover: - // leave it null - break; - case nsStyleImageLayers::Size::eAuto: - item->mYValue.SetAutoValue(); - break; - case nsStyleImageLayers::Size::eLengthPercentage: - // XXXbz is there a good reason we can't just - // SetCalcValue(&size.mHeight, item->mYValue) here? - if (!size.mHeight.mHasPercent && - // negative values must have come from calc() - size.mHeight.mLength >= 0) { - MOZ_ASSERT(size.mHeight.mPercent == 0.0f, - "Shouldn't have mPercent"); - nscoordToCSSValue(size.mHeight.mLength, item->mYValue); - } else if (size.mHeight.mLength == 0 && - // negative values must have come from calc() - size.mHeight.mPercent >= 0.0f) { - item->mYValue.SetPercentValue(size.mHeight.mPercent); - } else { - SetCalcValue(&size.mHeight, item->mYValue); - } - break; - } - } - - aComputedValue.SetAndAdoptCSSValuePairListValue(result.forget()); + case eCSSProperty_mask_size: { + const nsStyleImageLayers& layers = + static_cast(styleStruct)->mMask; + ExtractImageLayerSizePairList(layers, aComputedValue); break; } diff --git a/layout/style/nsCSSKeywordList.h b/layout/style/nsCSSKeywordList.h index 609dd6f7f7..c51a31a053 100644 --- a/layout/style/nsCSSKeywordList.h +++ b/layout/style/nsCSSKeywordList.h @@ -130,6 +130,7 @@ CSS_KEY(absolute, absolute) CSS_KEY(active, active) CSS_KEY(activeborder, activeborder) CSS_KEY(activecaption, activecaption) +CSS_KEY(add, add) CSS_KEY(additive, additive) CSS_KEY(alias, alias) CSS_KEY(all, all) @@ -255,6 +256,7 @@ CSS_KEY(enabled, enabled) CSS_KEY(end, end) CSS_KEY(ethiopic-numeric, ethiopic_numeric) CSS_KEY(ex, ex) +CSS_KEY(exclude, exclude) CSS_KEY(exclusion, exclusion) CSS_KEY(expanded, expanded) CSS_KEY(extends, extends) @@ -323,6 +325,7 @@ CSS_KEY(inset, inset) CSS_KEY(inside, inside) // CSS_KEY(inter-character, inter_character) // TODO see bug 1055672 CSS_KEY(interpolatematrix, interpolatematrix) +CSS_KEY(intersect, intersect) CSS_KEY(isolate, isolate) CSS_KEY(invert, invert) CSS_KEY(italic, italic) @@ -545,6 +548,7 @@ CSS_KEY(styleset, styleset) CSS_KEY(stylistic, stylistic) CSS_KEY(sub, sub) CSS_KEY(subgrid, subgrid) +CSS_KEY(substract, substract) CSS_KEY(super, super) CSS_KEY(sw-resize, sw_resize) CSS_KEY(swash, swash) diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 26560236f5..5d09820ac2 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -850,19 +850,19 @@ protected: #endif // Property specific parsing routines - bool ParseBackground(); + bool ParseImageLayers(const nsCSSProperty aTable[]); struct ImageLayersShorthandParseState { nsCSSValue& mColor; nsCSSValueList* mImage; nsCSSValuePairList* mRepeat; - nsCSSValueList* mAttachment; + nsCSSValueList* mAttachment; // A property for background layer only nsCSSValueList* mClip; nsCSSValueList* mOrigin; nsCSSValueList* mPosition; nsCSSValuePairList* mSize; - nsCSSValueList* mComposite; - nsCSSValueList* mMode; + nsCSSValueList* mComposite; // A property for mask layer only + nsCSSValueList* mMode; // A property for mask layer only ImageLayersShorthandParseState( nsCSSValue& aColor, nsCSSValueList* aImage, nsCSSValuePairList* aRepeat, nsCSSValueList* aAttachment, nsCSSValueList* aClip, @@ -876,7 +876,8 @@ protected: }; bool IsFunctionTokenValidForImageLayerImage(const nsCSSToken& aToken) const; - bool ParseBackgroundItem(ImageLayersShorthandParseState& aState); + bool ParseImageLayersItem(ImageLayersShorthandParseState& aState, + const nsCSSProperty aTable[]); bool ParseValueList(nsCSSProperty aPropID); // a single value prop-id bool ParseImageLayerRepeat(nsCSSProperty aPropID); @@ -11312,7 +11313,7 @@ CSSParserImpl::ParsePropertyByFunction(nsCSSProperty aPropID) { switch (aPropID) { // handle shorthand or multiple properties case eCSSProperty_background: - return ParseBackground(); + return ParseImageLayers(nsStyleImageLayers::kBackgroundLayerTable); case eCSSProperty_background_repeat: return ParseImageLayerRepeat(eCSSProperty_background_repeat); case eCSSProperty_background_position: @@ -11492,6 +11493,14 @@ CSSParserImpl::ParsePropertyByFunction(nsCSSProperty aPropID) return ParseClipPath(); case eCSSProperty_scroll_snap_type: return ParseScrollSnapType(); + case eCSSProperty_mask: + return ParseImageLayers(nsStyleImageLayers::kMaskLayerTable); + case eCSSProperty_mask_repeat: + return ParseImageLayerRepeat(eCSSProperty_mask_repeat); + case eCSSProperty_mask_position: + return ParseImageLayerPosition(eCSSProperty_mask_position); + case eCSSProperty_mask_size: + return ParseImageLayerSize(eCSSProperty_mask_size); case eCSSProperty_all: return ParseAll(); default: @@ -11745,7 +11754,7 @@ BoxPositionMaskToCSSValue(int32_t aMask, bool isX) } bool -CSSParserImpl::ParseBackground() +CSSParserImpl::ParseImageLayers(const nsCSSProperty aTable[]) { nsAutoParseCompoundProperty compound(this); @@ -11756,67 +11765,94 @@ CSSParserImpl::ParseBackground() if (ParseSingleTokenVariant(color, VARIANT_INHERIT, nullptr)) { // must be alone for (const nsCSSProperty* subprops = - nsCSSProps::SubpropertyEntryFor(eCSSProperty_background); + nsCSSProps::SubpropertyEntryFor(aTable[nsStyleImageLayers::shorthand]); *subprops != eCSSProperty_UNKNOWN; ++subprops) { AppendValue(*subprops, color); } return true; } - nsCSSValue image, repeat, attachment, clip, origin, position, size, composite, mode; + nsCSSValue image, repeat, attachment, clip, origin, position, size, + composite, maskMode; ImageLayersShorthandParseState state(color, image.SetListValue(), repeat.SetPairListValue(), attachment.SetListValue(), clip.SetListValue(), origin.SetListValue(), position.SetListValue(), size.SetPairListValue(), composite.SetListValue(), - mode.SetListValue()); + maskMode.SetListValue()); for (;;) { - if (!ParseBackgroundItem(state)) { + if (!ParseImageLayersItem(state, aTable)) { return false; } + // If we saw a color, this must be the last item. if (color.GetUnit() != eCSSUnit_Null) { + MOZ_ASSERT(aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN); break; } + // If there's a comma, expect another item. if (!ExpectSymbol(',', true)) { break; } + +#define APPENDNEXT(propID_, propMember_, propType_) \ + if (aTable[propID_] != eCSSProperty_UNKNOWN) { \ + propMember_->mNext = new propType_; \ + propMember_ = propMember_->mNext; \ + } // Chain another entry on all the lists. - state.mImage->mNext = new nsCSSValueList; - state.mImage = state.mImage->mNext; - state.mRepeat->mNext = new nsCSSValuePairList; - state.mRepeat = state.mRepeat->mNext; - state.mAttachment->mNext = new nsCSSValueList; - state.mAttachment = state.mAttachment->mNext; - state.mClip->mNext = new nsCSSValueList; - state.mClip = state.mClip->mNext; - state.mOrigin->mNext = new nsCSSValueList; - state.mOrigin = state.mOrigin->mNext; - state.mPosition->mNext = new nsCSSValueList; - state.mPosition = state.mPosition->mNext; - state.mSize->mNext = new nsCSSValuePairList; - state.mSize = state.mSize->mNext; + APPENDNEXT(nsStyleImageLayers::image, state.mImage, + nsCSSValueList); + APPENDNEXT(nsStyleImageLayers::repeat, state.mRepeat, + nsCSSValuePairList); + APPENDNEXT(nsStyleImageLayers::clip, state.mClip, + nsCSSValueList); + APPENDNEXT(nsStyleImageLayers::origin, state.mOrigin, + nsCSSValueList); + APPENDNEXT(nsStyleImageLayers::position, state.mPosition, + nsCSSValueList); + APPENDNEXT(nsStyleImageLayers::size, state.mSize, + nsCSSValuePairList); + APPENDNEXT(nsStyleImageLayers::attachment, state.mAttachment, + nsCSSValueList); + APPENDNEXT(nsStyleImageLayers::maskMode, state.mMode, + nsCSSValueList); + APPENDNEXT(nsStyleImageLayers::composite, state.mComposite, + nsCSSValueList); +#undef APPENDNEXT } // If we get to this point without seeing a color, provide a default. - if (color.GetUnit() == eCSSUnit_Null) { - color.SetIntegerColorValue(NS_RGBA(0,0,0,0), eCSSUnit_RGBAColor); + if (aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN) { + if (color.GetUnit() == eCSSUnit_Null) { + color.SetIntegerColorValue(NS_RGBA(0,0,0,0), eCSSUnit_RGBAColor); + } } - AppendValue(eCSSProperty_background_image, image); - AppendValue(eCSSProperty_background_repeat, repeat); - AppendValue(eCSSProperty_background_attachment, attachment); - AppendValue(eCSSProperty_background_clip, clip); - AppendValue(eCSSProperty_background_origin, origin); - AppendValue(eCSSProperty_background_position, position); - AppendValue(eCSSProperty_background_size, size); - AppendValue(eCSSProperty_background_color, color); +#define APPENDVALUE(propID_, propValue_) \ + if (propID_ != eCSSProperty_UNKNOWN) { \ + AppendValue(propID_, propValue_); \ + } + + APPENDVALUE(aTable[nsStyleImageLayers::image], image); + APPENDVALUE(aTable[nsStyleImageLayers::repeat], repeat); + APPENDVALUE(aTable[nsStyleImageLayers::clip], clip); + APPENDVALUE(aTable[nsStyleImageLayers::origin], origin); + APPENDVALUE(aTable[nsStyleImageLayers::position], position); + APPENDVALUE(aTable[nsStyleImageLayers::size], size); + APPENDVALUE(aTable[nsStyleImageLayers::color], color); + APPENDVALUE(aTable[nsStyleImageLayers::attachment], attachment); + APPENDVALUE(aTable[nsStyleImageLayers::maskMode], maskMode); + APPENDVALUE(aTable[nsStyleImageLayers::composite], composite); + +#undef APPENDVALUE + return true; } -// Helper for ParseBackgroundItem. Returns true if the passed-in nsCSSToken is +// Helper for ParseImageLayersItem. Returns true if the passed-in nsCSSToken is // a function which is accepted for background-image. bool CSSParserImpl::IsFunctionTokenValidForImageLayerImage( @@ -11847,8 +11883,9 @@ CSSParserImpl::IsFunctionTokenValidForImageLayerImage( // Parse one item of the background shorthand property. bool -CSSParserImpl::ParseBackgroundItem(CSSParserImpl::ImageLayersShorthandParseState& aState) - +CSSParserImpl::ParseImageLayersItem( + CSSParserImpl::ImageLayersShorthandParseState& aState, + const nsCSSProperty aTable[]) { // Fill in the values that the shorthand will set if we don't find // other values. @@ -11868,13 +11905,18 @@ CSSParserImpl::ParseBackgroundItem(CSSParserImpl::ImageLayersShorthandParseState positionArr->Item(3).SetPercentValue(0.0f); aState.mSize->mXValue.SetAutoValue(); aState.mSize->mYValue.SetAutoValue(); - + aState.mComposite->mValue.SetIntValue(NS_STYLE_MASK_COMPOSITE_ADD, + eCSSUnit_Enumerated); + aState.mMode->mValue.SetIntValue(NS_STYLE_MASK_MODE_AUTO, + eCSSUnit_Enumerated); bool haveColor = false, haveImage = false, haveRepeat = false, haveAttach = false, havePositionAndSize = false, haveOrigin = false, + haveComposite = false, + haveMode = false, haveSomething = false; while (GetToken(true)) { @@ -11898,18 +11940,20 @@ CSSParserImpl::ParseBackgroundItem(CSSParserImpl::ImageLayersShorthandParseState return false; haveImage = true; if (ParseSingleValueProperty(aState.mImage->mValue, - eCSSProperty_background_image) != + aTable[nsStyleImageLayers::image]) != CSSParseResult::Ok) { NS_NOTREACHED("should be able to parse"); return false; } - } else if (nsCSSProps::FindKeyword(keyword, + } else if (aTable[nsStyleImageLayers::attachment] != + eCSSProperty_UNKNOWN && + nsCSSProps::FindKeyword(keyword, nsCSSProps::kImageLayerAttachmentKTable, dummy)) { if (haveAttach) return false; haveAttach = true; if (ParseSingleValueProperty(aState.mAttachment->mValue, - eCSSProperty_background_attachment) != + aTable[nsStyleImageLayers::attachment]) != CSSParseResult::Ok) { NS_NOTREACHED("should be able to parse"); return false; @@ -11948,7 +11992,7 @@ CSSParserImpl::ParseBackgroundItem(CSSParserImpl::ImageLayersShorthandParseState return false; haveOrigin = true; if (ParseSingleValueProperty(aState.mOrigin->mValue, - eCSSProperty_background_origin) != + aTable[nsStyleImageLayers::origin]) != CSSParseResult::Ok) { NS_NOTREACHED("should be able to parse"); return false; @@ -11959,10 +12003,10 @@ CSSParserImpl::ParseBackgroundItem(CSSParserImpl::ImageLayersShorthandParseState // 'background-clip' and 'background-origin' use the same keyword table MOZ_ASSERT(nsCSSProps::kKeywordTableTable[ - eCSSProperty_background_origin] == + aTable[nsStyleImageLayers::origin]] == nsCSSProps::kImageLayerOriginKTable); MOZ_ASSERT(nsCSSProps::kKeywordTableTable[ - eCSSProperty_background_clip] == + aTable[nsStyleImageLayers::clip]] == nsCSSProps::kImageLayerOriginKTable); static_assert(NS_STYLE_IMAGELAYER_CLIP_BORDER == NS_STYLE_IMAGELAYER_ORIGIN_BORDER && @@ -11974,7 +12018,7 @@ CSSParserImpl::ParseBackgroundItem(CSSParserImpl::ImageLayersShorthandParseState CSSParseResult result = ParseSingleValueProperty(aState.mClip->mValue, - eCSSProperty_background_clip); + aTable[nsStyleImageLayers::clip]); MOZ_ASSERT(result != CSSParseResult::Error, "how can failing to parse a single background-clip value " "consume tokens?"); @@ -11984,15 +12028,41 @@ CSSParserImpl::ParseBackgroundItem(CSSParserImpl::ImageLayersShorthandParseState // See assertions above showing these values are compatible. aState.mClip->mValue = aState.mOrigin->mValue; } - } else { + } else if (aTable[nsStyleImageLayers::composite] != eCSSProperty_UNKNOWN && + nsCSSProps::FindKeyword(keyword, + nsCSSProps::kImageLayerCompositeKTable, dummy)) { + if (haveComposite) + return false; + haveComposite = true; + if (ParseSingleValueProperty(aState.mComposite->mValue, + aTable[nsStyleImageLayers::composite]) != + CSSParseResult::Ok) { + NS_NOTREACHED("should be able to parse"); + return false; + } + } else if (aTable[nsStyleImageLayers::maskMode] != eCSSProperty_UNKNOWN && + nsCSSProps::FindKeyword(keyword, + nsCSSProps::kImageLayerModeKTable, dummy)) { + if (haveMode) + return false; + haveMode = true; + if (ParseSingleValueProperty(aState.mMode->mValue, + aTable[nsStyleImageLayers::maskMode]) != + CSSParseResult::Ok) { + NS_NOTREACHED("should be able to parse"); + return false; + } + } else if (aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN) { if (haveColor) return false; haveColor = true; if (ParseSingleValueProperty(aState.mColor, - eCSSProperty_background_color) != - CSSParseResult::Ok) { + aTable[nsStyleImageLayers::color]) != + CSSParseResult::Ok) { return false; } + } else { + return false; } } else if (tt == eCSSToken_URL || (tt == eCSSToken_Function && @@ -12001,7 +12071,7 @@ CSSParserImpl::ParseBackgroundItem(CSSParserImpl::ImageLayersShorthandParseState return false; haveImage = true; if (ParseSingleValueProperty(aState.mImage->mValue, - eCSSProperty_background_image) != + aTable[nsStyleImageLayers::image]) != CSSParseResult::Ok) { return false; } @@ -12025,18 +12095,21 @@ CSSParserImpl::ParseBackgroundItem(CSSParserImpl::ImageLayersShorthandParseState aState.mSize->mXValue = scratch.mXValue; aState.mSize->mYValue = scratch.mYValue; } - } else { + } else if (aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN) { if (haveColor) return false; haveColor = true; // Note: This parses 'inherit', 'initial' and 'unset', but // we've already checked for them, so it's ok. if (ParseSingleValueProperty(aState.mColor, - eCSSProperty_background_color) != - CSSParseResult::Ok) { + aTable[nsStyleImageLayers::color]) != + CSSParseResult::Ok) { return false; } + } else { + return false; } + haveSomething = true; } @@ -12044,7 +12117,7 @@ CSSParserImpl::ParseBackgroundItem(CSSParserImpl::ImageLayersShorthandParseState } // This function is very similar to ParseScrollSnapCoordinate, -// ParseImageLayerPosition, and ParseBackgroundSize. +// ParseImageLayerPosition, and ParseImageLayersSize. bool CSSParserImpl::ParseValueList(nsCSSProperty aPropID) { @@ -12121,7 +12194,7 @@ CSSParserImpl::ParseImageLayerRepeatValues(nsCSSValuePair& aValue) } // This function is very similar to ParseScrollSnapCoordinate, -// ParseBackgroundList, and ParseBackgroundSize. +// ParseImageLayers, ParseImageLayerSize. bool CSSParserImpl::ParseImageLayerPosition(nsCSSProperty aPropID) { @@ -12439,7 +12512,7 @@ CSSParserImpl::ParsePositionValue(nsCSSValue& aOut) } // This function is very similar to ParseScrollSnapCoordinate, -// ParseBackgroundList, and ParseBackgroundPosition. +// ParseImageLayers, and ParseImageLayerPosition. bool CSSParserImpl::ParseImageLayerSize(nsCSSProperty aPropID) { diff --git a/layout/style/nsCSSPropAliasList.h b/layout/style/nsCSSPropAliasList.h index f048fdca97..fe079695ed 100644 --- a/layout/style/nsCSSPropAliasList.h +++ b/layout/style/nsCSSPropAliasList.h @@ -366,4 +366,41 @@ CSS_PROP_ALIAS(-webkit-user-select, WebkitUserSelect, WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-mask, + mask, + WebkitMask, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-mask-clip, + mask_clip, + WebkitMaskClip, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-mask-composite, + mask_composite, + WebkitMaskComposite, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-mask-image, + mask_image, + WebkitMaskImage, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-mask-mode, + mask_mode, + WebkitMaskMode, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-mask-origin, + mask_origin, + WebkitMaskOrigin, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-mask-position, + mask_position, + WebkitMaskPosition, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-mask-repeat, + mask_repeat, + WebkitMaskRepeat, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-mask-size, + mask_size, + WebkitMaskSize, + WEBKIT_PREFIX_PREF) + #undef WEBKIT_PREFIX_PREF diff --git a/layout/style/nsCSSPropList.h b/layout/style/nsCSSPropList.h index 345a974d97..e04b0c37a4 100644 --- a/layout/style/nsCSSPropList.h +++ b/layout/style/nsCSSPropList.h @@ -3673,7 +3673,7 @@ CSS_PROP_POSITION( kWidthKTable, offsetof(nsStylePosition, mWidth), eStyleAnimType_Coord) -CSS_PROP_USERINTERFACE( +CSS_PROP_UIRESET( -moz-window-dragging, _moz_window_dragging, CSS_PROP_DOMPROP_PREFIXED(WindowDragging), @@ -4074,17 +4074,105 @@ CSS_PROP_SVG( nullptr, CSS_PROP_NO_OFFSET, eStyleAnimType_None) -CSS_PROP_SVGRESET( +CSS_PROP_SHORTHAND( mask, mask, Mask, - CSS_PROPERTY_PARSE_VALUE | - CSS_PROPERTY_CREATES_STACKING_CONTEXT, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_SVGRESET( + mask-clip, + mask_clip, + MaskClip, + CSS_PROPERTY_PARSE_VALUE_LIST | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, "", - VARIANT_HUO, + VARIANT_KEYWORD, // used by list parsing + kImageLayerOriginKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_SVGRESET( + mask-composite, + mask_composite, + MaskComposite, + CSS_PROPERTY_PARSE_VALUE_LIST | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + "", + VARIANT_KEYWORD, // used by list parsing + kImageLayerCompositeKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_SVGRESET( + mask-image, + mask_image, + MaskImage, + CSS_PROPERTY_PARSE_VALUE_LIST | + CSS_PROPERTY_CREATES_STACKING_CONTEXT | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS | + CSS_PROPERTY_START_IMAGE_LOADS, + "", + VARIANT_IMAGE, // used by list parsing nullptr, CSS_PROP_NO_OFFSET, eStyleAnimType_None) +CSS_PROP_SVGRESET( + mask-mode, + mask_mode, + MaskMode, + CSS_PROPERTY_PARSE_VALUE_LIST | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + "", + VARIANT_KEYWORD, // used by list parsing + kImageLayerModeKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_SVGRESET( + mask-origin, + mask_origin, + MaskOrigin, + CSS_PROPERTY_PARSE_VALUE_LIST | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + "", + VARIANT_KEYWORD, // used by list parsing + kImageLayerOriginKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_SVGRESET( + mask-repeat, + mask_repeat, + MaskRepeat, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + "", + VARIANT_KEYWORD, // used by list parsing + kImageLayerRepeatKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_SVGRESET( + mask-position, + mask_position, + MaskPosition, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS | + CSS_PROPERTY_STORES_CALC, + "", + 0, + kImageLayerPositionKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Custom) +CSS_PROP_SVGRESET( + mask-size, + mask_size, + MaskSize, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_STORES_CALC, + "", + 0, + kImageLayerSizeKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Custom) CSS_PROP_SVGRESET( mask-type, mask_type, diff --git a/layout/style/nsCSSProps.cpp b/layout/style/nsCSSProps.cpp index f307af6dfc..dbeb018655 100644 --- a/layout/style/nsCSSProps.cpp +++ b/layout/style/nsCSSProps.cpp @@ -919,6 +919,24 @@ const KTableEntry nsCSSProps::kImageLayerSizeKTable[] = { { eCSSKeyword_UNKNOWN, -1 } }; +const KTableEntry nsCSSProps::kImageLayerModeKTable[] = { + { eCSSKeyword_alpha, NS_STYLE_MASK_MODE_ALPHA }, + { eCSSKeyword_luminance, NS_STYLE_MASK_MODE_LUMINANCE }, + // FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=1224424 + // It's ambigious at mask shorthand parsing while we have both mask-mode:auto + // and mask-size:auto. + { eCSSKeyword_auto, NS_STYLE_MASK_MODE_AUTO }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kImageLayerCompositeKTable[] = { + { eCSSKeyword_add, NS_STYLE_MASK_COMPOSITE_ADD }, + { eCSSKeyword_substract, NS_STYLE_MASK_COMPOSITE_SUBSTRACT }, + { eCSSKeyword_intersect, NS_STYLE_MASK_COMPOSITE_INTERSECT }, + { eCSSKeyword_exclude, NS_STYLE_MASK_COMPOSITE_EXCLUDE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + const KTableEntry nsCSSProps::kBlendModeKTable[] = { { eCSSKeyword_normal, NS_STYLE_BLEND_NORMAL }, { eCSSKeyword_multiply, NS_STYLE_BLEND_MULTIPLY }, @@ -2080,6 +2098,7 @@ const KTableEntry nsCSSProps::kWidthKTable[] = { }; const KTableEntry nsCSSProps::kWindowDraggingKTable[] = { + { eCSSKeyword_default, NS_STYLE_WINDOW_DRAGGING_DEFAULT }, { eCSSKeyword_drag, NS_STYLE_WINDOW_DRAGGING_DRAG }, { eCSSKeyword_no_drag, NS_STYLE_WINDOW_DRAGGING_NO_DRAG }, { eCSSKeyword_UNKNOWN, -1 } @@ -2840,6 +2859,21 @@ static const nsCSSProperty gScrollSnapTypeSubpropTable[] = { eCSSProperty_UNKNOWN }; +static const nsCSSProperty gMaskSubpropTable[] = { + eCSSProperty_mask_image, + eCSSProperty_mask_repeat, + eCSSProperty_mask_position, + eCSSProperty_mask_clip, + eCSSProperty_mask_origin, + eCSSProperty_mask_size, + eCSSProperty_mask_composite, + eCSSProperty_mask_mode, + eCSSProperty_UNKNOWN +}; + +// FIXME: mask-border tables should be added when we implement +// mask-border properties. + const nsCSSProperty *const nsCSSProps::kSubpropertyTable[eCSSProperty_COUNT - eCSSProperty_COUNT_no_shorthands] = { #define CSS_PROP_PUBLIC_OR_PRIVATE(publicname_, privatename_) privatename_ diff --git a/layout/style/nsCSSProps.h b/layout/style/nsCSSProps.h index 55eec06bd2..9afc434d6f 100644 --- a/layout/style/nsCSSProps.h +++ b/layout/style/nsCSSProps.h @@ -676,6 +676,8 @@ public: static const KTableEntry kImageLayerRepeatKTable[]; static const KTableEntry kImageLayerRepeatPartKTable[]; static const KTableEntry kImageLayerSizeKTable[]; + static const KTableEntry kImageLayerCompositeKTable[]; + static const KTableEntry kImageLayerModeKTable[]; static const KTableEntry kBlendModeKTable[]; static const KTableEntry kBorderCollapseKTable[]; static const KTableEntry kBorderColorKTable[]; diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index 6edfa8b658..7e81b13139 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -1826,7 +1826,7 @@ nsComputedDOMStyle::DoGetBackgroundAttachment() { return GetBackgroundList(&nsStyleImageLayers::Layer::mAttachment, &nsStyleImageLayers::mAttachmentCount, - StyleBackground()->mLayers, + StyleBackground()->mImage, nsCSSProps::kImageLayerAttachmentKTable); } @@ -1835,7 +1835,7 @@ nsComputedDOMStyle::DoGetBackgroundClip() { return GetBackgroundList(&nsStyleImageLayers::Layer::mClip, &nsStyleImageLayers::mClipCount, - StyleBackground()->mLayers, + StyleBackground()->mImage, nsCSSProps::kImageLayerOriginKTable); } @@ -2132,16 +2132,14 @@ nsComputedDOMStyle::SetValueToStyleImage(const nsStyleImage& aStyleImage, } already_AddRefed -nsComputedDOMStyle::DoGetBackgroundImage() +nsComputedDOMStyle::DoGetImageLayerImage(const nsStyleImageLayers& aLayers) { - const nsStyleBackground* bg = StyleBackground(); - RefPtr valueList = GetROCSSValueList(true); - for (uint32_t i = 0, i_end = bg->mLayers.mImageCount; i < i_end; ++i) { + for (uint32_t i = 0, i_end = aLayers.mImageCount; i < i_end; ++i) { RefPtr val = new nsROCSSPrimitiveValue; - const nsStyleImage& image = bg->mLayers.mLayers[i].mImage; + const nsStyleImage& image = aLayers.mLayers[i].mImage; SetValueToStyleImage(image, val); valueList->AppendCSSValue(val.forget()); } @@ -2150,63 +2148,14 @@ nsComputedDOMStyle::DoGetBackgroundImage() } already_AddRefed -nsComputedDOMStyle::DoGetBackgroundBlendMode() +nsComputedDOMStyle::DoGetImageLayerPosition(const nsStyleImageLayers& aLayers) { - return GetBackgroundList(&nsStyleImageLayers::Layer::mBlendMode, - &nsStyleImageLayers::mBlendModeCount, - StyleBackground()->mLayers, - nsCSSProps::kBlendModeKTable); -} - -already_AddRefed -nsComputedDOMStyle::DoGetBackgroundOrigin() -{ - return GetBackgroundList(&nsStyleImageLayers::Layer::mOrigin, - &nsStyleImageLayers::mOriginCount, - StyleBackground()->mLayers, - nsCSSProps::kImageLayerOriginKTable); -} - -void -nsComputedDOMStyle::SetValueToPositionCoord( - const nsStyleImageLayers::Position::PositionCoord& aCoord, - nsROCSSPrimitiveValue* aValue) -{ - if (!aCoord.mHasPercent) { - MOZ_ASSERT(aCoord.mPercent == 0.0f, - "Shouldn't have mPercent!"); - aValue->SetAppUnits(aCoord.mLength); - } else if (aCoord.mLength == 0) { - aValue->SetPercent(aCoord.mPercent); - } else { - SetValueToCalc(&aCoord, aValue); - } -} - -void -nsComputedDOMStyle::SetValueToPosition( - const nsStyleImageLayers::Position& aPosition, - nsDOMCSSValueList* aValueList) -{ - RefPtr valX = new nsROCSSPrimitiveValue; - SetValueToPositionCoord(aPosition.mXPosition, valX); - aValueList->AppendCSSValue(valX.forget()); - - RefPtr valY = new nsROCSSPrimitiveValue; - SetValueToPositionCoord(aPosition.mYPosition, valY); - aValueList->AppendCSSValue(valY.forget()); -} - -already_AddRefed -nsComputedDOMStyle::DoGetBackgroundPosition() -{ - const nsStyleBackground* bg = StyleBackground(); - RefPtr valueList = GetROCSSValueList(true); - for (uint32_t i = 0, i_end = bg->mLayers.mPositionCount; i < i_end; ++i) { + for (uint32_t i = 0, i_end = aLayers.mPositionCount; i < i_end; ++i) { RefPtr itemList = GetROCSSValueList(false); - SetValueToPosition(bg->mLayers.mLayers[i].mPosition, itemList); + + SetValueToPosition(aLayers.mLayers[i].mPosition, itemList); valueList->AppendCSSValue(itemList.forget()); } @@ -2214,18 +2163,16 @@ nsComputedDOMStyle::DoGetBackgroundPosition() } already_AddRefed -nsComputedDOMStyle::DoGetBackgroundRepeat() +nsComputedDOMStyle::DoGetImageLayerRepeat(const nsStyleImageLayers& aLayers) { - const nsStyleBackground* bg = StyleBackground(); - RefPtr valueList = GetROCSSValueList(true); - for (uint32_t i = 0, i_end = bg->mLayers.mRepeatCount; i < i_end; ++i) { + for (uint32_t i = 0, i_end = aLayers.mRepeatCount; i < i_end; ++i) { RefPtr itemList = GetROCSSValueList(false); RefPtr valX = new nsROCSSPrimitiveValue; - const uint8_t& xRepeat = bg->mLayers.mLayers[i].mRepeat.mXRepeat; - const uint8_t& yRepeat = bg->mLayers.mLayers[i].mRepeat.mYRepeat; + const uint8_t& xRepeat = aLayers.mLayers[i].mRepeat.mXRepeat; + const uint8_t& yRepeat = aLayers.mLayers[i].mRepeat.mYRepeat; bool hasContraction = true; unsigned contraction; @@ -2264,14 +2211,12 @@ nsComputedDOMStyle::DoGetBackgroundRepeat() } already_AddRefed -nsComputedDOMStyle::DoGetBackgroundSize() +nsComputedDOMStyle::DoGetImageLayerSize(const nsStyleImageLayers& aLayers) { - const nsStyleBackground* bg = StyleBackground(); - RefPtr valueList = GetROCSSValueList(true); - for (uint32_t i = 0, i_end = bg->mLayers.mSizeCount; i < i_end; ++i) { - const nsStyleImageLayers::Size &size = bg->mLayers.mLayers[i].mSize; + for (uint32_t i = 0, i_end = aLayers.mSizeCount; i < i_end; ++i) { + const nsStyleImageLayers::Size &size = aLayers.mLayers[i].mSize; switch (size.mWidthType) { case nsStyleImageLayers::Size::eContain: @@ -2344,6 +2289,82 @@ nsComputedDOMStyle::DoGetBackgroundSize() return valueList.forget(); } +already_AddRefed +nsComputedDOMStyle::DoGetBackgroundImage() +{ + const nsStyleImageLayers& layers = StyleBackground()->mImage; + return DoGetImageLayerImage(layers); +} + +already_AddRefed +nsComputedDOMStyle::DoGetBackgroundBlendMode() +{ + return GetBackgroundList(&nsStyleImageLayers::Layer::mBlendMode, + &nsStyleImageLayers::mBlendModeCount, + StyleBackground()->mImage, + nsCSSProps::kBlendModeKTable); +} + +already_AddRefed +nsComputedDOMStyle::DoGetBackgroundOrigin() +{ + return GetBackgroundList(&nsStyleImageLayers::Layer::mOrigin, + &nsStyleImageLayers::mOriginCount, + StyleBackground()->mImage, + nsCSSProps::kImageLayerOriginKTable); +} + +void +nsComputedDOMStyle::SetValueToPositionCoord( + const nsStyleImageLayers::Position::PositionCoord& aCoord, + nsROCSSPrimitiveValue* aValue) +{ + if (!aCoord.mHasPercent) { + MOZ_ASSERT(aCoord.mPercent == 0.0f, + "Shouldn't have mPercent!"); + aValue->SetAppUnits(aCoord.mLength); + } else if (aCoord.mLength == 0) { + aValue->SetPercent(aCoord.mPercent); + } else { + SetValueToCalc(&aCoord, aValue); + } +} + +void +nsComputedDOMStyle::SetValueToPosition( + const nsStyleImageLayers::Position& aPosition, + nsDOMCSSValueList* aValueList) +{ + RefPtr valX = new nsROCSSPrimitiveValue; + SetValueToPositionCoord(aPosition.mXPosition, valX); + aValueList->AppendCSSValue(valX.forget()); + + RefPtr valY = new nsROCSSPrimitiveValue; + SetValueToPositionCoord(aPosition.mYPosition, valY); + aValueList->AppendCSSValue(valY.forget()); +} + +already_AddRefed +nsComputedDOMStyle::DoGetBackgroundPosition() +{ + const nsStyleImageLayers& layers = StyleBackground()->mImage; + return DoGetImageLayerPosition(layers); +} + +already_AddRefed +nsComputedDOMStyle::DoGetBackgroundRepeat() +{ + const nsStyleImageLayers& layers = StyleBackground()->mImage; + return DoGetImageLayerRepeat(layers); +} + +already_AddRefed +nsComputedDOMStyle::DoGetBackgroundSize() +{ + const nsStyleImageLayers& layers = StyleBackground()->mImage; + return DoGetImageLayerSize(layers); +} + already_AddRefed nsComputedDOMStyle::DoGetGridTemplateAreas() { @@ -3825,7 +3846,7 @@ nsComputedDOMStyle::DoGetWindowDragging() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( - nsCSSProps::ValueToKeywordEnum(StyleUserInterface()->mWindowDragging, + nsCSSProps::ValueToKeywordEnum(StyleUIReset()->mWindowDragging, nsCSSProps::kWindowDraggingKTable)); return val.forget(); } @@ -6004,18 +6025,100 @@ nsComputedDOMStyle::DoGetFilter() already_AddRefed nsComputedDOMStyle::DoGetMask() { + const nsStyleSVGReset* svg = StyleSVGReset(); + const nsStyleImageLayers::Layer& firstLayer = svg->mMask.mLayers[0]; + + // Mask is now a shorthand, but it used to be a longhand, so that we + // need to support computed style for the cases where it used to be + // a longhand. + if (svg->mMask.mImageCount > 1 || + firstLayer.mClip != NS_STYLE_IMAGELAYER_CLIP_BORDER || + firstLayer.mOrigin != NS_STYLE_IMAGELAYER_ORIGIN_PADDING || + firstLayer.mComposite != NS_STYLE_MASK_COMPOSITE_ADD || + firstLayer.mMaskMode != NS_STYLE_MASK_MODE_AUTO || + !firstLayer.mPosition.IsInitialValue() || + !firstLayer.mRepeat.IsInitialValue() || + !firstLayer.mSize.IsInitialValue() || + !(firstLayer.mImage.GetType() == eStyleImageType_Null || + firstLayer.mImage.GetType() == eStyleImageType_Image)){ + return nullptr; + } + RefPtr val = new nsROCSSPrimitiveValue; - const nsStyleSVGReset* svg = StyleSVGReset(); - - if (svg->mMask) - val->SetURI(svg->mMask); - else + if (firstLayer.mSourceURI) { + val->SetURI(firstLayer.mSourceURI); + } else { val->SetIdent(eCSSKeyword_none); + } return val.forget(); } +already_AddRefed +nsComputedDOMStyle::DoGetMaskClip() +{ + return GetBackgroundList(&nsStyleImageLayers::Layer::mClip, + &nsStyleImageLayers::mClipCount, + StyleSVGReset()->mMask, + nsCSSProps::kImageLayerOriginKTable); +} + +already_AddRefed +nsComputedDOMStyle::DoGetMaskComposite() +{ + return GetBackgroundList(&nsStyleImageLayers::Layer::mComposite, + &nsStyleImageLayers::mCompositeCount, + StyleSVGReset()->mMask, + nsCSSProps::kImageLayerCompositeKTable); +} + +already_AddRefed +nsComputedDOMStyle::DoGetMaskImage() +{ + const nsStyleImageLayers& layers = StyleSVGReset()->mMask; + return DoGetImageLayerImage(layers); +} + +already_AddRefed +nsComputedDOMStyle::DoGetMaskMode() +{ + return GetBackgroundList(&nsStyleImageLayers::Layer::mMaskMode, + &nsStyleImageLayers::mMaskModeCount, + StyleSVGReset()->mMask, + nsCSSProps::kImageLayerModeKTable); +} + +already_AddRefed +nsComputedDOMStyle::DoGetMaskOrigin() +{ + return GetBackgroundList(&nsStyleImageLayers::Layer::mOrigin, + &nsStyleImageLayers::mOriginCount, + StyleSVGReset()->mMask, + nsCSSProps::kImageLayerOriginKTable); +} + +already_AddRefed +nsComputedDOMStyle::DoGetMaskPosition() +{ + const nsStyleImageLayers& layers = StyleSVGReset()->mMask; + return DoGetImageLayerPosition(layers); +} + +already_AddRefed +nsComputedDOMStyle::DoGetMaskRepeat() +{ + const nsStyleImageLayers& layers = StyleSVGReset()->mMask; + return DoGetImageLayerRepeat(layers); +} + +already_AddRefed +nsComputedDOMStyle::DoGetMaskSize() +{ + const nsStyleImageLayers& layers = StyleSVGReset()->mMask; + return DoGetImageLayerSize(layers); +} + already_AddRefed nsComputedDOMStyle::DoGetMaskType() { diff --git a/layout/style/nsComputedDOMStyle.h b/layout/style/nsComputedDOMStyle.h index 298b4030bc..b6d43471f5 100644 --- a/layout/style/nsComputedDOMStyle.h +++ b/layout/style/nsComputedDOMStyle.h @@ -283,6 +283,12 @@ private: already_AddRefed DoGetGridColumnGap(); already_AddRefed DoGetGridRowGap(); + /* StyleImageLayer properties */ + already_AddRefed DoGetImageLayerImage(const nsStyleImageLayers& aLayers); + already_AddRefed DoGetImageLayerPosition(const nsStyleImageLayers& aLayers); + already_AddRefed DoGetImageLayerRepeat(const nsStyleImageLayers& aLayers); + already_AddRefed DoGetImageLayerSize(const nsStyleImageLayers& aLayers); + /* Background properties */ already_AddRefed DoGetBackgroundAttachment(); already_AddRefed DoGetBackgroundColor(); @@ -294,6 +300,17 @@ private: already_AddRefed DoGetBackgroundOrigin(); already_AddRefed DoGetBackgroundSize(); + /* Mask properties */ + already_AddRefed DoGetMask(); + already_AddRefed DoGetMaskImage(); + already_AddRefed DoGetMaskPosition(); + already_AddRefed DoGetMaskRepeat(); + already_AddRefed DoGetMaskClip(); + already_AddRefed DoGetMaskOrigin(); + already_AddRefed DoGetMaskSize(); + already_AddRefed DoGetMaskMode(); + already_AddRefed DoGetMaskComposite(); + /* Padding properties */ already_AddRefed DoGetPaddingTop(); already_AddRefed DoGetPaddingBottom(); @@ -542,7 +559,6 @@ private: already_AddRefed DoGetClipPath(); already_AddRefed DoGetFilter(); - already_AddRefed DoGetMask(); already_AddRefed DoGetMaskType(); already_AddRefed DoGetPaintOrder(); diff --git a/layout/style/nsComputedDOMStylePropertyList.h b/layout/style/nsComputedDOMStylePropertyList.h index 4f3d6ebeba..6a658644e5 100644 --- a/layout/style/nsComputedDOMStylePropertyList.h +++ b/layout/style/nsComputedDOMStylePropertyList.h @@ -318,6 +318,14 @@ COMPUTED_STYLE_PROP(marker_end, MarkerEnd) COMPUTED_STYLE_PROP(marker_mid, MarkerMid) COMPUTED_STYLE_PROP(marker_start, MarkerStart) COMPUTED_STYLE_PROP(mask, Mask) +COMPUTED_STYLE_PROP(mask_clip, MaskClip) +COMPUTED_STYLE_PROP(mask_composite, MaskComposite) +COMPUTED_STYLE_PROP(mask_image, MaskImage) +COMPUTED_STYLE_PROP(mask_mode, MaskMode) +COMPUTED_STYLE_PROP(mask_origin, MaskOrigin) +COMPUTED_STYLE_PROP(mask_position, MaskPosition) +COMPUTED_STYLE_PROP(mask_repeat, MaskRepeat) +COMPUTED_STYLE_PROP(mask_size, MaskSize) COMPUTED_STYLE_PROP(mask_type, MaskType) COMPUTED_STYLE_PROP(paint_order, PaintOrder) COMPUTED_STYLE_PROP(shape_rendering, ShapeRendering) diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index 44b086de13..a65c880917 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -4865,13 +4865,6 @@ nsRuleNode::ComputeUserInterfaceData(void* aStartStruct, parentUI->mUserFocus, NS_STYLE_USER_FOCUS_NONE, 0, 0, 0, 0); - // -moz-window-dragging: enum, inherit, initial - SetDiscrete(*aRuleData->ValueForWindowDragging(), - ui->mWindowDragging, conditions, - SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, - parentUI->mWindowDragging, - NS_STYLE_WINDOW_DRAGGING_NO_DRAG, 0, 0, 0, 0); - COMPUTE_END_INHERITED(UserInterface, ui) } @@ -4907,6 +4900,13 @@ nsRuleNode::ComputeUIResetData(void* aStartStruct, parentUI->mForceBrokenImageIcon, 0, 0, 0, 0, 0); + // -moz-window-dragging: enum, inherit, initial + SetDiscrete(*aRuleData->ValueForWindowDragging(), + ui->mWindowDragging, conditions, + SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, + parentUI->mWindowDragging, + NS_STYLE_WINDOW_DRAGGING_DEFAULT, 0, 0, 0, 0); + // -moz-window-shadow: enum, inherit, initial SetDiscrete(*aRuleData->ValueForWindowShadow(), ui->mWindowShadow, conditions, @@ -5095,6 +5095,25 @@ nsRuleNode::ComputeTimingFunction(const nsCSSValue& aValue, } } +static uint8_t +GetWillChangeBitFieldFromPropFlags(const nsCSSProperty& aProp) +{ + uint8_t willChangeBitField = 0; + if (nsCSSProps::PropHasFlags(aProp, CSS_PROPERTY_CREATES_STACKING_CONTEXT)) { + willChangeBitField |= NS_STYLE_WILL_CHANGE_STACKING_CONTEXT; + } + + if (nsCSSProps::PropHasFlags(aProp, CSS_PROPERTY_FIXPOS_CB)) { + willChangeBitField |= NS_STYLE_WILL_CHANGE_FIXPOS_CB; + } + + if (nsCSSProps::PropHasFlags(aProp, CSS_PROPERTY_ABSPOS_CB)) { + willChangeBitField |= NS_STYLE_WILL_CHANGE_ABSPOS_CB; + } + + return willChangeBitField; +} + const void* nsRuleNode::ComputeDisplayData(void* aStartStruct, const nsRuleData* aRuleData, @@ -6105,16 +6124,16 @@ nsRuleNode::ComputeDisplayData(void* aStartStruct, nsCSSProps::eEnabledForAllContent); if (prop != eCSSProperty_UNKNOWN && prop != eCSSPropertyExtra_variable) { - if (nsCSSProps::PropHasFlags(prop, - CSS_PROPERTY_CREATES_STACKING_CONTEXT)) { - display->mWillChangeBitField |= - NS_STYLE_WILL_CHANGE_STACKING_CONTEXT; - } - if (nsCSSProps::PropHasFlags(prop, CSS_PROPERTY_FIXPOS_CB)) { - display->mWillChangeBitField |= NS_STYLE_WILL_CHANGE_FIXPOS_CB; - } - if (nsCSSProps::PropHasFlags(prop, CSS_PROPERTY_ABSPOS_CB)) { - display->mWillChangeBitField |= NS_STYLE_WILL_CHANGE_ABSPOS_CB; + // If the property given is a shorthand, it indicates the expectation + // for all the longhands the shorthand expands to. + if (nsCSSProps::IsShorthand(prop)) { + for (const nsCSSProperty* shorthands = + nsCSSProps::SubpropertyEntryFor(prop); + *shorthands != eCSSProperty_UNKNOWN; ++shorthands) { + display->mWillChangeBitField |= GetWillChangeBitFieldFromPropFlags(*shorthands); + } + } else { + display->mWillChangeBitField |= GetWillChangeBitFieldFromPropFlags(prop); } } } @@ -6454,6 +6473,22 @@ struct BackgroundItemComputer } }; +template <> +struct BackgroundItemComputer > +{ + static void ComputeValue(nsStyleContext* aStyleContext, + const nsCSSValueList* aSpecifiedValue, + nsCOMPtr& aComputedValue, + RuleNodeCacheConditions& aConditions) + { + if (eCSSUnit_Image == aSpecifiedValue->mValue.GetUnit()) { + aComputedValue = aSpecifiedValue->mValue.GetURLValue(); + } else if (eCSSUnit_Null != aSpecifiedValue->mValue.GetUnit()) { + aComputedValue = nullptr; + } + } +}; + /* Helper function for ComputePositionValue. * This function computes a single PositionCoord from two nsCSSValue objects, * which represent an edge and an offset from that edge. @@ -6832,99 +6867,118 @@ nsRuleNode::ComputeBackgroundData(void* aStartStruct, // background-image: url (stored as image), none, inherit [list] nsStyleImage initialImage; SetImageLayerList(aContext, *aRuleData->ValueForBackgroundImage(), - bg->mLayers.mLayers, - parentBG->mLayers.mLayers, &nsStyleImageLayers::Layer::mImage, - initialImage, parentBG->mLayers.mImageCount, bg->mLayers.mImageCount, + bg->mImage.mLayers, + parentBG->mImage.mLayers, + &nsStyleImageLayers::Layer::mImage, + initialImage, parentBG->mImage.mImageCount, + bg->mImage.mImageCount, maxItemCount, rebuild, conditions); // background-repeat: enum, inherit, initial [pair list] nsStyleImageLayers::Repeat initialRepeat; initialRepeat.SetInitialValues(); SetImageLayerPairList(aContext, *aRuleData->ValueForBackgroundRepeat(), - bg->mLayers.mLayers, - parentBG->mLayers.mLayers, &nsStyleImageLayers::Layer::mRepeat, - initialRepeat, parentBG->mLayers.mRepeatCount, - bg->mLayers.mRepeatCount, maxItemCount, rebuild, + bg->mImage.mLayers, + parentBG->mImage.mLayers, + &nsStyleImageLayers::Layer::mRepeat, + initialRepeat, parentBG->mImage.mRepeatCount, + bg->mImage.mRepeatCount, maxItemCount, rebuild, conditions); // background-attachment: enum, inherit, initial [list] SetImageLayerList(aContext, *aRuleData->ValueForBackgroundAttachment(), - bg->mLayers.mLayers, parentBG->mLayers.mLayers, + bg->mImage.mLayers, parentBG->mImage.mLayers, &nsStyleImageLayers::Layer::mAttachment, uint8_t(NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL), - parentBG->mLayers.mAttachmentCount, - bg->mLayers.mAttachmentCount, maxItemCount, rebuild, + parentBG->mImage.mAttachmentCount, + bg->mImage.mAttachmentCount, maxItemCount, rebuild, conditions); // background-clip: enum, inherit, initial [list] SetImageLayerList(aContext, *aRuleData->ValueForBackgroundClip(), - bg->mLayers.mLayers, - parentBG->mLayers.mLayers, &nsStyleImageLayers::Layer::mClip, - uint8_t(NS_STYLE_IMAGELAYER_CLIP_BORDER), parentBG->mLayers.mClipCount, - bg->mLayers.mClipCount, maxItemCount, rebuild, conditions); + bg->mImage.mLayers, + parentBG->mImage.mLayers, + &nsStyleImageLayers::Layer::mClip, + uint8_t(NS_STYLE_IMAGELAYER_CLIP_BORDER), + parentBG->mImage.mClipCount, + bg->mImage.mClipCount, maxItemCount, rebuild, conditions); // background-blend-mode: enum, inherit, initial [list] SetImageLayerList(aContext, *aRuleData->ValueForBackgroundBlendMode(), - bg->mLayers.mLayers, - parentBG->mLayers.mLayers, &nsStyleImageLayers::Layer::mBlendMode, - uint8_t(NS_STYLE_BLEND_NORMAL), parentBG->mLayers.mBlendModeCount, - bg->mLayers.mBlendModeCount, maxItemCount, rebuild, + bg->mImage.mLayers, + parentBG->mImage.mLayers, + &nsStyleImageLayers::Layer::mBlendMode, + uint8_t(NS_STYLE_BLEND_NORMAL), + parentBG->mImage.mBlendModeCount, + bg->mImage.mBlendModeCount, maxItemCount, rebuild, conditions); // background-origin: enum, inherit, initial [list] SetImageLayerList(aContext, *aRuleData->ValueForBackgroundOrigin(), - bg->mLayers.mLayers, - parentBG->mLayers.mLayers, &nsStyleImageLayers::Layer::mOrigin, - uint8_t(NS_STYLE_IMAGELAYER_ORIGIN_PADDING), parentBG->mLayers.mOriginCount, - bg->mLayers.mOriginCount, maxItemCount, rebuild, + bg->mImage.mLayers, + parentBG->mImage.mLayers, + &nsStyleImageLayers::Layer::mOrigin, + uint8_t(NS_STYLE_IMAGELAYER_ORIGIN_PADDING), + parentBG->mImage.mOriginCount, + bg->mImage.mOriginCount, maxItemCount, rebuild, conditions); // background-position: enum, length, percent (flags), inherit [pair list] nsStyleImageLayers::Position initialPosition; initialPosition.SetInitialPercentValues(0.0f); SetImageLayerList(aContext, *aRuleData->ValueForBackgroundPosition(), - bg->mLayers.mLayers, - parentBG->mLayers.mLayers, &nsStyleImageLayers::Layer::mPosition, - initialPosition, parentBG->mLayers.mPositionCount, - bg->mLayers.mPositionCount, maxItemCount, rebuild, + bg->mImage.mLayers, + parentBG->mImage.mLayers, + &nsStyleImageLayers::Layer::mPosition, + initialPosition, parentBG->mImage.mPositionCount, + bg->mImage.mPositionCount, maxItemCount, rebuild, conditions); // background-size: enum, length, auto, inherit, initial [pair list] nsStyleImageLayers::Size initialSize; initialSize.SetInitialValues(); SetImageLayerPairList(aContext, *aRuleData->ValueForBackgroundSize(), - bg->mLayers.mLayers, - parentBG->mLayers.mLayers, &nsStyleImageLayers::Layer::mSize, - initialSize, parentBG->mLayers.mSizeCount, - bg->mLayers.mSizeCount, maxItemCount, rebuild, + bg->mImage.mLayers, + parentBG->mImage.mLayers, + &nsStyleImageLayers::Layer::mSize, + initialSize, parentBG->mImage.mSizeCount, + bg->mImage.mSizeCount, maxItemCount, rebuild, conditions); if (rebuild) { // Delete any extra items. We need to keep layers in which any // property was specified. - bg->mLayers.mLayers.TruncateLength(maxItemCount); + bg->mImage.mLayers.TruncateLength(maxItemCount); - uint32_t fillCount = bg->mLayers.mImageCount; - FillBackgroundList(bg->mLayers.mLayers, &nsStyleImageLayers::Layer::mImage, - bg->mLayers.mImageCount, fillCount); - FillBackgroundList(bg->mLayers.mLayers, &nsStyleImageLayers::Layer::mRepeat, - bg->mLayers.mRepeatCount, fillCount); - FillBackgroundList(bg->mLayers.mLayers, &nsStyleImageLayers::Layer::mAttachment, - bg->mLayers.mAttachmentCount, fillCount); - FillBackgroundList(bg->mLayers.mLayers, &nsStyleImageLayers::Layer::mClip, - bg->mLayers.mClipCount, fillCount); - FillBackgroundList(bg->mLayers.mLayers, &nsStyleImageLayers::Layer::mBlendMode, - bg->mLayers.mBlendModeCount, fillCount); - FillBackgroundList(bg->mLayers.mLayers, &nsStyleImageLayers::Layer::mOrigin, - bg->mLayers.mOriginCount, fillCount); - FillBackgroundList(bg->mLayers.mLayers, &nsStyleImageLayers::Layer::mPosition, - bg->mLayers.mPositionCount, fillCount); - FillBackgroundList(bg->mLayers.mLayers, &nsStyleImageLayers::Layer::mSize, - bg->mLayers.mSizeCount, fillCount); + uint32_t fillCount = bg->mImage.mImageCount; + FillBackgroundList(bg->mImage.mLayers, + &nsStyleImageLayers::Layer::mImage, + bg->mImage.mImageCount, fillCount); + FillBackgroundList(bg->mImage.mLayers, + &nsStyleImageLayers::Layer::mRepeat, + bg->mImage.mRepeatCount, fillCount); + FillBackgroundList(bg->mImage.mLayers, + &nsStyleImageLayers::Layer::mAttachment, + bg->mImage.mAttachmentCount, fillCount); + FillBackgroundList(bg->mImage.mLayers, + &nsStyleImageLayers::Layer::mClip, + bg->mImage.mClipCount, fillCount); + FillBackgroundList(bg->mImage.mLayers, + &nsStyleImageLayers::Layer::mBlendMode, + bg->mImage.mBlendModeCount, fillCount); + FillBackgroundList(bg->mImage.mLayers, + &nsStyleImageLayers::Layer::mOrigin, + bg->mImage.mOriginCount, fillCount); + FillBackgroundList(bg->mImage.mLayers, + &nsStyleImageLayers::Layer::mPosition, + bg->mImage.mPositionCount, fillCount); + FillBackgroundList(bg->mImage.mLayers, + &nsStyleImageLayers::Layer::mSize, + bg->mImage.mSizeCount, fillCount); } // Now that the dust has settled, register the images with the document - bg->mLayers.TrackImages(aContext->PresContext()); + bg->mImage.TrackImages(aContext->PresContext()); COMPUTE_END_RESET(Background, bg) } @@ -9701,19 +9755,6 @@ nsRuleNode::ComputeSVGResetData(void* aStartStruct, NS_NOTREACHED("unexpected unit"); } - // mask: url, none, inherit - const nsCSSValue* maskValue = aRuleData->ValueForMask(); - if (eCSSUnit_URL == maskValue->GetUnit()) { - svgReset->mMask = maskValue->GetURLValue(); - } else if (eCSSUnit_None == maskValue->GetUnit() || - eCSSUnit_Initial == maskValue->GetUnit() || - eCSSUnit_Unset == maskValue->GetUnit()) { - svgReset->mMask = nullptr; - } else if (eCSSUnit_Inherit == maskValue->GetUnit()) { - conditions.SetUncacheable(); - svgReset->mMask = parentSVGReset->mMask; - } - // mask-type: enum, inherit, initial SetDiscrete(*aRuleData->ValueForMaskType(), svgReset->mMaskType, @@ -9722,6 +9763,135 @@ nsRuleNode::ComputeSVGResetData(void* aStartStruct, parentSVGReset->mMaskType, NS_STYLE_MASK_TYPE_LUMINANCE, 0, 0, 0, 0); + uint32_t maxItemCount = 1; + bool rebuild = false; + + // mask-image: none | | | | + nsStyleImage initialImage; + SetImageLayerList(aContext, *aRuleData->ValueForMaskImage(), + svgReset->mMask.mLayers, + parentSVGReset->mMask.mLayers, + &nsStyleImageLayers::Layer::mImage, + initialImage, parentSVGReset->mMask.mImageCount, + svgReset->mMask.mImageCount, + maxItemCount, rebuild, conditions); + SetImageLayerList(aContext, *aRuleData->ValueForMaskImage(), + svgReset->mMask.mLayers, + parentSVGReset->mMask.mLayers, + &nsStyleImageLayers::Layer::mSourceURI, + nsCOMPtr(), parentSVGReset->mMask.mImageCount, + svgReset->mMask.mImageCount, + maxItemCount, rebuild, conditions); + + // mask-repeat: enum, inherit, initial [pair list] + nsStyleImageLayers::Repeat initialRepeat; + initialRepeat.SetInitialValues(); + SetImageLayerPairList(aContext, *aRuleData->ValueForMaskRepeat(), + svgReset->mMask.mLayers, + parentSVGReset->mMask.mLayers, + &nsStyleImageLayers::Layer::mRepeat, + initialRepeat, parentSVGReset->mMask.mRepeatCount, + svgReset->mMask.mRepeatCount, maxItemCount, rebuild, + conditions); + + // mask-clip: enum, inherit, initial [list] + SetImageLayerList(aContext, *aRuleData->ValueForMaskClip(), + svgReset->mMask.mLayers, + parentSVGReset->mMask.mLayers, + &nsStyleImageLayers::Layer::mClip, + uint8_t(NS_STYLE_IMAGELAYER_CLIP_BORDER), + parentSVGReset->mMask.mClipCount, + svgReset->mMask.mClipCount, maxItemCount, rebuild, + conditions); + + // mask-origin: enum, inherit, initial [list] + SetImageLayerList(aContext, *aRuleData->ValueForMaskOrigin(), + svgReset->mMask.mLayers, + parentSVGReset->mMask.mLayers, + &nsStyleImageLayers::Layer::mOrigin, + uint8_t(NS_STYLE_IMAGELAYER_ORIGIN_PADDING), + parentSVGReset->mMask.mOriginCount, + svgReset->mMask.mOriginCount, maxItemCount, rebuild, + conditions); + + // mask-position: enum, length, percent (flags), inherit [pair list] + nsStyleImageLayers::Position initialPosition; + initialPosition.SetInitialPercentValues(0.0f); + SetImageLayerList(aContext, *aRuleData->ValueForMaskPosition(), + svgReset->mMask.mLayers, + parentSVGReset->mMask.mLayers, + &nsStyleImageLayers::Layer::mPosition, + initialPosition, parentSVGReset->mMask.mPositionCount, + svgReset->mMask.mPositionCount, maxItemCount, rebuild, + conditions); + + // mask-size: enum, length, auto, inherit, initial [pair list] + nsStyleImageLayers::Size initialSize; + initialSize.SetInitialValues(); + SetImageLayerPairList(aContext, *aRuleData->ValueForMaskSize(), + svgReset->mMask.mLayers, + parentSVGReset->mMask.mLayers, + &nsStyleImageLayers::Layer::mSize, + initialSize, parentSVGReset->mMask.mSizeCount, + svgReset->mMask.mSizeCount, maxItemCount, rebuild, + conditions); + + // mask-mode: enum, inherit, initial [list] + SetImageLayerList(aContext, *aRuleData->ValueForMaskMode(), + svgReset->mMask.mLayers, + parentSVGReset->mMask.mLayers, + &nsStyleImageLayers::Layer::mMaskMode, + uint8_t(NS_STYLE_MASK_MODE_AUTO), + parentSVGReset->mMask.mMaskModeCount, + svgReset->mMask.mMaskModeCount, maxItemCount, rebuild, conditions); + + // mask-composite: enum, inherit, initial [list] + SetImageLayerList(aContext, *aRuleData->ValueForMaskComposite(), + svgReset->mMask.mLayers, + parentSVGReset->mMask.mLayers, + &nsStyleImageLayers::Layer::mComposite, + uint8_t(NS_STYLE_MASK_COMPOSITE_ADD), + parentSVGReset->mMask.mCompositeCount, + svgReset->mMask.mCompositeCount, maxItemCount, rebuild, conditions); + + if (rebuild) { + // Delete any extra items. We need to keep layers in which any + // property was specified. + svgReset->mMask.mLayers.TruncateLength(maxItemCount); + + uint32_t fillCount = svgReset->mMask.mImageCount; + + FillBackgroundList(svgReset->mMask.mLayers, + &nsStyleImageLayers::Layer::mImage, + svgReset->mMask.mImageCount, fillCount); + FillBackgroundList(svgReset->mMask.mLayers, + &nsStyleImageLayers::Layer::mSourceURI, + svgReset->mMask.mImageCount, fillCount); + FillBackgroundList(svgReset->mMask.mLayers, + &nsStyleImageLayers::Layer::mRepeat, + svgReset->mMask.mRepeatCount, fillCount); + FillBackgroundList(svgReset->mMask.mLayers, + &nsStyleImageLayers::Layer::mClip, + svgReset->mMask.mClipCount, fillCount); + FillBackgroundList(svgReset->mMask.mLayers, + &nsStyleImageLayers::Layer::mOrigin, + svgReset->mMask.mOriginCount, fillCount); + FillBackgroundList(svgReset->mMask.mLayers, + &nsStyleImageLayers::Layer::mPosition, + svgReset->mMask.mPositionCount, fillCount); + FillBackgroundList(svgReset->mMask.mLayers, + &nsStyleImageLayers::Layer::mSize, + svgReset->mMask.mSizeCount, fillCount); + FillBackgroundList(svgReset->mMask.mLayers, + &nsStyleImageLayers::Layer::mMaskMode, + svgReset->mMask.mMaskModeCount, fillCount); + FillBackgroundList(svgReset->mMask.mLayers, + &nsStyleImageLayers::Layer::mComposite, + svgReset->mMask.mCompositeCount, fillCount); + } + + svgReset->mMask.TrackImages(aContext->PresContext()); + COMPUTE_END_RESET(SVGReset, svgReset) } diff --git a/layout/style/nsStyleConsts.h b/layout/style/nsStyleConsts.h index 53f6185c5b..c7bdadea8a 100644 --- a/layout/style/nsStyleConsts.h +++ b/layout/style/nsStyleConsts.h @@ -120,8 +120,9 @@ enum class StyleBoxSizing : uint8_t { #define NS_STYLE_USER_MODIFY_WRITE_ONLY 2 // -moz-window-dragging -#define NS_STYLE_WINDOW_DRAGGING_DRAG 0 -#define NS_STYLE_WINDOW_DRAGGING_NO_DRAG 1 +#define NS_STYLE_WINDOW_DRAGGING_DEFAULT 0 +#define NS_STYLE_WINDOW_DRAGGING_DRAG 1 +#define NS_STYLE_WINDOW_DRAGGING_NO_DRAG 2 // box-align #define NS_STYLE_BOX_ALIGN_STRETCH 0 @@ -305,6 +306,11 @@ enum class FillMode : uint32_t; #define NS_STYLE_IMAGELAYER_SIZE_CONTAIN 0 #define NS_STYLE_IMAGELAYER_SIZE_COVER 1 +// Mask mode +#define NS_STYLE_MASK_MODE_ALPHA 0 +#define NS_STYLE_MASK_MODE_LUMINANCE 1 +#define NS_STYLE_MASK_MODE_AUTO 2 + // See nsStyleBackground #define NS_STYLE_BG_INLINE_POLICY_EACH_BOX 0 #define NS_STYLE_BG_INLINE_POLICY_CONTINUOUS 1 @@ -1154,6 +1160,12 @@ enum class FillMode : uint32_t; #define NS_STYLE_BLEND_COLOR 14 #define NS_STYLE_BLEND_LUMINOSITY 15 +// composite +#define NS_STYLE_MASK_COMPOSITE_ADD 0 +#define NS_STYLE_MASK_COMPOSITE_SUBSTRACT 1 +#define NS_STYLE_MASK_COMPOSITE_INTERSECT 2 +#define NS_STYLE_MASK_COMPOSITE_EXCLUDE 3 + // See nsStyleText::mControlCharacterVisibility #define NS_STYLE_CONTROL_CHARACTER_VISIBILITY_HIDDEN 0 #define NS_STYLE_CONTROL_CHARACTER_VISIBILITY_VISIBLE 1 diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index ef7b20b256..002c0aa86a 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -1246,7 +1246,6 @@ nsStyleSVGReset::nsStyleSVGReset() mStopColor = NS_RGB(0,0,0); mFloodColor = NS_RGB(0,0,0); mLightingColor = NS_RGB(255,255,255); - mMask = nullptr; mStopOpacity = 1.0f; mFloodOpacity = 1.0f; mDominantBaseline = NS_STYLE_DOMINANT_BASELINE_AUTO; @@ -1260,6 +1259,7 @@ nsStyleSVGReset::~nsStyleSVGReset() } nsStyleSVGReset::nsStyleSVGReset(const nsStyleSVGReset& aSource) + : mMask(aSource.mMask) { MOZ_COUNT_CTOR(nsStyleSVGReset); mStopColor = aSource.mStopColor; @@ -1267,7 +1267,6 @@ nsStyleSVGReset::nsStyleSVGReset(const nsStyleSVGReset& aSource) mLightingColor = aSource.mLightingColor; mClipPath = aSource.mClipPath; mFilters = aSource.mFilters; - mMask = aSource.mMask; mStopOpacity = aSource.mStopOpacity; mFloodOpacity = aSource.mFloodOpacity; mDominantBaseline = aSource.mDominantBaseline; @@ -1275,18 +1274,25 @@ nsStyleSVGReset::nsStyleSVGReset(const nsStyleSVGReset& aSource) mMaskType = aSource.mMaskType; } +void nsStyleSVGReset::Destroy(nsPresContext* aContext) { + mMask.UntrackImages(aContext); + + this->~nsStyleSVGReset(); + aContext->PresShell()-> + FreeByObjectID(mozilla::eArenaObjectID_nsStyleSVGReset, this); +} + nsChangeHint nsStyleSVGReset::CalcDifference(const nsStyleSVGReset& aOther) const { nsChangeHint hint = nsChangeHint(0); if (mClipPath != aOther.mClipPath || - !EqualURIs(mMask, aOther.mMask) || mFilters != aOther.mFilters) { NS_UpdateHint(hint, nsChangeHint_UpdateEffects); NS_UpdateHint(hint, nsChangeHint_RepaintFrame); // We only actually need to update the overflow area for filter - // changes. However, mask and clip-path changes require that we - // update the PreEffectsBBoxProperty, which is done during overflow + // changes. However, clip-path changes require that we update + // the PreEffectsBBoxProperty, which is done during overflow // computation. NS_UpdateHint(hint, nsChangeHint_UpdateOverflow); } @@ -1312,6 +1318,8 @@ nsChangeHint nsStyleSVGReset::CalcDifference(const nsStyleSVGReset& aOther) cons NS_UpdateHint(hint, nsChangeHint_RepaintFrame); } + hint |= mMask.CalcDifference(aOther.mMask); + return hint; } @@ -1333,7 +1341,7 @@ nsStyleSVGPaint::SetType(nsStyleSVGPaintType aType) mType = aType; } -nsStyleSVGPaint& nsStyleSVGPaint::operator=(const nsStyleSVGPaint& aOther) +nsStyleSVGPaint& nsStyleSVGPaint::operator=(const nsStyleSVGPaint& aOther) { if (this == &aOther) return *this; @@ -2203,6 +2211,34 @@ nsStyleImage::operator==(const nsStyleImage& aOther) const // nsStyleImageLayers // +const nsCSSProperty nsStyleImageLayers::kBackgroundLayerTable[] = { + eCSSProperty_background, // shorthand + eCSSProperty_background_color, // color + eCSSProperty_background_image, // image + eCSSProperty_background_repeat, // repeat + eCSSProperty_background_position, // position + eCSSProperty_background_clip, // clip + eCSSProperty_background_origin, // origin + eCSSProperty_background_size, // size + eCSSProperty_background_attachment, // attachment + eCSSProperty_UNKNOWN, // maskMode + eCSSProperty_UNKNOWN // composite +}; + +const nsCSSProperty nsStyleImageLayers::kMaskLayerTable[] = { + eCSSProperty_mask, // shorthand + eCSSProperty_UNKNOWN, // color + eCSSProperty_mask_image, // image + eCSSProperty_mask_repeat, // repeat + eCSSProperty_mask_position, // position + eCSSProperty_mask_clip, // clip + eCSSProperty_mask_origin, // origin + eCSSProperty_mask_size, // size + eCSSProperty_UNKNOWN, // attachment + eCSSProperty_mask_mode, // maskMode + eCSSProperty_mask_composite // composite +}; + nsStyleImageLayers::nsStyleImageLayers() : mAttachmentCount(1) , mClipCount(1) @@ -2211,7 +2247,9 @@ nsStyleImageLayers::nsStyleImageLayers() , mPositionCount(1) , mImageCount(1) , mSizeCount(1) + , mMaskModeCount(1) , mBlendModeCount(1) + , mCompositeCount(1) { MOZ_COUNT_CTOR(nsStyleImageLayers); mLayers.AppendElement(); @@ -2226,7 +2264,9 @@ nsStyleImageLayers::nsStyleImageLayers(const nsStyleImageLayers &aSource) , mPositionCount(aSource.mPositionCount) , mImageCount(aSource.mImageCount) , mSizeCount(aSource.mSizeCount) + , mMaskModeCount(aSource.mMaskModeCount) , mBlendModeCount(aSource.mBlendModeCount) + , mCompositeCount(aSource.mCompositeCount) , mLayers(aSource.mLayers) // deep copy { MOZ_COUNT_CTOR(nsStyleImageLayers); @@ -2241,10 +2281,84 @@ nsStyleImageLayers::nsStyleImageLayers(const nsStyleImageLayers &aSource) mPositionCount = std::max(mPositionCount, count); mImageCount = std::max(mImageCount, count); mSizeCount = std::max(mSizeCount, count); + mMaskModeCount = std::max(mMaskModeCount, count); mBlendModeCount = std::max(mBlendModeCount, count); + mCompositeCount = std::max(mCompositeCount, count); } } +nsChangeHint +nsStyleImageLayers::CalcDifference(const nsStyleImageLayers& aOther) const +{ + nsChangeHint hint = nsChangeHint(0); + + const nsStyleImageLayers& moreLayers = + mImageCount > aOther.mImageCount ? + *this : aOther; + const nsStyleImageLayers& lessLayers = + mImageCount > aOther.mImageCount ? + aOther : *this; + + NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, moreLayers) { + if (i < lessLayers.mImageCount) { + nsChangeHint layerDifference = + moreLayers.mLayers[i].CalcDifference(lessLayers.mLayers[i]); + hint |= layerDifference; + if (layerDifference && + ((moreLayers.mLayers[i].mImage.GetType() == eStyleImageType_Element) || + (lessLayers.mLayers[i].mImage.GetType() == eStyleImageType_Element))) { + hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame; + } + } else { + hint |= nsChangeHint_RepaintFrame; + if (moreLayers.mLayers[i].mImage.GetType() == eStyleImageType_Element) { + hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame; + } + } + } + + if (hint) { + return hint; + } + + if (mBlendModeCount != aOther.mBlendModeCount || + mClipCount != aOther.mClipCount || + mCompositeCount != aOther.mCompositeCount || + mMaskModeCount != aOther.mMaskModeCount || + mOriginCount != aOther.mOriginCount || + mRepeatCount != aOther.mRepeatCount || + mPositionCount != aOther.mPositionCount || + mSizeCount != aOther.mSizeCount) { + NS_UpdateHint(hint, nsChangeHint_NeutralChange); + } + + return hint; +} + +bool +nsStyleImageLayers::HasLayerWithImage() const +{ + for (uint32_t i = 0; i < mImageCount; i++) { + if (mLayers[i].mSourceURI) { + return true; + } + } + + return false; +} + +bool +nsStyleImageLayers::Position::IsInitialValue() const +{ + if (mXPosition.mPercent == 0.0 && mXPosition.mLength == 0 && + mXPosition.mHasPercent && mYPosition.mPercent == 0.0 && + mYPosition.mLength == 0 && mYPosition.mHasPercent) { + return true; + } + + return false; +} + void nsStyleImageLayers::Position::SetInitialPercentValues(float aPercentVal) { @@ -2381,7 +2495,9 @@ nsStyleImageLayers::Layer::Layer() : mClip(NS_STYLE_IMAGELAYER_CLIP_BORDER), mOrigin(NS_STYLE_IMAGELAYER_ORIGIN_PADDING), mAttachment(NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL), - mBlendMode(NS_STYLE_BLEND_NORMAL) + mBlendMode(NS_STYLE_BLEND_NORMAL), + mComposite(NS_STYLE_MASK_COMPOSITE_ADD), + mMaskMode(NS_STYLE_MASK_MODE_AUTO) { mPosition.SetInitialPercentValues(0.0f); // Initial value is "0% 0%" mImage.SetNull(); @@ -2415,25 +2531,38 @@ nsStyleImageLayers::Layer::operator==(const Layer& aOther) const mBlendMode == aOther.mBlendMode && mPosition == aOther.mPosition && mSize == aOther.mSize && - mImage == aOther.mImage; + mImage == aOther.mImage && + mMaskMode == aOther.mMaskMode && + mComposite == aOther.mComposite && + EqualURIs(mSourceURI, aOther.mSourceURI); } nsChangeHint nsStyleImageLayers::Layer::CalcDifference(const nsStyleImageLayers::Layer& aOther) const { nsChangeHint hint = nsChangeHint(0); - if (mAttachment != aOther.mAttachment || - mClip != aOther.mClip || - mOrigin != aOther.mOrigin || - mRepeat != aOther.mRepeat || - mBlendMode != aOther.mBlendMode || - mSize != aOther.mSize || - mImage != aOther.mImage) { + if (!EqualURIs(mSourceURI, aOther.mSourceURI)) { + NS_UpdateHint(hint, nsChangeHint_UpdateEffects); + NS_UpdateHint(hint, nsChangeHint_RepaintFrame); + // Mask changes require that we update the PreEffectsBBoxProperty, + // which is done during overflow computation. + NS_UpdateHint(hint, nsChangeHint_UpdateOverflow); + } else if (mAttachment != aOther.mAttachment || + mClip != aOther.mClip || + mOrigin != aOther.mOrigin || + mRepeat != aOther.mRepeat || + mBlendMode != aOther.mBlendMode || + mSize != aOther.mSize || + mImage != aOther.mImage || + mMaskMode != aOther.mMaskMode || + mComposite != aOther.mComposite) { hint |= nsChangeHint_RepaintFrame; } + if (mPosition != aOther.mPosition) { hint |= nsChangeHint_SchedulePaint; } + return hint; } @@ -2448,7 +2577,7 @@ nsStyleBackground::nsStyleBackground() } nsStyleBackground::nsStyleBackground(const nsStyleBackground& aSource) - : mLayers(aSource.mLayers) + : mImage(aSource.mImage) , mBackgroundColor(aSource.mBackgroundColor) { MOZ_COUNT_CTOR(nsStyleBackground); @@ -2464,7 +2593,7 @@ void nsStyleBackground::Destroy(nsPresContext* aContext) { // Untrack all the images stored in our layers - mLayers.UntrackImages(aContext); + mImage.UntrackImages(aContext); this->~nsStyleBackground(); aContext->PresShell()-> @@ -2473,57 +2602,20 @@ nsStyleBackground::Destroy(nsPresContext* aContext) nsChangeHint nsStyleBackground::CalcDifference(const nsStyleBackground& aOther) const { - const nsStyleImageLayers& moreLayers = - mLayers.mImageCount > aOther.mLayers.mImageCount ? - this->mLayers : aOther.mLayers; - const nsStyleImageLayers& lessLayers = - mLayers.mImageCount > aOther.mLayers.mImageCount ? - aOther.mLayers : this->mLayers; - nsChangeHint hint = nsChangeHint(0); - - NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, moreLayers) { - if (i < lessLayers.mImageCount) { - nsChangeHint layerDifference = moreLayers.mLayers[i]. - CalcDifference(lessLayers.mLayers[i]); - hint |= layerDifference; - if (layerDifference && - ((moreLayers.mLayers[i].mImage.GetType() == eStyleImageType_Element) || - (lessLayers.mLayers[i].mImage.GetType() == eStyleImageType_Element))) { - hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame; - } - } else { - hint |= nsChangeHint_RepaintFrame; - if (moreLayers.mLayers[i].mImage.GetType() == eStyleImageType_Element) { - hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame; - } - } - } - if (mBackgroundColor != aOther.mBackgroundColor) { hint |= nsChangeHint_RepaintFrame; } - if (hint) { - return hint; - } + hint |= mImage.CalcDifference(aOther.mImage); - if (mLayers.mAttachmentCount != aOther.mLayers.mAttachmentCount || - mLayers.mClipCount != aOther.mLayers.mClipCount || - mLayers.mOriginCount != aOther.mLayers.mOriginCount || - mLayers.mRepeatCount != aOther.mLayers.mRepeatCount || - mLayers.mPositionCount != aOther.mLayers.mPositionCount || - mLayers.mSizeCount != aOther.mLayers.mSizeCount) { - return nsChangeHint_NeutralChange; - } - - return NS_STYLE_HINT_NONE; + return hint; } bool nsStyleBackground::HasFixedBackground() const { - NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, mLayers) { - const nsStyleImageLayers::Layer &layer = mLayers.mLayers[i]; + NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, mImage) { + const nsStyleImageLayers::Layer &layer = mImage.mLayers[i]; if (layer.mAttachment == NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED && !layer.mImage.IsEmpty()) { return true; @@ -2535,7 +2627,7 @@ bool nsStyleBackground::HasFixedBackground() const bool nsStyleBackground::IsTransparent() const { return BottomLayer().mImage.IsEmpty() && - mLayers.mImageCount == 1 && + mImage.mImageCount == 1 && NS_GET_A(mBackgroundColor) == 0; } @@ -3697,7 +3789,6 @@ nsStyleUserInterface::nsStyleUserInterface(void) mUserInput = NS_STYLE_USER_INPUT_AUTO; mUserModify = NS_STYLE_USER_MODIFY_READ_ONLY; mUserFocus = NS_STYLE_USER_FOCUS_NONE; - mWindowDragging = NS_STYLE_WINDOW_DRAGGING_NO_DRAG; mCursor = NS_STYLE_CURSOR_AUTO; // fix for bugzilla bug 51113 @@ -3709,7 +3800,6 @@ nsStyleUserInterface::nsStyleUserInterface(const nsStyleUserInterface& aSource) mUserInput(aSource.mUserInput), mUserModify(aSource.mUserModify), mUserFocus(aSource.mUserFocus), - mWindowDragging(aSource.mWindowDragging), mCursor(aSource.mCursor) { MOZ_COUNT_CTOR(nsStyleUserInterface); @@ -3749,10 +3839,6 @@ nsChangeHint nsStyleUserInterface::CalcDifference(const nsStyleUserInterface& aO NS_UpdateHint(hint, nsChangeHint_NeutralChange); } - if (mWindowDragging != aOther.mWindowDragging) { - NS_UpdateHint(hint, nsChangeHint_SchedulePaint); - } - return hint; } @@ -3781,6 +3867,7 @@ nsStyleUIReset::nsStyleUIReset(void) mUserSelect = NS_STYLE_USER_SELECT_AUTO; mForceBrokenImageIcon = 0; mIMEMode = NS_STYLE_IME_MODE_AUTO; + mWindowDragging = NS_STYLE_WINDOW_DRAGGING_DEFAULT; mWindowShadow = NS_STYLE_WINDOW_SHADOW_DEFAULT; } @@ -3790,6 +3877,7 @@ nsStyleUIReset::nsStyleUIReset(const nsStyleUIReset& aSource) mUserSelect = aSource.mUserSelect; mForceBrokenImageIcon = aSource.mForceBrokenImageIcon; mIMEMode = aSource.mIMEMode; + mWindowDragging = aSource.mWindowDragging; mWindowShadow = aSource.mWindowShadow; } @@ -3811,6 +3899,11 @@ nsChangeHint nsStyleUIReset::CalcDifference(const nsStyleUIReset& aOther) const } if (mUserSelect != aOther.mUserSelect) return NS_STYLE_HINT_VISUAL; + + if (mWindowDragging != aOther.mWindowDragging) { + return nsChangeHint_SchedulePaint; + } + return NS_STYLE_HINT_NONE; } diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index 608dc221b2..4d469f3c63 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -96,6 +96,8 @@ 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. struct nsStyleFont @@ -358,6 +360,7 @@ private: nsStyleGradient* mGradient; char16_t* mElementId; }; + // This is _currently_ used only in conjunction with eStyleImageType_Image. nsAutoPtr mCropRect; #ifdef DEBUG @@ -405,6 +408,21 @@ struct nsStyleImageLayers { MOZ_COUNT_DTOR(nsStyleImageLayers); } + // Indices into kBackgroundLayerTable and kMaskLayerTable + enum { + shorthand = 0, + color, + image, + repeat, + position, + clip, + origin, + size, + attachment, + maskMode, + composite + }; + struct Position; friend struct Position; struct Position { @@ -414,6 +432,8 @@ struct nsStyleImageLayers { // Initialize nothing Position() {} + bool IsInitialValue() const; + // Sets both mXPosition and mYPosition to the given percent value for the // initial property-value (e.g. 0.0f for "0% 0%", or 0.5f for "50% 50%") void SetInitialPercentValues(float aPercentVal); @@ -450,6 +470,10 @@ struct nsStyleImageLayers { }; Dimension mWidth, mHeight; + bool IsInitialValue() const { + return mWidthType == eAuto && mHeightType == eAuto; + } + nscoord ResolveWidthLengthPercentage(const nsSize& aBgPositioningArea) const { MOZ_ASSERT(mWidthType == eLengthPercentage, "resolving non-length/percent dimension!"); @@ -503,6 +527,11 @@ struct nsStyleImageLayers { // Initialize nothing Repeat() {} + bool IsInitialValue() const { + return mXRepeat == NS_STYLE_IMAGELAYER_REPEAT_REPEAT && + mYRepeat == NS_STYLE_IMAGELAYER_REPEAT_REPEAT; + } + // Initialize to initial values void SetInitialValues(); @@ -519,6 +548,13 @@ struct nsStyleImageLayers { friend struct Layer; struct Layer { nsStyleImage mImage; // [reset] + nsCOMPtr mSourceURI; // [reset] + // mask-only property + // This property is used for mask layer only. + // For a background layer, it should always + // be the initial value, which is nullptr. + // Store mask-image URI so that we can resolve + // SVG mask path later. Position mPosition; // [reset] See nsStyleConsts.h Size mSize; // [reset] uint8_t mClip; // [reset] See nsStyleConsts.h @@ -528,13 +564,25 @@ struct nsStyleImageLayers { // This property is used for background layer // only. For a mask layer, it should always // be the initial value, which is - // NS_STYLE_BG_ATTACHMENT_SCROLL. + // NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL. uint8_t mBlendMode; // [reset] See nsStyleConsts.h // background-only property // This property is used for background layer // only. For a mask layer, it should always // be the initial value, which is // NS_STYLE_BLEND_NORMAL. + uint8_t mComposite; // [reset] See nsStyleConsts.h + // mask-only property + // This property is used for mask layer only. + // For a background layer, it should always + // be the initial value, which is + // NS_STYLE_COMPOSITE_MODE_ADD. + uint8_t mMaskMode; // [reset] See nsStyleConsts.h + // mask-only property + // This property is used for mask layer only. + // For a background layer, it should always + // be the initial value, which is + // NS_STYLE_MASK_MODE_AUTO. Repeat mRepeat; // [reset] See nsStyleConsts.h // Initializes only mImage @@ -579,7 +627,9 @@ struct nsStyleImageLayers { mPositionCount, mImageCount, mSizeCount, - mBlendModeCount; + mMaskModeCount, + mBlendModeCount, + mCompositeCount; // Layers are stored in an array, matching the top-to-bottom order in // which they are specified in CSS. The number of layers to be used @@ -604,6 +654,14 @@ struct nsStyleImageLayers { for (uint32_t i = 0; i < mImageCount; ++i) mLayers[i].UntrackImages(aContext); } + + nsChangeHint CalcDifference(const nsStyleImageLayers& aOther) const; + + bool HasLayerWithImage() const; + + static const nsCSSProperty kBackgroundLayerTable[]; + static const nsCSSProperty kMaskLayerTable[]; + #define NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(var_, layers_) \ for (uint32_t var_ = (layers_).mImageCount; var_-- != 0; ) #define NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT_WITH_RANGE(var_, layers_, start_, count_) \ @@ -645,9 +703,9 @@ struct nsStyleBackground { // FIXME: Should be in nsStyleStructInlines.h. bool HasFixedBackground() const; - const nsStyleImageLayers::Layer& BottomLayer() const { return mLayers.BottomLayer(); } + const nsStyleImageLayers::Layer& BottomLayer() const { return mImage.BottomLayer(); } - nsStyleImageLayers mLayers; + nsStyleImageLayers mImage; nscolor mBackgroundColor; // [reset] }; @@ -2892,6 +2950,7 @@ struct nsStyleUIReset uint8_t mUserSelect; // [reset] (selection-style) uint8_t mForceBrokenImageIcon; // [reset] (0 if not forcing, otherwise forcing) uint8_t mIMEMode; // [reset] + uint8_t mWindowDragging; // [reset] uint8_t mWindowShadow; // [reset] }; @@ -2958,7 +3017,6 @@ struct nsStyleUserInterface uint8_t mUserInput; // [inherited] uint8_t mUserModify; // [inherited] (modify-content) uint8_t mUserFocus; // [inherited] (auto-select) - uint8_t mWindowDragging; // [inherited] uint8_t mCursor; // [inherited] See nsStyleConsts.h @@ -3400,16 +3458,14 @@ struct nsStyleSVGReset return aContext->PresShell()-> AllocateByObjectID(mozilla::eArenaObjectID_nsStyleSVGReset, sz); } - void Destroy(nsPresContext* aContext) { - this->~nsStyleSVGReset(); - aContext->PresShell()-> - FreeByObjectID(mozilla::eArenaObjectID_nsStyleSVGReset, this); - } + void Destroy(nsPresContext* aContext); nsChangeHint CalcDifference(const nsStyleSVGReset& aOther) const; static nsChangeHint MaxDifference() { - return NS_CombineHint(nsChangeHint_UpdateEffects, - NS_CombineHint(nsChangeHint_UpdateOverflow, NS_STYLE_HINT_REFLOW)); + return nsChangeHint_UpdateEffects | + nsChangeHint_UpdateOverflow | + nsChangeHint_NeutralChange | + NS_STYLE_HINT_REFLOW; } static nsChangeHint DifferenceAlwaysHandledForDescendants() { // CalcDifference never returns the reflow hints that are sometimes @@ -3423,13 +3479,17 @@ struct nsStyleSVGReset return !mFilters.IsEmpty(); } + bool HasClipPath() const { + return mClipPath.GetType() != NS_STYLE_CLIP_PATH_NONE; + } + bool HasNonScalingStroke() const { return mVectorEffect == NS_STYLE_VECTOR_EFFECT_NON_SCALING_STROKE; } + nsStyleImageLayers mMask; nsStyleClipPath mClipPath; // [reset] nsTArray mFilters; // [reset] - nsCOMPtr mMask; // [reset] nscolor mStopColor; // [reset] nscolor mFloodColor; // [reset] nscolor mLightingColor; // [reset] diff --git a/layout/style/test/chrome/bug418986-2.js b/layout/style/test/chrome/bug418986-2.js index 694f212928..e1351f82a0 100644 --- a/layout/style/test/chrome/bug418986-2.js +++ b/layout/style/test/chrome/bug418986-2.js @@ -4,8 +4,6 @@ /* jshint loopfunc:true */ /* global window, screen, ok, SpecialPowers, matchMedia */ -SimpleTest.waitForExplicitFinish(); - // Expected values. Format: [name, pref_off_value, pref_on_value] // If pref_*_value is an array with two values, then we will match // any value in between those two values. If a value is null, then @@ -160,19 +158,22 @@ let cssLine = function (query, clazz, id, color) { " { background-color: " + color + "; } }\n"; }; +// __constructQuery(key, val)__. +// Creates a CSS media query from key and val. If key is an array of +// two elements, constructs a range query (using min- and max-). +let constructQuery = function (key, val) { + return Array.isArray(val) ? + "(min-" + key + ": " + val[0] + ") and (max-" + key + ": " + val[1] + ")" : + "(" + key + ": " + val + ")"; +}; + // __mediaQueryCSSLine(key, val, color)__. // Creates a line containing a CSS media query and a CSS expression. let mediaQueryCSSLine = function (key, val, color) { if (val === null) { return ""; } - let query; - if (Array.isArray(val)) { - query = "(min-" + key + ": " + val[0] + ") and (max-" + key + ": " + val[1] + ")"; - } else { - query = "(" + key + ": " + val + ")"; - } - return cssLine(query, "spoof", key, color); + return cssLine(constructQuery(key, val), "spoof", key, color); }; // __suppressedMediaQueryCSSLine(key, color)__. @@ -242,33 +243,67 @@ let testOSXFontSmoothing = function (resisting) { "-moz-osx-font-smoothing"); }; -// An iterator yielding pref values for two consecutive tests. -let prefVals = (for (prefVal of [false, true]) prefVal); +// __sleep(timeoutMs)__. +// Returns a promise that resolves after the given timeout. +let sleep = function (timeoutMs) { + return new Promise(function(resolve, reject) { + window.setTimeout(resolve); + }); +}; + +// __testMediaQueriesInPictureElements(resisting)__. +// Test to see if media queries are properly spoofed in picture elements +// when we are resisting fingerprinting. A generator function +// to be used with SpawnTask.js. +let testMediaQueriesInPictureElements = function* (resisting) { + let lines = ""; + for (let [key, offVal, onVal] of expected_values) { + let expected = resisting ? onVal : offVal; + if (expected) { + let query = constructQuery(key, expected); + lines += "\n"; + lines += " \n"; + lines += " " + key + "\n"; + lines += "
\n"; + } + } + document.getElementById("pictures").innerHTML = lines; + var testImages = document.getElementsByClassName("testImage"); + yield sleep(0); + for (let testImage of testImages) { + ok(testImage.currentSrc.endsWith("/match.png"), "Media query '" + testImage.title + "' in picture should match."); + } +}; + +// __pushPref(key, value)__. +// Set a pref value asynchronously, returning a promise that resolves +// when it succeeds. +let pushPref = function (key, value) { + return new Promise(function(resolve, reject) { + SpecialPowers.pushPrefEnv({"set": [[key, value]]}, resolve); + }); +}; // __test(isContent)__. -// Run all tests. -let test = function(isContent) { - let {value: prefValue, done} = prefVals.next(); - if (done) { - SimpleTest.finish(); - return; +// Run all tests. A generator function to be used +// with SpawnTask.js. +let test = function* (isContent) { + for (prefValue of [false, true]) { + yield pushPref("privacy.resistFingerprinting", prefValue); + let resisting = prefValue && isContent; + expected_values.forEach( + function ([key, offVal, onVal]) { + testMatch(key, resisting ? onVal : offVal); + }); + testToggles(resisting); + if (OS === "WINNT") { + testWindowsSpecific(resisting, "-moz-os-version", windows_versions); + testWindowsSpecific(resisting, "-moz-windows-theme", windows_themes); + } + testCSS(resisting); + if (OS === "Darwin") { + testOSXFontSmoothing(resisting); + } + yield testMediaQueriesInPictureElements(resisting); } - SpecialPowers.pushPrefEnv({set: [["privacy.resistFingerprinting", prefValue]]}, - function () { - let resisting = prefValue && isContent; - expected_values.forEach( - function ([key, offVal, onVal]) { - testMatch(key, resisting ? onVal : offVal); - }); - testToggles(resisting); - if (OS === "WINNT") { - testWindowsSpecific(resisting, "-moz-os-version", windows_versions); - testWindowsSpecific(resisting, "-moz-windows-theme", windows_themes); - } - testCSS(resisting); - if (OS === "Darwin") { - testOSXFontSmoothing(resisting); - } - test(isContent); - }); }; diff --git a/layout/style/test/chrome/chrome.ini b/layout/style/test/chrome/chrome.ini index 61f0244e69..11a3c96810 100644 --- a/layout/style/test/chrome/chrome.ini +++ b/layout/style/test/chrome/chrome.ini @@ -6,6 +6,8 @@ support-files = bug535806-html.html bug535806-xul.xul hover_helper.html + match.png + mismatch.png [test_addSheet.html] [test_additional_sheets.html] diff --git a/layout/style/test/chrome/match.png b/layout/style/test/chrome/match.png new file mode 100644 index 0000000000000000000000000000000000000000..d3f299bf58248c86158c94facf3bdb305cbdd795 GIT binary patch literal 1210 zcmV;r1V#IaP)f9RHj&B8d_NLg>775@Cx$^+ZY49rEN^mzro=Z<`uRv26#)`n4o-Uv9k1` zSB@fF7>@IO=kZ*1%w6m$6F)_iUli0mYQ%lCW=5kpJ?I2^Oflrg3o$Y^GHvatt!ii| zwOF}3`ad%sE5L2J?GP^r!i0VTVdWBB9=t>sApkBFcEM$~3x2=x8~jWBW?Zl(vlU@J z;Rq=Y!i9lf{;`(y>@M!Z(C8o@SNK{7@b_6NNQDtFnM_bL6`{0AK^IAXatkWrcVU)b z7A{`zglNZ-85dM#pMq$C7y%`V(099cTEJcM9`tLk(te9Q0;eU14I8W!{xRn?LcBvM z;4>OEA}f~BSxvHLMEb6P&Y+_U2A29GRvd?l*h4gYm9f)_?=% zEvBk?JNr;u4`}19H zcPQ0$C_1qn&98lng|5D6zW6n|Z+2s|Gznu2F~&PaIL~&by;WT*tPj~hUG&%M#1=Vg zz=0l17?x|OO8Jf3=wdtLcA=m#54q8A;KtYxRe0}9wYW8Y6S+rokQ9-OZO`Px-Dw_O zwsupU#nK{A5l9%G?QOR>=EDsiL+B!e#5)9&`bl`b=|OQFPWb|>0%NdtZ!C^=G_u7? z>sbR{$}G72T`7LbX=7M#h0dr$)S{=bP%cDAekTnX0=R132iU}@YHw$6Sk}-s#2w@g4~|}^{m4UzB}8W*$wbu;ySW2Q!lp6pIDB3IOyhRV z89ZOT+Opu*BiR&0|L|2*w(LaZ$qJDBPuxKiHUlPoA|8uCed2zcy!;(jRjs7`%GQ+Q z#o%?A^N_P86UcdG{Ch}a0y6VZ2C7wixB)Ek6eGAygp}o}$coCL0Y(_FZ>yt#iNXcP z@+gRo{-2TF@G4$r?qtfcRJ!2$nio*;Y$2YQzm%#&MZN*NM@oQApnjg?3_?0@mE>f#fYQ$V)@8mxO{Or1TB< z!fTEK@j+{0C$PhTa}8`q-E;l_3izJ4;INPf%puuU8O1AX z71}u*$}7wv*;X0FD{K|oIULF>%puuU8O1AX71}u*$}7wv*;W~Ke=5A=t5U15ZH=LS Y0NTcg$=Xm)tpET307*qoM6N<$g6-QvZ2$lO literal 0 HcmV?d00001 diff --git a/layout/style/test/chrome/mismatch.png b/layout/style/test/chrome/mismatch.png new file mode 100644 index 0000000000000000000000000000000000000000..8f9da3f00ff947f14075b37bac24269a27d6b202 GIT binary patch literal 1573 zcmV+=2HN?FP)+!rN)jtJVnJ{)q9lSb27&?6i4DYp(MYkENemVg%jnbC zP)7%q7!?eovBVk`1OyAcBe}cIV~|MXy^~=klXL&Om$PTj{q48kUO*>iO>lY4+F-~_ zI|4cqpmyNYQ4*jSKS+QQpdbOkaH&8EP>_INxKyA7C`dpsTq;lk6eJ)RE)^&N3K9?u zmkN{s1qld-O9j>@K(CF5U}kNPCpQzZdFqHaQhP*1!@_fhfw$sWu=$bp?evSy=Be=-r#vaD_$+oOLJ>Z93baq_6-h zN82Fd;T=vxFAx;_5Dl6%!}H`nv2v6(mR?Lp?#pLb^l`fy9!2|Q-|V>nQ>)%eUMr;C zyZP3Q=!}amq$2l429^x$pnb9`ks1N?+xsibZ2F+Qyc{Q#zha+Qx0ttcaQB8w;9BVD z>LUHYANX!~XMS$d(F!TIul;L>t!^X#{g=_ElTx(7=EzJ-t`=bM`DA3Lr||h&S#+ru zwk8MG2;j^E@o3kr7XwWCJsuIR_MFRm`)QcleT>rLVm@H#SX<0Kd=&**FA?V46HWok z(El?()U8*a*C$<1z>m{MV@7lw>NjqRI{NjXU%vsj*R@kB^cph>x_Wg`n469Li+qr9 zC<@lYT;LqI22G4jc#qQJBK&qF8pQ>9_|V;lpS>z8Mf990xOXWQUu``FB zcHBw4NO=T%7f%NLs;rFj(ODl{i9^p(6QQG{`-UB9N>XEhtt`4KRr`snek61biu3a@ zE@&-|gw4g^FTO>d;(G*;+JzuQ&7KV3UFQ^Y&&AEyqqueMI7~k1j@uVb!E?Qel@!>= zxWY&21?Jls@jXE!9}O8|fr;O5Lw@E999-s)KI6UM=)C}qj9T)`PYR2wXkinjm1)=| z-?&oIA)hXfM6)(#+&v{lg`AJBshfW0B5m;=iyLPSYs!w-YYdQlEqt6EW~vhKAmKd9 zl(x0(I|#wbu$X`H4!28XfFX{Sa0~f?6{A;IpOcb;&PGO{{}g|QOlufeC67|ytAAc~ z586jP%qEBLWWQbE)EzC_TQV4G@2FXmDxg%|kA6UfCn2bo%)t4mb+reO%=*hMF=%dP z!AZ%}HhYbzP+et!+x*;EnWS23GwWflT=lG{PuTye0({%OD#VYWWGVhi;A11hL4Hi# zKnhd?q;s|J(H9;OyK(p88EpLAkx{pT9Th$2Q z(eQZ;CH3xg?3p(e_O71j>-O6CBYj;l+;<^bE4DzYqe6LvN5i053&ibQkId909v-Eo zCCZd3N6xdfzX7aO1*)rFQkaiOihPw7NRyiekT#b_9;i(MbqoQrRoaLqI1LhQu8t0lFf=hm z;<3H|_#20MZ<`V0m2Xo$Bp%(ZO}ZMz)EdAVo#sEdNCFfrngl2T3K9?umkN{s1qld- zO9e`Rf&>J^r2-{DK>~u|Qh^enAOXQ}sXz%(kbq#gRG06?k&PQo(Khzu|uY XKlOUk;hZpA00000NkvXXu0mjfI)mQa literal 0 HcmV?d00001 diff --git a/layout/style/test/chrome/test_bug418986-2.xul b/layout/style/test/chrome/test_bug418986-2.xul index 8424e15ac1..2e5f8e6879 100644 --- a/layout/style/test/chrome/test_bug418986-2.xul +++ b/layout/style/test/chrome/test_bug418986-2.xul @@ -7,13 +7,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=418986 @@ -21,7 +22,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=418986 diff --git a/layout/style/test/mochitest.ini b/layout/style/test/mochitest.ini index a803bd6440..ce4a2d0060 100644 --- a/layout/style/test/mochitest.ini +++ b/layout/style/test/mochitest.ini @@ -6,6 +6,8 @@ support-files = ccd-standards.html css_properties.js chrome/bug418986-2.js + chrome/match.png + chrome/mismatch.png descriptor_database.js empty.html media_queries_dynamic_xbl_binding.xml @@ -129,8 +131,10 @@ support-files = file_bug1055933_circle-xxl.png support-files = file_bug1089417_iframe.html [test_bug1112014.html] [test_bug1203766.html] +[test_bug1232829.html] [test_cascade.html] [test_ch_ex_no_infloops.html] +[test_clip-path_polygon.html] [test_compute_data_with_start_struct.html] skip-if = toolkit == 'android' [test_computed_style.html] @@ -254,6 +258,9 @@ skip-if = (android_version == '18' && debug) # bug 1159532 skip-if = buildapp == 'b2g' || toolkit == 'android' #bug 775227 # b2g(times out, needs more time + various failures) b2g-debug(times out, needs more time + various failures) b2g-desktop(times out, needs more time + various failures) [test_transitions_step_functions.html] [test_unclosed_parentheses.html] +[test_unicode_range_loading.html] +support-files = ../../reftests/fonts/markA.woff ../../reftests/fonts/markB.woff ../../reftests/fonts/markC.woff ../../reftests/fonts/markD.woff +skip-if = (toolkit == "gtk2") || (toolkit == "gtk3") # bug 1056479 [test_units_angle.html] [test_units_frequency.html] [test_units_length.html] diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index b65b306c79..95ef9ee1b3 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -4113,10 +4113,210 @@ var gCSSProperties = { "mask": { domProp: "mask", inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + /* FIXME: All mask-border-* should be added when we implement them. */ + subproperties: ["mask-clip", "mask-image", "mask-mode", "mask-origin", "mask-position", "mask-repeat", "mask-size" , "mask-composite"], + initial_values: [ "auto", "none", "repeat", "add", "0% 0%", "top left", "left top", "0% 0% / auto", "top left / auto", "left top / auto", "0% 0% / auto auto", + "top left none", "left top none", "none left top", "none top left", "none 0% 0%", "left top / auto none", "left top / auto auto none", + "auto none repeat add top left", "left top repeat none add", "none repeat add top left / auto", "left top / auto repeat none add auto", "none repeat add 0% 0% / auto auto auto" ], + other_values: [ + "none alpha repeat add left top", + "url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==)", + "repeat url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==') alpha left top add", + "repeat-x", + "repeat-y", + "no-repeat", + "none repeat-y alpha add 0% 0%", + "substract", + "0% top substract alpha repeat none", + "top", + "left", + "50% 50%", + "center", + "top / 100px", + "left / contain", + "left / cover", + "10px / 10%", + "10em / calc(20px)", + "top left / 100px 100px", + "top left / 100px auto", + "top left / 100px 10%", + "top left / 100px calc(20px)", + "bottom right add none alpha repeat", + "50% alpha", + "alpha 50%", + "50%", + "url(#mymask)", + "-moz-radial-gradient(10% bottom, #ffffff, black) add no-repeat", + "-moz-linear-gradient(10px 10px -45deg, red, blue) repeat", + "-moz-linear-gradient(10px 10px -0.125turn, red, blue) repeat", + "-moz-repeating-radial-gradient(10% bottom, #ffffff, black) add no-repeat", + "-moz-repeating-linear-gradient(10px 10px -45deg, red, blue) repeat", + "-moz-element(#test) alpha", + /* multiple mask-image */ + "url(404.png), url(404.png)", + "repeat-x, substract, none", + "0% top url(404.png), url(404.png) 0% top", + "substract repeat-y top left url(404.png), repeat-x alpha", + "url(404.png), -moz-linear-gradient(20px 20px -45deg, blue, green), -moz-element(#a) alpha", + "top left / contain, bottom right / cover", + /* test cases with clip+origin in the shorthand */ + "url(404.png) alpha padding-box", + "url(404.png) border-box alpha", + "content-box url(404.png)", + "url(404.png) alpha padding-box padding-box", + "url(404.png) alpha padding-box border-box", + "content-box border-box url(404.png)", + ], + invalid_values: [ + /* mixes with keywords have to be in correct order */ + "50% left", "top 50%", + /* no quirks mode colors */ + "-moz-radial-gradient(10% bottom, ffffff, black) add no-repeat", + /* no quirks mode lengths */ + "-moz-linear-gradient(10 10px -45deg, red, blue) repeat", + "-moz-linear-gradient(10px 10 -45deg, red, blue) repeat", + "linear-gradient(red -99, yellow, green, blue 120%)", + /* bug 258080: don't accept background-position separated */ + "left url(404.png) top", "top url(404.png) left", + "alpha padding-box url(404.png) border-box", + "alpha padding-box url(404.png) padding-box", + "-moz-element(#a rubbish)", + ] + }, + "mask-clip": { + domProp: "maskClip", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "border-box" ], + other_values: [ "content-box", "padding-box", "border-box, padding-box", "padding-box, padding-box, padding-box", "border-box, border-box" ], + invalid_values: [ "margin-box", "content-box content-box" ] + }, + "mask-image": { + domProp: "maskImage", + inherited: false, type: CSS_TYPE_LONGHAND, initial_values: [ "none" ], - other_values: [ "url(#mymask)" ], - invalid_values: [] + other_values: [ + "url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==)", "url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==')", 'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==")', + "none, none", + "none, none, none, none, none", + "url(#mymask)", + "url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==), none", + "none, url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==), none", + "url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==), url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==)", + ].concat(validGradientAndElementValues), + invalid_values: [ + ].concat(invalidGradientAndElementValues), + unbalanced_values: [ + ].concat(unbalancedGradientAndElementValues) + }, + "mask-mode": { + domProp: "maskMode", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ "alpha", "luminance", "auto, auto", "auto, alpha", "alpha, luminance, auto"], + invalid_values: [ "auto auto", "alpha auto" ] + }, + "mask-composite": { + domProp: "maskComposite", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "add" ], + other_values: [ "substract", "intersect", "exclude", "add, add", "substract, intersect", "substract, substract, add"], + invalid_values: [ "add substract", "intersect exclude" ] + }, + "mask-origin": { + domProp: "maskOrigin", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "padding-box" ], + other_values: [ "border-box", "content-box", "border-box, padding-box", "padding-box, padding-box, padding-box", "border-box, border-box" ], + invalid_values: [ "margin-box", "padding-box padding-box" ] + }, + "mask-position": { + domProp: "maskPosition", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "top 0% left 0%", "top 0% left", "top left", "left top", "0% 0%", "0% top", "left 0%" ], + other_values: [ "top", "left", "right", "bottom", "center", "center bottom", "bottom center", "center right", "right center", "center top", "top center", "center left", "left center", "right bottom", "bottom right", "50%", "top left, top left", "top left, top right", "top right, top left", "left top, 0% 0%", "10% 20%, 30%, 40%", "top left, bottom right", "right bottom, left top", "0%", "0px", "30px", "0%, 10%, 20%, 30%", "top, top, top, top, top", + "calc(20px)", + "calc(20px) 10px", + "10px calc(20px)", + "calc(20px) 25%", + "25% calc(20px)", + "calc(20px) calc(20px)", + "calc(20px + 1em) calc(20px / 2)", + "calc(20px + 50%) calc(50% - 10px)", + "calc(-20px) calc(-50%)", + "calc(-20%) calc(-50%)", + "0px 0px", + "right 20px top 60px", + "right 20px bottom 60px", + "left 20px top 60px", + "left 20px bottom 60px", + "right -50px top -50px", + "left -50px bottom -50px", + "right 20px top -50px", + "right -20px top 50px", + "right 3em bottom 10px", + "bottom 3em right 10px", + "top 3em right 10px", + "left 15px", + "10px top", + "left top 15px", + "left 10px top", + "left 20%", + "right 20%" + ], + invalid_values: [ "center 10px center 4px", "center 10px center", + "top 20%", "bottom 20%", "50% left", "top 50%", + "50% bottom 10%", "right 10% 50%", "left right", + "top bottom", "left 10% right", + "top 20px bottom 20px", "left left", + "0px calc(0px + rubbish)"], + }, + "mask-repeat": { + domProp: "maskRepeat", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "repeat", "repeat repeat" ], + other_values: [ "repeat-x", "repeat-y", "no-repeat", + "repeat-x, repeat-x", + "repeat, no-repeat", + "repeat-y, no-repeat, repeat-y", + "repeat, repeat, repeat", + "repeat no-repeat", + "no-repeat repeat", + "no-repeat no-repeat", + "repeat repeat, repeat repeat", + ], + invalid_values: [ "repeat repeat repeat", + "repeat-x repeat-y", + "repeat repeat-x", + "repeat repeat-y", + "repeat-x repeat", + "repeat-y repeat" ] + }, + "mask-size": { + domProp: "maskSize", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto", "auto auto" ], + other_values: [ "contain", "cover", "100px auto", "auto 100px", "100% auto", "auto 100%", "25% 50px", "3em 40%", + "calc(20px)", + "calc(20px) 10px", + "10px calc(20px)", + "calc(20px) 25%", + "25% calc(20px)", + "calc(20px) calc(20px)", + "calc(20px + 1em) calc(20px / 2)", + "calc(20px + 50%) calc(50% - 10px)", + "calc(-20px) calc(-50%)", + "calc(-20%) calc(-50%)" + ], + invalid_values: [ "contain contain", "cover cover", "cover auto", "auto cover", "contain cover", "cover contain", "-5px 3px", "3px -5px", "auto -5px", "-5px auto", "5 3", "10px calc(10px + rubbish)" ] }, "shape-rendering": { domProp: "shapeRendering", @@ -4233,10 +4433,10 @@ var gCSSProperties = { }, "-moz-window-dragging": { domProp: "MozWindowDragging", - inherited: true, + inherited: false, type: CSS_TYPE_LONGHAND, - initial_values: [ "no-drag" ], - other_values: [ "drag" ], + initial_values: [ "default" ], + other_values: [ "drag", "no-drag" ], invalid_values: [ "none" ] }, "align-content": { @@ -4885,7 +5085,7 @@ function get_computed_value(cs, property) var info = gCSSProperties[property]; if (info.type == CSS_TYPE_TRUE_SHORTHAND || (info.type == CSS_TYPE_SHORTHAND_AND_LONGHAND && - property == "text-decoration")) { + (property == "text-decoration" || property == "mask"))) { var results = []; for (var idx in info.subproperties) { var subprop = info.subproperties[idx]; @@ -7058,6 +7258,71 @@ if (IsCSSPropertyPrefEnabled("layout.css.prefixes.webkit")) { alias_for: "-moz-user-select", subproperties: [ "-moz-user-select" ], }; + gCSSProperties["-webkit-mask"] = { + domProp: "webkitMask", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + alias_for: "mask", + subproperties: [ "mask-clip", "mask-image", "mask-mode", "mask-origin", "mask-position", "mask-repeat", "mask-size" , "mask-composite" ], + }; + gCSSProperties["-webkit-mask-clip"] = { + domProp: "webkitMaskClip", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "mask-clip", + subproperties: [ "mask-clip" ], + }; + + gCSSProperties["-webkit-mask-composite"] = { + domProp: "webkitMaskComposite", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "mask-composite", + subproperties: [ "mask-composite" ], + }; + + gCSSProperties["-webkit-mask-image"] = { + domProp: "webkitMaskImage", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "mask-image", + subproperties: [ "mask-image" ], + }; + gCSSProperties["-webkit-mask-mode"] = { + domProp: "webkitMaskMode", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "mask-mode", + subproperties: [ "mask-mode" ], + }; + gCSSProperties["-webkit-mask-origin"] = { + domProp: "webkitMaskOrigin", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "mask-origin", + subproperties: [ "mask-origin" ], + }; + gCSSProperties["-webkit-mask-position"] = { + domProp: "webkitMaskPosition", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "mask-position", + subproperties: [ "mask-position" ], + }; + gCSSProperties["-webkit-mask-repeat"] = { + domProp: "webkitMaskRepeat", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "mask-repeat", + subproperties: [ "mask-repeat" ], + }; + gCSSProperties["-webkit-mask-size"] = { + domProp: "webkitMaskSize", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "mask-size", + subproperties: [ "mask-size" ], + }; } if (IsCSSPropertyPrefEnabled("layout.css.unset-value.enabled")) { diff --git a/layout/style/test/test_bug1232829.html b/layout/style/test/test_bug1232829.html new file mode 100644 index 0000000000..8981d56e07 --- /dev/null +++ b/layout/style/test/test_bug1232829.html @@ -0,0 +1,38 @@ + + + + + +Test for Bug 1232829 + + + + + + + + diff --git a/layout/style/test/test_bug418986-2.html b/layout/style/test/test_bug418986-2.html index 15d7c71ce4..aa9ff8af1f 100644 --- a/layout/style/test/test_bug418986-2.html +++ b/layout/style/test/test_bug418986-2.html @@ -7,13 +7,16 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=418986 Test 2/3 for Bug #418986: Resist fingerprinting by preventing exposure of screen and system info + @@ -23,6 +26,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=418986 +

 
diff --git a/layout/style/test/test_clip-path_polygon.html b/layout/style/test/test_clip-path_polygon.html new file mode 100644 index 0000000000..9f25accd16 --- /dev/null +++ b/layout/style/test/test_clip-path_polygon.html @@ -0,0 +1,41 @@ + + + +clip-path with polygon() hit test + + + +
+

+ + \ No newline at end of file diff --git a/layout/style/test/test_transitions_per_property.html b/layout/style/test/test_transitions_per_property.html index c91d83e6e0..0f0b81793e 100644 --- a/layout/style/test/test_transitions_per_property.html +++ b/layout/style/test/test_transitions_per_property.html @@ -170,6 +170,16 @@ var supported_properties = { test_length_unclamped, test_percent_unclamped ], "marker-offset": [ test_length_transition, test_length_unclamped ], + "mask-position": [ test_background_position_transition, + // FIXME: We don't currently test clamping, + // since mask-position uses calc() as + // an intermediate form. + /* test_length_percent_pair_unclamped */ ], + "mask-size": [ test_background_size_transition, + // FIXME: We don't currently test clamping, + // since mask-size uses calc() as an + // intermediate form. + /* test_length_percent_pair_clamped */ ], "max-height": [ test_length_transition, test_percent_transition, test_length_percent_calc_transition, test_length_clamped, test_percent_clamped ], @@ -756,7 +766,8 @@ for (prop in gCSSProperties) { !("alias_for" in info) && !prop.match(/^transition-/) && // FIXME (Bug 119078): THIS SHOULD REALLY NOT BE NEEDED! - prop != "-moz-binding") { + prop != "-moz-binding" && + prop != "mask") { if ("prerequisites" in info) { var prereqs = info.prerequisites; diff --git a/layout/style/test/test_unicode_range_loading.html b/layout/style/test/test_unicode_range_loading.html new file mode 100644 index 0000000000..43622e2ae5 --- /dev/null +++ b/layout/style/test/test_unicode_range_loading.html @@ -0,0 +1,366 @@ + + + + + unicode-range load tests using font loading api + + + + + + + + + +

+

+
+
+
+ + + + diff --git a/layout/style/test/test_value_storage.html b/layout/style/test/test_value_storage.html index 82fab607aa..d2a55cd72d 100644 --- a/layout/style/test/test_value_storage.html +++ b/layout/style/test/test_value_storage.html @@ -222,7 +222,8 @@ function test_property(property) property + ": " + value + "'"); } - if (info.type != CSS_TYPE_TRUE_SHORTHAND) { + if (info.type != CSS_TYPE_TRUE_SHORTHAND && + property != "mask") { gDeclaration.removeProperty(property); gDeclaration.setProperty(property, step1comp, ""); var func = xfail_compute(property, value) ? todo_is : is; diff --git a/layout/svg/moz.build b/layout/svg/moz.build index a59e8f0e2b..565d490098 100644 --- a/layout/svg/moz.build +++ b/layout/svg/moz.build @@ -18,6 +18,7 @@ EXPORTS += [ ] UNIFIED_SOURCES += [ + 'nsCSSClipPathInstance.cpp', 'nsCSSFilterInstance.cpp', 'nsFilterInstance.cpp', 'nsSVGAFrame.cpp', diff --git a/layout/svg/nsCSSClipPathInstance.cpp b/layout/svg/nsCSSClipPathInstance.cpp new file mode 100644 index 0000000000..591cb282a7 --- /dev/null +++ b/layout/svg/nsCSSClipPathInstance.cpp @@ -0,0 +1,241 @@ +/* -*- 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/. */ + +// Main header first: +#include "nsCSSClipPathInstance.h" + +#include "gfx2DGlue.h" +#include "gfxPlatform.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/gfx/PathHelpers.h" +#include "nsCSSRendering.h" +#include "nsIFrame.h" +#include "nsRenderingContext.h" +#include "nsRuleNode.h" + +using namespace mozilla; +using namespace mozilla::gfx; + +/* static*/ void +nsCSSClipPathInstance::ApplyBasicShapeClip(gfxContext& aContext, + nsIFrame* aFrame) +{ + auto& clipPathStyle = aFrame->StyleSVGReset()->mClipPath; + int32_t type = clipPathStyle.GetType(); + MOZ_ASSERT(type != NS_STYLE_CLIP_PATH_NONE, "unexpected none value"); + // In the future nsCSSClipPathInstance may handle references as + // well. For the time being return early. + if (type == NS_STYLE_CLIP_PATH_URL) { + return; + } + + nsCSSClipPathInstance instance(aFrame, clipPathStyle); + + aContext.NewPath(); + RefPtr path = instance.CreateClipPath(aContext.GetDrawTarget()); + aContext.SetPath(path); + aContext.Clip(); +} + +/* static*/ bool +nsCSSClipPathInstance::HitTestBasicShapeClip(nsIFrame* aFrame, + const gfxPoint& aPoint) +{ + auto& clipPathStyle = aFrame->StyleSVGReset()->mClipPath; + int32_t type = clipPathStyle.GetType(); + MOZ_ASSERT(type != NS_STYLE_CLIP_PATH_NONE, "unexpected none value"); + // In the future nsCSSClipPathInstance may handle references as + // well. For the time being return early. + if (type == NS_STYLE_CLIP_PATH_URL) { + return false; + } + + nsCSSClipPathInstance instance(aFrame, clipPathStyle); + + RefPtr drawTarget = + gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget(); + RefPtr path = instance.CreateClipPath(drawTarget); + float pixelRatio = float(nsPresContext::AppUnitsPerCSSPixel()) / + aFrame->PresContext()->AppUnitsPerDevPixel(); + return path->ContainsPoint(ToPoint(aPoint) * pixelRatio, Matrix()); +} + +already_AddRefed +nsCSSClipPathInstance::CreateClipPath(DrawTarget* aDrawTarget) +{ + nsRect r; + // XXXkrit SVG needs to use different boxes. + switch (mClipPathStyle.GetSizingBox()) { + case NS_STYLE_CLIP_SHAPE_SIZING_CONTENT: + r = mTargetFrame->GetContentRectRelativeToSelf(); + break; + case NS_STYLE_CLIP_SHAPE_SIZING_PADDING: + r = mTargetFrame->GetPaddingRectRelativeToSelf(); + break; + case NS_STYLE_CLIP_SHAPE_SIZING_MARGIN: + r = mTargetFrame->GetMarginRectRelativeToSelf(); + break; + default: // Use the border box + r = mTargetFrame->GetRectRelativeToSelf(); + } + + if (mClipPathStyle.GetType() != NS_STYLE_CLIP_PATH_SHAPE) { + // TODO Clip to border-radius/reference box if no shape + // was specified. + RefPtr builder = aDrawTarget->CreatePathBuilder(); + return builder->Finish(); + } + + nscoord appUnitsPerDevPixel = + mTargetFrame->PresContext()->AppUnitsPerDevPixel(); + r = ToAppUnits(r.ToNearestPixels(appUnitsPerDevPixel), appUnitsPerDevPixel); + + nsStyleBasicShape* basicShape = mClipPathStyle.GetBasicShape(); + switch (basicShape->GetShapeType()) { + case nsStyleBasicShape::Type::eCircle: + return CreateClipPathCircle(aDrawTarget, r); + case nsStyleBasicShape::Type::eEllipse: + return CreateClipPathEllipse(aDrawTarget, r); + case nsStyleBasicShape::Type::ePolygon: + return CreateClipPathPolygon(aDrawTarget, r); + case nsStyleBasicShape::Type::eInset: + // XXXkrit support all basic shapes + break; + default: + MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected shape type"); + } + // Return an empty Path: + RefPtr builder = aDrawTarget->CreatePathBuilder(); + return builder->Finish(); +} + +static void +EnumerationToLength(nscoord& aCoord, int32_t aType, + nscoord aCenter, nscoord aPosMin, nscoord aPosMax) +{ + nscoord dist1 = abs(aPosMin - aCenter); + nscoord dist2 = abs(aPosMax - aCenter); + switch (aType) { + case NS_RADIUS_FARTHEST_SIDE: + aCoord = dist1 > dist2 ? dist1 : dist2; + break; + case NS_RADIUS_CLOSEST_SIDE: + aCoord = dist1 > dist2 ? dist2 : dist1; + break; + default: + NS_NOTREACHED("unknown keyword"); + break; + } +} + +already_AddRefed +nsCSSClipPathInstance::CreateClipPathCircle(DrawTarget* aDrawTarget, + const nsRect& aRefBox) +{ + nsStyleBasicShape* basicShape = mClipPathStyle.GetBasicShape(); + + RefPtr builder = aDrawTarget->CreatePathBuilder(); + + nsPoint topLeft, anchor; + nsSize size = nsSize(aRefBox.width, aRefBox.height); + nsImageRenderer::ComputeObjectAnchorPoint(basicShape->GetPosition(), + size, size, + &topLeft, &anchor); + Point center = Point(anchor.x + aRefBox.x, anchor.y + aRefBox.y); + + const nsTArray& coords = basicShape->Coordinates(); + MOZ_ASSERT(coords.Length() == 1, "wrong number of arguments"); + float referenceLength = sqrt((aRefBox.width * aRefBox.width + + aRefBox.height * aRefBox.height) / 2.0); + nscoord r = 0; + if (coords[0].GetUnit() == eStyleUnit_Enumerated) { + nscoord horizontal, vertical; + EnumerationToLength(horizontal, coords[0].GetIntValue(), + center.x, aRefBox.x, aRefBox.x + aRefBox.width); + EnumerationToLength(vertical, coords[0].GetIntValue(), + center.y, aRefBox.y, aRefBox.y + aRefBox.height); + if (coords[0].GetIntValue() == NS_RADIUS_FARTHEST_SIDE) { + r = horizontal > vertical ? horizontal : vertical; + } else { + r = horizontal < vertical ? horizontal : vertical; + } + } else { + r = nsRuleNode::ComputeCoordPercentCalc(coords[0], referenceLength); + } + + nscoord appUnitsPerDevPixel = + mTargetFrame->PresContext()->AppUnitsPerDevPixel(); + builder->Arc(center / appUnitsPerDevPixel, r / appUnitsPerDevPixel, + 0, Float(2 * M_PI)); + builder->Close(); + return builder->Finish(); +} + +already_AddRefed +nsCSSClipPathInstance::CreateClipPathEllipse(DrawTarget* aDrawTarget, + const nsRect& aRefBox) +{ + nsStyleBasicShape* basicShape = mClipPathStyle.GetBasicShape(); + + RefPtr builder = aDrawTarget->CreatePathBuilder(); + + nsPoint topLeft, anchor; + nsSize size = nsSize(aRefBox.width, aRefBox.height); + nsImageRenderer::ComputeObjectAnchorPoint(basicShape->GetPosition(), + size, size, + &topLeft, &anchor); + Point center = Point(anchor.x + aRefBox.x, anchor.y + aRefBox.y); + + const nsTArray& coords = basicShape->Coordinates(); + MOZ_ASSERT(coords.Length() == 2, "wrong number of arguments"); + nscoord rx = 0, ry = 0; + if (coords[0].GetUnit() == eStyleUnit_Enumerated) { + EnumerationToLength(rx, coords[0].GetIntValue(), + center.x, aRefBox.x, aRefBox.x + aRefBox.width); + } else { + rx = nsRuleNode::ComputeCoordPercentCalc(coords[0], aRefBox.width); + } + if (coords[1].GetUnit() == eStyleUnit_Enumerated) { + EnumerationToLength(ry, coords[1].GetIntValue(), + center.y, aRefBox.y, aRefBox.y + aRefBox.height); + } else { + ry = nsRuleNode::ComputeCoordPercentCalc(coords[1], aRefBox.height); + } + + nscoord appUnitsPerDevPixel = + mTargetFrame->PresContext()->AppUnitsPerDevPixel(); + EllipseToBezier(builder.get(), + center / appUnitsPerDevPixel, + Size(rx, ry) / appUnitsPerDevPixel); + builder->Close(); + return builder->Finish(); +} + +already_AddRefed +nsCSSClipPathInstance::CreateClipPathPolygon(DrawTarget* aDrawTarget, + const nsRect& aRefBox) +{ + nsStyleBasicShape* basicShape = mClipPathStyle.GetBasicShape(); + const nsTArray& coords = basicShape->Coordinates(); + MOZ_ASSERT(coords.Length() % 2 == 0 && + coords.Length() >= 2, "wrong number of arguments"); + + FillRule fillRule = basicShape->GetFillRule() == NS_STYLE_FILL_RULE_NONZERO ? + FillRule::FILL_WINDING : FillRule::FILL_EVEN_ODD; + RefPtr builder = aDrawTarget->CreatePathBuilder(fillRule); + + nscoord x = nsRuleNode::ComputeCoordPercentCalc(coords[0], aRefBox.width); + nscoord y = nsRuleNode::ComputeCoordPercentCalc(coords[1], aRefBox.height); + nscoord appUnitsPerDevPixel = + mTargetFrame->PresContext()->AppUnitsPerDevPixel(); + builder->MoveTo(Point(aRefBox.x + x, aRefBox.y + y) / appUnitsPerDevPixel); + for (size_t i = 2; i < coords.Length(); i += 2) { + x = nsRuleNode::ComputeCoordPercentCalc(coords[i], aRefBox.width); + y = nsRuleNode::ComputeCoordPercentCalc(coords[i + 1], aRefBox.height); + builder->LineTo(Point(aRefBox.x + x, aRefBox.y + y) / appUnitsPerDevPixel); + } + builder->Close(); + return builder->Finish(); +} diff --git a/layout/svg/nsCSSClipPathInstance.h b/layout/svg/nsCSSClipPathInstance.h new file mode 100644 index 0000000000..9277ccf535 --- /dev/null +++ b/layout/svg/nsCSSClipPathInstance.h @@ -0,0 +1,57 @@ +/* -*- 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 __NS_CSSCLIPPATHINSTANCE_H__ +#define __NS_CSSCLIPPATHINSTANCE_H__ + +#include "nsStyleStruct.h" +#include "nsRect.h" +#include "mozilla/gfx/2D.h" + +class nsIFrame; +class nsRenderingContext; + +namespace mozilla { + +class nsCSSClipPathInstance +{ + typedef mozilla::gfx::DrawTarget DrawTarget; + typedef mozilla::gfx::Path Path; + +public: + static void ApplyBasicShapeClip(gfxContext& aContext, + nsIFrame* aFrame); + // aPoint is in CSS pixels. + static bool HitTestBasicShapeClip(nsIFrame* aFrame, + const gfxPoint& aPoint); +private: + explicit nsCSSClipPathInstance(nsIFrame* aFrame, + const nsStyleClipPath aClipPathStyle) + : mTargetFrame(aFrame) + , mClipPathStyle(aClipPathStyle) + { + } + + already_AddRefed CreateClipPath(DrawTarget* aDrawTarget); + + already_AddRefed CreateClipPathCircle(DrawTarget* aDrawTarget, + const nsRect& aRefBox); + + already_AddRefed CreateClipPathEllipse(DrawTarget* aDrawTarget, + const nsRect& aRefBox); + + already_AddRefed CreateClipPathPolygon(DrawTarget* aDrawTarget, + const nsRect& aRefBox); + + /** + * The frame for the element that is currently being clipped. + */ + nsIFrame* mTargetFrame; + nsStyleClipPath mClipPathStyle; +}; + +} // namespace mozilla + +#endif diff --git a/layout/svg/nsSVGClipPathFrame.cpp b/layout/svg/nsSVGClipPathFrame.cpp index 5648657d96..a46e35b1c7 100644 --- a/layout/svg/nsSVGClipPathFrame.cpp +++ b/layout/svg/nsSVGClipPathFrame.cpp @@ -30,173 +30,208 @@ NS_NewSVGClipPathFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) NS_IMPL_FRAMEARENA_HELPERS(nsSVGClipPathFrame) -nsresult -nsSVGClipPathFrame::ApplyClipOrPaintClipMask(gfxContext& aContext, - nsIFrame* aClippedFrame, - const gfxMatrix& aMatrix) +void +nsSVGClipPathFrame::ApplyClipPath(gfxContext& aContext, + nsIFrame* aClippedFrame, + const gfxMatrix& aMatrix) { + MOZ_ASSERT(IsTrivial(), "Caller needs to use GetClipMask"); + DrawTarget& aDrawTarget = *aContext.GetDrawTarget(); + // No need for AutoClipPathReferencer since simple clip paths can't create a + // reference loop. + + // Restore current transform after applying clip path: + gfxContextMatrixAutoSaveRestore autoRestore(&aContext); + + RefPtr clipPath; + + nsISVGChildFrame* singleClipPathChild = nullptr; + IsTrivial(&singleClipPathChild); + + if (singleClipPathChild) { + nsSVGPathGeometryFrame* pathFrame = do_QueryFrame(singleClipPathChild); + if (pathFrame) { + nsSVGPathGeometryElement* pathElement = + static_cast(pathFrame->GetContent()); + gfxMatrix toChildsUserSpace = pathElement-> + PrependLocalTransformsTo(GetClipPathTransform(aClippedFrame) * aMatrix, + eUserSpaceToParent); + gfxMatrix newMatrix = + aContext.CurrentMatrix().PreMultiply(toChildsUserSpace).NudgeToIntegers(); + if (!newMatrix.IsSingular()) { + aContext.SetMatrix(newMatrix); + FillRule clipRule = + nsSVGUtils::ToFillRule(pathFrame->StyleSVG()->mClipRule); + clipPath = pathElement->GetOrBuildPath(aDrawTarget, clipRule); + } + } + } + + if (clipPath) { + aContext.Clip(clipPath); + } else { + // The spec says clip away everything if we have no children or the + // clipping path otherwise can't be resolved: + aContext.Clip(Rect()); + } +} + +already_AddRefed +nsSVGClipPathFrame::GetClipMask(gfxContext& aReferenceContext, + nsIFrame* aClippedFrame, + const gfxMatrix& aMatrix, + Matrix* aMaskTransform, + SourceSurface* aExtraMask, + const Matrix& aExtraMasksTransform) +{ + MOZ_ASSERT(!IsTrivial(), "Caller needs to use ApplyClipPath"); + + DrawTarget& aReferenceDT = *aReferenceContext.GetDrawTarget(); + // If the flag is set when we get here, it means this clipPath frame // has already been used painting the current clip, and the document // has a clip reference loop. if (mInUse) { NS_WARNING("Clip loop detected!"); - return NS_OK; + return nullptr; } AutoClipPathReferencer clipRef(this); - mMatrixForChildren = GetClipPathTransform(aClippedFrame) * aMatrix; - - nsISVGChildFrame* singleClipPathChild = nullptr; - - if (IsTrivial(&singleClipPathChild)) { - gfxContextMatrixAutoSaveRestore autoRestore(&aContext); - RefPtr clipPath; - if (singleClipPathChild) { - nsSVGPathGeometryFrame* pathFrame = do_QueryFrame(singleClipPathChild); - if (pathFrame) { - nsSVGPathGeometryElement* pathElement = - static_cast(pathFrame->GetContent()); - gfxMatrix toChildsUserSpace = pathElement-> - PrependLocalTransformsTo(mMatrixForChildren, eUserSpaceToParent); - gfxMatrix newMatrix = - aContext.CurrentMatrix().PreMultiply(toChildsUserSpace).NudgeToIntegers(); - if (!newMatrix.IsSingular()) { - aContext.SetMatrix(newMatrix); - clipPath = pathElement->GetOrBuildPath(aDrawTarget, - nsSVGUtils::ToFillRule(pathFrame->StyleSVG()->mClipRule)); - } - } - } - if (clipPath) { - aContext.Clip(clipPath); - } else { - // The spec says clip away everything if we have no children or the - // clipping path otherwise can't be resolved: - aContext.Clip(Rect()); - } - return NS_OK; - } - - // This is a non-trivial clipPath, so we need to paint its contents into a - // temporary surface and use that to mask the clipped content. Note that - // nsSVGPathGeometryFrame::Render checks for the NS_STATE_SVG_CLIPPATH_CHILD - // state bit and paints into our mask surface using opaque black in that case. - - // Check if this clipPath is itself clipped by another clipPath: - nsSVGClipPathFrame *clipPathFrame = - nsSVGEffects::GetEffectProperties(this).GetClipPathFrame(nullptr); - bool referencedClipIsTrivial; - if (clipPathFrame) { - referencedClipIsTrivial = clipPathFrame->IsTrivial(); - aContext.Save(); - if (referencedClipIsTrivial) { - clipPathFrame->ApplyClipOrPaintClipMask(aContext, aClippedFrame, aMatrix); - } else { - Matrix maskTransform; - RefPtr mask = clipPathFrame->GetClipMask(aContext, aClippedFrame, aMatrix, &maskTransform); - - aContext.PushGroupForBlendBack(gfxContentType::ALPHA, 1.0, mask, maskTransform); - } - } - - for (nsIFrame* kid = mFrames.FirstChild(); kid; - kid = kid->GetNextSibling()) { - nsISVGChildFrame* SVGFrame = do_QueryFrame(kid); - if (SVGFrame) { - // The CTM of each frame referencing us can be different. - SVGFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED); - - bool isOK = true; - nsSVGClipPathFrame *clipPathFrame = - nsSVGEffects::GetEffectProperties(kid).GetClipPathFrame(&isOK); - if (!isOK) { - continue; - } - - bool isTrivial; - - if (clipPathFrame) { - isTrivial = clipPathFrame->IsTrivial(); - aContext.Save(); - if (isTrivial) { - clipPathFrame->ApplyClipOrPaintClipMask(aContext, aClippedFrame, aMatrix); - } else { - Matrix maskTransform; - RefPtr mask = clipPathFrame->GetClipMask(aContext, aClippedFrame, aMatrix, &maskTransform); - - aContext.PushGroupForBlendBack(gfxContentType::ALPHA, 1.0, mask, maskTransform); - } - } - - gfxMatrix toChildsUserSpace = mMatrixForChildren; - nsIFrame* child = do_QueryFrame(SVGFrame); - nsIContent* childContent = child->GetContent(); - if (childContent->IsSVGElement()) { - toChildsUserSpace = - static_cast(childContent)-> - PrependLocalTransformsTo(mMatrixForChildren, eUserSpaceToParent); - } - SVGFrame->PaintSVG(aContext, toChildsUserSpace); - - if (clipPathFrame) { - if (!isTrivial) { - aContext.PopGroupAndBlend(); - } - aContext.Restore(); - } - } - } - - if (clipPathFrame) { - if (!referencedClipIsTrivial) { - aContext.PopGroupAndBlend(); - } - aContext.Restore(); - } - - return NS_OK; -} - -already_AddRefed -nsSVGClipPathFrame::GetClipMask(gfxContext& aReferenceContext, nsIFrame* aClippedFrame, - const gfxMatrix& aMatrix, Matrix* aMaskTransform, - SourceSurface* aInputMask, const Matrix& aInputMaskTransform) -{ - if (IsTrivial()) { - return nullptr; - } - - IntRect intRect; + IntRect devSpaceClipExtents; { gfxContextMatrixAutoSaveRestore autoRestoreMatrix(&aReferenceContext); aReferenceContext.SetMatrix(gfxMatrix()); gfxRect rect = aReferenceContext.GetClipExtents(); - intRect = RoundedOut(ToRect(rect)); + devSpaceClipExtents = RoundedOut(ToRect(rect)); + if (devSpaceClipExtents.IsEmpty()) { + // We don't need to create a mask surface, all drawing is clipped anyway. + return nullptr; + } } - RefPtr maskDT = aReferenceContext.GetDrawTarget()->CreateSimilarDrawTarget(intRect.Size(), SurfaceFormat::A8); + RefPtr maskDT = + aReferenceDT.CreateSimilarDrawTarget(devSpaceClipExtents.Size(), + SurfaceFormat::A8); - gfxMatrix mat = - aReferenceContext.CurrentMatrix() * gfxMatrix::Translation(-intRect.TopLeft()); + gfxMatrix mat = aReferenceContext.CurrentMatrix() * + gfxMatrix::Translation(-devSpaceClipExtents.TopLeft()); + + // Paint this clipPath's contents into maskDT: { RefPtr ctx = new gfxContext(maskDT); ctx->SetMatrix(mat); - ApplyClipOrPaintClipMask(*ctx, aClippedFrame, aMatrix); + + // We need to set mMatrixForChildren here so that under the PaintSVG calls + // on our children (below) our GetCanvasTM() method will return the correct + // transform. + mMatrixForChildren = GetClipPathTransform(aClippedFrame) * aMatrix; + + // Check if this clipPath is itself clipped by another clipPath: + nsSVGClipPathFrame* clipPathThatClipsClipPath = + nsSVGEffects::GetEffectProperties(this).GetClipPathFrame(nullptr); + bool clippingOfClipPathRequiredMasking; + if (clipPathThatClipsClipPath) { + ctx->Save(); + clippingOfClipPathRequiredMasking = !clipPathThatClipsClipPath->IsTrivial(); + if (!clippingOfClipPathRequiredMasking) { + clipPathThatClipsClipPath->ApplyClipPath(*ctx, aClippedFrame, aMatrix); + } else { + Matrix maskTransform; + RefPtr mask = + clipPathThatClipsClipPath->GetClipMask(*ctx, aClippedFrame, + aMatrix, &maskTransform); + ctx->PushGroupForBlendBack(gfxContentType::ALPHA, 1.0, + mask, maskTransform); + // The corresponding PopGroupAndBlend call below will mask the + // blend using |mask|. + } + } + + // Paint our children into the mask: + for (nsIFrame* kid = mFrames.FirstChild(); kid; + kid = kid->GetNextSibling()) { + nsISVGChildFrame* SVGFrame = do_QueryFrame(kid); + if (SVGFrame) { + // The CTM of each frame referencing us can be different. + SVGFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED); + + bool isOK = true; + // Children of this clipPath may themselves be clipped. + nsSVGClipPathFrame *clipPathThatClipsChild = + nsSVGEffects::GetEffectProperties(kid).GetClipPathFrame(&isOK); + if (!isOK) { + continue; + } + + bool childsClipPathRequiresMasking; + + if (clipPathThatClipsChild) { + childsClipPathRequiresMasking = !clipPathThatClipsChild->IsTrivial(); + ctx->Save(); + if (!childsClipPathRequiresMasking) { + clipPathThatClipsChild->ApplyClipPath(*ctx, aClippedFrame, aMatrix); + } else { + Matrix maskTransform; + RefPtr mask = + clipPathThatClipsChild->GetClipMask(*ctx, aClippedFrame, + aMatrix, &maskTransform); + ctx->PushGroupForBlendBack(gfxContentType::ALPHA, 1.0, + mask, maskTransform); + // The corresponding PopGroupAndBlend call below will mask the + // blend using |mask|. + } + } + + gfxMatrix toChildsUserSpace = mMatrixForChildren; + nsIFrame* child = do_QueryFrame(SVGFrame); + nsIContent* childContent = child->GetContent(); + if (childContent->IsSVGElement()) { + toChildsUserSpace = + static_cast(childContent)-> + PrependLocalTransformsTo(mMatrixForChildren, eUserSpaceToParent); + } + + // Our children have NS_STATE_SVG_CLIPPATH_CHILD set on them, and + // nsSVGPathGeometryFrame::Render checks for that state bit and paints + // only the geometry (opaque black) if set. + SVGFrame->PaintSVG(*ctx, toChildsUserSpace); + + if (clipPathThatClipsChild) { + if (childsClipPathRequiresMasking) { + ctx->PopGroupAndBlend(); + } + ctx->Restore(); + } + } + } + + + if (clipPathThatClipsClipPath) { + if (clippingOfClipPathRequiredMasking) { + ctx->PopGroupAndBlend(); + } + ctx->Restore(); + } } + // Moz2D transforms in the opposite direction to Thebes mat.Invert(); - if (aInputMask) { - MOZ_ASSERT(!aInputMaskTransform.HasNonTranslation()); + if (aExtraMask) { + MOZ_ASSERT(!aExtraMasksTransform.HasNonTranslation()); RefPtr currentMask = maskDT->Snapshot(); maskDT->SetTransform(Matrix()); - maskDT->ClearRect(Rect(0, 0, intRect.width, intRect.height)); - maskDT->MaskSurface(SurfacePattern(currentMask, ExtendMode::CLAMP), aInputMask, - Point(aInputMaskTransform._31 - intRect.x, aInputMaskTransform._32 - intRect.y)); + maskDT->ClearRect(Rect(0, 0, + devSpaceClipExtents.width, + devSpaceClipExtents.height)); + maskDT->MaskSurface(SurfacePattern(currentMask, ExtendMode::CLAMP), + aExtraMask, + Point(aExtraMasksTransform._31 - devSpaceClipExtents.x, + aExtraMasksTransform._32 - devSpaceClipExtents.y)); } *aMaskTransform = ToMatrix(mat); diff --git a/layout/svg/nsSVGClipPathFrame.h b/layout/svg/nsSVGClipPathFrame.h index d0a65225f7..00f037ad9c 100644 --- a/layout/svg/nsSVGClipPathFrame.h +++ b/layout/svg/nsSVGClipPathFrame.h @@ -20,6 +20,10 @@ class nsSVGClipPathFrame : public nsSVGClipPathFrameBase { friend nsIFrame* NS_NewSVGClipPathFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); + + typedef mozilla::gfx::Matrix Matrix; + typedef mozilla::gfx::SourceSurface SourceSurface; + protected: explicit nsSVGClipPathFrame(nsStyleContext* aContext) : nsSVGClipPathFrameBase(aContext) @@ -39,31 +43,46 @@ public: // nsSVGClipPathFrame methods: /** - * If the SVG clipPath is simple (as determined by the IsTrivial() method), - * calling this method simply pushes a clip path onto the DrawTarget. If the - * SVG clipPath is not simple then calling this method will paint the - * clipPath's contents (geometry being filled only, with opaque black) to the - * DrawTarget. + * Applies the clipPath by pushing a clip path onto the DrawTarget. * - * XXXjwatt Maybe split this into two methods. + * This method must only be used if IsTrivial() returns true, otherwise use + * GetClipMask. + * + * @param aContext The context that the clip path is to be applied to. + * @param aClippedFrame The/an nsIFrame of the element that references this + * clipPath that is currently being processed. + * @param aMatrix The transform from aClippedFrame's user space to aContext's + * current transform. */ - nsresult ApplyClipOrPaintClipMask(gfxContext& aContext, - nsIFrame* aClippedFrame, - const gfxMatrix &aMatrix); + void ApplyClipPath(gfxContext& aContext, + nsIFrame* aClippedFrame, + const gfxMatrix &aMatrix); /** - * If the SVG clipPath is simple (as determined by the IsTrivial() method), - * calling this method simply returns null. If the SVG clipPath is not - * simple then calling this method will return a mask surface containing - * the clipped geometry. The reference context will be used to determine the - * backend for the SourceSurface as well as the size, which will be limited - * to the device clip extents on the context. + * Returns an alpha mask surface containing the clipping geometry. + * + * This method must only be used if IsTrivial() returns false, otherwise use + * ApplyClipPath. + * + * @param aReferenceContext Used to determine the backend for and size of the + * returned SourceSurface, the size being limited to the device space clip + * extents on the context. + * @param aClippedFrame The/an nsIFrame of the element that references this + * clipPath that is currently being processed. + * @param aMatrix The transform from aClippedFrame's user space to aContext's + * current transform. + * @param [out] aMaskTransform The transform to use with the returned + * surface. + * @param [in, optional] aExtraMask An extra surface that the returned + * surface should be masked with. + * @param [in, optional] aExtraMasksTransform The transform to use with + * aExtraMask. Should be passed when aExtraMask is passed. */ - already_AddRefed + already_AddRefed GetClipMask(gfxContext& aReferenceContext, nsIFrame* aClippedFrame, const gfxMatrix& aMatrix, Matrix* aMaskTransform, - mozilla::gfx::SourceSurface* aInputMask = nullptr, - const mozilla::gfx::Matrix& aInputMaskTransform = mozilla::gfx::Matrix()); + SourceSurface* aExtraMask = nullptr, + const Matrix& aExtraMasksTransform = Matrix()); /** * aPoint is expected to be in aClippedFrame's SVG user space. diff --git a/layout/svg/nsSVGEffects.cpp b/layout/svg/nsSVGEffects.cpp index 9dc280d5c2..9edcad3f7c 100644 --- a/layout/svg/nsSVGEffects.cpp +++ b/layout/svg/nsSVGEffects.cpp @@ -575,8 +575,14 @@ nsSVGEffects::GetEffectProperties(nsIFrame *aFrame) } else { result.mClipPath = nullptr; } - result.mMask = - GetPaintingProperty(style->mMask, aFrame, MaskProperty()); + + // FIXME: Bug 1228280. + // Before fixing bug 1228280, we support only single svg mask as before. + MOZ_ASSERT(style->mMask.mImageCount > 0); + nsCOMPtr uri = style->mMask.mLayers[0].mSourceURI; + result.mMask = uri ? GetPaintingProperty(uri, aFrame, MaskProperty()) : + nullptr; + return result; } diff --git a/layout/svg/nsSVGIntegrationUtils.cpp b/layout/svg/nsSVGIntegrationUtils.cpp index 0d88bede34..5ec3ecd50b 100644 --- a/layout/svg/nsSVGIntegrationUtils.cpp +++ b/layout/svg/nsSVGIntegrationUtils.cpp @@ -9,6 +9,7 @@ // Keep others in (case-insensitive) order: #include "gfxDrawable.h" #include "nsCSSAnonBoxes.h" +#include "nsCSSClipPathInstance.h" #include "nsDisplayList.h" #include "nsFilterInstance.h" #include "nsLayoutUtils.h" @@ -23,6 +24,7 @@ #include "FrameLayerBuilder.h" #include "BasicLayers.h" #include "mozilla/gfx/Point.h" +#include "nsCSSRendering.h" using namespace mozilla; using namespace mozilla::layers; @@ -152,8 +154,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(); - return (style->HasFilters() || - style->mClipPath.GetType() != NS_STYLE_CLIP_PATH_NONE || style->mMask); + bool hasValidLayers = style->mMask.HasLayerWithImage(); + + return (style->HasFilters() || style->HasClipPath() || hasValidLayers); } // For non-SVG frames, this gives the offset to the frame's "user space". @@ -409,6 +412,7 @@ void nsSVGIntegrationUtils::PaintFramesWithEffects(gfxContext& aContext, nsIFrame* aFrame, const nsRect& aDirtyRect, + const nsRect& aBorderArea, nsDisplayListBuilder* aBuilder, LayerManager *aLayerManager) { @@ -464,10 +468,6 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(gfxContext& aContext, bool isOK = effectProperties.HasNoFilterOrHasValidFilter(); nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK); - nsSVGMaskFrame *maskFrame = effectProperties.GetMaskFrame(&isOK); - if (!isOK) { - return; // Some resource is missing. We shouldn't paint anything. - } bool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : true; @@ -510,7 +510,13 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(gfxContext& aContext, gfxMatrix cssPxToDevPxMatrix = GetCSSPxToDevPxMatrix(aFrame); + const nsStyleSVGReset *svgReset = firstFrame->StyleSVGReset(); + // Keep moving forward even if svgMaskFrame is nullptr or isOK is false. + // 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; @@ -518,21 +524,11 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(gfxContext& aContext, /* 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) { + if (opacity != 1.0f || (clipPathFrame && !isTrivialClip) + || aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL + || svgMaskFrame || hasValidLayers) { complexEffects = true; - Matrix maskTransform; - RefPtr maskSurface = - maskFrame ? maskFrame->GetMaskForMaskedFrame(&aContext, - aFrame, cssPxToDevPxMatrix, opacity, &maskTransform) - : nullptr; - - if (maskFrame && !maskSurface) { - // Entire surface is clipped out. - return; - } - aContext.Save(); nsRect clipRect = aFrame->GetVisualOverflowRectRelativeToSelf() + toUserSpace; @@ -540,6 +536,60 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(gfxContext& aContext, aFrame->PresContext()->AppUnitsPerDevPixel(), *drawTarget)); + Matrix maskTransform; + RefPtr maskSurface; + if (svgMaskFrame) { + maskSurface = svgMaskFrame->GetMaskForMaskedFrame(&aContext, + aFrame, + cssPxToDevPxMatrix, + opacity, + &maskTransform); + } else if (hasValidLayers) { + gfxRect clipRect = aContext.GetClipExtents(); + { + gfxContextMatrixAutoSaveRestore matRestore(&aContext); + + aContext.SetMatrix(gfxMatrix()); + clipRect = aContext.GetClipExtents(); + } + IntRect drawRect = RoundedOut(ToRect(clipRect)); + RefPtr targetDT = aContext.GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(), SurfaceFormat::A8); + if (!targetDT) { + aContext.Restore(); + return; + } + + RefPtr target = new gfxContext(targetDT); + target->SetMatrix(matrixAutoSaveRestore.Matrix() * gfxMatrix::Translation(-drawRect.TopLeft())); + + // Generate mask surface. + 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, + nullptr, + -1); + maskSurface = targetDT->Snapshot(); + + // Compute mask transform. + Matrix mat = ToMatrix(aContext.CurrentMatrix()); + mat.Invert(); + maskTransform = Matrix::Translation(drawRect.x, drawRect.y) * mat; + } + + if ((svgMaskFrame || hasValidLayers) && !maskSurface) { + // Entire surface is clipped out. + aContext.Restore(); + return; + } + if (aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { // Create a temporary context to draw to so we can blend it back with // another operator. @@ -554,6 +604,10 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(gfxContext& aContext, IntRect drawRect = RoundedOut(ToRect(clipRect)); RefPtr targetDT = aContext.GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(), SurfaceFormat::B8G8R8A8); + if (!targetDT) { + aContext.Restore(); + return; + } target = new gfxContext(targetDT); target->SetMatrix(aContext.CurrentMatrix() * gfxMatrix::Translation(-drawRect.TopLeft())); targetOffset = drawRect.TopLeft(); @@ -570,7 +624,8 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(gfxContext& aContext, } } - if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)) { + if (opacity != 1.0f || svgMaskFrame || hasValidLayers || + (clipPathFrame && !isTrivialClip)) { target->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity, maskSurface, maskTransform); } } @@ -580,7 +635,10 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(gfxContext& aContext, */ if (clipPathFrame && isTrivialClip) { aContext.Save(); - clipPathFrame->ApplyClipOrPaintClipMask(aContext, aFrame, cssPxToDevPxMatrix); + clipPathFrame->ApplyClipPath(aContext, aFrame, cssPxToDevPxMatrix); + } else if (!clipPathFrame && svgReset->HasClipPath()) { + aContext.Save(); + nsCSSClipPathInstance::ApplyBasicShapeClip(aContext, aFrame); } /* Paint the child */ @@ -601,7 +659,8 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(gfxContext& aContext, basic->SetTarget(oldCtx); } - if (clipPathFrame && isTrivialClip) { + if ((clipPathFrame && isTrivialClip) || + (!clipPathFrame && svgReset->HasClipPath())) { aContext.Restore(); } @@ -610,7 +669,8 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(gfxContext& aContext, return; } - if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)) { + if (opacity != 1.0f || svgMaskFrame || hasValidLayers || + (clipPathFrame && !isTrivialClip)) { target->PopGroupAndBlend(); } diff --git a/layout/svg/nsSVGIntegrationUtils.h b/layout/svg/nsSVGIntegrationUtils.h index 00514e042d..c15fd1384e 100644 --- a/layout/svg/nsSVGIntegrationUtils.h +++ b/layout/svg/nsSVGIntegrationUtils.h @@ -129,6 +129,7 @@ public: static void PaintFramesWithEffects(gfxContext& aCtx, nsIFrame* aFrame, const nsRect& aDirtyRect, + const nsRect& aBorderArea, nsDisplayListBuilder* aBuilder, mozilla::layers::LayerManager* aManager); diff --git a/layout/svg/nsSVGUtils.cpp b/layout/svg/nsSVGUtils.cpp index 1e5f3caad8..ea93fa96de 100644 --- a/layout/svg/nsSVGUtils.cpp +++ b/layout/svg/nsSVGUtils.cpp @@ -18,6 +18,7 @@ #include "mozilla/gfx/2D.h" #include "mozilla/gfx/PatternHelpers.h" #include "mozilla/Preferences.h" +#include "nsCSSClipPathInstance.h" #include "nsCSSFrameConstructor.h" #include "nsDisplayList.h" #include "nsFilterInstance.h" @@ -661,7 +662,7 @@ nsSVGUtils::PaintFrameWithEffects(nsIFrame *aFrame, */ if (clipPathFrame && isTrivialClip) { aContext.Save(); - clipPathFrame->ApplyClipOrPaintClipMask(aContext, aFrame, aTransform); + clipPathFrame->ApplyClipPath(aContext, aFrame, aTransform); } /* Paint the child */ @@ -725,8 +726,13 @@ nsSVGUtils::HitTestClip(nsIFrame *aFrame, const gfxPoint &aPoint) { nsSVGEffects::EffectProperties props = nsSVGEffects::GetEffectProperties(aFrame); - if (!props.mClipPath) + if (!props.mClipPath) { + const nsStyleSVGReset *style = aFrame->StyleSVGReset(); + if (style->HasClipPath()) { + return nsCSSClipPathInstance::HitTestBasicShapeClip(aFrame, aPoint); + } return true; + } bool isOK = true; nsSVGClipPathFrame *clipPathFrame = props.GetClipPathFrame(&isOK); diff --git a/layout/tables/nsTablePainter.cpp b/layout/tables/nsTablePainter.cpp index 1b1ecb9b68..50a0e07ecb 100644 --- a/layout/tables/nsTablePainter.cpp +++ b/layout/tables/nsTablePainter.cpp @@ -122,7 +122,7 @@ TableBackgroundPainter::TableBackgroundData::ShouldSetBCBorder() const return false; } - const nsStyleImageLayers& layers = mFrame->StyleBackground()->mLayers; + const nsStyleImageLayers& layers = mFrame->StyleBackground()->mImage; NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, layers) { if (!layers.mLayers[i].mImage.IsEmpty()) return true; diff --git a/media/libcubeb/src/audiotrack_definitions.h b/media/libcubeb/src/audiotrack_definitions.h index da3032d408..2beeca2de7 100644 --- a/media/libcubeb/src/audiotrack_definitions.h +++ b/media/libcubeb/src/audiotrack_definitions.h @@ -52,9 +52,7 @@ enum event_type { }; /** - * From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioSystem.h - * and - * https://android.googlesource.com/platform/system/core/+/android-4.2.2_r1/include/system/audio.h + * From https://android.googlesource.com/platform/system/core/+/android-4.2.2_r1/include/system/audio.h */ #define AUDIO_STREAM_TYPE_MUSIC 3 @@ -66,13 +64,6 @@ enum { AUDIO_CHANNEL_OUT_STEREO_ICS = (AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS | AUDIO_CHANNEL_OUT_FRONT_RIGHT_ICS) } AudioTrack_ChannelMapping_ICS; -enum { - AUDIO_CHANNEL_OUT_FRONT_LEFT_Froyo = 0x4, - AUDIO_CHANNEL_OUT_FRONT_RIGHT_Froyo = 0x8, - AUDIO_CHANNEL_OUT_MONO_Froyo = AUDIO_CHANNEL_OUT_FRONT_LEFT_Froyo, - AUDIO_CHANNEL_OUT_STEREO_Froyo = (AUDIO_CHANNEL_OUT_FRONT_LEFT_Froyo | AUDIO_CHANNEL_OUT_FRONT_RIGHT_Froyo) -} AudioTrack_ChannelMapping_Froyo; - typedef enum { AUDIO_FORMAT_PCM = 0x00000000, AUDIO_FORMAT_PCM_SUB_16_BIT = 0x1, diff --git a/media/libcubeb/src/cubeb_audiotrack.c b/media/libcubeb/src/cubeb_audiotrack.c index 31422a160d..59e3be2a24 100644 --- a/media/libcubeb/src/cubeb_audiotrack.c +++ b/media/libcubeb/src/cubeb_audiotrack.c @@ -56,19 +56,13 @@ struct AudioTrack { * can get the minimum frame count with this signature, and we are * running gingerbread. */ /* static */ status_t (*get_min_frame_count_gingerbread)(int* frame_count, int stream_type, uint32_t rate); - /* if this symbol is not availble, and the next one is, we know - * we are on a Froyo (Android 2.2) device. */ void* (*ctor)(void* instance, int, unsigned int, int, int, int, unsigned int, void (*)(int, void*, void*), void*, int, int); - void* (*ctor_froyo)(void* instance, int, unsigned int, int, int, int, unsigned int, void (*)(int, void*, void*), void*, int); void* (*dtor)(void* instance); void (*start)(void* instance); void (*pause)(void* instance); uint32_t (*latency)(void* instance); status_t (*check)(void* instance); status_t (*get_position)(void* instance, uint32_t* position); - /* only used on froyo. */ - /* static */ int (*get_output_frame_count)(int* frame_count, int stream); - /* static */ int (*get_output_latency)(uint32_t* latency, int stream); /* static */ int (*get_output_samplingrate)(int* samplerate, int stream); status_t (*set_marker_position)(void* instance, unsigned int); status_t (*set_volume)(void* instance, float left, float right); @@ -137,13 +131,6 @@ audiotrack_refill(int event, void* user, void* info) } } -/* We are running on froyo if we found the right AudioTrack constructor */ -static int -audiotrack_version_is_froyo(cubeb * ctx) -{ - return ctx->klass.ctor_froyo != NULL; -} - /* We are running on gingerbread if we found the gingerbread signature for * getMinFrameCount */ static int @@ -156,35 +143,6 @@ int audiotrack_get_min_frame_count(cubeb * ctx, cubeb_stream_params * params, int * min_frame_count) { status_t status; - /* Recent Android have a getMinFrameCount method. On Froyo, we have to compute it by hand. */ - if (audiotrack_version_is_froyo(ctx)) { - int samplerate, frame_count, latency, min_buffer_count; - status = ctx->klass.get_output_frame_count(&frame_count, params->stream_type); - if (status) { - ALOG("error getting the output frame count."); - return CUBEB_ERROR; - } - status = ctx->klass.get_output_latency((uint32_t*)&latency, params->stream_type); - if (status) { - ALOG("error getting the output frame count."); - return CUBEB_ERROR; - } - status = ctx->klass.get_output_samplingrate(&samplerate, params->stream_type); - if (status) { - ALOG("error getting the output frame count."); - return CUBEB_ERROR; - } - - /* Those numbers were found reading the Android source. It is the minimum - * numbers that will be accepted by the AudioTrack class, hence yielding the - * best latency possible. - * See https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/media/libmedia/AudioTrack.cpp - * around line 181 for Android 2.2 */ - min_buffer_count = latency / ((1000 * frame_count) / samplerate); - min_buffer_count = min_buffer_count < 2 ? min_buffer_count : 2; - *min_frame_count = (frame_count * params->rate * min_buffer_count) / samplerate; - return CUBEB_OK; - } /* Recent Android have a getMinFrameCount method. */ if (!audiotrack_version_is_gingerbread(ctx)) { status = ctx->klass.get_min_frame_count(min_frame_count, params->stream_type, params->rate); @@ -221,12 +179,8 @@ audiotrack_init(cubeb ** context, char const * context_name) return CUBEB_ERROR; } - /* Recent Android first, then Froyo. */ + /* Recent Android first, then Gingerbread. */ DLSYM_DLERROR("_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_ii", ctx->klass.ctor, ctx->library); - if (!ctx->klass.ctor) { - DLSYM_DLERROR("_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_i", ctx->klass.ctor_froyo, ctx->library); - assert(ctx->klass.ctor_froyo); - } DLSYM_DLERROR("_ZN7android10AudioTrackD1Ev", ctx->klass.dtor, ctx->library); DLSYM_DLERROR("_ZNK7android10AudioTrack7latencyEv", ctx->klass.latency, ctx->library); @@ -234,16 +188,10 @@ audiotrack_init(cubeb ** context, char const * context_name) DLSYM_DLERROR("_ZN7android11AudioSystem21getOutputSamplingRateEPii", ctx->klass.get_output_samplingrate, ctx->library); - /* |getMinFrameCount| is not available on Froyo, and is available on - * gingerbread and ICS with a different signature. */ - if (audiotrack_version_is_froyo(ctx)) { - DLSYM_DLERROR("_ZN7android11AudioSystem19getOutputFrameCountEPii", ctx->klass.get_output_frame_count, ctx->library); - DLSYM_DLERROR("_ZN7android11AudioSystem16getOutputLatencyEPji", ctx->klass.get_output_latency, ctx->library); - } else { - DLSYM_DLERROR("_ZN7android10AudioTrack16getMinFrameCountEPi19audio_stream_type_tj", ctx->klass.get_min_frame_count, ctx->library); - if (!ctx->klass.get_min_frame_count) { - DLSYM_DLERROR("_ZN7android10AudioTrack16getMinFrameCountEPiij", ctx->klass.get_min_frame_count_gingerbread, ctx->library); - } + /* |getMinFrameCount| is available on gingerbread and ICS with different signatures. */ + DLSYM_DLERROR("_ZN7android10AudioTrack16getMinFrameCountEPi19audio_stream_type_tj", ctx->klass.get_min_frame_count, ctx->library); + if (!ctx->klass.get_min_frame_count) { + DLSYM_DLERROR("_ZN7android10AudioTrack16getMinFrameCountEPiij", ctx->klass.get_min_frame_count_gingerbread, ctx->library); } DLSYM_DLERROR("_ZN7android10AudioTrack5startEv", ctx->klass.start, ctx->library); @@ -254,11 +202,10 @@ audiotrack_init(cubeb ** context, char const * context_name) /* check that we have a combination of symbol that makes sense */ c = &ctx->klass; - if(!((c->ctor || c->ctor_froyo) && /* at least on ctor. */ + if(!(c->ctor && c->dtor && c->latency && c->check && /* at least one way to get the minimum frame count to request. */ - ((c->get_output_frame_count && c->get_output_latency && c->get_output_samplingrate) || - c->get_min_frame_count || + (c->get_min_frame_count || c->get_min_frame_count_gingerbread) && c->start && c->pause && c->get_position && c->set_marker_position)) { ALOG("Could not find all the symbols we need."); @@ -375,36 +322,15 @@ audiotrack_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_ assert(stm->instance && "cubeb: EOM"); /* gingerbread uses old channel layout enum */ - if (audiotrack_version_is_froyo(ctx) || audiotrack_version_is_gingerbread(ctx)) { + if (audiotrack_version_is_gingerbread(ctx)) { channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_Legacy : AUDIO_CHANNEL_OUT_MONO_Legacy; } else { channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_ICS : AUDIO_CHANNEL_OUT_MONO_ICS; } - if (audiotrack_version_is_froyo(ctx)) { - ctx->klass.ctor_froyo(stm->instance, - stm->params.stream_type, - stm->params.rate, - AUDIO_FORMAT_PCM_16_BIT, - channels, - min_frame_count, - 0, - audiotrack_refill, - stm, - 0); - } else { - ctx->klass.ctor(stm->instance, - stm->params.stream_type, - stm->params.rate, - AUDIO_FORMAT_PCM_16_BIT, - channels, - min_frame_count, - 0, - audiotrack_refill, - stm, - 0, - 0); - } + ctx->klass.ctor(stm->instance, stm->params.stream_type, stm->params.rate, + AUDIO_FORMAT_PCM_16_BIT, channels, min_frame_count, 0, + audiotrack_refill, stm, 0, 0); assert((*(uint32_t*)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE - 4)) == 0xbaadbaad); diff --git a/media/libstagefright/binding/H264.cpp b/media/libstagefright/binding/H264.cpp index 91c9798ed9..85171428fd 100644 --- a/media/libstagefright/binding/H264.cpp +++ b/media/libstagefright/binding/H264.cpp @@ -26,7 +26,6 @@ public: uint32_t ReadBits(size_t aNum) { - MOZ_ASSERT(mBitReader.numBitsLeft()); MOZ_ASSERT(aNum <= 32); if (mBitReader.numBitsLeft() < aNum) { return 0; @@ -48,7 +47,10 @@ public: i++; } if (i == 32) { - MOZ_ASSERT(false); + // This can happen if the data is invalid, or if it's + // short, since ReadBit() will return 0 when it runs + // off the end of the buffer. + NS_WARNING("Invalid H.264 data"); return 0; } uint32_t r = ReadBits(i); @@ -140,7 +142,9 @@ ConditionDimension(float aValue) /* static */ bool H264::DecodeSPS(const mozilla::MediaByteBuffer* aSPS, SPSData& aDest) { - MOZ_ASSERT(aSPS); + if (!aSPS) { + return false; + } BitReader br(aSPS); int32_t lastScale; @@ -490,12 +494,16 @@ H264::DecodeSPSFromExtraData(const mozilla::MediaByteBuffer* aExtraData, SPSData return false; } + reader.DiscardRemaining(); + RefPtr rawNAL = new mozilla::MediaByteBuffer; rawNAL->AppendElements(ptr, length); RefPtr sps = DecodeNALUnit(rawNAL); - reader.DiscardRemaining(); + if (!sps) { + return false; + } return DecodeSPS(sps, aDest); } diff --git a/media/libvorbis/COPYING b/media/libvorbis/COPYING index 28de72a970..8f1d18cc2b 100644 --- a/media/libvorbis/COPYING +++ b/media/libvorbis/COPYING @@ -1,4 +1,4 @@ -Copyright (c) 2002-2008 Xiph.org Foundation +Copyright (c) 2002-2015 Xiph.org Foundation Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions diff --git a/media/libvorbis/README b/media/libvorbis/README index 3e969e0ceb..343be9a452 100644 --- a/media/libvorbis/README +++ b/media/libvorbis/README @@ -5,7 +5,7 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * -* THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * +* THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2015 * * by the Xiph.org Foundation, http://www.xiph.org/ * * * ******************************************************************** diff --git a/media/libvorbis/README_MOZILLA b/media/libvorbis/README_MOZILLA index 7c7454c3e3..1211ac074b 100644 --- a/media/libvorbis/README_MOZILLA +++ b/media/libvorbis/README_MOZILLA @@ -3,8 +3,8 @@ subversion repository using the update.sh script. The only changes made were those applied by update.sh and the addition/update of Makefile.in and moz.build files for the Mozilla build system. -The upstream version used was libvorbis 1.3.4. -https://svn.xiph.org/tags/vorbis/libvorbis-1.3.4@19059 +The upstream version used was libvorbis 1.3.5. +https://svn.xiph.org/tags/vorbis/libvorbis-1.3.5@19464 Some files are renamed during the copy to prevent clashes with object file names with other Mozilla libraries. diff --git a/media/libvorbis/bug1117571-r19419.patch b/media/libvorbis/bug1117571-r19419.patch deleted file mode 100644 index f802ee5899..0000000000 --- a/media/libvorbis/bug1117571-r19419.patch +++ /dev/null @@ -1,22 +0,0 @@ -diff -r 55f3224d7513 media/libvorbis/lib/vorbis_synthesis.c ---- a/media/libvorbis/lib/vorbis_synthesis.c Sat Jan 03 20:02:33 2015 -0800 -+++ b/media/libvorbis/lib/vorbis_synthesis.c Sun Jan 04 11:17:02 2015 -0800 -@@ -159,17 +159,17 @@ long vorbis_packet_blocksize(vorbis_info - while(v>1){ - modebits++; - v>>=1; - } - - /* read our mode and pre/post windowsize */ - mode=oggpack_read(&opb,modebits); - } -- if(mode==-1)return(OV_EBADPACKET); -+ if(mode==-1 || !ci->mode_param[mode])return(OV_EBADPACKET); - return(ci->blocksizes[ci->mode_param[mode]->blockflag]); - } - - int vorbis_synthesis_halfrate(vorbis_info *vi,int flag){ - /* set / clear half-sample-rate mode */ - codec_setup_info *ci=vi->codec_setup; - - /* right now, our MDCT can't handle < 64 sample windows. */ diff --git a/media/libvorbis/update.sh b/media/libvorbis/update.sh index 07396e4e3d..ae82a8d8b3 100755 --- a/media/libvorbis/update.sh +++ b/media/libvorbis/update.sh @@ -81,4 +81,4 @@ cp $1/lib/books/floor/floor_books.h ./lib/books/floor/ cp $1/lib/books/uncoupled/res_books_uncoupled.h ./lib/books/uncoupled/ # Add any patches against upstream here. -patch -p3 < ./bug1117571-r19419.patch +# ...nothing to apply... diff --git a/media/libvpx/1237848-check-lookahead-ctx.patch b/media/libvpx/1237848-check-lookahead-ctx.patch new file mode 100644 index 0000000000..a2ebe5cd75 --- /dev/null +++ b/media/libvpx/1237848-check-lookahead-ctx.patch @@ -0,0 +1,50 @@ +# HG changeset patch +# User Gerald Squelart +# Parent 5f9ba76eb3b1fd9377bbdb4cc2f98a7e75eabdfb +Bug 1237848 - Check lookahead ctx - r=rillian + +Copied from https://chromium-review.googlesource.com/324510 + +diff --git a/media/libvpx/vp8/encoder/lookahead.c b/media/libvpx/vp8/encoder/lookahead.c +--- a/media/libvpx/vp8/encoder/lookahead.c ++++ b/media/libvpx/vp8/encoder/lookahead.c +@@ -176,16 +176,17 @@ vp8_lookahead_push(struct lookahead_ctx + + + struct lookahead_entry* + vp8_lookahead_pop(struct lookahead_ctx *ctx, + int drain) + { + struct lookahead_entry* buf = NULL; + ++ assert(ctx != NULL); + if(ctx->sz && (drain || ctx->sz == ctx->max_sz - 1)) + { + buf = pop(ctx, &ctx->read_idx); + ctx->sz--; + } + return buf; + } + +diff --git a/media/libvpx/vp9/encoder/vp9_lookahead.c b/media/libvpx/vp9/encoder/vp9_lookahead.c +--- a/media/libvpx/vp9/encoder/vp9_lookahead.c ++++ b/media/libvpx/vp9/encoder/vp9_lookahead.c +@@ -202,17 +202,17 @@ int vp9_lookahead_push(struct lookahead_ + return 0; + } + + + struct lookahead_entry *vp9_lookahead_pop(struct lookahead_ctx *ctx, + int drain) { + struct lookahead_entry *buf = NULL; + +- if (ctx->sz && (drain || ctx->sz == ctx->max_sz - MAX_PRE_FRAMES)) { ++ if (ctx && ctx->sz && (drain || ctx->sz == ctx->max_sz - MAX_PRE_FRAMES)) { + buf = pop(ctx, &ctx->read_idx); + ctx->sz--; + } + return buf; + } + + + struct lookahead_entry *vp9_lookahead_peek(struct lookahead_ctx *ctx, diff --git a/media/libvpx/Makefile.in b/media/libvpx/Makefile.in index 4a236b1ae9..43815da06b 100644 --- a/media/libvpx/Makefile.in +++ b/media/libvpx/Makefile.in @@ -8,8 +8,6 @@ AS=$(VPX_AS) ASM_SUFFIX=$(VPX_ASM_SUFFIX) ifdef VPX_ARM_ASM -# Building on an ARM platform with a supported assembler, include -# the optimized assembly in the build. ifdef VPX_AS_CONVERSION # The ARM asm is written in ARM RVCT syntax, but we actually build it with diff --git a/media/libvpx/apple-clang.patch b/media/libvpx/apple-clang.patch deleted file mode 100644 index 549ff0d2e8..0000000000 --- a/media/libvpx/apple-clang.patch +++ /dev/null @@ -1,29 +0,0 @@ -diff --git a/media/libvpx/vp9/common/x86/vp9_subpixel_8t_intrin_avx2.c b/media/libvpx/vp9/common/x86/vp9_subpixel_8t_intrin_avx2.c ---- a/media/libvpx/vp9/common/x86/vp9_subpixel_8t_intrin_avx2.c -+++ b/media/libvpx/vp9/common/x86/vp9_subpixel_8t_intrin_avx2.c -@@ -27,21 +27,24 @@ DECLARE_ALIGNED(32, static const uint8_t - 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12 - }; - - DECLARE_ALIGNED(32, static const uint8_t, filt4_global_avx2[32]) = { - 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, - 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14 - }; - - #if defined(__clang__) - # if __clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ <= 3) || \ -- (defined(__APPLE__) && __clang_major__ == 5 && __clang_minor__ == 0) -+ (defined(__APPLE__) && \ -+ (__clang_major__ == 4 && __clang_minor__ >= 0 && \ -+ __clang_minor__ <= 2) || \ -+ (__clang_major__ == 5 && __clang_minor__ == 0)) - # define MM256_BROADCASTSI128_SI256(x) \ - _mm_broadcastsi128_si256((__m128i const *)&(x)) - # else // clang > 3.3, and not 5.0 on macosx. - # define MM256_BROADCASTSI128_SI256(x) _mm256_broadcastsi128_si256(x) - # endif // clang <= 3.3 - #elif defined(__GNUC__) - # if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ <= 6) - # define MM256_BROADCASTSI128_SI256(x) \ - _mm_broadcastsi128_si256((__m128i const *)&(x)) - # elif __GNUC__ == 4 && __GNUC_MINOR__ == 7 diff --git a/media/libvpx/cast-char-to-uint-before-shift.patch b/media/libvpx/cast-char-to-uint-before-shift.patch new file mode 100644 index 0000000000..b5d0c4f45b --- /dev/null +++ b/media/libvpx/cast-char-to-uint-before-shift.patch @@ -0,0 +1,28 @@ +# HG changeset patch +# User Gerald Squelart +# Parent 3d0a39b9f8cd9b07dac0263cfbaa23649d8b3138 +Bug 1224371 - Cast uint8_t to uint32_t before shift - r=jya + +Note: C-style cast because it is C code. + +diff --git a/media/libvpx/vp9/decoder/vp9_decoder.c b/media/libvpx/vp9/decoder/vp9_decoder.c +--- a/media/libvpx/vp9/decoder/vp9_decoder.c ++++ b/media/libvpx/vp9/decoder/vp9_decoder.c +@@ -494,16 +494,16 @@ vpx_codec_err_t vp9_parse_superframe_ind + decrypt_cb(decrypt_state, x, clear_buffer, frames * mag); + x = clear_buffer; + } + + for (i = 0; i < frames; ++i) { + uint32_t this_sz = 0; + + for (j = 0; j < mag; ++j) +- this_sz |= (*x++) << (j * 8); ++ this_sz |= (uint32_t)(*x++) << (j * 8); + sizes[i] = this_sz; + } + *count = frames; + } + } + return VPX_CODEC_OK; + } diff --git a/media/libvpx/clamp-abs-QIndex.patch b/media/libvpx/clamp-abs-QIndex.patch new file mode 100644 index 0000000000..74f3bc88fc --- /dev/null +++ b/media/libvpx/clamp-abs-QIndex.patch @@ -0,0 +1,31 @@ +# HG changeset patch +# User Gerald Squelart +# Parent a2eeff55028dc78e98a16e1d8840d77378f37408 +Bug 1224361 - Clamp QIndex also in abs-value mode - r=rillian + +diff --git a/media/libvpx/vp8/decoder/decodeframe.c b/media/libvpx/vp8/decoder/decodeframe.c +--- a/media/libvpx/vp8/decoder/decodeframe.c ++++ b/media/libvpx/vp8/decoder/decodeframe.c +@@ -66,20 +66,19 @@ void vp8_mb_init_dequantizer(VP8D_COMP * + if (xd->segmentation_enabled) + { + /* Abs Value */ + if (xd->mb_segement_abs_delta == SEGMENT_ABSDATA) + QIndex = xd->segment_feature_data[MB_LVL_ALT_Q][mbmi->segment_id]; + + /* Delta Value */ + else +- { + QIndex = pc->base_qindex + xd->segment_feature_data[MB_LVL_ALT_Q][mbmi->segment_id]; +- QIndex = (QIndex >= 0) ? ((QIndex <= MAXQ) ? QIndex : MAXQ) : 0; /* Clamp to valid range */ +- } ++ ++ QIndex = (QIndex >= 0) ? ((QIndex <= MAXQ) ? QIndex : MAXQ) : 0; /* Clamp to valid range */ + } + else + QIndex = pc->base_qindex; + + /* Set up the macroblock dequant constants */ + xd->dequant_y1_dc[0] = 1; + xd->dequant_y1[0] = pc->Y1dequant[QIndex][0]; + xd->dequant_y2[0] = pc->Y2dequant[QIndex][0]; diff --git a/media/libvpx/clamp_abs_lvl_seg.patch b/media/libvpx/clamp_abs_lvl_seg.patch new file mode 100644 index 0000000000..3043626edb --- /dev/null +++ b/media/libvpx/clamp_abs_lvl_seg.patch @@ -0,0 +1,38 @@ +# HG changeset patch +# User Gerald Squelart +# Parent b9e641a34c2fb9e6f3d3a02200bc2d800b6ca168 +Bug 1224363 - Clamp seg_lvl also in abs-value mode - r=rillian + +Even when the segment feature data is in absolute mode, it is still read as a +6-bit value with an added sign, so it could have values between -63 and +63. +Later, this signed value is used without checks as a filter level, which is +used to access an entry in an array of size MAX_LOOP_FILTER+1=64. + +This patch just extends the existing clamping (that was done only to relative- +mode data) to absolute mode data, before it is blindly 'memset' in +lfi->lvl[seg][0], which was where the out-of-bound filter_value was read in +subsequent vp8_loop_filter_row_simple. + +diff --git a/media/libvpx/vp8/common/loopfilter.c b/media/libvpx/vp8/common/loopfilter.c +--- a/media/libvpx/vp8/common/loopfilter.c ++++ b/media/libvpx/vp8/common/loopfilter.c +@@ -136,18 +136,18 @@ void vp8_loop_filter_frame_init(VP8_COMM + /* Abs value */ + if (mbd->mb_segement_abs_delta == SEGMENT_ABSDATA) + { + lvl_seg = mbd->segment_feature_data[MB_LVL_ALT_LF][seg]; + } + else /* Delta Value */ + { + lvl_seg += mbd->segment_feature_data[MB_LVL_ALT_LF][seg]; +- lvl_seg = (lvl_seg > 0) ? ((lvl_seg > 63) ? 63: lvl_seg) : 0; + } ++ lvl_seg = (lvl_seg > 0) ? ((lvl_seg > 63) ? 63: lvl_seg) : 0; + } + + if (!mbd->mode_ref_lf_delta_enabled) + { + /* we could get rid of this if we assume that deltas are set to + * zero when not in use; encoder always uses deltas + */ + memset(lfi->lvl[seg][0], lvl_seg, 4 * 4 ); diff --git a/media/libvpx/clang-cl.patch b/media/libvpx/clang-cl.patch new file mode 100644 index 0000000000..2ca9be8837 --- /dev/null +++ b/media/libvpx/clang-cl.patch @@ -0,0 +1,98 @@ +Bug 1233983 - Make libvpx build with clang-cl + +diff --git a/media/libvpx/vp8/common/generic/systemdependent.c b/media/libvpx/vp8/common/generic/systemdependent.c +index 4393ced..8ee7e02 100644 +--- a/media/libvpx/vp8/common/generic/systemdependent.c ++++ b/media/libvpx/vp8/common/generic/systemdependent.c +@@ -24,6 +24,7 @@ + #include + #elif defined(_WIN32) + #include ++#include + typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO); + #elif defined(__OS2__) + #define INCL_DOS +diff --git a/media/libvpx/vp8/common/rtcd.c b/media/libvpx/vp8/common/rtcd.c +index ab0e9b4..98c2ecd 100644 +--- a/media/libvpx/vp8/common/rtcd.c ++++ b/media/libvpx/vp8/common/rtcd.c +@@ -11,6 +11,9 @@ + #define RTCD_C + #include "./vp8_rtcd.h" + #include "vpx_ports/vpx_once.h" ++#ifdef _MSC_VER ++#include ++#endif + + + void vp8_rtcd() +diff --git a/media/libvpx/vp8/decoder/threading.c b/media/libvpx/vp8/decoder/threading.c +index 6801532..a76672f 100644 +--- a/media/libvpx/vp8/decoder/threading.c ++++ b/media/libvpx/vp8/decoder/threading.c +@@ -28,6 +28,9 @@ + #if CONFIG_ERROR_CONCEALMENT + #include "error_concealment.h" + #endif ++#ifdef _MSC_VER ++#include ++#endif + + #define CALLOC_ARRAY(p, n) CHECK_MEM_ERROR((p), vpx_calloc(sizeof(*(p)), (n))) + #define CALLOC_ARRAY_ALIGNED(p, n, algn) do { \ +diff --git a/media/libvpx/vp8/encoder/encodeframe.c b/media/libvpx/vp8/encoder/encodeframe.c +index d381d8d..5e84fb4 100644 +--- a/media/libvpx/vp8/encoder/encodeframe.c ++++ b/media/libvpx/vp8/encoder/encodeframe.c +@@ -34,6 +34,9 @@ + #include "bitstream.h" + #endif + #include "encodeframe.h" ++#ifdef _MSC_VER ++#include ++#endif + + extern void vp8_stuff_mb(VP8_COMP *cpi, MACROBLOCK *x, TOKENEXTRA **t) ; + extern void vp8_calc_ref_frame_costs(int *ref_frame_cost, +diff --git a/media/libvpx/vp8/encoder/ethreading.c b/media/libvpx/vp8/encoder/ethreading.c +index 4e234cc..519ae73b 100644 +--- a/media/libvpx/vp8/encoder/ethreading.c ++++ b/media/libvpx/vp8/encoder/ethreading.c +@@ -14,6 +14,9 @@ + #include "vp8/common/extend.h" + #include "bitstream.h" + #include "encodeframe.h" ++#ifdef _MSC_VER ++#include ++#endif + + #if CONFIG_MULTITHREAD + +diff --git a/media/libvpx/vpx_dsp/vpx_dsp_rtcd.c b/media/libvpx/vpx_dsp/vpx_dsp_rtcd.c +index 5fe27b6..d247603 100644 +--- a/media/libvpx/vpx_dsp/vpx_dsp_rtcd.c ++++ b/media/libvpx/vpx_dsp/vpx_dsp_rtcd.c +@@ -11,6 +11,9 @@ + #define RTCD_C + #include "./vpx_dsp_rtcd.h" + #include "vpx_ports/vpx_once.h" ++#ifdef _MSC_VER ++#include ++#endif + + void vpx_dsp_rtcd() { + once(setup_rtcd_internal); +diff --git a/media/libvpx/vpx_scale/vpx_scale_rtcd.c b/media/libvpx/vpx_scale/vpx_scale_rtcd.c +index bea603f..65532ba 100644 +--- a/media/libvpx/vpx_scale/vpx_scale_rtcd.c ++++ b/media/libvpx/vpx_scale/vpx_scale_rtcd.c +@@ -11,6 +11,9 @@ + #define RTCD_C + #include "./vpx_scale_rtcd.h" + #include "vpx_ports/vpx_once.h" ++#ifdef _MSC_VER ++#include ++#endif + + void vpx_scale_rtcd() + { diff --git a/media/libvpx/moz.build b/media/libvpx/moz.build index c063681c6e..4414f8f1c0 100644 --- a/media/libvpx/moz.build +++ b/media/libvpx/moz.build @@ -22,10 +22,8 @@ if CONFIG['VPX_X86_ASM']: if '64' in CONFIG['OS_TEST']: SOURCES += files['X86-64_ASM'] - # AVX2 only supported on - # Darwin and Windows toolchains right now - if CONFIG['OS_TARGET'] in ('Darwin', 'WINNT'): - SOURCES += files['AVX2'] + #avx2 can be enabled on all supported compilters + SOURCES += files['AVX2'] #postproc is only enabled on x86 with asm SOURCES += files['VP8_POSTPROC'] @@ -47,6 +45,12 @@ if CONFIG['VPX_ARM_ASM']: if f.endswith('.c') and 'neon' in f: SOURCES[f].flags += CONFIG['VPX_ASFLAGS'] + if CONFIG['OS_TARGET'] == 'Android': + # For cpu-features.h + LOCAL_INCLUDES += [ + '%%%s/sources/android/cpufeatures' % CONFIG['ANDROID_NDK'], + ] + # boolhuff_armv5te.asm defines the same functions as boolhuff.c instead of # using RTCD, so we have to make sure we only add one of the two. if 'vp8/encoder/arm/armv5te/boolhuff_armv5te.asm' not in arm_asm_files: @@ -86,7 +90,7 @@ if CONFIG['CLANG_CL'] or not CONFIG['_MSC_VER']: SOURCES[f].flags += ['-mavx2'] # Suppress warnings in third-party code. -if CONFIG['GNU_CC']: +if CONFIG['GNU_CC'] or CONFIG['CLANG_CL']: CFLAGS += [ '-Wno-sign-compare', '-Wno-unused-function', # so many of these warnings; just ignore them diff --git a/media/libvpx/vpx_once.patch b/media/libvpx/vpx_once.patch new file mode 100644 index 0000000000..08a83fb8cf --- /dev/null +++ b/media/libvpx/vpx_once.patch @@ -0,0 +1,145 @@ +commit 64f73cc1f7f57ca6643b027eae63041fec408ea8 +Author: Ralph Giles +Date: Fri Nov 6 16:42:49 2015 -0800 + + Bug 1218124 - Use InterlockCompare in win32 vpx_once(). r=gerald + +diff --git a/media/libvpx/vpx_ports/vpx_once.h b/media/libvpx/vpx_ports/vpx_once.h +index f1df394..da04db4 100644 +--- a/media/libvpx/vpx_ports/vpx_once.h ++++ b/media/libvpx/vpx_ports/vpx_once.h +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2011 The WebM project authors. All Rights Reserved. ++ * Copyright (c) 2015 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source +@@ -13,63 +13,83 @@ + + #include "vpx_config.h" + ++/* Implement a function wrapper to guarantee initialization ++ * thread-safety for library singletons. ++ * ++ * NOTE: These functions use static locks, and can only be ++ * used with one common argument per compilation unit. So ++ * ++ * file1.c: ++ * vpx_once(foo); ++ * ... ++ * vpx_once(foo); ++ * ++ * file2.c: ++ * vpx_once(bar); ++ * ++ * will ensure foo() and bar() are each called only once, but in ++ * ++ * file1.c: ++ * vpx_once(foo); ++ * vpx_once(bar): ++ * ++ * bar() will never be called because the lock is used up ++ * by the call to foo(). ++ */ ++ + #if CONFIG_MULTITHREAD && defined(_WIN32) + #include + #include ++/* Declare a per-compilation-unit state variable to track the progress ++ * of calling func() only once. This must be at global scope because ++ * local initializers are not thread-safe in MSVC prior to Visual ++ * Studio 2015. ++ * ++ * As a static, once_state will be zero-initialized as program start. ++ */ ++static LONG once_state; + static void once(void (*func)(void)) + { +- static CRITICAL_SECTION *lock; +- static LONG waiters; +- static int done; +- void *lock_ptr = &lock; +- +- /* If the initialization is complete, return early. This isn't just an +- * optimization, it prevents races on the destruction of the global +- * lock. ++ /* Try to advance once_state from its initial value of 0 to 1. ++ * Only one thread can succeed in doing so. + */ +- if(done) ++ if (InterlockedCompareExchange(&once_state, 1, 0) == 0) { ++ /* We're the winning thread, having set once_state to 1. ++ * Call our function. */ ++ func(); ++ /* Now advance once_state to 2, unblocking any other threads. */ ++ InterlockedIncrement(&once_state); + return; +- +- InterlockedIncrement(&waiters); +- +- /* Get a lock. We create one and try to make it the one-true-lock, +- * throwing it away if we lost the race. +- */ +- +- { +- /* Scope to protect access to new_lock */ +- CRITICAL_SECTION *new_lock = malloc(sizeof(CRITICAL_SECTION)); +- InitializeCriticalSection(new_lock); +- if (InterlockedCompareExchangePointer(lock_ptr, new_lock, NULL) != NULL) +- { +- DeleteCriticalSection(new_lock); +- free(new_lock); +- } + } + +- /* At this point, we have a lock that can be synchronized on. We don't +- * care which thread actually performed the allocation. ++ /* We weren't the winning thread, but we want to block on ++ * the state variable so we don't return before func() ++ * has finished executing elsewhere. ++ * ++ * Try to advance once_state from 2 to 2, which is only possible ++ * after the winning thead advances it from 1 to 2. + */ +- +- EnterCriticalSection(lock); +- +- if (!done) +- { +- func(); +- done = 1; ++ while (InterlockedCompareExchange(&once_state, 2, 2) != 2) { ++ /* State isn't yet 2. Try again. ++ * ++ * We are used for singleton initialization functions, ++ * which should complete quickly. Contention will likewise ++ * be rare, so it's worthwhile to use a simple but cpu- ++ * intensive busy-wait instead of successive backoff, ++ * waiting on a kernel object, or another heavier-weight scheme. ++ * ++ * We can at least yield our timeslice. ++ */ ++ Sleep(0); + } + +- LeaveCriticalSection(lock); +- +- /* Last one out should free resources. The destructed objects are +- * protected by checking if(done) above. ++ /* We've seen once_state advance to 2, so we know func() ++ * has been called. And we've left once_state as we found it, ++ * so other threads will have the same experience. ++ * ++ * It's safe to return now. + */ +- if(!InterlockedDecrement(&waiters)) +- { +- DeleteCriticalSection(lock); +- free(lock); +- lock = NULL; +- } ++ return; + } + + diff --git a/media/mtransport/nricectx.h b/media/mtransport/nricectx.h index 97fc6ab02c..081bc104e2 100644 --- a/media/mtransport/nricectx.h +++ b/media/mtransport/nricectx.h @@ -174,20 +174,21 @@ class NrIceTurnServer : public NrIceStunServer { class NrIceProxyServer { public: - NrIceProxyServer() : - host_(), port_(0) { + NrIceProxyServer(const std::string& host, uint16_t port, + const std::string& alpn) : + host_(host), port_(port), alpn_(alpn) { } - NrIceProxyServer(const std::string& host, uint16_t port) : - host_(host), port_(port) { - } + NrIceProxyServer() : NrIceProxyServer("", 0, "") {} const std::string& host() const { return host_; } uint16_t port() const { return port_; } + const std::string& alpn() const { return alpn_; } private: std::string host_; uint16_t port_; + std::string alpn_; }; diff --git a/media/mtransport/test/proxy_tunnel_socket_unittest.cpp b/media/mtransport/test/proxy_tunnel_socket_unittest.cpp index a5af236787..c6b5d886f4 100644 --- a/media/mtransport/test/proxy_tunnel_socket_unittest.cpp +++ b/media/mtransport/test/proxy_tunnel_socket_unittest.cpp @@ -46,9 +46,13 @@ const uint16_t kProxyPort = 9999; const std::string kHelloMessage = "HELLO"; const std::string kGarbageMessage = "xxxxxxxxxx"; -std::string connect_message(const std::string &host, uint16_t port, const std::string &tail = "") { +std::string connect_message(const std::string &host, uint16_t port, const std::string &alpn, const std::string &tail) { std::stringstream ss; - ss << "CONNECT " << host << ":" << port << " HTTP/1.0\r\n\r\n" << tail; + ss << "CONNECT " << host << ":" << port << " HTTP/1.0\r\n"; + if (!alpn.empty()) { + ss << "ALPN: " << alpn << "\r\n"; + } + ss << "\r\n" << tail; return ss.str(); } @@ -116,8 +120,6 @@ class ProxyTunnelSocketTest : public ::testing::Test { } void SetUp() { - RefPtr dummy(new DummySocket()); - nr_resolver_ = resolver_impl_.get_nr_resolver(); int r = nr_str_port_to_transport_addr( @@ -132,7 +134,18 @@ class ProxyTunnelSocketTest : public ::testing::Test { nr_proxy_tunnel_config_set_resolver(config_, nr_resolver_); nr_proxy_tunnel_config_set_proxy(config_, kProxyAddr.c_str(), kProxyPort); - r = nr_socket_proxy_tunnel_create( + Configure(); + } + + // This reconfigures the socket with the updated information in config_. + void Configure() { + if (nr_socket_) { + EXPECT_EQ(0, nr_socket_destroy(&nr_socket_)); + EXPECT_EQ(nullptr, nr_socket_); + } + + RefPtr dummy(new DummySocket()); + int r = nr_socket_proxy_tunnel_create( config_, dummy->get_nr_socket(), &nr_socket_); @@ -141,6 +154,16 @@ class ProxyTunnelSocketTest : public ::testing::Test { socket_impl_ = dummy.forget(); // Now owned by nr_socket_. } + void Connect(int expectedReturn = 0) { + int r = nr_socket_connect(nr_socket_, &remote_addr_); + EXPECT_EQ(expectedReturn, r); + + size_t written = 0; + r = nr_socket_write(nr_socket_, kHelloMessage.c_str(), kHelloMessage.size(), &written, 0); + EXPECT_EQ(0, r); + EXPECT_EQ(kHelloMessage.size(), written); + } + nr_socket *socket() { return nr_socket_; } protected: @@ -166,56 +189,67 @@ TEST_F(ProxyTunnelSocketTest, TestConnectProxyAddress) { } TEST_F(ProxyTunnelSocketTest, TestConnectProxyRequest) { - int r = nr_socket_connect(nr_socket_, &remote_addr_); - ASSERT_EQ(0, r); + Connect(); - size_t written = 0; - r = nr_socket_write(nr_socket_, kHelloMessage.c_str(), kHelloMessage.size(), &written, 0); - ASSERT_EQ(0, r); + std::string msg = connect_message(kRemoteAddr, kRemotePort, "", kHelloMessage); + socket_impl_->CheckWriteBuffer(reinterpret_cast(msg.c_str()), msg.size()); +} - std::string msg = connect_message(kRemoteAddr, kRemotePort, kHelloMessage); +TEST_F(ProxyTunnelSocketTest, TestAlpnConnect) { + const std::string alpn = "this,is,alpn"; + int r = nr_proxy_tunnel_config_set_alpn(config_, alpn.c_str()); + EXPECT_EQ(0, r); + + Configure(); + Connect(); + + std::string msg = connect_message(kRemoteAddr, kRemotePort, alpn, kHelloMessage); + socket_impl_->CheckWriteBuffer(reinterpret_cast(msg.c_str()), msg.size()); +} + +TEST_F(ProxyTunnelSocketTest, TestNullAlpnConnect) { + int r = nr_proxy_tunnel_config_set_alpn(config_, nullptr); + EXPECT_EQ(0, r); + + Configure(); + Connect(); + + std::string msg = connect_message(kRemoteAddr, kRemotePort, "", kHelloMessage); socket_impl_->CheckWriteBuffer(reinterpret_cast(msg.c_str()), msg.size()); } TEST_F(ProxyTunnelSocketTest, TestConnectProxyHostRequest) { - int r = nr_socket_destroy(&nr_socket_); - ASSERT_EQ(0, r); - - RefPtr dummy(new DummySocket()); - nr_proxy_tunnel_config_set_proxy(config_, kProxyHost.c_str(), kProxyPort); + Configure(); + // Because kProxyHost is a domain name and not an IP address, + // nr_socket_connect will need to resolve an IP address before continuing. It + // does that, and assumes that resolving the IP will take some time, so it + // returns R_WOULDBLOCK. + // + // However, In this test setup, the resolution happens inline immediately, so + // nr_socket_connect is called recursively on the inner socket in + // nr_socket_proxy_tunnel_resolved_cb. That also completes. Thus, the socket + // is actually successfully connected after this call, even though + // nr_socket_connect reports an error. + // + // Arguably nr_socket_proxy_tunnel_connect() is busted, because it shouldn't + // report an error when it doesn't need any further assistance from the + // calling code, but that's pretty minor. + Connect(R_WOULDBLOCK); - r = nr_socket_proxy_tunnel_create( - config_, - dummy->get_nr_socket(), - &nr_socket_); - ASSERT_EQ(0, r); - - socket_impl_ = dummy.forget(); // Now owned by nr_socket_. - - r = nr_socket_connect(nr_socket_, &remote_addr_); - ASSERT_EQ(R_WOULDBLOCK, r); - - size_t written = 0; - r = nr_socket_write(nr_socket_, kHelloMessage.c_str(), kHelloMessage.size(), &written, 0); - ASSERT_EQ(0, r); - - std::string msg = connect_message(kRemoteAddr, kRemotePort, kHelloMessage); + std::string msg = connect_message(kRemoteAddr, kRemotePort, "", kHelloMessage); socket_impl_->CheckWriteBuffer(reinterpret_cast(msg.c_str()), msg.size()); } TEST_F(ProxyTunnelSocketTest, TestConnectProxyWrite) { - int r = nr_socket_connect(nr_socket_, &remote_addr_); - ASSERT_EQ(0, r); - - size_t written = 0; - r = nr_socket_write(nr_socket_, kHelloMessage.c_str(), kHelloMessage.size(), &written, 0); - ASSERT_EQ(0, r); + Connect(); socket_impl_->ClearWriteBuffer(); - r = nr_socket_write(nr_socket_, kHelloMessage.c_str(), kHelloMessage.size(), &written, 0); - ASSERT_EQ(0, r); + size_t written = 0; + int r = nr_socket_write(nr_socket_, kHelloMessage.c_str(), kHelloMessage.size(), &written, 0); + EXPECT_EQ(0, r); + EXPECT_EQ(kHelloMessage.size(), written); socket_impl_->CheckWriteBuffer(reinterpret_cast(kHelloMessage.c_str()), kHelloMessage.size()); diff --git a/media/mtransport/test/transport_unittests.cpp b/media/mtransport/test/transport_unittests.cpp index 409e5d4dda..2c2d0f8251 100644 --- a/media/mtransport/test/transport_unittests.cpp +++ b/media/mtransport/test/transport_unittests.cpp @@ -91,8 +91,6 @@ class TransportLayerDummy : public TransportLayer { bool *destroyed_; }; -class TransportLayerLossy; - class Inspector { public: virtual ~Inspector() {} @@ -440,7 +438,7 @@ class TransportTestPeer : public sigslot::has_slots<> { public: TransportTestPeer(nsCOMPtr target, std::string name) : name_(name), target_(target), - received_(0), flow_(new TransportFlow(name)), + received_packets_(0),received_bytes_(0),flow_(new TransportFlow(name)), loopback_(new TransportLayerLoopback()), logging_(new TransportLayerLogging()), lossy_(new TransportLayerLossy()), @@ -724,13 +722,18 @@ class TransportTestPeer : public sigslot::has_slots<> { void PacketReceived(TransportFlow * flow, const unsigned char* data, size_t len) { std::cerr << "Received " << len << " bytes" << std::endl; - ++received_; + ++received_packets_; + received_bytes_ += len; } void SetLoss(uint32_t loss) { lossy_->SetLoss(loss); } + void SetCombinePackets(bool combine) { + loopback_->CombinePackets(combine); + } + void SetInspector(UniquePtr inspector) { lossy_->SetInspector(Move(inspector)); } @@ -768,7 +771,9 @@ class TransportTestPeer : public sigslot::has_slots<> { return state() == TransportLayer::TS_ERROR; } - size_t received() { return received_; } + size_t receivedPackets() { return received_packets_; } + + size_t receivedBytes() { return received_bytes_; } uint16_t cipherSuite() const { nsresult rv; @@ -798,7 +803,8 @@ class TransportTestPeer : public sigslot::has_slots<> { private: std::string name_; nsCOMPtr target_; - size_t received_; + size_t received_packets_; + size_t received_bytes_; RefPtr flow_; TransportLayerLoopback *loopback_; TransportLayerLogging *logging_; @@ -919,8 +925,8 @@ class TransportTest : public ::testing::Test { ASSERT_TRUE_WAIT(p2_->connected(), 10000); } - void TransferTest(size_t count) { - unsigned char buf[1000]; + void TransferTest(size_t count, size_t bytes = 1024) { + unsigned char buf[bytes]; for (size_t i= 0; i 0); } - std::cerr << "Received == " << p2_->received() << std::endl; - ASSERT_TRUE_WAIT(count == p2_->received(), 10000); + std::cerr << "Received == " << p2_->receivedPackets() << " packets" << std::endl; + ASSERT_TRUE_WAIT(count == p2_->receivedPackets(), 10000); + ASSERT_TRUE((count * sizeof(buf)) == p2_->receivedBytes()); } protected: @@ -1058,13 +1065,13 @@ TEST_F(TransportTest, TestConnectTwoDigests) { TEST_F(TransportTest, TestConnectTwoDigestsFirstBad) { SetDtlsPeer(2, 1); - ConnectSocketExpectFail(); + ConnectSocket(); } TEST_F(TransportTest, TestConnectTwoDigestsSecondBad) { SetDtlsPeer(2, 2); - ConnectSocketExpectFail(); + ConnectSocket(); } TEST_F(TransportTest, TestConnectTwoDigestsBothBad) { @@ -1143,6 +1150,28 @@ TEST_F(TransportTest, TestTransfer) { TransferTest(1); } +TEST_F(TransportTest, TestTransferMaxSize) { + SetDtlsPeer(); + ConnectSocket(); + /* transportlayerdtls uses a 9216 bytes buffer - as this test uses the + * loopback implementation it does not have to take into account the extra + * bytes added by the DTLS layer below. */ + TransferTest(1, 9216); +} + +TEST_F(TransportTest, TestTransferMultiple) { + SetDtlsPeer(); + ConnectSocket(); + TransferTest(3); +} + +TEST_F(TransportTest, TestTransferCombinedPackets) { + SetDtlsPeer(); + ConnectSocket(); + p2_->SetCombinePackets(true); + TransferTest(3); +} + TEST_F(TransportTest, TestConnectLoseFirst) { SetDtlsPeer(); p1_->SetLoss(0); @@ -1155,10 +1184,32 @@ TEST_F(TransportTest, TestConnectIce) { ConnectIce(); } -TEST_F(TransportTest, TestTransferIce) { +TEST_F(TransportTest, TestTransferIceMaxSize) { SetDtlsPeer(); ConnectIce(); - TransferTest(1); + /* nICEr and transportlayerdtls both use 9216 bytes buffers. But the DTLS + * layer add extra bytes to the packet, which size depends on chosen cipher + * etc. Sending more then 9216 bytes works, but on the receiving side the call + * to PR_recvfrom() will truncate any packet bigger then nICEr's buffer size + * of 9216 bytes, which then results in the DTLS layer discarding the packet. + * Therefore we leave some headroom (according to + * https://bugzilla.mozilla.org/show_bug.cgi?id=1214269#c29 256 bytes should + * be save choice) here for the DTLS bytes to make it safely into the + * receiving buffer in nICEr. */ + TransferTest(1, 8960); +} + +TEST_F(TransportTest, TestTransferIceMultiple) { + SetDtlsPeer(); + ConnectIce(); + TransferTest(3); +} + +TEST_F(TransportTest, TestTransferIceCombinedPackets) { + SetDtlsPeer(); + ConnectIce(); + p2_->SetCombinePackets(true); + TransferTest(3); } // test the default configuration against a peer that supports only diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_socket.c b/media/mtransport/third_party/nICEr/src/ice/ice_socket.c index eb3e5cad61..a52280c55b 100644 --- a/media/mtransport/third_party/nICEr/src/ice/ice_socket.c +++ b/media/mtransport/third_party/nICEr/src/ice/ice_socket.c @@ -46,7 +46,7 @@ static void nr_ice_socket_readable_cb(NR_SOCKET s, int how, void *cb_arg) int r; nr_ice_stun_ctx *sc1,*sc2; nr_ice_socket *sock=cb_arg; - UCHAR buf[8192]; + UCHAR buf[9216]; char string[256]; nr_transport_addr addr; int len; diff --git a/media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.c b/media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.c index c171fb5474..5d9fc07131 100644 --- a/media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.c +++ b/media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.c @@ -41,7 +41,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define MAX_HTTP_CONNECT_ADDR_SIZE 256 #define MAX_HTTP_CONNECT_BUFFER_SIZE 1024 -#define ENDLN "\r\n\r\n" +#define MAX_ALPN_LENGTH 64 +#ifndef CRLF +#define CRLF "\r\n" +#endif +#define END_HEADERS CRLF CRLF typedef struct nr_socket_proxy_tunnel_ { nr_proxy_tunnel_config *config; @@ -94,7 +98,8 @@ static int send_http_connect(nr_socket_proxy_tunnel *sock) int port; int printed; char addr[MAX_HTTP_CONNECT_ADDR_SIZE]; - char mesg[MAX_HTTP_CONNECT_ADDR_SIZE + 64]; + char mesg[MAX_HTTP_CONNECT_ADDR_SIZE + MAX_ALPN_LENGTH + 128]; + size_t offset = 0; size_t bytes_sent; r_log(LOG_GENERIC,LOG_DEBUG,"send_http_connect"); @@ -107,16 +112,32 @@ static int send_http_connect(nr_socket_proxy_tunnel *sock) ABORT(r); } - printed = snprintf(mesg, sizeof(mesg), "CONNECT %s:%d HTTP/1.0%s", addr, port, ENDLN); - if (printed < 0 || ((size_t)printed >= sizeof(mesg))) { + printed = snprintf(mesg + offset, sizeof(mesg) - offset, + "CONNECT %s:%d HTTP/1.0", addr, port); + offset += printed; + if (printed < 0 || (offset >= sizeof(mesg))) { ABORT(R_FAILED); } - if ((r=nr_socket_write(sock->inner, mesg, strlen(mesg), &bytes_sent, 0))) { + if (sock->config->alpn) { + printed = snprintf(mesg + offset, sizeof(mesg) - offset, + CRLF "ALPN: %s", sock->config->alpn); + offset += printed; + if (printed < 0 || (offset >= sizeof(mesg))) { + ABORT(R_FAILED); + } + } + if (offset + sizeof(END_HEADERS) >= sizeof(mesg)) { + ABORT(R_FAILED); + } + memcpy(mesg + offset, END_HEADERS, strlen(END_HEADERS)); + offset += strlen(END_HEADERS); + + if ((r=nr_socket_write(sock->inner, mesg, offset, &bytes_sent, 0))) { ABORT(r); } - if (bytes_sent < strlen(mesg)) { + if (bytes_sent < offset) { /* TODO(bug 1116583): buffering and wait for */ r_log(LOG_GENERIC,LOG_DEBUG,"send_http_connect should be buffering %lu", (unsigned long)bytes_sent); ABORT(R_IO_ERROR); @@ -133,10 +154,10 @@ static char *find_http_terminator(char *response, size_t len) { char *term = response; char *end = response + len; - int N = strlen(ENDLN); + int N = strlen(END_HEADERS); for (; term = memchr(term, '\r', end - term); ++term) { - if (end - term >= N && memcmp(term, ENDLN, N) == 0) { + if (end - term >= N && memcmp(term, END_HEADERS, N) == 0) { return term; } } @@ -367,7 +388,7 @@ int nr_socket_proxy_tunnel_read(void *obj, void * restrict buf, size_t maxlen, ABORT(R_FAILED); } - ptr = http_term + strlen(ENDLN); + ptr = http_term + strlen(END_HEADERS); pending = sock->buffered_bytes - (ptr - sock->buffer); if (pending == 0) { @@ -431,6 +452,7 @@ int nr_proxy_tunnel_config_destroy(nr_proxy_tunnel_config **configpp) *configpp = 0; RFREE(configp->proxy_host); + RFREE(configp->alpn); RFREE(configp); return 0; @@ -471,6 +493,34 @@ int nr_proxy_tunnel_config_set_resolver(nr_proxy_tunnel_config *config, return 0; } +int nr_proxy_tunnel_config_set_alpn(nr_proxy_tunnel_config *config, + const char *alpn) +{ + r_log(LOG_GENERIC,LOG_DEBUG,"nr_proxy_tunnel_config_set_alpn"); + + if (alpn && (strlen(alpn) > MAX_ALPN_LENGTH)) { + return R_BAD_ARGS; + } + + if (config->alpn) { + RFREE(config->alpn); + } + + config->alpn = NULL; + + if (alpn) { + char *alpndup = r_strdup(alpn); + + if (!alpndup) { + return R_NO_MEMORY; + } + + config->alpn = alpndup; + } + + return 0; +} + int nr_proxy_tunnel_config_copy(nr_proxy_tunnel_config *config, nr_proxy_tunnel_config **copypp) { int r,_status; @@ -485,6 +535,9 @@ int nr_proxy_tunnel_config_copy(nr_proxy_tunnel_config *config, nr_proxy_tunnel_ if ((r=nr_proxy_tunnel_config_set_resolver(copy, config->resolver))) ABORT(r); + if ((r=nr_proxy_tunnel_config_set_alpn(copy, config->alpn))) + ABORT(r); + *copypp = copy; _status=0; diff --git a/media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.h b/media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.h index e1836758d1..e9c4393e1d 100644 --- a/media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.h +++ b/media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.h @@ -44,6 +44,7 @@ typedef struct nr_proxy_tunnel_config_ { nr_resolver *resolver; char *proxy_host; UINT2 proxy_port; + char *alpn; } nr_proxy_tunnel_config; int nr_proxy_tunnel_config_create(nr_proxy_tunnel_config **config); @@ -56,6 +57,9 @@ int nr_proxy_tunnel_config_set_proxy(nr_proxy_tunnel_config *config, int nr_proxy_tunnel_config_set_resolver(nr_proxy_tunnel_config *config, nr_resolver *resolver); +int nr_proxy_tunnel_config_set_alpn(nr_proxy_tunnel_config *config, + const char *alpn); + int nr_socket_proxy_tunnel_create(nr_proxy_tunnel_config *config, nr_socket *inner, nr_socket **socketpp); diff --git a/media/mtransport/transportlayerdtls.cpp b/media/mtransport/transportlayerdtls.cpp index a14bffd583..0f6209298e 100644 --- a/media/mtransport/transportlayerdtls.cpp +++ b/media/mtransport/transportlayerdtls.cpp @@ -75,8 +75,10 @@ struct Packet { }; void TransportLayerNSPRAdapter::PacketReceived(const void *data, int32_t len) { - input_.push(new Packet()); - input_.back()->Assign(data, len); + if (enabled_) { + input_.push(new Packet()); + input_.back()->Assign(data, len); + } } int32_t TransportLayerNSPRAdapter::Recv(void *buf, int32_t buflen) { @@ -102,6 +104,11 @@ int32_t TransportLayerNSPRAdapter::Recv(void *buf, int32_t buflen) { } int32_t TransportLayerNSPRAdapter::Write(const void *buf, int32_t length) { + if (!enabled_) { + MOZ_MTLOG(ML_WARNING, "Writing to disabled transport layer"); + return -1; + } + TransportResult r = output_->SendPacket( static_cast(buf), length); if (r >= 0) { @@ -200,8 +207,12 @@ static PRStatus TransportLayerListen(PRFileDesc *f, int32_t depth) { } static PRStatus TransportLayerShutdown(PRFileDesc *f, int32_t how) { - UNIMPLEMENTED; - return PR_FAILURE; + // This is only called from NSS when we are the server and the client refuses + // to provide a certificate. In this case, the handshake is destined for + // failure, so we will just let this pass. + TransportLayerNSPRAdapter *io = reinterpret_cast(f->secret); + io->SetEnabled(false); + return PR_SUCCESS; } // This function does not support peek, or waiting until `to` @@ -494,7 +505,7 @@ bool TransportLayerDtls::Setup() { pr_fd.forget(); // ownership transfered to ssl_fd; if (role_ == CLIENT) { - MOZ_MTLOG(ML_DEBUG, "Setting up DTLS as client"); + MOZ_MTLOG(ML_INFO, "Setting up DTLS as client"); rv = SSL_GetClientAuthDataHook(ssl_fd, GetClientAuthDataHook, this); if (rv != SECSuccess) { @@ -502,7 +513,7 @@ bool TransportLayerDtls::Setup() { return false; } } else { - MOZ_MTLOG(ML_DEBUG, "Setting up DTLS as server"); + MOZ_MTLOG(ML_INFO, "Setting up DTLS as server"); // Server side rv = SSL_ConfigSecureServer(ssl_fd, identity_->cert(), identity_->privkey(), @@ -617,6 +628,7 @@ bool TransportLayerDtls::Setup() { downward_->SignalPacketReceived.connect(this, &TransportLayerDtls::PacketReceived); if (downward_->state() == TS_OPEN) { + TL_SET_STATE(TS_CONNECTING); Handshake(); } @@ -736,7 +748,7 @@ bool TransportLayerDtls::SetupCipherSuites(PRFileDesc* ssl_fd) const { } for (size_t i = 0; i < PR_ARRAY_SIZE(EnabledCiphers); ++i) { - MOZ_MTLOG(ML_INFO, LAYER_INFO << "Enabling: " << EnabledCiphers[i]); + MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Enabling: " << EnabledCiphers[i]); rv = SSL_CipherPrefSet(ssl_fd, EnabledCiphers[i], PR_TRUE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, LAYER_INFO << @@ -746,7 +758,7 @@ bool TransportLayerDtls::SetupCipherSuites(PRFileDesc* ssl_fd) const { } for (size_t i = 0; i < PR_ARRAY_SIZE(DisabledCiphers); ++i) { - MOZ_MTLOG(ML_INFO, LAYER_INFO << "Disabling: " << DisabledCiphers[i]); + MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Disabling: " << DisabledCiphers[i]); PRBool enabled = false; rv = SSL_CipherPrefGet(ssl_fd, DisabledCiphers[i], &enabled); @@ -812,7 +824,15 @@ void TransportLayerDtls::StateChange(TransportLayer *layer, State state) { case TS_OPEN: MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Lower layer is now open; starting TLS"); - Handshake(); + // Async, since the ICE layer might need to send a STUN response, and we + // don't want the handshake to start until that is sent. + TL_SET_STATE(TS_CONNECTING); + timer_->Cancel(); + timer_->SetTarget(target_); + timer_->InitWithFuncCallback(TimerCallback, + this, + 0, + nsITimer::TYPE_ONE_SHOT); break; case TS_CLOSED: @@ -828,8 +848,6 @@ void TransportLayerDtls::StateChange(TransportLayer *layer, State state) { } void TransportLayerDtls::Handshake() { - TL_SET_STATE(TS_CONNECTING); - // Clear the retransmit timer timer_->Cancel(); @@ -860,6 +878,7 @@ void TransportLayerDtls::Handshake() { MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Malformed DTLS message; ignoring"); // If this were TLS (and not DTLS), this would be fatal, but // here we're required to ignore bad messages, so fall through + MOZ_FALLTHROUGH; case PR_WOULD_BLOCK_ERROR: MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "Handshake would have blocked"); PRIntervalTime timeout; @@ -959,26 +978,31 @@ void TransportLayerDtls::PacketReceived(TransportLayer* layer, // Now try a recv if we're open, since there might be data left if (state_ == TS_OPEN) { - unsigned char buf[2000]; + // nICEr uses a 9216 bytes buffer to allow support for jumbo frames + unsigned char buf[9216]; - int32_t rv = PR_Recv(ssl_fd_, buf, sizeof(buf), 0, PR_INTERVAL_NO_WAIT); - if (rv > 0) { - // We have data - MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Read " << rv << " bytes from NSS"); - SignalPacketReceived(this, buf, rv); - } else if (rv == 0) { - TL_SET_STATE(TS_CLOSED); - } else { - int32_t err = PR_GetError(); - - if (err == PR_WOULD_BLOCK_ERROR) { - // This gets ignored - MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Receive would have blocked"); + int32_t rv; + // One packet might contain several DTLS packets + do { + rv = PR_Recv(ssl_fd_, buf, sizeof(buf), 0, PR_INTERVAL_NO_WAIT); + if (rv > 0) { + // We have data + MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Read " << rv << " bytes from NSS"); + SignalPacketReceived(this, buf, rv); + } else if (rv == 0) { + TL_SET_STATE(TS_CLOSED); } else { - MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "NSS Error " << err); - TL_SET_STATE(TS_ERROR); + int32_t err = PR_GetError(); + + if (err == PR_WOULD_BLOCK_ERROR) { + // This gets ignored + MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Receive would have blocked"); + } else { + MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "NSS Error " << err); + TL_SET_STATE(TS_ERROR); + } } - } + } while (rv > 0); } } @@ -1189,15 +1213,12 @@ SECStatus TransportLayerDtls::AuthCertificateHook(PRFileDesc *fd, RefPtr digest = digests_[i]; rv = CheckDigest(digest, peer_cert); - if (rv != SECSuccess) - break; - } - - if (rv == SECSuccess) { - // Matches all digests, we are good to go - cert_ok_ = true; - peer_cert = peer_cert.forget(); - return SECSuccess; + // Matches a digest, we are good to go + if (rv == SECSuccess) { + cert_ok_ = true; + peer_cert = peer_cert.forget(); + return SECSuccess; + } } } break; diff --git a/media/mtransport/transportlayerdtls.h b/media/mtransport/transportlayerdtls.h index 7594c6e714..cc72ee7cbc 100644 --- a/media/mtransport/transportlayerdtls.h +++ b/media/mtransport/transportlayerdtls.h @@ -33,17 +33,20 @@ class TransportLayerNSPRAdapter { public: explicit TransportLayerNSPRAdapter(TransportLayer *output) : output_(output), - input_() {} + input_(), + enabled_(true) {} void PacketReceived(const void *data, int32_t len); int32_t Recv(void *buf, int32_t buflen); int32_t Write(const void *buf, int32_t length); + void SetEnabled(bool enabled) { enabled_ = enabled; } private: DISALLOW_COPY_ASSIGN(TransportLayerNSPRAdapter); TransportLayer *output_; std::queue input_; + bool enabled_; }; class TransportLayerDtls final : public TransportLayer { diff --git a/media/mtransport/transportlayerloopback.cpp b/media/mtransport/transportlayerloopback.cpp index a1a3d2ccdc..11c3b4244d 100644 --- a/media/mtransport/transportlayerloopback.cpp +++ b/media/mtransport/transportlayerloopback.cpp @@ -78,13 +78,23 @@ TransportLayerLoopback::SendPacket(const unsigned char *data, size_t len) { nsresult TransportLayerLoopback::QueuePacket(const unsigned char *data, size_t len) { - MOZ_MTLOG(ML_DEBUG, LAYER_INFO << " Enqueuing packet of length " << len); MOZ_ASSERT(packets_lock_); PR_Lock(packets_lock_); - packets_.push(new QueuedPacket()); - packets_.back()->Assign(data, len); + if (combinePackets_ && !packets_.empty()) { + QueuedPacket *packet = packets_.front(); + packets_.pop(); + + MOZ_MTLOG(ML_DEBUG, LAYER_INFO << " Enqueuing combined packets of length " << packet->len() << " and " << len); + packets_.push(new QueuedPacket()); + packets_.back()->Assign(packet->data(), packet->len(), + data, len); + } else { + MOZ_MTLOG(ML_DEBUG, LAYER_INFO << " Enqueuing packet of length " << len); + packets_.push(new QueuedPacket()); + packets_.back()->Assign(data, len); + } PRStatus r = PR_Unlock(packets_lock_); MOZ_ASSERT(r == PR_SUCCESS); diff --git a/media/mtransport/transportlayerloopback.h b/media/mtransport/transportlayerloopback.h index 59aac11c3a..8ad5e28657 100644 --- a/media/mtransport/transportlayerloopback.h +++ b/media/mtransport/transportlayerloopback.h @@ -36,7 +36,8 @@ class TransportLayerLoopback : public TransportLayer { timer_(nullptr), packets_(), packets_lock_(nullptr), - deliverer_(nullptr) {} + deliverer_(nullptr), + combinePackets_(false) {} ~TransportLayerLoopback() { while (!packets_.empty()) { @@ -67,6 +68,8 @@ class TransportLayerLoopback : public TransportLayer { } } + void CombinePackets(bool combine) { combinePackets_ = combine; } + // Overrides for TransportLayer virtual TransportResult SendPacket(const unsigned char *data, size_t len); @@ -93,6 +96,16 @@ class TransportLayerLoopback : public TransportLayer { len_ = len; } + void Assign(const unsigned char *data1, size_t len1, + const unsigned char *data2, size_t len2) { + data_ = new unsigned char[len1 + len2]; + memcpy(static_cast(data_), + static_cast(data1), len1); + memcpy(static_cast(data_ + len1), + static_cast(data2), len2); + len_ = len1 + len2; + } + const unsigned char *data() const { return data_; } size_t len() const { return len_; } @@ -133,6 +146,7 @@ class TransportLayerLoopback : public TransportLayer { std::queue packets_; PRLock *packets_lock_; RefPtr deliverer_; + bool combinePackets_; }; } // close namespace diff --git a/media/webrtc/.gclient b/media/webrtc/.gclient index 26d4f1d77d..14532a1f3c 100644 --- a/media/webrtc/.gclient +++ b/media/webrtc/.gclient @@ -1,1007 +1,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - newtab-dev/.gclient at 3263a9649b2a172deb2f532c3a2ecd6fbc4de726 · mozilla/newtab-dev · GitHub - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Skip to content - - - - - - - - - - - -
- -
- - - - -
- - - -
- - - - - - - - - -
-
-
- - - - - - - - - - - - -
- This repository has been archived by the owner. It is now read-only. -
- - - - - - - -
- -
- -
-

- - - - - / - - newtab-dev - - Archived -

- - -
- - - -
- - - - - -
- - - - - - -
-
- - - - - - - - Permalink - - - - - -
- - -
- - Tree: - 3263a9649b - - - - -
- - - -
-
-
- -
- - Find file - - - Copy path - -
-
- - -
- - Find file - - - Copy path - -
-
- - - - -
- - -
-
- - 1 recent contributor - - -
- -

- Users who have contributed to this file recently -

-
- -
-
-
-
- - - - - - -
- -
-
- - 10 lines (10 sloc) - - 234 Bytes -
- -
- -
- Raw - Blame - History -
- - -
- - - - -
-
-
- - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
solutions = [
{ "name" : "trunk",
"url" : "http://webrtc.googlecode.com/svn/trunk/peerconnection",
"deps_file" : "DEPS",
"managed" : True,
"custom_deps" : {
},
"safesync_url": "",
},
]
- - - -
- -
- - - -
- - -
- - -
-
- - - -
-
- -
-
- - -
- - - - - - -
- - - You can’t perform that action at this time. -
- - - - - - - - - - - - - - - - - - - - - - - +solutions = [ + { "name" : "trunk", + "url" : "http://webrtc.googlecode.com/svn/trunk/peerconnection", + "deps_file" : "DEPS", + "managed" : True, + "custom_deps" : { + }, + "safesync_url": "", + }, +] diff --git a/media/webrtc/.gclient_entries b/media/webrtc/.gclient_entries index de93b99823..e00be99175 100644 --- a/media/webrtc/.gclient_entries +++ b/media/webrtc/.gclient_entries @@ -1,1091 +1,31 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - newtab-dev/.gclient_entries at 3263a9649b2a172deb2f532c3a2ecd6fbc4de726 · mozilla/newtab-dev · GitHub - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Skip to content - - - - - - - - - - - -
- -
- - - - -
- - - -
- - - - - - - - - -
-
-
- - - - - - - - - - - - -
- This repository has been archived by the owner. It is now read-only. -
- - - - - - - -
- -
- -
-

- - - - - / - - newtab-dev - - Archived -

- - -
- - - -
- - - - - -
- - - - - - -
-
- - - - - - - - Permalink - - - - - -
- - -
- - Tree: - 3263a9649b - - - - -
- - - -
-
-
- -
- - Find file - - - Copy path - -
-
- - -
- - Find file - - - Copy path - -
-
- - - - -
- - -
-
- - 1 recent contributor - - -
- -

- Users who have contributed to this file recently -

-
- -
-
-
-
- - - - - - -
- -
-
- - 31 lines (31 sloc) - - 2.57 KB -
- -
- -
- Raw - Blame - History -
- - -
- - - - -
-
-
- - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
entries = {
'trunk': 'http://webrtc.googlecode.com/svn/trunk/peerconnection',
'trunk/build': 'http://src.chromium.org/svn/trunk/src/build@120526',
'trunk/src': 'http://webrtc.googlecode.com/svn/trunk/src@1538',
'trunk/test': 'http://webrtc.googlecode.com/svn/trunk/test@1538',
'trunk/testing': 'http://src.chromium.org/svn/trunk/src/testing@120526',
'trunk/testing/gmock': 'http://googlemock.googlecode.com/svn/trunk@386',
'trunk/testing/gtest': 'http://googletest.googlecode.com/svn/trunk@573',
'trunk/third_party/expat/': 'http://src.chromium.org/svn/trunk/src/third_party/expat@120526',
'trunk/third_party/google-gflags': 'http://webrtc.googlecode.com/svn/trunk/third_party/google-gflags@1538',
'trunk/third_party/google-gflags/src': 'http://google-gflags.googlecode.com/svn/trunk/src@45',
'trunk/third_party/jsoncpp/': 'http://src.chromium.org/svn/trunk/src/third_party/jsoncpp@120526',
'trunk/third_party/jsoncpp/source': 'http://jsoncpp.svn.sourceforge.net/svnroot/jsoncpp/trunk/jsoncpp@248',
'trunk/third_party/libjingle/source': 'http://libjingle.googlecode.com/svn/trunk@115',
'trunk/third_party/libjpeg/': 'http://src.chromium.org/svn/trunk/src/third_party/libjpeg@120526',
'trunk/third_party/libjpeg_turbo/': 'http://src.chromium.org/svn/trunk/deps/third_party/libjpeg_turbo@119959',
'trunk/third_party/libsrtp/': 'http://src.chromium.org/svn/trunk/deps/third_party/libsrtp@119742',
'trunk/third_party/libvpx': 'http://webrtc.googlecode.com/svn/trunk/third_party/libvpx@1538',
'trunk/third_party/libvpx/source/libvpx': 'http://git.chromium.org/webm/libvpx.git@e479379a',
'trunk/third_party/libyuv': 'http://libyuv.googlecode.com/svn/trunk@121',
'trunk/third_party/protobuf/': 'http://src.chromium.org/svn/trunk/src/third_party/protobuf@120526',
'trunk/third_party/yasm/': 'http://src.chromium.org/svn/trunk/src/third_party/yasm@120526',
'trunk/third_party/yasm/binaries': 'http://src.chromium.org/svn/trunk/deps/third_party/yasm/binaries@74228',
'trunk/third_party/yasm/source/patched-yasm': 'http://src.chromium.org/svn/trunk/deps/third_party/yasm/patched-yasm@73761',
'trunk/tools': 'http://webrtc.googlecode.com/svn/trunk/tools@1538',
'trunk/tools/clang/scripts': 'http://src.chromium.org/svn/trunk/src/tools/clang/scripts@120526',
'trunk/tools/gyp': 'http://gyp.googlecode.com/svn/trunk@1187',
'trunk/tools/python': 'http://src.chromium.org/svn/trunk/src/tools/python@120526',
'trunk/tools/valgrind': 'http://src.chromium.org/svn/trunk/src/tools/valgrind@120526',
'trunk/tools/win/supalink': 'http://src.chromium.org/svn/trunk/src/tools/win/supalink@120526',
}
- - - -
- -
- - - -
- - -
- - -
-
- - - -
-
- -
-
- - -
- - - - - - -
- - - You can’t perform that action at this time. -
- - - - - - - - - - - - - - - - - - - - - - - +entries = { + 'trunk': 'http://webrtc.googlecode.com/svn/trunk/peerconnection', + 'trunk/build': 'http://src.chromium.org/svn/trunk/src/build@120526', + 'trunk/src': 'http://webrtc.googlecode.com/svn/trunk/src@1538', + 'trunk/test': 'http://webrtc.googlecode.com/svn/trunk/test@1538', + 'trunk/testing': 'http://src.chromium.org/svn/trunk/src/testing@120526', + 'trunk/testing/gmock': 'http://googlemock.googlecode.com/svn/trunk@386', + 'trunk/testing/gtest': 'http://googletest.googlecode.com/svn/trunk@573', + 'trunk/third_party/expat/': 'http://src.chromium.org/svn/trunk/src/third_party/expat@120526', + 'trunk/third_party/google-gflags': 'http://webrtc.googlecode.com/svn/trunk/third_party/google-gflags@1538', + 'trunk/third_party/google-gflags/src': 'http://google-gflags.googlecode.com/svn/trunk/src@45', + 'trunk/third_party/jsoncpp/': 'http://src.chromium.org/svn/trunk/src/third_party/jsoncpp@120526', + 'trunk/third_party/jsoncpp/source': 'http://jsoncpp.svn.sourceforge.net/svnroot/jsoncpp/trunk/jsoncpp@248', + 'trunk/third_party/libjingle/source': 'http://libjingle.googlecode.com/svn/trunk@115', + 'trunk/third_party/libjpeg/': 'http://src.chromium.org/svn/trunk/src/third_party/libjpeg@120526', + 'trunk/third_party/libjpeg_turbo/': 'http://src.chromium.org/svn/trunk/deps/third_party/libjpeg_turbo@119959', + 'trunk/third_party/libsrtp/': 'http://src.chromium.org/svn/trunk/deps/third_party/libsrtp@119742', + 'trunk/third_party/libvpx': 'http://webrtc.googlecode.com/svn/trunk/third_party/libvpx@1538', + 'trunk/third_party/libvpx/source/libvpx': 'http://git.chromium.org/webm/libvpx.git@e479379a', + 'trunk/third_party/libyuv': 'http://libyuv.googlecode.com/svn/trunk@121', + 'trunk/third_party/protobuf/': 'http://src.chromium.org/svn/trunk/src/third_party/protobuf@120526', + 'trunk/third_party/yasm/': 'http://src.chromium.org/svn/trunk/src/third_party/yasm@120526', + 'trunk/third_party/yasm/binaries': 'http://src.chromium.org/svn/trunk/deps/third_party/yasm/binaries@74228', + 'trunk/third_party/yasm/source/patched-yasm': 'http://src.chromium.org/svn/trunk/deps/third_party/yasm/patched-yasm@73761', + 'trunk/tools': 'http://webrtc.googlecode.com/svn/trunk/tools@1538', + 'trunk/tools/clang/scripts': 'http://src.chromium.org/svn/trunk/src/tools/clang/scripts@120526', + 'trunk/tools/gyp': 'http://gyp.googlecode.com/svn/trunk@1187', + 'trunk/tools/python': 'http://src.chromium.org/svn/trunk/src/tools/python@120526', + 'trunk/tools/valgrind': 'http://src.chromium.org/svn/trunk/src/tools/valgrind@120526', + 'trunk/tools/win/supalink': 'http://src.chromium.org/svn/trunk/src/tools/win/supalink@120526', +} diff --git a/media/webrtc/signaling/src/jsep/JsepSession.h b/media/webrtc/signaling/src/jsep/JsepSession.h index 0f4d1a911e..9cf59d1250 100644 --- a/media/webrtc/signaling/src/jsep/JsepSession.h +++ b/media/webrtc/signaling/src/jsep/JsepSession.h @@ -143,12 +143,13 @@ public: uint16_t level, std::string* mid, bool* skipped) = 0; - virtual nsresult EndOfLocalCandidates( + virtual nsresult UpdateDefaultCandidate( const std::string& defaultCandidateAddr, uint16_t defaultCandidatePort, const std::string& defaultRtcpCandidateAddr, uint16_t defaultRtcpCandidatePort, uint16_t level) = 0; + virtual nsresult EndOfLocalCandidates(uint16_t level) = 0; virtual nsresult Close() = 0; // ICE controlling or controlled diff --git a/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp b/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp index af856a4aac..d8df537c4a 100644 --- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp +++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp @@ -649,10 +649,9 @@ std::string JsepSessionImpl::GetLocalDescription() const { std::ostringstream os; - if (mPendingLocalDescription) { - mPendingLocalDescription->Serialize(os); - } else if (mCurrentLocalDescription) { - mCurrentLocalDescription->Serialize(os); + mozilla::Sdp* sdp = GetParsedLocalDescription(); + if (sdp) { + sdp->Serialize(os); } return os.str(); } @@ -661,10 +660,9 @@ std::string JsepSessionImpl::GetRemoteDescription() const { std::ostringstream os; - if (mPendingRemoteDescription) { - mPendingRemoteDescription->Serialize(os); - } else if (mCurrentRemoteDescription) { - mCurrentRemoteDescription->Serialize(os); + mozilla::Sdp* sdp = GetParsedRemoteDescription(); + if (sdp) { + sdp->Serialize(os); } return os.str(); } @@ -2116,13 +2114,9 @@ JsepSessionImpl::AddRemoteIceCandidate(const std::string& candidate, { mLastError.clear(); - mozilla::Sdp* sdp = 0; + mozilla::Sdp* sdp = GetParsedRemoteDescription(); - if (mPendingRemoteDescription) { - sdp = mPendingRemoteDescription.get(); - } else if (mCurrentRemoteDescription) { - sdp = mCurrentRemoteDescription.get(); - } else { + if (!sdp) { JSEP_SET_ERROR("Cannot add ICE candidate in state " << GetStateStr(mState)); return NS_ERROR_UNEXPECTED; } @@ -2138,13 +2132,9 @@ JsepSessionImpl::AddLocalIceCandidate(const std::string& candidate, { mLastError.clear(); - mozilla::Sdp* sdp = 0; + mozilla::Sdp* sdp = GetParsedLocalDescription(); - if (mPendingLocalDescription) { - sdp = mPendingLocalDescription.get(); - } else if (mCurrentLocalDescription) { - sdp = mCurrentLocalDescription.get(); - } else { + if (!sdp) { JSEP_SET_ERROR("Cannot add ICE candidate in state " << GetStateStr(mState)); return NS_ERROR_UNEXPECTED; } @@ -2176,21 +2166,18 @@ JsepSessionImpl::AddLocalIceCandidate(const std::string& candidate, } nsresult -JsepSessionImpl::EndOfLocalCandidates(const std::string& defaultCandidateAddr, - uint16_t defaultCandidatePort, - const std::string& defaultRtcpCandidateAddr, - uint16_t defaultRtcpCandidatePort, - uint16_t level) +JsepSessionImpl::UpdateDefaultCandidate( + const std::string& defaultCandidateAddr, + uint16_t defaultCandidatePort, + const std::string& defaultRtcpCandidateAddr, + uint16_t defaultRtcpCandidatePort, + uint16_t level) { mLastError.clear(); - mozilla::Sdp* sdp = 0; + mozilla::Sdp* sdp = GetParsedLocalDescription(); - if (mPendingLocalDescription) { - sdp = mPendingLocalDescription.get(); - } else if (mCurrentLocalDescription) { - sdp = mCurrentLocalDescription.get(); - } else { + if (!sdp) { JSEP_SET_ERROR("Cannot add ICE candidate in state " << GetStateStr(mState)); return NS_ERROR_UNEXPECTED; } @@ -2230,6 +2217,42 @@ JsepSessionImpl::EndOfLocalCandidates(const std::string& defaultCandidateAddr, return NS_OK; } +nsresult +JsepSessionImpl::EndOfLocalCandidates(uint16_t level) +{ + mLastError.clear(); + + mozilla::Sdp* sdp = GetParsedLocalDescription(); + + if (!sdp) { + JSEP_SET_ERROR("Cannot mark end of local ICE candidates in state " + << GetStateStr(mState)); + return NS_ERROR_UNEXPECTED; + } + + if (level >= sdp->GetMediaSectionCount()) { + return NS_OK; + } + + // If offer/answer isn't done, it is too early to tell whether this update + // needs to be applied to other m-sections. + SdpHelper::BundledMids bundledMids; + if (mState == kJsepStateStable) { + nsresult rv = GetNegotiatedBundledMids(&bundledMids); + if (NS_FAILED(rv)) { + MOZ_ASSERT(false); + mLastError += " (This should have been caught sooner!)"; + return NS_ERROR_FAILURE; + } + } + + mSdpHelper.SetIceGatheringComplete(sdp, + level, + bundledMids); + + return NS_OK; +} + nsresult JsepSessionImpl::GetNegotiatedBundledMids(SdpHelper::BundledMids* bundledMids) { @@ -2274,6 +2297,30 @@ JsepSessionImpl::EnableOfferMsection(SdpMediaSection* msection) return NS_OK; } +mozilla::Sdp* +JsepSessionImpl::GetParsedLocalDescription() const +{ + if (mPendingLocalDescription) { + return mPendingLocalDescription.get(); + } else if (mCurrentLocalDescription) { + return mCurrentLocalDescription.get(); + } + + return nullptr; +} + +mozilla::Sdp* +JsepSessionImpl::GetParsedRemoteDescription() const +{ + if (mPendingRemoteDescription) { + return mPendingRemoteDescription.get(); + } else if (mCurrentRemoteDescription) { + return mCurrentRemoteDescription.get(); + } + + return nullptr; +} + const Sdp* JsepSessionImpl::GetAnswer() const { diff --git a/media/webrtc/signaling/src/jsep/JsepSessionImpl.h b/media/webrtc/signaling/src/jsep/JsepSessionImpl.h index 29c800aaf5..bcf13abdae 100644 --- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.h +++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.h @@ -122,11 +122,14 @@ public: std::string* mid, bool* skipped) override; - virtual nsresult EndOfLocalCandidates(const std::string& defaultCandidateAddr, - uint16_t defaultCandidatePort, - const std::string& defaultRtcpCandidateAddr, - uint16_t defaultRtcpCandidatePort, - uint16_t level) override; + virtual nsresult UpdateDefaultCandidate( + const std::string& defaultCandidateAddr, + uint16_t defaultCandidatePort, + const std::string& defaultRtcpCandidateAddr, + uint16_t defaultRtcpCandidatePort, + uint16_t level) override; + + virtual nsresult EndOfLocalCandidates(uint16_t level) override; virtual nsresult Close() override; @@ -269,6 +272,8 @@ private: nsresult EnableOfferMsection(SdpMediaSection* msection); + mozilla::Sdp* GetParsedLocalDescription() const; + mozilla::Sdp* GetParsedRemoteDescription() const; const Sdp* GetAnswer() const; std::vector mLocalTracks; diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp index 9792b3d96f..5593b6347c 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp @@ -791,6 +791,9 @@ PeerConnectionImpl::Initialize(PeerConnectionObserver& aObserver, mMedia->SignalIceGatheringStateChange.connect( this, &PeerConnectionImpl::IceGatheringStateChange); + mMedia->SignalUpdateDefaultCandidate.connect( + this, + &PeerConnectionImpl::UpdateDefaultCandidate); mMedia->SignalEndOfLocalCandidates.connect( this, &PeerConnectionImpl::EndOfLocalCandidates); @@ -3042,17 +3045,23 @@ PeerConnectionImpl::IceGatheringStateChange( } void -PeerConnectionImpl::EndOfLocalCandidates(const std::string& defaultAddr, - uint16_t defaultPort, - const std::string& defaultRtcpAddr, - uint16_t defaultRtcpPort, - uint16_t level) { +PeerConnectionImpl::UpdateDefaultCandidate(const std::string& defaultAddr, + uint16_t defaultPort, + const std::string& defaultRtcpAddr, + uint16_t defaultRtcpPort, + uint16_t level) { CSFLogDebug(logTag, "%s", __FUNCTION__); - mJsepSession->EndOfLocalCandidates(defaultAddr, - defaultPort, - defaultRtcpAddr, - defaultRtcpPort, - level); + mJsepSession->UpdateDefaultCandidate(defaultAddr, + defaultPort, + defaultRtcpAddr, + defaultRtcpPort, + level); +} + +void +PeerConnectionImpl::EndOfLocalCandidates(uint16_t level) { + CSFLogDebug(logTag, "%s", __FUNCTION__); + mJsepSession->EndOfLocalCandidates(level); } #if !defined(MOZILLA_EXTERNAL_LINKAGE) diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h index ef228e9d21..efba691109 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h @@ -316,11 +316,12 @@ public: NrIceCtx::ConnectionState state); void IceGatheringStateChange(NrIceCtx* ctx, NrIceCtx::GatheringState state); - void EndOfLocalCandidates(const std::string& defaultAddr, - uint16_t defaultPort, - const std::string& defaultRtcpAddr, - uint16_t defaultRtcpPort, - uint16_t level); + void UpdateDefaultCandidate(const std::string& defaultAddr, + uint16_t defaultPort, + const std::string& defaultRtcpAddr, + uint16_t defaultRtcpPort, + uint16_t level); + void EndOfLocalCandidates(uint16_t level); void IceStreamReady(NrIceMediaStream *aStream); static void ListenThread(void *aData); diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp index bc434f2d72..701131d140 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp @@ -164,46 +164,62 @@ OnProxyAvailable(nsICancelable *request, nsIChannel *aChannel, nsIProxyInfo *proxyinfo, nsresult result) { + + if (result == NS_ERROR_ABORT) { + // NS_ERROR_ABORT means that the PeerConnectionMedia is no longer waiting + return NS_OK; + } + CSFLogInfo(logTag, "%s: Proxy Available: %d", __FUNCTION__, (int)result); if (NS_SUCCEEDED(result) && proxyinfo) { - CSFLogInfo(logTag, "%s: Had proxyinfo", __FUNCTION__); - nsresult rv; - nsCString httpsProxyHost; - int32_t httpsProxyPort; - - rv = proxyinfo->GetHost(httpsProxyHost); - if (NS_FAILED(rv)) { - CSFLogError(logTag, "%s: Failed to get proxy server host", __FUNCTION__); - return rv; - } - - rv = proxyinfo->GetPort(&httpsProxyPort); - if (NS_FAILED(rv)) { - CSFLogError(logTag, "%s: Failed to get proxy server port", __FUNCTION__); - return rv; - } - - if (pcm_->mIceCtx.get()) { - assert(httpsProxyPort >= 0 && httpsProxyPort < (1 << 16)); - pcm_->mProxyServer.reset( - new NrIceProxyServer(httpsProxyHost.get(), - static_cast(httpsProxyPort))); - } else { - CSFLogError(logTag, "%s: Failed to set proxy server (ICE ctx unavailable)", - __FUNCTION__); - } + SetProxyOnPcm(*proxyinfo); } - if (result != NS_ERROR_ABORT) { - // NS_ERROR_ABORT means that the PeerConnectionMedia is no longer waiting - pcm_->mProxyResolveCompleted = true; - pcm_->FlushIceCtxOperationQueueIfReady(); - } + pcm_->mProxyResolveCompleted = true; + pcm_->FlushIceCtxOperationQueueIfReady(); return NS_OK; } +void +PeerConnectionMedia::ProtocolProxyQueryHandler::SetProxyOnPcm( + nsIProxyInfo& proxyinfo) +{ + CSFLogInfo(logTag, "%s: Had proxyinfo", __FUNCTION__); + nsresult rv; + nsCString httpsProxyHost; + int32_t httpsProxyPort; + + rv = proxyinfo.GetHost(httpsProxyHost); + if (NS_FAILED(rv)) { + CSFLogError(logTag, "%s: Failed to get proxy server host", __FUNCTION__); + return; + } + + rv = proxyinfo.GetPort(&httpsProxyPort); + if (NS_FAILED(rv)) { + CSFLogError(logTag, "%s: Failed to get proxy server port", __FUNCTION__); + return; + } + + if (pcm_->mIceCtx.get()) { + assert(httpsProxyPort >= 0 && httpsProxyPort < (1 << 16)); + // Note that this could check if PrivacyRequested() is set on the PC and + // remove "webrtc" from the ALPN list. But that would only work if the PC + // was constructed with a peerIdentity constraint, not when isolated + // streams are added. If we ever need to signal to the proxy that the + // media is isolated, then we would need to restructure this code. + pcm_->mProxyServer.reset( + new NrIceProxyServer(httpsProxyHost.get(), + static_cast(httpsProxyPort), + "webrtc,c-webrtc")); + } else { + CSFLogError(logTag, "%s: Failed to set proxy server (ICE ctx unavailable)", + __FUNCTION__); + } +} + NS_IMPL_ISUPPORTS(PeerConnectionMedia::ProtocolProxyQueryHandler, nsIProtocolProxyCallback) #endif // !defined(MOZILLA_XPCOMRT_API) @@ -294,6 +310,10 @@ nsresult PeerConnectionMedia::Init(const std::vector& stun_serv #if !defined(MOZILLA_EXTERNAL_LINKAGE) bool ice_tcp = Preferences::GetBool("media.peerconnection.ice.tcp", false); + if (!XRE_IsParentProcess()) { + CSFLogError(logTag, "%s: ICE TCP not support on e10s", __FUNCTION__); + ice_tcp = false; + } bool default_address_only = Preferences::GetBool( "media.peerconnection.ice.default_address_only", false); #else @@ -792,6 +812,8 @@ PeerConnectionMedia::SelfDestruct_m() mLocalSourceStreams.Clear(); mRemoteSourceStreams.Clear(); + mMainThread = nullptr; + // Final self-destruct. this->Release(); } @@ -895,26 +917,13 @@ PeerConnectionMedia::IceGatheringStateChange_s(NrIceCtx* ctx, } NrIceCandidate candidate; - nsresult res = stream->GetDefaultCandidate(1, &candidate); NrIceCandidate rtcpCandidate; - // Optional; component won't exist if doing rtcp-mux - if (NS_FAILED(stream->GetDefaultCandidate(2, &rtcpCandidate))) { - rtcpCandidate.cand_addr.host.clear(); - rtcpCandidate.cand_addr.port = 0; - } - if (NS_SUCCEEDED(res)) { - EndOfLocalCandidates(candidate.cand_addr.host, - candidate.cand_addr.port, - rtcpCandidate.cand_addr.host, - rtcpCandidate.cand_addr.port, - i); - } else { - CSFLogError(logTag, "%s: GetDefaultCandidate failed for level %u, " - "res=%u", - __FUNCTION__, - static_cast(i), - static_cast(res)); - } + GetDefaultCandidates(*stream, &candidate, &rtcpCandidate); + EndOfLocalCandidates(candidate.cand_addr.host, + candidate.cand_addr.port, + rtcpCandidate.cand_addr.host, + rtcpCandidate.cand_addr.port, + i); } } @@ -949,13 +958,17 @@ PeerConnectionMedia::IceConnectionStateChange_s(NrIceCtx* ctx, void PeerConnectionMedia::OnCandidateFound_s(NrIceMediaStream *aStream, - const std::string &candidate) + const std::string &aCandidateLine) { ASSERT_ON_THREAD(mSTSThread); MOZ_ASSERT(aStream); CSFLogDebug(logTag, "%s: %s", __FUNCTION__, aStream->name().c_str()); + NrIceCandidate candidate; + NrIceCandidate rtcpCandidate; + GetDefaultCandidates(*aStream, &candidate, &rtcpCandidate); + // ShutdownMediaTransport_s has not run yet because it unhooks this function // from its signal, which means that SelfDestruct_m has not been dispatched // yet either, so this PCMedia will still be around when this dispatch reaches @@ -963,7 +976,11 @@ PeerConnectionMedia::OnCandidateFound_s(NrIceMediaStream *aStream, GetMainThread()->Dispatch( WrapRunnable(this, &PeerConnectionMedia::OnCandidateFound_m, - candidate, + aCandidateLine, + candidate.cand_addr.host, + candidate.cand_addr.port, + rtcpCandidate.cand_addr.host, + rtcpCandidate.cand_addr.port, aStream->GetLevel()), NS_DISPATCH_NORMAL); } @@ -973,16 +990,41 @@ PeerConnectionMedia::EndOfLocalCandidates(const std::string& aDefaultAddr, uint16_t aDefaultPort, const std::string& aDefaultRtcpAddr, uint16_t aDefaultRtcpPort, - uint16_t aMLine) { - // We will still be around because we have not started teardown yet + uint16_t aMLine) +{ GetMainThread()->Dispatch( WrapRunnable(this, &PeerConnectionMedia::EndOfLocalCandidates_m, - aDefaultAddr, aDefaultPort, - aDefaultRtcpAddr, aDefaultRtcpPort, aMLine), + aDefaultAddr, + aDefaultPort, + aDefaultRtcpAddr, + aDefaultRtcpPort, + aMLine), NS_DISPATCH_NORMAL); } +void +PeerConnectionMedia::GetDefaultCandidates(const NrIceMediaStream& aStream, + NrIceCandidate* aCandidate, + NrIceCandidate* aRtcpCandidate) +{ + nsresult res = aStream.GetDefaultCandidate(1, aCandidate); + // Optional; component won't exist if doing rtcp-mux + if (NS_FAILED(aStream.GetDefaultCandidate(2, aRtcpCandidate))) { + aRtcpCandidate->cand_addr.host.clear(); + aRtcpCandidate->cand_addr.port = 0; + } + if (NS_FAILED(res)) { + aCandidate->cand_addr.host.clear(); + aCandidate->cand_addr.port = 0; + CSFLogError(logTag, "%s: GetDefaultCandidates failed for level %u, " + "res=%u", + __FUNCTION__, + static_cast(aStream.GetLevel()), + static_cast(res)); + } +} + void PeerConnectionMedia::IceGatheringStateChange_m(NrIceCtx* ctx, NrIceCtx::GatheringState state) @@ -1008,11 +1050,22 @@ PeerConnectionMedia::IceStreamReady_s(NrIceMediaStream *aStream) } void -PeerConnectionMedia::OnCandidateFound_m(const std::string &candidate, +PeerConnectionMedia::OnCandidateFound_m(const std::string& aCandidateLine, + const std::string& aDefaultAddr, + uint16_t aDefaultPort, + const std::string& aDefaultRtcpAddr, + uint16_t aDefaultRtcpPort, uint16_t aMLine) { ASSERT_ON_THREAD(mMainThread); - SignalCandidate(candidate, aMLine); + if (!aDefaultAddr.empty()) { + SignalUpdateDefaultCandidate(aDefaultAddr, + aDefaultPort, + aDefaultRtcpAddr, + aDefaultRtcpPort, + aMLine); + } + SignalCandidate(aCandidateLine, aMLine); } void @@ -1021,11 +1074,15 @@ PeerConnectionMedia::EndOfLocalCandidates_m(const std::string& aDefaultAddr, const std::string& aDefaultRtcpAddr, uint16_t aDefaultRtcpPort, uint16_t aMLine) { - SignalEndOfLocalCandidates(aDefaultAddr, - aDefaultPort, - aDefaultRtcpAddr, - aDefaultRtcpPort, - aMLine); + ASSERT_ON_THREAD(mMainThread); + if (!aDefaultAddr.empty()) { + SignalUpdateDefaultCandidate(aDefaultAddr, + aDefaultPort, + aDefaultRtcpAddr, + aDefaultRtcpPort, + aMLine); + } + SignalEndOfLocalCandidates(aMLine); } void @@ -1072,10 +1129,7 @@ void PeerConnectionMedia::RemoveTransportFlow(int aIndex, bool aRtcp) { int index_inner = GetTransportFlowIndex(aIndex, aRtcp); - TransportFlow* flow = mTransportFlows[index_inner].forget().take(); - if (flow) { - NS_ProxyRelease(GetSTSThread(), flow); - } + NS_ProxyRelease(GetSTSThread(), mTransportFlows[index_inner].forget()); } void diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h index b1e849644c..6bd66313f8 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h @@ -221,7 +221,10 @@ class RemoteSourceStreamInfo : public SourceStreamInfo { }; class PeerConnectionMedia : public sigslot::has_slots<> { - ~PeerConnectionMedia() {} + ~PeerConnectionMedia() + { + MOZ_RELEASE_ASSERT(!mMainThread); + } public: explicit PeerConnectionMedia(PeerConnectionImpl *parent); @@ -393,6 +396,8 @@ class PeerConnectionMedia : public sigslot::has_slots<> { // This passes address, port, level of the default candidate. sigslot::signal5 + SignalUpdateDefaultCandidate; + sigslot::signal1 SignalEndOfLocalCandidates; private: @@ -409,8 +414,9 @@ class PeerConnectionMedia : public sigslot::has_slots<> { NS_DECL_ISUPPORTS private: - RefPtr pcm_; - virtual ~ProtocolProxyQueryHandler() {} + void SetProxyOnPcm(nsIProxyInfo& proxyinfo); + RefPtr pcm_; + virtual ~ProtocolProxyQueryHandler() {} }; #endif // !defined(MOZILLA_XPCOMRT_API) @@ -451,18 +457,26 @@ class PeerConnectionMedia : public sigslot::has_slots<> { NrIceCtx::ConnectionState state); void IceStreamReady_s(NrIceMediaStream *aStream); void OnCandidateFound_s(NrIceMediaStream *aStream, - const std::string &candidate); + const std::string& aCandidate); void EndOfLocalCandidates(const std::string& aDefaultAddr, uint16_t aDefaultPort, const std::string& aDefaultRtcpAddr, uint16_t aDefaultRtcpPort, uint16_t aMLine); + void GetDefaultCandidates(const NrIceMediaStream& aStream, + NrIceCandidate* aCandidate, + NrIceCandidate* aRtcpCandidate); void IceGatheringStateChange_m(NrIceCtx* ctx, NrIceCtx::GatheringState state); void IceConnectionStateChange_m(NrIceCtx* ctx, NrIceCtx::ConnectionState state); - void OnCandidateFound_m(const std::string &candidate, uint16_t aMLine); + void OnCandidateFound_m(const std::string& aCandidateLine, + const std::string& aDefaultAddr, + uint16_t aDefaultPort, + const std::string& aDefaultRtcpAddr, + uint16_t aDefaultRtcpPort, + uint16_t aMLine); void EndOfLocalCandidates_m(const std::string& aDefaultAddr, uint16_t aDefaultPort, const std::string& aDefaultRtcpAddr, diff --git a/media/webrtc/signaling/src/sdp/SdpHelper.cpp b/media/webrtc/signaling/src/sdp/SdpHelper.cpp index f386637563..2e314ac1d3 100644 --- a/media/webrtc/signaling/src/sdp/SdpHelper.cpp +++ b/media/webrtc/signaling/src/sdp/SdpHelper.cpp @@ -292,6 +292,27 @@ SdpHelper::AddCandidateToSdp(Sdp* sdp, return NS_OK; } +void +SdpHelper::SetIceGatheringComplete(Sdp* sdp, + uint16_t level, + BundledMids bundledMids) +{ + SdpMediaSection& msection = sdp->GetMediaSection(level); + + if (kSlaveBundle == GetMsectionBundleType(*sdp, + level, + bundledMids, + nullptr)) { + return; // Slave bundle m-section. Skip. + } + + SdpAttributeList& attrs = msection.GetAttributeList(); + attrs.SetAttribute( + new SdpFlagAttribute(SdpAttribute::kEndOfCandidatesAttribute)); + // Remove trickle-ice option + attrs.RemoveAttribute(SdpAttribute::kIceOptionsAttribute); +} + void SdpHelper::SetDefaultAddresses(const std::string& defaultCandidateAddr, uint16_t defaultCandidatePort, @@ -302,33 +323,33 @@ SdpHelper::SetDefaultAddresses(const std::string& defaultCandidateAddr, BundledMids bundledMids) { SdpMediaSection& msection = sdp->GetMediaSection(level); + std::string masterMid; - if (msection.GetAttributeList().HasAttribute(SdpAttribute::kMidAttribute)) { - std::string mid(msection.GetAttributeList().GetMid()); - if (bundledMids.count(mid)) { - const SdpMediaSection* masterBundleMsection(bundledMids[mid]); - if (msection.GetLevel() != masterBundleMsection->GetLevel()) { - // Slave bundle m-section. Skip. - return; + MsectionBundleType bundleType = GetMsectionBundleType(*sdp, + level, + bundledMids, + &masterMid); + if (kSlaveBundle == bundleType) { + return; // Slave bundle m-section. Skip. + } + if (kMasterBundle == bundleType) { + // Master bundle m-section. Set defaultCandidateAddr and + // defaultCandidatePort on all bundled m-sections. + const SdpMediaSection* masterBundleMsection(bundledMids[masterMid]); + for (auto i = bundledMids.begin(); i != bundledMids.end(); ++i) { + if (i->second != masterBundleMsection) { + continue; } - - // Master bundle m-section. Set defaultCandidateAddr and - // defaultCandidatePort on all bundled m-sections. - for (auto i = bundledMids.begin(); i != bundledMids.end(); ++i) { - if (i->second != masterBundleMsection) { - continue; - } - SdpMediaSection* bundledMsection = FindMsectionByMid(*sdp, i->first); - if (!bundledMsection) { - MOZ_ASSERT(false); - continue; - } - SetDefaultAddresses(defaultCandidateAddr, - defaultCandidatePort, - defaultRtcpCandidateAddr, - defaultRtcpCandidatePort, - bundledMsection); + SdpMediaSection* bundledMsection = FindMsectionByMid(*sdp, i->first); + if (!bundledMsection) { + MOZ_ASSERT(false); + continue; } + SetDefaultAddresses(defaultCandidateAddr, + defaultCandidatePort, + defaultRtcpCandidateAddr, + defaultRtcpCandidatePort, + bundledMsection); } } @@ -337,14 +358,6 @@ SdpHelper::SetDefaultAddresses(const std::string& defaultCandidateAddr, defaultRtcpCandidateAddr, defaultRtcpCandidatePort, &msection); - - // TODO(bug 1095793): Will this have an mid someday? - - SdpAttributeList& attrs = msection.GetAttributeList(); - attrs.SetAttribute( - new SdpFlagAttribute(SdpAttribute::kEndOfCandidatesAttribute)); - // Remove trickle-ice option - attrs.RemoveAttribute(SdpAttribute::kIceOptionsAttribute); } void @@ -695,6 +708,31 @@ SdpHelper::AddCommonExtmaps( } } +SdpHelper::MsectionBundleType +SdpHelper::GetMsectionBundleType(const Sdp& sdp, + uint16_t level, + BundledMids& bundledMids, + std::string* masterMid) const +{ + const SdpMediaSection& msection = sdp.GetMediaSection(level); + if (msection.GetAttributeList().HasAttribute(SdpAttribute::kMidAttribute)) { + std::string mid(msection.GetAttributeList().GetMid()); + if (bundledMids.count(mid)) { + const SdpMediaSection* masterBundleMsection(bundledMids[mid]); + if (msection.GetLevel() != masterBundleMsection->GetLevel()) { + return kSlaveBundle; + } + + // allow the caller not to care about the masterMid + if (masterMid) { + *masterMid = mid; + } + return kMasterBundle; + } + } + return kNoBundle; +} + } // namespace mozilla diff --git a/media/webrtc/signaling/src/sdp/SdpHelper.h b/media/webrtc/signaling/src/sdp/SdpHelper.h index 847db724b4..421b2f4977 100644 --- a/media/webrtc/signaling/src/sdp/SdpHelper.h +++ b/media/webrtc/signaling/src/sdp/SdpHelper.h @@ -22,6 +22,12 @@ class Sdp; class SdpHelper { public: + enum MsectionBundleType { + kNoBundle, + kSlaveBundle, + kMasterBundle + }; + // Takes a std::string* into which error strings will be written for the // lifetime of the SdpHelper. explicit SdpHelper(std::string* errorDest) : mLastError(*errorDest) {} @@ -65,6 +71,9 @@ class SdpHelper { const std::string& candidate, const std::string& mid, uint16_t level); + void SetIceGatheringComplete(Sdp* sdp, + uint16_t level, + BundledMids bundledMids); void SetDefaultAddresses(const std::string& defaultCandidateAddr, uint16_t defaultCandidatePort, const std::string& defaultRtcpCandidateAddr, @@ -79,6 +88,10 @@ class SdpHelper { SdpMediaSection* msection); void SetupMsidSemantic(const std::vector& msids, Sdp* sdp) const; + MsectionBundleType GetMsectionBundleType(const Sdp& sdp, + uint16_t level, + BundledMids& bundledMids, + std::string* masterMid) const; std::string GetCNAME(const SdpMediaSection& msection) const; diff --git a/media/webrtc/signaling/test/jsep_session_unittest.cpp b/media/webrtc/signaling/test/jsep_session_unittest.cpp index cb577baaca..0e5408e456 100644 --- a/media/webrtc/signaling/test/jsep_session_unittest.cpp +++ b/media/webrtc/signaling/test/jsep_session_unittest.cpp @@ -631,6 +631,14 @@ protected: // Stomp existing defaults mDefaultCandidates[level][component] = std::make_pair("192.168.0.1", port); + session.UpdateDefaultCandidate( + mDefaultCandidates[level][RTP].first, + mDefaultCandidates[level][RTP].second, + // Will be empty string if not present, which is how we indicate + // that there is no default for RTCP + mDefaultCandidates[level][RTCP].first, + mDefaultCandidates[level][RTCP].second, + level); } void FinishGathering(JsepSession& session) const @@ -638,7 +646,9 @@ protected: // Copy so we can be terse and use [] for (auto levelAndCandidates : mDefaultCandidates) { ASSERT_EQ(1U, levelAndCandidates.second.count(RTP)); - session.EndOfLocalCandidates( + // do a final UpdateDefaultCandidate here in case candidates were + // cleared during renegotiation. + session.UpdateDefaultCandidate( levelAndCandidates.second[RTP].first, levelAndCandidates.second[RTP].second, // Will be empty string if not present, which is how we indicate @@ -646,6 +656,7 @@ protected: levelAndCandidates.second[RTCP].first, levelAndCandidates.second[RTCP].second, levelAndCandidates.first); + session.EndOfLocalCandidates(levelAndCandidates.first); } } diff --git a/media/webrtc/trunk/webrtc/common_video/libyuv/webrtc_libyuv.cc b/media/webrtc/trunk/webrtc/common_video/libyuv/webrtc_libyuv.cc index 6788550e07..b05d1315e2 100644 --- a/media/webrtc/trunk/webrtc/common_video/libyuv/webrtc_libyuv.cc +++ b/media/webrtc/trunk/webrtc/common_video/libyuv/webrtc_libyuv.cc @@ -12,6 +12,7 @@ #include #include +#include // NOTE(ajm): Path provided by gyp. #include "libyuv.h" // NOLINT @@ -69,6 +70,16 @@ void Calc16ByteAlignedStride(int width, int* stride_y, int* stride_uv) { size_t CalcBufferSize(VideoType type, int width, int height) { assert(width >= 0); assert(height >= 0); + // Verify we won't overflow; max is width*height*4 + // 0x7FFF * 0x7FFF * 4 = < 0x100000000 + assert(width <= 0x7FFF); // guarantees no overflow and cheaper than multiply + assert(height <= 0x7FFF); + assert(std::numeric_limits::max() >= 0xFFFFFFFF); + // assert() is debug only + if (width > 0x7FFF || height > 0x7FFF) { + return SIZE_MAX; // very likely forces OOM + } + size_t buffer_size = 0; switch (type) { case kI420: @@ -97,6 +108,7 @@ size_t CalcBufferSize(VideoType type, int width, int height) { break; default: assert(false); + buffer_size = SIZE_MAX; break; } return buffer_size; diff --git a/media/webrtc/webrtc_version.h b/media/webrtc/webrtc_version.h index caf714fe62..6860e9f24c 100644 --- a/media/webrtc/webrtc_version.h +++ b/media/webrtc/webrtc_version.h @@ -1,1095 +1,3 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - newtab-dev/webrtc_version.h at 3263a9649b2a172deb2f532c3a2ecd6fbc4de726 · mozilla/newtab-dev · GitHub - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Skip to content - - - - - - - - - - - -
- -
- - - - -
- - - -
- - - - - - - - - -
-
-
- - - - - - - - - - - - -
- This repository has been archived by the owner. It is now read-only. -
- - - - - - - -
- -
- -
-

- - - - - / - - newtab-dev - - Archived -

- - -
- - - -
- - - - - -
- - - - - - -
-
- - - - - - - - Permalink - - - - - -
- - -
- - Tree: - 3263a9649b - - - - -
- - - -
-
-
- -
- - Find file - - - Copy path - -
-
- - -
- - Find file - - - Copy path - -
-
- - - - -
- - -
-
- - 1 recent contributor - - -
- -

- Users who have contributed to this file recently -

-
- -
-
-
-
- - - - - - -
- -
-
- - 3 lines (2 sloc) - - 88 Bytes -
- -
- -
- Raw - Blame - History -
- - -
- - - - -
-
-
- - - - - -
- - - - - - - - - - - - - - -
#define WEBRTC_SVNVERSION 7864
#define WEBRTC_PULL_DATE "Wed Dec 10 12:23:33 EST 2014"
-
- - - -
- -
- - - -
- - -
- - -
-
- - - -
-
- -
-
- - -
- - - - - - -
- - - You can’t perform that action at this time. -
- - - - - - - - - - - - - - - - - - - - - - +#define WEBRTC_SVNVERSION 7864 +#define WEBRTC_PULL_DATE "Wed Dec 10 12:23:33 EST 2014" diff --git a/modules/libjar/nsJARChannel.cpp b/modules/libjar/nsJARChannel.cpp index 9e8e911d0c..6e3e62a128 100644 --- a/modules/libjar/nsJARChannel.cpp +++ b/modules/libjar/nsJARChannel.cpp @@ -217,7 +217,7 @@ nsJARChannel::nsJARChannel() nsJARChannel::~nsJARChannel() { - NS_ReleaseOnMainThread(mLoadInfo); + NS_ReleaseOnMainThread(mLoadInfo.forget()); // release owning reference to the jar handler nsJARProtocolHandler *handler = gJarHandler; diff --git a/mozglue/build/Nuwa.cpp b/mozglue/build/Nuwa.cpp index 42d9bad34a..4be83e6996 100644 --- a/mozglue/build/Nuwa.cpp +++ b/mozglue/build/Nuwa.cpp @@ -2077,7 +2077,7 @@ IsNuwaReady() { return sNuwaReady; } -#if defined(DEBUG) || defined(ENABLE_TESTS) +#if defined(DEBUG) MFBT_API void NuwaAssertNotFrozen(unsigned int aThread, const char* aThreadName) { if (!sIsNuwaProcess || !sIsFreezing) { diff --git a/mozglue/build/Nuwa.h b/mozglue/build/Nuwa.h index 37c8baebf6..13d71ebfef 100644 --- a/mozglue/build/Nuwa.h +++ b/mozglue/build/Nuwa.h @@ -187,7 +187,7 @@ MFBT_API bool IsNuwaProcess(); */ MFBT_API bool IsNuwaReady(); -#if defined(DEBUG) || defined(ENABLE_TESTS) +#if defined(DEBUG) /** * Asserts that aThread is not frozen. */ diff --git a/mozglue/build/WindowsDllBlocklist.cpp b/mozglue/build/WindowsDllBlocklist.cpp index 26ad70d6cf..be377be1b2 100644 --- a/mozglue/build/WindowsDllBlocklist.cpp +++ b/mozglue/build/WindowsDllBlocklist.cpp @@ -16,6 +16,7 @@ #include "nsAutoPtr.h" #include "nsWindowsDllInterceptor.h" +#include "mozilla/UniquePtr.h" #include "mozilla/WindowsVersion.h" #include "nsWindowsHelpers.h" @@ -481,14 +482,17 @@ DllBlockSet::Write(HANDLE file) ::LeaveCriticalSection(&sLock); } -static -wchar_t* getFullPath (PWCHAR filePath, wchar_t* fname) +static UniquePtr +getFullPath (PWCHAR filePath, wchar_t* fname) { // In Windows 8, the first parameter seems to be used for more than just the // path name. For example, its numerical value can be 1. Passing a non-valid // pointer to SearchPathW will cause a crash, so we need to check to see if we // are handed a valid pointer, and otherwise just pass nullptr to SearchPathW. - PWCHAR sanitizedFilePath = (intptr_t(filePath) < 4096) ? nullptr : filePath; + PWCHAR sanitizedFilePath = nullptr; + if ((uintptr_t(filePath) >= 65536) && ((uintptr_t(filePath) & 1) == 0)) { + sanitizedFilePath = filePath; + } // figure out the length of the string that we need DWORD pathlen = SearchPathW(sanitizedFilePath, fname, L".dll", 0, nullptr, @@ -497,14 +501,14 @@ wchar_t* getFullPath (PWCHAR filePath, wchar_t* fname) return nullptr; } - wchar_t* full_fname = new wchar_t[pathlen+1]; + auto full_fname = MakeUnique(pathlen+1); if (!full_fname) { // couldn't allocate memory? return nullptr; } // now actually grab it - SearchPathW(sanitizedFilePath, fname, L".dll", pathlen + 1, full_fname, + SearchPathW(sanitizedFilePath, fname, L".dll", pathlen + 1, full_fname.get(), nullptr); return full_fname; } @@ -532,7 +536,7 @@ patched_LdrLoadDll (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileNam int len = moduleFileName->Length / 2; wchar_t *fname = moduleFileName->Buffer; - nsAutoArrayPtr full_fname; + UniquePtr full_fname; // The filename isn't guaranteed to be null terminated, but in practice // it always will be; ensure that this is so, and bail if not. @@ -655,23 +659,23 @@ patched_LdrLoadDll (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileNam } if (info->flags & DllBlockInfo::USE_TIMESTAMP) { - fVersion = GetTimestamp(full_fname); + fVersion = GetTimestamp(full_fname.get()); if (fVersion > info->maxVersion) { load_ok = true; } } else { DWORD zero; - DWORD infoSize = GetFileVersionInfoSizeW(full_fname, &zero); + DWORD infoSize = GetFileVersionInfoSizeW(full_fname.get(), &zero); // If we failed to get the version information, we block. if (infoSize != 0) { - nsAutoArrayPtr infoData(new unsigned char[infoSize]); + auto infoData = MakeUnique(infoSize); VS_FIXEDFILEINFO *vInfo; UINT vInfoLen; - if (GetFileVersionInfoW(full_fname, 0, infoSize, infoData) && - VerQueryValueW(infoData, L"\\", (LPVOID*) &vInfo, &vInfoLen)) + if (GetFileVersionInfoW(full_fname.get(), 0, infoSize, infoData.get()) && + VerQueryValueW(infoData.get(), L"\\", (LPVOID*) &vInfo, &vInfoLen)) { fVersion = ((unsigned long long)vInfo->dwFileVersionMS) << 32 | @@ -707,7 +711,7 @@ continue_loading: return STATUS_DLL_NOT_FOUND; } - if (IsVistaOrLater() && !CheckASLR(full_fname)) { + if (IsVistaOrLater() && !CheckASLR(full_fname.get())) { printf_stderr("LdrLoadDll: Blocking load of '%s'. XPCOM components must support ASLR.\n", dllName); return STATUS_DLL_NOT_FOUND; } diff --git a/mozglue/linker/szip.cpp b/mozglue/linker/szip.cpp index a6c4b27e1b..050b0b5436 100644 --- a/mozglue/linker/szip.cpp +++ b/mozglue/linker/szip.cpp @@ -425,7 +425,11 @@ int SzipCompress::do_compress(Buffer &origBuf, Buffer &outBuf, zStream.avail_in = avail; zStream.next_in = data; ret = deflate(&zStream, Z_FINISH); - MOZ_ASSERT(ret == Z_STREAM_END); + /* Under normal conditions, deflate returns Z_STREAM_END. If there is not + * enough room to compress, deflate returns Z_OK and avail_out is 0. We + * still want to deflateEnd in that case, so fall through. It will bail + * on the avail_out test that follows. */ + MOZ_ASSERT(ret == Z_STREAM_END || ret == Z_OK); ret = deflateEnd(&zStream); MOZ_ASSERT(ret == Z_OK); if (zStream.avail_out <= 0) diff --git a/mozglue/misc/StackWalk.cpp b/mozglue/misc/StackWalk.cpp index 2f60734542..dbf668cbb8 100644 --- a/mozglue/misc/StackWalk.cpp +++ b/mozglue/misc/StackWalk.cpp @@ -47,13 +47,6 @@ static CriticalAddress gCriticalAddress; ((defined(__GNUC__) && (defined(__i386) || defined(PPC))) || \ defined(HAVE__UNWIND_BACKTRACE))) -#if MOZ_STACKWALK_SUPPORTS_MACOSX -#include -#include -#ifdef MOZ_WIDGET_COCOA -#include -#endif - #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) #define HAVE___LIBC_STACK_END 1 #else @@ -64,6 +57,19 @@ static CriticalAddress gCriticalAddress; extern MOZ_EXPORT void* __libc_stack_end; // from ld-linux.so #endif +#ifdef ANDROID +#include +#include +#include +#endif + +#if MOZ_STACKWALK_SUPPORTS_MACOSX +#include +#include +#ifdef MOZ_WIDGET_COCOA +#include +#endif + typedef void malloc_logger_t(uint32_t aType, uintptr_t aArg1, uintptr_t aArg2, uintptr_t aArg3, @@ -811,7 +817,7 @@ MozDescribeCodeAddress(void* aPC, MozCodeAddressDetails* aDetails) modInfoRes = SymGetModuleInfoEspecial64(myProcess, addr, &modInfo, &lineInfo); if (modInfoRes) { - strncpy(aDetails->library, modInfo.ModuleName, + strncpy(aDetails->library, modInfo.LoadedImageName, sizeof(aDetails->library)); aDetails->library[mozilla::ArrayLength(aDetails->library) - 1] = '\0'; aDetails->loffset = (char*)aPC - (char*)modInfo.BaseOfImage; @@ -901,8 +907,35 @@ MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames, void* stackEnd; #if HAVE___LIBC_STACK_END stackEnd = __libc_stack_end; +#elif defined(XP_DARWIN) + stackEnd = pthread_get_stackaddr_np(pthread_self()); +#elif defined(ANDROID) + pthread_attr_t sattr; + pthread_attr_init(&sattr); + pthread_getattr_np(pthread_self(), &sattr); + void* stackBase = stackEnd = nullptr; + size_t stackSize = 0; + if (gettid() != getpid()) { + // bionic's pthread_attr_getstack doesn't tell the truth for the main + // thread (see bug 846670). So don't use it for the main thread. + if (!pthread_attr_getstack(&sattr, &stackBase, &stackSize)) { + stackEnd = static_cast(stackBase) + stackSize; + } else { + stackEnd = nullptr; + } + } + if (!stackEnd) { + // So consider the current frame pointer + an arbitrary size of 8MB + // (modulo overflow ; not really arbitrary as it's the default stack + // size for the main thread) if pthread_attr_getstack failed for + // some reason (or was skipped). + static const uintptr_t kMaxStackSize = 8 * 1024 * 1024; + uintptr_t maxStackStart = uintptr_t(-1) - kMaxStackSize; + uintptr_t stackStart = std::max(maxStackStart, uintptr_t(bp)); + stackEnd = reinterpret_cast(stackStart + kMaxStackSize); + } #else - stackEnd = reinterpret_cast(-1); +# error Unsupported configuration #endif return FramePointerStackWalk(aCallback, aSkipFrames, aMaxFrames, aClosure, bp, stackEnd); diff --git a/netwerk/base/TLSServerSocket.cpp b/netwerk/base/TLSServerSocket.cpp index a53d58657f..06109652fb 100644 --- a/netwerk/base/TLSServerSocket.cpp +++ b/netwerk/base/TLSServerSocket.cpp @@ -329,16 +329,14 @@ TLSServerConnectionInfo::~TLSServerConnectionInfo() return; } - nsITLSServerSecurityObserver* observer; + RefPtr observer; { MutexAutoLock lock(mLock); - mSecurityObserver.forget(&observer); + observer = mSecurityObserver.forget(); } if (observer) { - nsCOMPtr mainThread; - NS_GetMainThread(getter_AddRefs(mainThread)); - NS_ProxyRelease(mainThread, observer); + NS_ReleaseOnMainThread(observer.forget()); } } diff --git a/netwerk/base/nsBaseChannel.cpp b/netwerk/base/nsBaseChannel.cpp index 2dcff66b70..ef30feb417 100644 --- a/netwerk/base/nsBaseChannel.cpp +++ b/netwerk/base/nsBaseChannel.cpp @@ -66,7 +66,7 @@ nsBaseChannel::nsBaseChannel() nsBaseChannel::~nsBaseChannel() { - NS_ReleaseOnMainThread(mLoadInfo); + NS_ReleaseOnMainThread(mLoadInfo.forget()); } nsresult diff --git a/netwerk/base/nsProtocolProxyService.cpp b/netwerk/base/nsProtocolProxyService.cpp index 2a1d9a0fd9..ee21b94710 100644 --- a/netwerk/base/nsProtocolProxyService.cpp +++ b/netwerk/base/nsProtocolProxyService.cpp @@ -128,31 +128,20 @@ private: // main thread to delete safely, but if this request had its // callbacks called normally they will all be null and this is a nop - nsCOMPtr mainThread; - NS_GetMainThread(getter_AddRefs(mainThread)); - if (mChannel) { - nsIChannel *forgettable; - mChannel.forget(&forgettable); - NS_ProxyRelease(mainThread, forgettable, false); + NS_ReleaseOnMainThread(mChannel.forget()); } if (mCallback) { - nsIProtocolProxyCallback *forgettable; - mCallback.forget(&forgettable); - NS_ProxyRelease(mainThread, forgettable, false); + NS_ReleaseOnMainThread(mCallback.forget()); } if (mProxyInfo) { - nsIProxyInfo *forgettable; - mProxyInfo.forget(&forgettable); - NS_ProxyRelease(mainThread, forgettable, false); + NS_ReleaseOnMainThread(mProxyInfo.forget()); } if (mXPComPPS) { - nsIProtocolProxyService *forgettable; - mXPComPPS.forget(&forgettable); - NS_ProxyRelease(mainThread, forgettable, false); + NS_ReleaseOnMainThread(mXPComPPS.forget()); } } } diff --git a/netwerk/base/nsServerSocket.cpp b/netwerk/base/nsServerSocket.cpp index d9ca570bf4..6d50e26468 100644 --- a/netwerk/base/nsServerSocket.cpp +++ b/netwerk/base/nsServerSocket.cpp @@ -233,15 +233,17 @@ nsServerSocket::OnSocketDetached(PRFileDesc *fd) mListener->OnStopListening(this, mCondition); // need to atomically clear mListener. see our Close() method. - nsIServerSocketListener *listener = nullptr; + RefPtr listener = nullptr; { MutexAutoLock lock(mLock); - mListener.swap(listener); + listener = mListener.forget(); } + // XXX we need to proxy the release to the listener's target thread to work // around bug 337492. - if (listener) - NS_ProxyRelease(mListenerTarget, listener); + if (listener) { + NS_ProxyRelease(mListenerTarget, listener.forget()); + } } } diff --git a/netwerk/base/nsStreamListenerTee.cpp b/netwerk/base/nsStreamListenerTee.cpp index 455974d758..b13e940185 100644 --- a/netwerk/base/nsStreamListenerTee.cpp +++ b/netwerk/base/nsStreamListenerTee.cpp @@ -39,12 +39,7 @@ nsStreamListenerTee::OnStopRequest(nsIRequest *request, // release sink on the same thread where the data was written (bug 716293) if (mEventTarget) { - nsIOutputStream *sink = nullptr; - mSink.swap(sink); - if (NS_FAILED(NS_ProxyRelease(mEventTarget, sink))) { - NS_WARNING("Releasing sink on the current thread!"); - NS_RELEASE(sink); - } + NS_ProxyRelease(mEventTarget, mSink.forget()); } else { mSink = 0; diff --git a/netwerk/base/nsTransportUtils.cpp b/netwerk/base/nsTransportUtils.cpp index 835c7c0bbb..7040914497 100644 --- a/netwerk/base/nsTransportUtils.cpp +++ b/netwerk/base/nsTransportUtils.cpp @@ -37,7 +37,7 @@ private: { // our reference to mSink could be the last, so be sure to release // it on the target thread. otherwise, we could get into trouble. - NS_ProxyRelease(mTarget, mSink); + NS_ProxyRelease(mTarget, dont_AddRef(mSink)); } public: diff --git a/netwerk/base/nsUDPSocket.cpp b/netwerk/base/nsUDPSocket.cpp index 5880186c7f..190fb7a5c9 100644 --- a/netwerk/base/nsUDPSocket.cpp +++ b/netwerk/base/nsUDPSocket.cpp @@ -527,15 +527,15 @@ nsUDPSocket::OnSocketDetached(PRFileDesc *fd) if (mListener) { // need to atomically clear mListener. see our Close() method. - nsCOMPtr listener; + RefPtr listener = nullptr; { MutexAutoLock lock(mLock); - mListener.swap(listener); + listener = mListener.forget(); } if (listener) { listener->OnStopListening(this, mCondition); - NS_ProxyRelease(mListenerTarget, listener); + NS_ProxyRelease(mListenerTarget, listener.forget()); } } } diff --git a/netwerk/cache/nsCacheService.cpp b/netwerk/cache/nsCacheService.cpp index 204fd6e3dc..6dee6b6159 100644 --- a/netwerk/cache/nsCacheService.cpp +++ b/netwerk/cache/nsCacheService.cpp @@ -2655,7 +2655,7 @@ nsCacheService::ReleaseObject_Locked(nsISupports * obj, if (!target || (NS_SUCCEEDED(target->IsOnCurrentThread(&isCur)) && isCur)) { gService->mDoomedObjects.AppendElement(obj); } else { - NS_ProxyRelease(target, obj); + NS_ProxyRelease(target, dont_AddRef(obj)); } } diff --git a/netwerk/cache2/CacheIndex.h b/netwerk/cache2/CacheIndex.h index ae0cd4c0a2..767f945523 100644 --- a/netwerk/cache2/CacheIndex.h +++ b/netwerk/cache2/CacheIndex.h @@ -1041,16 +1041,7 @@ private: : mObserver(aWeakObserver) { } virtual ~DiskConsumptionObserver() { if (mObserver && !NS_IsMainThread()) { - nsIWeakReference *obs; - mObserver.forget(&obs); - - nsCOMPtr mainThread = do_GetMainThread(); - if (mainThread) { - NS_ProxyRelease(mainThread, obs); - } else { - NS_WARNING("Cannot get main thread, leaking weak reference to " - "CacheStorageConsumptionObserver."); - } + NS_ReleaseOnMainThread(mObserver.forget()); } } diff --git a/netwerk/cache2/CacheStorageService.h b/netwerk/cache2/CacheStorageService.h index ef9b11ed8d..d7e49ed46a 100644 --- a/netwerk/cache2/CacheStorageService.h +++ b/netwerk/cache2/CacheStorageService.h @@ -370,10 +370,7 @@ private: template void ProxyRelease(nsCOMPtr &object, nsIThread* thread) { - T* release; - object.forget(&release); - - NS_ProxyRelease(thread, release); + NS_ProxyRelease(thread, object.forget()); } template diff --git a/netwerk/ipc/RemoteOpenFileChild.cpp b/netwerk/ipc/RemoteOpenFileChild.cpp index b66ba40dae..8678036652 100644 --- a/netwerk/ipc/RemoteOpenFileChild.cpp +++ b/netwerk/ipc/RemoteOpenFileChild.cpp @@ -107,35 +107,10 @@ RemoteOpenFileChild::~RemoteOpenFileChild() NotifyListener(NS_ERROR_UNEXPECTED); } } else { - nsCOMPtr mainThread = do_GetMainThread(); - if (mainThread) { - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_ProxyRelease(mainThread, mURI, true))); - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_ProxyRelease(mainThread, mAppURI, true))); - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_ProxyRelease(mainThread, mListener, - true))); - - TabChild* tabChild; - mTabChild.forget(&tabChild); - - if (tabChild) { - nsCOMPtr runnable = - NS_NewNonOwningRunnableMethod(tabChild, &TabChild::Release); - MOZ_ASSERT(runnable); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mainThread->Dispatch(runnable, - NS_DISPATCH_NORMAL))); - } - } else { - using mozilla::Unused; - - NS_WARNING("RemoteOpenFileChild released after thread shutdown, leaking " - "its members!"); - - Unused << mURI.forget(); - Unused << mAppURI.forget(); - Unused << mListener.forget(); - Unused << mTabChild.forget(); - } + NS_ReleaseOnMainThread(mURI.forget(), true); + NS_ReleaseOnMainThread(mAppURI.forget(), true); + NS_ReleaseOnMainThread(mListener.forget(), true); + NS_ReleaseOnMainThread(mTabChild.forget(), true); } if (mNSPRFileDesc) { diff --git a/netwerk/protocol/file/nsFileChannel.cpp b/netwerk/protocol/file/nsFileChannel.cpp index e11e878fc6..3f0a124603 100644 --- a/netwerk/protocol/file/nsFileChannel.cpp +++ b/netwerk/protocol/file/nsFileChannel.cpp @@ -129,9 +129,7 @@ nsFileCopyEvent::DoCopy() // Release the callback on the target thread to avoid destroying stuff on // the wrong thread. - nsIRunnable *doomed = nullptr; - mCallback.swap(doomed); - NS_ProxyRelease(mCallbackTarget, doomed); + NS_ProxyRelease(mCallbackTarget, mCallback.forget()); } } diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index 291891b1ce..0983a7ac9f 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -127,7 +127,7 @@ HttpBaseChannel::~HttpBaseChannel() { LOG(("Destroying HttpBaseChannel @%x\n", this)); - NS_ReleaseOnMainThread(mLoadInfo); + NS_ReleaseOnMainThread(mLoadInfo.forget()); // Make sure we don't leak CleanRedirectCacheChainIfNecessary(); diff --git a/netwerk/protocol/rtsp/controller/RtspControllerParent.cpp b/netwerk/protocol/rtsp/controller/RtspControllerParent.cpp index 8a7e9856ea..fc9091a0d4 100644 --- a/netwerk/protocol/rtsp/controller/RtspControllerParent.cpp +++ b/netwerk/protocol/rtsp/controller/RtspControllerParent.cpp @@ -40,13 +40,8 @@ RtspControllerParent::Destroy() // RtspControllerParent is deleted. This ensures we only delete the // RtspControllerParent on the main thread. if (!NS_IsMainThread()) { - nsCOMPtr mainThread = do_GetMainThread(); - NS_ENSURE_TRUE_VOID(mainThread); RefPtr doomed(this); - if (NS_FAILED(NS_ProxyRelease(mainThread, - static_cast(doomed), true))) { - NS_WARNING("Failed to proxy release to main thread!"); - } + NS_ReleaseOnMainThread(doomed.forget(), true); } else { delete this; } diff --git a/netwerk/protocol/websocket/BaseWebSocketChannel.cpp b/netwerk/protocol/websocket/BaseWebSocketChannel.cpp index 786e4ca259..3d35746122 100644 --- a/netwerk/protocol/websocket/BaseWebSocketChannel.cpp +++ b/netwerk/protocol/websocket/BaseWebSocketChannel.cpp @@ -360,11 +360,8 @@ BaseWebSocketChannel::ListenerAndContextContainer::~ListenerAndContextContainer( { MOZ_ASSERT(mListener); - nsCOMPtr mainThread; - NS_GetMainThread(getter_AddRefs(mainThread)); - - NS_ProxyRelease(mainThread, mListener, false); - NS_ProxyRelease(mainThread, mContext, false); + NS_ReleaseOnMainThread(mListener.forget()); + NS_ReleaseOnMainThread(mContext.forget()); } } // namespace net diff --git a/netwerk/protocol/websocket/WebSocketChannel.cpp b/netwerk/protocol/websocket/WebSocketChannel.cpp index 7b1eb07771..2a9fe5347b 100644 --- a/netwerk/protocol/websocket/WebSocketChannel.cpp +++ b/netwerk/protocol/websocket/WebSocketChannel.cpp @@ -1216,14 +1216,14 @@ WebSocketChannel::~WebSocketChannel() while ((mCurrentOut = (OutboundMessage *) mOutgoingMessages.PopFront())) delete mCurrentOut; - NS_ReleaseOnMainThread(mURI); - NS_ReleaseOnMainThread(mOriginalURI); + NS_ReleaseOnMainThread(mURI.forget()); + NS_ReleaseOnMainThread(mOriginalURI.forget()); mListenerMT = nullptr; - NS_ReleaseOnMainThread(mLoadGroup); - NS_ReleaseOnMainThread(mLoadInfo); - NS_ReleaseOnMainThread(static_cast(mService.forget().take())); + NS_ReleaseOnMainThread(mLoadGroup.forget()); + NS_ReleaseOnMainThread(mLoadInfo.forget()); + NS_ReleaseOnMainThread(mService.forget()); } NS_IMETHODIMP diff --git a/netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp b/netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp index f4f7c1a266..5cb5735827 100644 --- a/netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp +++ b/netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp @@ -56,12 +56,7 @@ WyciwygChannelChild::~WyciwygChannelChild() { LOG(("Destroying WyciwygChannelChild @%x\n", this)); if (mLoadInfo) { - nsCOMPtr mainThread; - NS_GetMainThread(getter_AddRefs(mainThread)); - - nsILoadInfo *forgetableLoadInfo; - mLoadInfo.forget(&forgetableLoadInfo); - NS_ProxyRelease(mainThread, forgetableLoadInfo, false); + NS_ReleaseOnMainThread(mLoadInfo.forget()); } } diff --git a/netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp b/netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp index 9c9c571785..4e15668756 100644 --- a/netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp +++ b/netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp @@ -40,13 +40,7 @@ public: ~nsWyciwygAsyncEvent() { - nsCOMPtr thread = do_GetMainThread(); - NS_WARN_IF_FALSE(thread, "Couldn't get the main thread!"); - if (thread) { - nsIWyciwygChannel *chan = static_cast(mChannel); - mozilla::Unused << mChannel.forget(); - NS_ProxyRelease(thread, chan); - } + NS_ReleaseOnMainThread(mChannel.forget()); } protected: RefPtr mChannel; @@ -109,12 +103,7 @@ nsWyciwygChannel::nsWyciwygChannel() nsWyciwygChannel::~nsWyciwygChannel() { if (mLoadInfo) { - nsCOMPtr mainThread; - NS_GetMainThread(getter_AddRefs(mainThread)); - - nsILoadInfo *forgetableLoadInfo; - mLoadInfo.forget(&forgetableLoadInfo); - NS_ProxyRelease(mainThread, forgetableLoadInfo, false); + NS_ReleaseOnMainThread(mLoadInfo.forget(), false); } } diff --git a/netwerk/sctp/datachannel/DataChannel.cpp b/netwerk/sctp/datachannel/DataChannel.cpp index e3940b7fb5..4df90e6946 100644 --- a/netwerk/sctp/datachannel/DataChannel.cpp +++ b/netwerk/sctp/datachannel/DataChannel.cpp @@ -196,7 +196,7 @@ DataChannelConnection::~DataChannelConnection() ASSERT_WEBRTC(NS_IsMainThread()); if (mTransportFlow) { ASSERT_WEBRTC(mSTS); - NS_ProxyRelease(mSTS, mTransportFlow); + NS_ProxyRelease(mSTS, mTransportFlow.forget()); } if (mInternalIOThread) { @@ -2420,7 +2420,7 @@ DataChannelConnection::ReadBlob(already_AddRefed aThis, // Bug 966602: Doesn't return an error to the caller via onerror. // We must release DataChannelConnection on MainThread to avoid issues (bug 876167) // aThis is now owned by the runnable; release it there - NS_ProxyRelease(mainThread, runnable); + NS_ProxyRelease(mainThread, runnable.forget()); return; } aBlob->Close(); diff --git a/security/manager/ssl/nsNSSCallbacks.cpp b/security/manager/ssl/nsNSSCallbacks.cpp index 322b34d89d..226333ed80 100644 --- a/security/manager/ssl/nsNSSCallbacks.cpp +++ b/security/manager/ssl/nsNSSCallbacks.cpp @@ -573,8 +573,7 @@ nsHTTPListener::~nsHTTPListener() } if (mLoader) { - nsCOMPtr mainThread(do_GetMainThread()); - NS_ProxyRelease(mainThread, mLoader); + NS_ReleaseOnMainThread(mLoader.forget()); } } diff --git a/services/common/async.js b/services/common/async.js index 680f5e45ff..2c2b818f6c 100644 --- a/services/common/async.js +++ b/services/common/async.js @@ -2,14 +2,10 @@ * 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 MERGED_COMPARTMENT - this.EXPORTED_SYMBOLS = ["Async"]; var {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components; -#endif - // Constants for makeSyncCallback, waitForSyncCallback. const CB_READY = {}; const CB_COMPLETE = {}; diff --git a/services/common/hawkclient.js b/services/common/hawkclient.js index 0b6c5eb1d2..7fad7fb99b 100644 --- a/services/common/hawkclient.js +++ b/services/common/hawkclient.js @@ -28,7 +28,6 @@ this.EXPORTED_SYMBOLS = ["HawkClient"]; var {interfaces: Ci, utils: Cu} = Components; -Cu.import("resource://services-common/utils.js"); Cu.import("resource://services-crypto/utils.js"); Cu.import("resource://services-common/hawkrequest.js"); Cu.import("resource://services-common/observers.js"); @@ -271,8 +270,7 @@ this.HawkClient.prototype = { // gets the same one. _onComplete.call(this, error); } catch (ex) { - log.error("Unhandled exception processing response:" + - CommonUtils.exceptionStr(ex)); + log.error("Unhandled exception processing response", ex); deferred.reject(ex); } } diff --git a/services/common/hawkrequest.js b/services/common/hawkrequest.js index cb679faf22..7cd31a3066 100644 --- a/services/common/hawkrequest.js +++ b/services/common/hawkrequest.js @@ -168,7 +168,7 @@ this.Intl.prototype = { this._accepted = Services.prefs.getComplexValue( "intl.accept_languages", Ci.nsIPrefLocalizedString).data; } catch (err) { - this._log.error("Error reading intl.accept_languages pref: " + CommonUtils.exceptionStr(err)); + this._log.error("Error reading intl.accept_languages pref", err); } }, diff --git a/services/common/modules-testing/storageserver.js b/services/common/modules-testing/storageserver.js index 5ebc04cd32..7a529b6ea4 100644 --- a/services/common/modules-testing/storageserver.js +++ b/services/common/modules-testing/storageserver.js @@ -573,8 +573,7 @@ StorageServerCollection.prototype = { failed[record.id] = "no bso configured"; } } catch (ex) { - this._log.info("Exception when processing BSO: " + - CommonUtils.exceptionStr(ex)); + this._log.info("Exception when processing BSO", ex); failed[record.id] = "Exception when processing."; } } @@ -933,7 +932,7 @@ StorageServer.prototype = { } catch (ex) { _("=========================================="); _("Got exception starting Storage HTTP server on port " + this.port); - _("Error: " + CommonUtils.exceptionStr(ex)); + _("Error: " + Log.exceptionStr(ex)); _("Is there a process already listening on port " + this.port + "?"); _("=========================================="); do_throw(ex); @@ -1288,7 +1287,7 @@ StorageServer.prototype = { if (e instanceof HttpError) { this.respond(req, resp, e.code, e.description, "", {}, timestamp); } else { - this._log.warn(CommonUtils.exceptionStr(e)); + this._log.warn("StorageServer: handleDefault caught an error", e); throw e; } } @@ -1393,8 +1392,7 @@ StorageServer.prototype = { try { return handler.call(this, handler, req, resp, version, username, rest); } catch (ex) { - this._log.warn("Got exception during request: " + - CommonUtils.exceptionStr(ex)); + this._log.warn("Got exception during request", ex); throw ex; } } diff --git a/services/common/moz-kinto-client.js b/services/common/moz-kinto-client.js new file mode 100644 index 0000000000..d07e15e5f9 --- /dev/null +++ b/services/common/moz-kinto-client.js @@ -0,0 +1,3609 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * This file is generated from kinto.js - do not modify directly. + */ + +this.EXPORTED_SYMBOLS = ["loadKinto"]; +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.loadKinto = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o record); + } + }, { + key: "update", + value: function update(record) { + var params = { + collection_name: this.collection, + record_id: record.id, + record: JSON.stringify(record) + }; + return this._executeStatement(statements.updateData, params).then(() => record); + } + }, { + key: "get", + value: function get(id) { + var params = { + collection_name: this.collection, + record_id: id + }; + return this._executeStatement(statements.getRecord, params).then(result => { + if (result.length == 0) { + return; + } + return JSON.parse(result[0].getResultByName("record")); + }); + } + }, { + key: "delete", + value: function _delete(id) { + var params = { + collection_name: this.collection, + record_id: id + }; + return this._executeStatement(statements.deleteData, params).then(() => id); + } + }, { + key: "list", + value: function list() { + var params = { + collection_name: this.collection + }; + return this._executeStatement(statements.listRecords, params).then(result => { + var records = []; + for (var k = 0; k < result.length; k++) { + var row = result[k]; + records.push(JSON.parse(row.getResultByName("record"))); + } + return records; + }); + } + }, { + key: "saveLastModified", + value: function saveLastModified(lastModified) { + var parsedLastModified = parseInt(lastModified, 10) || null; + var params = { + collection_name: this.collection, + last_modified: parsedLastModified + }; + return this._executeStatement(statements.saveLastModified, params).then(() => parsedLastModified); + } + }, { + key: "getLastModified", + value: function getLastModified() { + var params = { + collection_name: this.collection + }; + return this._executeStatement(statements.getLastModified, params).then(result => { + if (result.length == 0) { + return 0; + } + return result[0].getResultByName("last_modified"); + }); + } + }]); + + return FirefoxAdapter; +})(_srcAdaptersBase2["default"]); + +exports["default"] = FirefoxAdapter; +module.exports = exports["default"]; + +},{"../src/adapters/base":11}],2:[function(require,module,exports){ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +var _get = function get(_x2, _x3, _x4) { var _again = true; _function: while (_again) { var object = _x2, property = _x3, receiver = _x4; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x2 = parent; _x3 = property; _x4 = receiver; _again = true; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +exports["default"] = loadKinto; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var _srcAdaptersBase = require("../src/adapters/base"); + +var _srcAdaptersBase2 = _interopRequireDefault(_srcAdaptersBase); + +var _srcKintoBase = require("../src/KintoBase"); + +var _srcKintoBase2 = _interopRequireDefault(_srcKintoBase); + +var _FirefoxStorage = require("./FirefoxStorage"); + +var _FirefoxStorage2 = _interopRequireDefault(_FirefoxStorage); + +var Cu = Components.utils; + +function loadKinto() { + var _Cu$import = Cu["import"]("resource://devtools/shared/event-emitter.js", {}); + + var EventEmitter = _Cu$import.EventEmitter; + + Cu.importGlobalProperties(['fetch']); + + var KintoFX = (function (_KintoBase) { + _inherits(KintoFX, _KintoBase); + + _createClass(KintoFX, null, [{ + key: "adapters", + get: function get() { + return { + BaseAdapter: _srcAdaptersBase2["default"], + FirefoxAdapter: _FirefoxStorage2["default"] + }; + } + }]); + + function KintoFX() { + var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; + + _classCallCheck(this, KintoFX); + + var emitter = {}; + EventEmitter.decorate(emitter); + + var defaults = { + events: emitter + }; + + var expandedOptions = Object.assign(defaults, options); + _get(Object.getPrototypeOf(KintoFX.prototype), "constructor", this).call(this, expandedOptions); + } + + return KintoFX; + })(_srcKintoBase2["default"]); + + return KintoFX; +} + +module.exports = exports["default"]; + +},{"../src/KintoBase":10,"../src/adapters/base":11,"./FirefoxStorage":1}],3:[function(require,module,exports){ +// http://wiki.commonjs.org/wiki/Unit_Testing/1.0 +// +// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! +// +// Originally from narwhal.js (http://narwhaljs.org) +// Copyright (c) 2009 Thomas Robinson <280north.com> +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the 'Software'), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +// when used in node, this will actually load the util module we depend on +// versus loading the builtin util module as happens otherwise +// this is a bug in node module loading as far as I am concerned +var util = require('util/'); + +var pSlice = Array.prototype.slice; +var hasOwn = Object.prototype.hasOwnProperty; + +// 1. The assert module provides functions that throw +// AssertionError's when particular conditions are not met. The +// assert module must conform to the following interface. + +var assert = module.exports = ok; + +// 2. The AssertionError is defined in assert. +// new assert.AssertionError({ message: message, +// actual: actual, +// expected: expected }) + +assert.AssertionError = function AssertionError(options) { + this.name = 'AssertionError'; + this.actual = options.actual; + this.expected = options.expected; + this.operator = options.operator; + if (options.message) { + this.message = options.message; + this.generatedMessage = false; + } else { + this.message = getMessage(this); + this.generatedMessage = true; + } + var stackStartFunction = options.stackStartFunction || fail; + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, stackStartFunction); + } + else { + // non v8 browsers so we can have a stacktrace + var err = new Error(); + if (err.stack) { + var out = err.stack; + + // try to strip useless frames + var fn_name = stackStartFunction.name; + var idx = out.indexOf('\n' + fn_name); + if (idx >= 0) { + // once we have located the function frame + // we need to strip out everything before it (and its line) + var next_line = out.indexOf('\n', idx + 1); + out = out.substring(next_line + 1); + } + + this.stack = out; + } + } +}; + +// assert.AssertionError instanceof Error +util.inherits(assert.AssertionError, Error); + +function replacer(key, value) { + if (util.isUndefined(value)) { + return '' + value; + } + if (util.isNumber(value) && !isFinite(value)) { + return value.toString(); + } + if (util.isFunction(value) || util.isRegExp(value)) { + return value.toString(); + } + return value; +} + +function truncate(s, n) { + if (util.isString(s)) { + return s.length < n ? s : s.slice(0, n); + } else { + return s; + } +} + +function getMessage(self) { + return truncate(JSON.stringify(self.actual, replacer), 128) + ' ' + + self.operator + ' ' + + truncate(JSON.stringify(self.expected, replacer), 128); +} + +// At present only the three keys mentioned above are used and +// understood by the spec. Implementations or sub modules can pass +// other keys to the AssertionError's constructor - they will be +// ignored. + +// 3. All of the following functions must throw an AssertionError +// when a corresponding condition is not met, with a message that +// may be undefined if not provided. All assertion methods provide +// both the actual and expected values to the assertion error for +// display purposes. + +function fail(actual, expected, message, operator, stackStartFunction) { + throw new assert.AssertionError({ + message: message, + actual: actual, + expected: expected, + operator: operator, + stackStartFunction: stackStartFunction + }); +} + +// EXTENSION! allows for well behaved errors defined elsewhere. +assert.fail = fail; + +// 4. Pure assertion tests whether a value is truthy, as determined +// by !!guard. +// assert.ok(guard, message_opt); +// This statement is equivalent to assert.equal(true, !!guard, +// message_opt);. To test strictly for the value true, use +// assert.strictEqual(true, guard, message_opt);. + +function ok(value, message) { + if (!value) fail(value, true, message, '==', assert.ok); +} +assert.ok = ok; + +// 5. The equality assertion tests shallow, coercive equality with +// ==. +// assert.equal(actual, expected, message_opt); + +assert.equal = function equal(actual, expected, message) { + if (actual != expected) fail(actual, expected, message, '==', assert.equal); +}; + +// 6. The non-equality assertion tests for whether two objects are not equal +// with != assert.notEqual(actual, expected, message_opt); + +assert.notEqual = function notEqual(actual, expected, message) { + if (actual == expected) { + fail(actual, expected, message, '!=', assert.notEqual); + } +}; + +// 7. The equivalence assertion tests a deep equality relation. +// assert.deepEqual(actual, expected, message_opt); + +assert.deepEqual = function deepEqual(actual, expected, message) { + if (!_deepEqual(actual, expected)) { + fail(actual, expected, message, 'deepEqual', assert.deepEqual); + } +}; + +function _deepEqual(actual, expected) { + // 7.1. All identical values are equivalent, as determined by ===. + if (actual === expected) { + return true; + + } else if (util.isBuffer(actual) && util.isBuffer(expected)) { + if (actual.length != expected.length) return false; + + for (var i = 0; i < actual.length; i++) { + if (actual[i] !== expected[i]) return false; + } + + return true; + + // 7.2. If the expected value is a Date object, the actual value is + // equivalent if it is also a Date object that refers to the same time. + } else if (util.isDate(actual) && util.isDate(expected)) { + return actual.getTime() === expected.getTime(); + + // 7.3 If the expected value is a RegExp object, the actual value is + // equivalent if it is also a RegExp object with the same source and + // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). + } else if (util.isRegExp(actual) && util.isRegExp(expected)) { + return actual.source === expected.source && + actual.global === expected.global && + actual.multiline === expected.multiline && + actual.lastIndex === expected.lastIndex && + actual.ignoreCase === expected.ignoreCase; + + // 7.4. Other pairs that do not both pass typeof value == 'object', + // equivalence is determined by ==. + } else if (!util.isObject(actual) && !util.isObject(expected)) { + return actual == expected; + + // 7.5 For all other Object pairs, including Array objects, equivalence is + // determined by having the same number of owned properties (as verified + // with Object.prototype.hasOwnProperty.call), the same set of keys + // (although not necessarily the same order), equivalent values for every + // corresponding key, and an identical 'prototype' property. Note: this + // accounts for both named and indexed properties on Arrays. + } else { + return objEquiv(actual, expected); + } +} + +function isArguments(object) { + return Object.prototype.toString.call(object) == '[object Arguments]'; +} + +function objEquiv(a, b) { + if (util.isNullOrUndefined(a) || util.isNullOrUndefined(b)) + return false; + // an identical 'prototype' property. + if (a.prototype !== b.prototype) return false; + // if one is a primitive, the other must be same + if (util.isPrimitive(a) || util.isPrimitive(b)) { + return a === b; + } + var aIsArgs = isArguments(a), + bIsArgs = isArguments(b); + if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs)) + return false; + if (aIsArgs) { + a = pSlice.call(a); + b = pSlice.call(b); + return _deepEqual(a, b); + } + var ka = objectKeys(a), + kb = objectKeys(b), + key, i; + // having the same number of owned properties (keys incorporates + // hasOwnProperty) + if (ka.length != kb.length) + return false; + //the same set of keys (although not necessarily the same order), + ka.sort(); + kb.sort(); + //~~~cheap key test + for (i = ka.length - 1; i >= 0; i--) { + if (ka[i] != kb[i]) + return false; + } + //equivalent values for every corresponding key, and + //~~~possibly expensive deep test + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!_deepEqual(a[key], b[key])) return false; + } + return true; +} + +// 8. The non-equivalence assertion tests for any deep inequality. +// assert.notDeepEqual(actual, expected, message_opt); + +assert.notDeepEqual = function notDeepEqual(actual, expected, message) { + if (_deepEqual(actual, expected)) { + fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); + } +}; + +// 9. The strict equality assertion tests strict equality, as determined by ===. +// assert.strictEqual(actual, expected, message_opt); + +assert.strictEqual = function strictEqual(actual, expected, message) { + if (actual !== expected) { + fail(actual, expected, message, '===', assert.strictEqual); + } +}; + +// 10. The strict non-equality assertion tests for strict inequality, as +// determined by !==. assert.notStrictEqual(actual, expected, message_opt); + +assert.notStrictEqual = function notStrictEqual(actual, expected, message) { + if (actual === expected) { + fail(actual, expected, message, '!==', assert.notStrictEqual); + } +}; + +function expectedException(actual, expected) { + if (!actual || !expected) { + return false; + } + + if (Object.prototype.toString.call(expected) == '[object RegExp]') { + return expected.test(actual); + } else if (actual instanceof expected) { + return true; + } else if (expected.call({}, actual) === true) { + return true; + } + + return false; +} + +function _throws(shouldThrow, block, expected, message) { + var actual; + + if (util.isString(expected)) { + message = expected; + expected = null; + } + + try { + block(); + } catch (e) { + actual = e; + } + + message = (expected && expected.name ? ' (' + expected.name + ').' : '.') + + (message ? ' ' + message : '.'); + + if (shouldThrow && !actual) { + fail(actual, expected, 'Missing expected exception' + message); + } + + if (!shouldThrow && expectedException(actual, expected)) { + fail(actual, expected, 'Got unwanted exception' + message); + } + + if ((shouldThrow && actual && expected && + !expectedException(actual, expected)) || (!shouldThrow && actual)) { + throw actual; + } +} + +// 11. Expected to throw an error: +// assert.throws(block, Error_opt, message_opt); + +assert.throws = function(block, /*optional*/error, /*optional*/message) { + _throws.apply(this, [true].concat(pSlice.call(arguments))); +}; + +// EXTENSION! This is annoying to write outside this module. +assert.doesNotThrow = function(block, /*optional*/message) { + _throws.apply(this, [false].concat(pSlice.call(arguments))); +}; + +assert.ifError = function(err) { if (err) {throw err;}}; + +var objectKeys = Object.keys || function (obj) { + var keys = []; + for (var key in obj) { + if (hasOwn.call(obj, key)) keys.push(key); + } + return keys; +}; + +},{"util/":7}],4:[function(require,module,exports){ +if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }; +} else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } +} + +},{}],5:[function(require,module,exports){ +// shim for using process in browser + +var process = module.exports = {}; +var queue = []; +var draining = false; +var currentQueue; +var queueIndex = -1; + +function cleanUpNextTick() { + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } +} + +function drainQueue() { + if (draining) { + return; + } + var timeout = setTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + clearTimeout(timeout); +} + +process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + setTimeout(drainQueue, 0); + } +}; + +// v8 likes predictible objects +function Item(fun, array) { + this.fun = fun; + this.array = array; +} +Item.prototype.run = function () { + this.fun.apply(null, this.array); +}; +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues +process.versions = {}; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; + +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; + +},{}],6:[function(require,module,exports){ +module.exports = function isBuffer(arg) { + return arg && typeof arg === 'object' + && typeof arg.copy === 'function' + && typeof arg.fill === 'function' + && typeof arg.readUInt8 === 'function'; +} +},{}],7:[function(require,module,exports){ +(function (process,global){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var formatRegExp = /%[sdj%]/g; +exports.format = function(f) { + if (!isString(f)) { + var objects = []; + for (var i = 0; i < arguments.length; i++) { + objects.push(inspect(arguments[i])); + } + return objects.join(' '); + } + + var i = 1; + var args = arguments; + var len = args.length; + var str = String(f).replace(formatRegExp, function(x) { + if (x === '%%') return '%'; + if (i >= len) return x; + switch (x) { + case '%s': return String(args[i++]); + case '%d': return Number(args[i++]); + case '%j': + try { + return JSON.stringify(args[i++]); + } catch (_) { + return '[Circular]'; + } + default: + return x; + } + }); + for (var x = args[i]; i < len; x = args[++i]) { + if (isNull(x) || !isObject(x)) { + str += ' ' + x; + } else { + str += ' ' + inspect(x); + } + } + return str; +}; + + +// Mark that a method should not be used. +// Returns a modified function which warns once by default. +// If --no-deprecation is set, then it is a no-op. +exports.deprecate = function(fn, msg) { + // Allow for deprecating things in the process of starting up. + if (isUndefined(global.process)) { + return function() { + return exports.deprecate(fn, msg).apply(this, arguments); + }; + } + + if (process.noDeprecation === true) { + return fn; + } + + var warned = false; + function deprecated() { + if (!warned) { + if (process.throwDeprecation) { + throw new Error(msg); + } else if (process.traceDeprecation) { + console.trace(msg); + } else { + console.error(msg); + } + warned = true; + } + return fn.apply(this, arguments); + } + + return deprecated; +}; + + +var debugs = {}; +var debugEnviron; +exports.debuglog = function(set) { + if (isUndefined(debugEnviron)) + debugEnviron = process.env.NODE_DEBUG || ''; + set = set.toUpperCase(); + if (!debugs[set]) { + if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { + var pid = process.pid; + debugs[set] = function() { + var msg = exports.format.apply(exports, arguments); + console.error('%s %d: %s', set, pid, msg); + }; + } else { + debugs[set] = function() {}; + } + } + return debugs[set]; +}; + + +/** + * Echos the value of a value. Trys to print the value out + * in the best way possible given the different types. + * + * @param {Object} obj The object to print out. + * @param {Object} opts Optional options object that alters the output. + */ +/* legacy: obj, showHidden, depth, colors*/ +function inspect(obj, opts) { + // default options + var ctx = { + seen: [], + stylize: stylizeNoColor + }; + // legacy... + if (arguments.length >= 3) ctx.depth = arguments[2]; + if (arguments.length >= 4) ctx.colors = arguments[3]; + if (isBoolean(opts)) { + // legacy... + ctx.showHidden = opts; + } else if (opts) { + // got an "options" object + exports._extend(ctx, opts); + } + // set default options + if (isUndefined(ctx.showHidden)) ctx.showHidden = false; + if (isUndefined(ctx.depth)) ctx.depth = 2; + if (isUndefined(ctx.colors)) ctx.colors = false; + if (isUndefined(ctx.customInspect)) ctx.customInspect = true; + if (ctx.colors) ctx.stylize = stylizeWithColor; + return formatValue(ctx, obj, ctx.depth); +} +exports.inspect = inspect; + + +// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics +inspect.colors = { + 'bold' : [1, 22], + 'italic' : [3, 23], + 'underline' : [4, 24], + 'inverse' : [7, 27], + 'white' : [37, 39], + 'grey' : [90, 39], + 'black' : [30, 39], + 'blue' : [34, 39], + 'cyan' : [36, 39], + 'green' : [32, 39], + 'magenta' : [35, 39], + 'red' : [31, 39], + 'yellow' : [33, 39] +}; + +// Don't use 'blue' not visible on cmd.exe +inspect.styles = { + 'special': 'cyan', + 'number': 'yellow', + 'boolean': 'yellow', + 'undefined': 'grey', + 'null': 'bold', + 'string': 'green', + 'date': 'magenta', + // "name": intentionally not styling + 'regexp': 'red' +}; + + +function stylizeWithColor(str, styleType) { + var style = inspect.styles[styleType]; + + if (style) { + return '\u001b[' + inspect.colors[style][0] + 'm' + str + + '\u001b[' + inspect.colors[style][1] + 'm'; + } else { + return str; + } +} + + +function stylizeNoColor(str, styleType) { + return str; +} + + +function arrayToHash(array) { + var hash = {}; + + array.forEach(function(val, idx) { + hash[val] = true; + }); + + return hash; +} + + +function formatValue(ctx, value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if (ctx.customInspect && + value && + isFunction(value.inspect) && + // Filter out the util module, it's inspect function is special + value.inspect !== exports.inspect && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value)) { + var ret = value.inspect(recurseTimes, ctx); + if (!isString(ret)) { + ret = formatValue(ctx, ret, recurseTimes); + } + return ret; + } + + // Primitive types cannot have properties + var primitive = formatPrimitive(ctx, value); + if (primitive) { + return primitive; + } + + // Look up the keys of the object. + var keys = Object.keys(value); + var visibleKeys = arrayToHash(keys); + + if (ctx.showHidden) { + keys = Object.getOwnPropertyNames(value); + } + + // IE doesn't make error fields non-enumerable + // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx + if (isError(value) + && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { + return formatError(value); + } + + // Some type of object without properties can be shortcutted. + if (keys.length === 0) { + if (isFunction(value)) { + var name = value.name ? ': ' + value.name : ''; + return ctx.stylize('[Function' + name + ']', 'special'); + } + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } + if (isDate(value)) { + return ctx.stylize(Date.prototype.toString.call(value), 'date'); + } + if (isError(value)) { + return formatError(value); + } + } + + var base = '', array = false, braces = ['{', '}']; + + // Make Array say that they are Array + if (isArray(value)) { + array = true; + braces = ['[', ']']; + } + + // Make functions say that they are functions + if (isFunction(value)) { + var n = value.name ? ': ' + value.name : ''; + base = ' [Function' + n + ']'; + } + + // Make RegExps say that they are RegExps + if (isRegExp(value)) { + base = ' ' + RegExp.prototype.toString.call(value); + } + + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + Date.prototype.toUTCString.call(value); + } + + // Make error with message first say the error + if (isError(value)) { + base = ' ' + formatError(value); + } + + if (keys.length === 0 && (!array || value.length == 0)) { + return braces[0] + base + braces[1]; + } + + if (recurseTimes < 0) { + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } else { + return ctx.stylize('[Object]', 'special'); + } + } + + ctx.seen.push(value); + + var output; + if (array) { + output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else { + output = keys.map(function(key) { + return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); + }); + } + + ctx.seen.pop(); + + return reduceToSingleString(output, base, braces); +} + + +function formatPrimitive(ctx, value) { + if (isUndefined(value)) + return ctx.stylize('undefined', 'undefined'); + if (isString(value)) { + var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + '\''; + return ctx.stylize(simple, 'string'); + } + if (isNumber(value)) + return ctx.stylize('' + value, 'number'); + if (isBoolean(value)) + return ctx.stylize('' + value, 'boolean'); + // For some reason typeof null is "object", so special case here. + if (isNull(value)) + return ctx.stylize('null', 'null'); +} + + +function formatError(value) { + return '[' + Error.prototype.toString.call(value) + ']'; +} + + +function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = []; + for (var i = 0, l = value.length; i < l; ++i) { + if (hasOwnProperty(value, String(i))) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + String(i), true)); + } else { + output.push(''); + } + } + keys.forEach(function(key) { + if (!key.match(/^\d+$/)) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + key, true)); + } + }); + return output; +} + + +function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { + var name, str, desc; + desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; + if (desc.get) { + if (desc.set) { + str = ctx.stylize('[Getter/Setter]', 'special'); + } else { + str = ctx.stylize('[Getter]', 'special'); + } + } else { + if (desc.set) { + str = ctx.stylize('[Setter]', 'special'); + } + } + if (!hasOwnProperty(visibleKeys, key)) { + name = '[' + key + ']'; + } + if (!str) { + if (ctx.seen.indexOf(desc.value) < 0) { + if (isNull(recurseTimes)) { + str = formatValue(ctx, desc.value, null); + } else { + str = formatValue(ctx, desc.value, recurseTimes - 1); + } + if (str.indexOf('\n') > -1) { + if (array) { + str = str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n').substr(2); + } else { + str = '\n' + str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n'); + } + } + } else { + str = ctx.stylize('[Circular]', 'special'); + } + } + if (isUndefined(name)) { + if (array && key.match(/^\d+$/)) { + return str; + } + name = JSON.stringify('' + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = ctx.stylize(name, 'name'); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = ctx.stylize(name, 'string'); + } + } + + return name + ': ' + str; +} + + +function reduceToSingleString(output, base, braces) { + var numLinesEst = 0; + var length = output.reduce(function(prev, cur) { + numLinesEst++; + if (cur.indexOf('\n') >= 0) numLinesEst++; + return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; + }, 0); + + if (length > 60) { + return braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; + } + + return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; +} + + +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. +function isArray(ar) { + return Array.isArray(ar); +} +exports.isArray = isArray; + +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; + +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; + +function isNullOrUndefined(arg) { + return arg == null; +} +exports.isNullOrUndefined = isNullOrUndefined; + +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; + +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; + +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; + +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; + +function isRegExp(re) { + return isObject(re) && objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} +exports.isObject = isObject; + +function isDate(d) { + return isObject(d) && objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; + +function isError(e) { + return isObject(e) && + (objectToString(e) === '[object Error]' || e instanceof Error); +} +exports.isError = isError; + +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; + +function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; +} +exports.isPrimitive = isPrimitive; + +exports.isBuffer = require('./support/isBuffer'); + +function objectToString(o) { + return Object.prototype.toString.call(o); +} + + +function pad(n) { + return n < 10 ? '0' + n.toString(10) : n.toString(10); +} + + +var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', + 'Oct', 'Nov', 'Dec']; + +// 26 Feb 16:19:34 +function timestamp() { + var d = new Date(); + var time = [pad(d.getHours()), + pad(d.getMinutes()), + pad(d.getSeconds())].join(':'); + return [d.getDate(), months[d.getMonth()], time].join(' '); +} + + +// log is just a thin wrapper to console.log that prepends a timestamp +exports.log = function() { + console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); +}; + + +/** + * Inherit the prototype methods from one constructor into another. + * + * The Function.prototype.inherits from lang.js rewritten as a standalone + * function (not on Function.prototype). NOTE: If this file is to be loaded + * during bootstrapping this function needs to be rewritten using some native + * functions as prototype setup using normal JavaScript does not work as + * expected during bootstrapping (see mirror.js in r114903). + * + * @param {function} ctor Constructor function which needs to inherit the + * prototype. + * @param {function} superCtor Constructor function to inherit prototype from. + */ +exports.inherits = require('inherits'); + +exports._extend = function(origin, add) { + // Don't do anything if add isn't an object + if (!add || !isObject(add)) return origin; + + var keys = Object.keys(add); + var i = keys.length; + while (i--) { + origin[keys[i]] = add[keys[i]]; + } + return origin; +}; + +function hasOwnProperty(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +} + +}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./support/isBuffer":6,"_process":5,"inherits":4}],8:[function(require,module,exports){ +(function (global){ + +var rng; + +if (global.crypto && crypto.getRandomValues) { + // WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto + // Moderately fast, high quality + var _rnds8 = new Uint8Array(16); + rng = function whatwgRNG() { + crypto.getRandomValues(_rnds8); + return _rnds8; + }; +} + +if (!rng) { + // Math.random()-based (RNG) + // + // If all else fails, use Math.random(). It's fast, but is of unspecified + // quality. + var _rnds = new Array(16); + rng = function() { + for (var i = 0, r; i < 16; i++) { + if ((i & 0x03) === 0) r = Math.random() * 0x100000000; + _rnds[i] = r >>> ((i & 0x03) << 3) & 0xff; + } + + return _rnds; + }; +} + +module.exports = rng; + + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}],9:[function(require,module,exports){ +// uuid.js +// +// Copyright (c) 2010-2012 Robert Kieffer +// MIT License - http://opensource.org/licenses/mit-license.php + +// Unique ID creation requires a high quality random # generator. We feature +// detect to determine the best RNG source, normalizing to a function that +// returns 128-bits of randomness, since that's what's usually required +var _rng = require('./rng'); + +// Maps for number <-> hex string conversion +var _byteToHex = []; +var _hexToByte = {}; +for (var i = 0; i < 256; i++) { + _byteToHex[i] = (i + 0x100).toString(16).substr(1); + _hexToByte[_byteToHex[i]] = i; +} + +// **`parse()` - Parse a UUID into it's component bytes** +function parse(s, buf, offset) { + var i = (buf && offset) || 0, ii = 0; + + buf = buf || []; + s.toLowerCase().replace(/[0-9a-f]{2}/g, function(oct) { + if (ii < 16) { // Don't overflow! + buf[i + ii++] = _hexToByte[oct]; + } + }); + + // Zero out remaining bytes if string was short + while (ii < 16) { + buf[i + ii++] = 0; + } + + return buf; +} + +// **`unparse()` - Convert UUID byte array (ala parse()) into a string** +function unparse(buf, offset) { + var i = offset || 0, bth = _byteToHex; + return bth[buf[i++]] + bth[buf[i++]] + + bth[buf[i++]] + bth[buf[i++]] + '-' + + bth[buf[i++]] + bth[buf[i++]] + '-' + + bth[buf[i++]] + bth[buf[i++]] + '-' + + bth[buf[i++]] + bth[buf[i++]] + '-' + + bth[buf[i++]] + bth[buf[i++]] + + bth[buf[i++]] + bth[buf[i++]] + + bth[buf[i++]] + bth[buf[i++]]; +} + +// **`v1()` - Generate time-based UUID** +// +// Inspired by https://github.com/LiosK/UUID.js +// and http://docs.python.org/library/uuid.html + +// random #'s we need to init node and clockseq +var _seedBytes = _rng(); + +// Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1) +var _nodeId = [ + _seedBytes[0] | 0x01, + _seedBytes[1], _seedBytes[2], _seedBytes[3], _seedBytes[4], _seedBytes[5] +]; + +// Per 4.2.2, randomize (14 bit) clockseq +var _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff; + +// Previous uuid creation time +var _lastMSecs = 0, _lastNSecs = 0; + +// See https://github.com/broofa/node-uuid for API details +function v1(options, buf, offset) { + var i = buf && offset || 0; + var b = buf || []; + + options = options || {}; + + var clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq; + + // UUID timestamps are 100 nano-second units since the Gregorian epoch, + // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so + // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs' + // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00. + var msecs = options.msecs !== undefined ? options.msecs : new Date().getTime(); + + // Per 4.2.1.2, use count of uuid's generated during the current clock + // cycle to simulate higher resolution clock + var nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1; + + // Time since last uuid creation (in msecs) + var dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs)/10000; + + // Per 4.2.1.2, Bump clockseq on clock regression + if (dt < 0 && options.clockseq === undefined) { + clockseq = clockseq + 1 & 0x3fff; + } + + // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new + // time interval + if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) { + nsecs = 0; + } + + // Per 4.2.1.2 Throw error if too many uuids are requested + if (nsecs >= 10000) { + throw new Error('uuid.v1(): Can\'t create more than 10M uuids/sec'); + } + + _lastMSecs = msecs; + _lastNSecs = nsecs; + _clockseq = clockseq; + + // Per 4.1.4 - Convert from unix epoch to Gregorian epoch + msecs += 12219292800000; + + // `time_low` + var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000; + b[i++] = tl >>> 24 & 0xff; + b[i++] = tl >>> 16 & 0xff; + b[i++] = tl >>> 8 & 0xff; + b[i++] = tl & 0xff; + + // `time_mid` + var tmh = (msecs / 0x100000000 * 10000) & 0xfffffff; + b[i++] = tmh >>> 8 & 0xff; + b[i++] = tmh & 0xff; + + // `time_high_and_version` + b[i++] = tmh >>> 24 & 0xf | 0x10; // include version + b[i++] = tmh >>> 16 & 0xff; + + // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant) + b[i++] = clockseq >>> 8 | 0x80; + + // `clock_seq_low` + b[i++] = clockseq & 0xff; + + // `node` + var node = options.node || _nodeId; + for (var n = 0; n < 6; n++) { + b[i + n] = node[n]; + } + + return buf ? buf : unparse(b); +} + +// **`v4()` - Generate random UUID** + +// See https://github.com/broofa/node-uuid for API details +function v4(options, buf, offset) { + // Deprecated - 'format' argument, as supported in v1.2 + var i = buf && offset || 0; + + if (typeof(options) == 'string') { + buf = options == 'binary' ? new Array(16) : null; + options = null; + } + options = options || {}; + + var rnds = options.random || (options.rng || _rng)(); + + // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` + rnds[6] = (rnds[6] & 0x0f) | 0x40; + rnds[8] = (rnds[8] & 0x3f) | 0x80; + + // Copy bytes to buffer, if provided + if (buf) { + for (var ii = 0; ii < 16; ii++) { + buf[i + ii] = rnds[ii]; + } + } + + return buf || unparse(rnds); +} + +// Export public API +var uuid = v4; +uuid.v1 = v1; +uuid.v4 = v4; +uuid.parse = parse; +uuid.unparse = unparse; + +module.exports = uuid; + +},{"./rng":8}],10:[function(require,module,exports){ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var _api = require("./api"); + +var _api2 = _interopRequireDefault(_api); + +var _collection = require("./collection"); + +var _collection2 = _interopRequireDefault(_collection); + +var _adaptersBase = require("./adapters/base"); + +var _adaptersBase2 = _interopRequireDefault(_adaptersBase); + +var DEFAULT_BUCKET_NAME = "default"; +var DEFAULT_REMOTE = "http://localhost:8888/v1"; + +/** + * KintoBase class. + */ + +var KintoBase = (function () { + _createClass(KintoBase, null, [{ + key: "adapters", + + /** + * Provides a public access to the base adapter class. Users can create a + * custom DB adapter by extending {@link BaseAdapter}. + * + * @type {Object} + */ + get: function get() { + return { + BaseAdapter: _adaptersBase2["default"] + }; + } + + /** + * Synchronization strategies. Available strategies are: + * + * - `MANUAL`: Conflicts will be reported in a dedicated array. + * - `SERVER_WINS`: Conflicts are resolved using remote data. + * - `CLIENT_WINS`: Conflicts are resolved using local data. + * + * @type {Object} + */ + }, { + key: "syncStrategy", + get: function get() { + return _collection2["default"].strategy; + } + + /** + * Constructor. + * + * Options: + * - `{String}` `remote` The server URL to use. + * - `{String}` `bucket` The collection bucket name. + * - `{EventEmitter}` `events` Events handler. + * - `{BaseAdapter}` `adapter` The base DB adapter class. + * - `{String}` `dbPrefix` The DB name prefix. + * - `{Object}` `headers` The HTTP headers to use. + * - `{String}` `requestMode` The HTTP CORS mode to use. + * + * @param {Object} options The options object. + */ + }]); + + function KintoBase() { + var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; + + _classCallCheck(this, KintoBase); + + var defaults = { + bucket: DEFAULT_BUCKET_NAME, + remote: DEFAULT_REMOTE + }; + this._options = Object.assign(defaults, options); + if (!this._options.adapter) { + throw new Error("No adapter provided"); + } + + var _options = this._options; + var remote = _options.remote; + var events = _options.events; + var headers = _options.headers; + var requestMode = _options.requestMode; + + this._api = new _api2["default"](remote, events, { headers: headers, requestMode: requestMode }); + + // public properties + /** + * The event emitter instance. + * @type {EventEmitter} + */ + this.events = this._options.events; + } + + /** + * Creates a {@link Collection} instance. The second (optional) parameter + * will set collection-level options like e.g. `remoteTransformers`. + * + * @param {String} collName The collection name. + * @param {Object} options May contain the following fields: + * remoteTransformers: Array + * @return {Collection} + */ + + _createClass(KintoBase, [{ + key: "collection", + value: function collection(collName) { + var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + + if (!collName) { + throw new Error("missing collection name"); + } + + var bucket = this._options.bucket; + return new _collection2["default"](bucket, collName, this._api, { + events: this._options.events, + adapter: this._options.adapter, + dbPrefix: this._options.dbPrefix, + idSchema: options.idSchema, + remoteTransformers: options.remoteTransformers + }); + } + }]); + + return KintoBase; +})(); + +exports["default"] = KintoBase; +module.exports = exports["default"]; + +},{"./adapters/base":11,"./api":12,"./collection":13}],11:[function(require,module,exports){ +"use strict"; + +/** + * Base db adapter. + * + * @abstract + */ +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var BaseAdapter = (function () { + function BaseAdapter() { + _classCallCheck(this, BaseAdapter); + } + + _createClass(BaseAdapter, [{ + key: "open", + + /** + * Opens a connection to the database. + * + * @abstract + * @return {Promise} + */ + value: function open() { + return Promise.resolve(); + } + + /** + * Closes current connection to the database. + * + * @abstract + * @return {Promise} + */ + }, { + key: "close", + value: function close() { + return Promise.resolve(); + } + + /** + * Deletes every records present in the database. + * + * @abstract + * @return {Promise} + */ + }, { + key: "clear", + value: function clear() { + throw new Error("Not Implemented."); + } + + /** + * Adds a record to the database. + * + * Note: An id value is required. + * + * @abstract + * @param {Object} record The record object, including an id. + * @return {Promise} + */ + }, { + key: "create", + value: function create(record) { + throw new Error("Not Implemented."); + } + + /** + * Updates a record from the IndexedDB database. + * + * @abstract + * @param {Object} record + * @return {Promise} + */ + }, { + key: "update", + value: function update(record) { + throw new Error("Not Implemented."); + } + + /** + * Retrieve a record by its primary key from the database. + * + * @abstract + * @param {String} id The record id. + * @return {Promise} + */ + }, { + key: "get", + value: function get(id) { + throw new Error("Not Implemented."); + } + + /** + * Deletes a record from the database. + * + * @abstract + * @param {String} id The record id. + * @return {Promise} + */ + }, { + key: "delete", + value: function _delete(id) { + throw new Error("Not Implemented."); + } + + /** + * Lists all records from the database. + * + * @abstract + * @return {Promise} + */ + }, { + key: "list", + value: function list() { + throw new Error("Not Implemented."); + } + + /** + * Store the lastModified value. + * + * @abstract + * @param {Number} lastModified + * @return {Promise} + */ + }, { + key: "saveLastModified", + value: function saveLastModified(lastModified) { + throw new Error("Not Implemented."); + } + + /** + * Retrieve saved lastModified value. + * + * @abstract + * @return {Promise} + */ + }, { + key: "getLastModified", + value: function getLastModified() { + throw new Error("Not Implemented."); + } + }]); + + return BaseAdapter; +})(); + +exports["default"] = BaseAdapter; +module.exports = exports["default"]; + +},{}],12:[function(require,module,exports){ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +exports.cleanRecord = cleanRecord; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var _utilsJs = require("./utils.js"); + +var _httpJs = require("./http.js"); + +var _httpJs2 = _interopRequireDefault(_httpJs); + +var RECORD_FIELDS_TO_CLEAN = ["_status", "last_modified"]; +/** + * Currently supported protocol version. + * @type {String} + */ +var SUPPORTED_PROTOCOL_VERSION = "v1"; + +exports.SUPPORTED_PROTOCOL_VERSION = SUPPORTED_PROTOCOL_VERSION; +/** + * Cleans a record object, excluding passed keys. + * + * @param {Object} record The record object. + * @param {Array} excludeFields The list of keys to exclude. + * @return {Object} A clean copy of source record object. + */ + +function cleanRecord(record) { + var excludeFields = arguments.length <= 1 || arguments[1] === undefined ? RECORD_FIELDS_TO_CLEAN : arguments[1]; + + return Object.keys(record).reduce((acc, key) => { + if (excludeFields.indexOf(key) === -1) { + acc[key] = record[key]; + } + return acc; + }, {}); +} + +/** + * High level HTTP client for the Kinto API. + */ + +var Api = (function () { + /** + * Constructor. + * + * Options: + * - {Object} headers The key-value headers to pass to each request. + * - {String} events The HTTP request mode. + * + * @param {String} remote The remote URL. + * @param {EventEmitter} events The events handler + * @param {Object} options The options object. + */ + + function Api(remote, events) { + var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; + + _classCallCheck(this, Api); + + if (typeof remote !== "string" || !remote.length) { + throw new Error("Invalid remote URL: " + remote); + } + if (remote[remote.length - 1] === "/") { + remote = remote.slice(0, -1); + } + this._backoffReleaseTime = null; + // public properties + /** + * The remote endpoint base URL. + * @type {String} + */ + this.remote = remote; + /** + * The optional generic headers. + * @type {Object} + */ + this.optionHeaders = options.headers || {}; + /** + * Current server settings, retrieved from the server. + * @type {Object} + */ + this.serverSettings = null; + /** + * The even emitter instance. + * @type {EventEmitter} + */ + if (!events) { + throw new Error("No events handler provided"); + } + this.events = events; + try { + /** + * The current server protocol version, eg. `v1`. + * @type {String} + */ + this.version = remote.match(/\/(v\d+)\/?$/)[1]; + } catch (err) { + throw new Error("The remote URL must contain the version: " + remote); + } + if (this.version !== SUPPORTED_PROTOCOL_VERSION) { + throw new Error("Unsupported protocol version: " + this.version); + } + /** + * The HTTP instance. + * @type {HTTP} + */ + this.http = new _httpJs2["default"](this.events, { requestMode: options.requestMode }); + this._registerHTTPEvents(); + } + + /** + * Backoff remaining time, in milliseconds. Defaults to zero if no backoff is + * ongoing. + * + * @return {Number} + */ + + _createClass(Api, [{ + key: "_registerHTTPEvents", + + /** + * Registers HTTP events. + */ + value: function _registerHTTPEvents() { + this.events.on("backoff", backoffMs => { + this._backoffReleaseTime = backoffMs; + }); + } + + /** + * Retrieves available server enpoints. + * + * Options: + * - {Boolean} fullUrl: Retrieve a fully qualified URL (default: true). + * + * @param {Object} options Options object. + * @return {String} + */ + }, { + key: "endpoints", + value: function endpoints() { + var options = arguments.length <= 0 || arguments[0] === undefined ? { fullUrl: true } : arguments[0]; + + var _root = options.fullUrl ? this.remote : "/" + this.version; + var urls = { + root: () => _root + "/", + batch: () => _root + "/batch", + bucket: _bucket => _root + "/buckets/" + _bucket, + collection: (bucket, coll) => urls.bucket(bucket) + "/collections/" + coll, + records: (bucket, coll) => urls.collection(bucket, coll) + "/records", + record: (bucket, coll, id) => urls.records(bucket, coll) + "/" + id + }; + return urls; + } + + /** + * Retrieves Kinto server settings. + * + * @return {Promise} + */ + }, { + key: "fetchServerSettings", + value: function fetchServerSettings() { + if (this.serverSettings) { + return Promise.resolve(this.serverSettings); + } + return this.http.request(this.endpoints().root()).then(res => { + this.serverSettings = res.json.settings; + return this.serverSettings; + }); + } + + /** + * Fetches latest changes from the remote server. + * + * @param {String} bucketName The bucket name. + * @param {String} collName The collection name. + * @param {Object} options The options object. + * @return {Promise} + */ + }, { + key: "fetchChangesSince", + value: function fetchChangesSince(bucketName, collName) { + var options = arguments.length <= 2 || arguments[2] === undefined ? { lastModified: null, headers: {} } : arguments[2]; + + var recordsUrl = this.endpoints().records(bucketName, collName); + var queryString = ""; + var headers = Object.assign({}, this.optionHeaders, options.headers); + + if (options.lastModified) { + queryString = "?_since=" + options.lastModified; + headers["If-None-Match"] = (0, _utilsJs.quote)(options.lastModified); + } + + return this.fetchServerSettings().then(_ => this.http.request(recordsUrl + queryString, { headers: headers })).then(res => { + // If HTTP 304, nothing has changed + if (res.status === 304) { + return { + lastModified: options.lastModified, + changes: [] + }; + } + // XXX: ETag are supposed to be opaque and stored «as-is». + // Extract response data + var etag = res.headers.get("ETag"); // e.g. '"42"' + etag = etag ? parseInt((0, _utilsJs.unquote)(etag), 10) : options.lastModified; + var records = res.json.data; + + // Check if server was flushed + var localSynced = options.lastModified; + var serverChanged = etag > options.lastModified; + var emptyCollection = records ? records.length === 0 : true; + if (localSynced && serverChanged && emptyCollection) { + throw Error("Server has been flushed."); + } + + return { lastModified: etag, changes: records }; + }); + } + + /** + * Builds an individual record batch request body. + * + * @param {Object} record The record object. + * @param {String} path The record endpoint URL. + * @param {Boolean} safe Safe update? + * @return {Object} The request body object. + */ + }, { + key: "_buildRecordBatchRequest", + value: function _buildRecordBatchRequest(record, path, safe) { + var isDeletion = record._status === "deleted"; + var method = isDeletion ? "DELETE" : "PUT"; + var body = isDeletion ? undefined : { data: cleanRecord(record) }; + var headers = {}; + if (safe) { + if (record.last_modified) { + // Safe replace. + headers["If-Match"] = (0, _utilsJs.quote)(record.last_modified); + } else if (!isDeletion) { + // Safe creation. + headers["If-None-Match"] = "*"; + } + } + return { method: method, headers: headers, path: path, body: body }; + } + + /** + * Process a batch request response. + * + * @param {Object} results The results object. + * @param {Array} records The initial records list. + * @param {Object} response The response HTTP object. + * @return {Promise} + */ + }, { + key: "_processBatchResponses", + value: function _processBatchResponses(results, records, response) { + // Handle individual batch subrequests responses + response.json.responses.forEach((response, index) => { + // TODO: handle 409 when unicity rule is violated (ex. POST with + // existing id, unique field, etc.) + if (response.status && response.status >= 200 && response.status < 400) { + results.published.push(response.body.data); + } else if (response.status === 404) { + results.skipped.push(response.body); + } else if (response.status === 412) { + results.conflicts.push({ + type: "outgoing", + local: records[index], + remote: response.body.details && response.body.details.existing || null + }); + } else { + results.errors.push({ + path: response.path, + sent: records[index], + error: response.body + }); + } + }); + return results; + } + + /** + * Sends batch update requests to the remote server. + * + * Options: + * - {Object} headers Headers to attach to main and all subrequests. + * - {Boolean} safe Safe update (default: `true`) + * + * @param {String} bucketName The bucket name. + * @param {String} collName The collection name. + * @param {Array} records The list of record updates to send. + * @param {Object} options The options object. + * @return {Promise} + */ + }, { + key: "batch", + value: function batch(bucketName, collName, records) { + var options = arguments.length <= 3 || arguments[3] === undefined ? { headers: {} } : arguments[3]; + + var safe = options.safe || true; + var headers = Object.assign({}, this.optionHeaders, options.headers); + var results = { + errors: [], + published: [], + conflicts: [], + skipped: [] + }; + if (!records.length) { + return Promise.resolve(results); + } + return this.fetchServerSettings().then(serverSettings => { + // Kinto 1.6.1 possibly exposes multiple setting prefixes + var maxRequests = serverSettings["batch_max_requests"] || serverSettings["cliquet.batch_max_requests"]; + if (maxRequests && records.length > maxRequests) { + return Promise.all((0, _utilsJs.partition)(records, maxRequests).map(chunk => { + return this.batch(bucketName, collName, chunk, options); + })).then(batchResults => { + // Assemble responses of chunked batch results into one single + // result object + return batchResults.reduce((acc, batchResult) => { + Object.keys(batchResult).forEach(key => { + acc[key] = results[key].concat(batchResult[key]); + }); + return acc; + }, results); + }); + } + return this.http.request(this.endpoints().batch(), { + method: "POST", + headers: headers, + body: JSON.stringify({ + defaults: { headers: headers }, + requests: records.map(record => { + var path = this.endpoints({ full: false }).record(bucketName, collName, record.id); + return this._buildRecordBatchRequest(record, path, safe); + }) + }) + }).then(res => this._processBatchResponses(results, records, res)); + }); + } + }, { + key: "backoff", + get: function get() { + var currentTime = new Date().getTime(); + if (this._backoffReleaseTime && currentTime < this._backoffReleaseTime) { + return this._backoffReleaseTime - currentTime; + } + return 0; + } + }]); + + return Api; +})(); + +exports["default"] = Api; + +},{"./http.js":15,"./utils.js":16}],13:[function(require,module,exports){ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; })(); + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var _adaptersBase = require("./adapters/base"); + +var _adaptersBase2 = _interopRequireDefault(_adaptersBase); + +var _utils = require("./utils"); + +var _api = require("./api"); + +var _uuid = require("uuid"); + +/** + * Synchronization result object. + */ + +var SyncResultObject = (function () { + _createClass(SyncResultObject, null, [{ + key: "defaults", + + /** + * Object default values. + * @type {Object} + */ + get: function get() { + return { + ok: true, + lastModified: null, + errors: [], + created: [], + updated: [], + deleted: [], + published: [], + conflicts: [], + skipped: [], + resolved: [] + }; + } + + /** + * Public constructor. + */ + }]); + + function SyncResultObject() { + _classCallCheck(this, SyncResultObject); + + /** + * Current synchronization result status; becomes `false` when conflicts or + * errors are registered. + * @type {Boolean} + */ + this.ok = true; + Object.assign(this, SyncResultObject.defaults); + } + + /** + * Adds entries for a given result type. + * + * @param {String} type The result type. + * @param {Array} entries The result entries. + * @return {SyncResultObject} + */ + + _createClass(SyncResultObject, [{ + key: "add", + value: function add(type, entries) { + if (!Array.isArray(this[type])) { + return; + } + this[type] = this[type].concat(entries); + this.ok = this.errors.length + this.conflicts.length === 0; + return this; + } + + /** + * Reinitializes result entries for a given result type. + * + * @param {String} type The result type. + * @return {SyncResultObject} + */ + }, { + key: "reset", + value: function reset(type) { + this[type] = SyncResultObject.defaults[type]; + this.ok = this.errors.length + this.conflicts.length === 0; + return this; + } + }]); + + return SyncResultObject; +})(); + +exports.SyncResultObject = SyncResultObject; + +function createUUIDSchema() { + return { + generate: function generate() { + return (0, _uuid.v4)(); + }, + + validate: function validate(id) { + return (0, _utils.isUUID4)(id); + } + }; +} + +/** + * Abstracts a collection of records stored in the local database, providing + * CRUD operations and synchronization helpers. + */ + +var Collection = (function () { + /** + * Constructor. + * + * Options: + * - `{BaseAdapter} adapter` The DB adapter (default: `IDB`) + * - `{String} dbPrefix` The DB name prefix (default: `""`) + * + * @param {String} bucket The bucket identifier. + * @param {String} name The collection name. + * @param {Api} api The Api instance. + * @param {Object} options The options object. + */ + + function Collection(bucket, name, api) { + var options = arguments.length <= 3 || arguments[3] === undefined ? {} : arguments[3]; + + _classCallCheck(this, Collection); + + this._bucket = bucket; + this._name = name; + this._lastModified = null; + + var DBAdapter = options.adapter; + if (!DBAdapter) { + throw new Error("No adapter provided"); + } + var dbPrefix = options.dbPrefix || ""; + var db = new DBAdapter("" + dbPrefix + bucket + "/" + name); + if (!(db instanceof _adaptersBase2["default"])) { + throw new Error("Unsupported adapter."); + } + // public properties + /** + * The db adapter instance + * @type {BaseAdapter} + */ + this.db = db; + /** + * The Api instance. + * @type {Api} + */ + this.api = api; + /** + * The event emitter instance. + * @type {EventEmitter} + */ + this.events = options.events; + /** + * The IdSchema instance. + * @type {Object} + */ + this.idSchema = this._validateIdSchema(options.idSchema); + /** + * The list of remote transformers. + * @type {Array} + */ + this.remoteTransformers = this._validateRemoteTransformers(options.remoteTransformers); + } + + /** + * The collection name. + * @type {String} + */ + + _createClass(Collection, [{ + key: "_validateIdSchema", + + /** + * Validates an idSchema. + * + * @param {Object|undefined} idSchema + * @return {Object} + */ + value: function _validateIdSchema(idSchema) { + if (typeof idSchema === "undefined") { + return createUUIDSchema(); + } + if (typeof idSchema !== "object") { + throw new Error("idSchema must be an object."); + } else if (typeof idSchema.generate !== "function") { + throw new Error("idSchema must provide a generate function."); + } else if (typeof idSchema.validate !== "function") { + throw new Error("idSchema must provide a validate function."); + } + return idSchema; + } + + /** + * Validates a list of remote transformers. + * + * @param {Array|undefined} remoteTransformers + * @return {Array} + */ + }, { + key: "_validateRemoteTransformers", + value: function _validateRemoteTransformers(remoteTransformers) { + if (typeof remoteTransformers === "undefined") { + return []; + } + if (!Array.isArray(remoteTransformers)) { + throw new Error("remoteTransformers should be an array."); + } + return remoteTransformers.map(transformer => { + if (typeof transformer !== "object") { + throw new Error("A transformer must be an object."); + } else if (typeof transformer.encode !== "function") { + throw new Error("A transformer must provide an encode function."); + } else if (typeof transformer.decode !== "function") { + throw new Error("A transformer must provide a decode function."); + } + return transformer; + }); + } + + /** + * Deletes every records in the current collection and marks the collection as + * never synced. + * + * @return {Promise} + */ + }, { + key: "clear", + value: function clear() { + return this.db.clear().then(_ => this.db.saveLastModified(null)).then(_ => ({ data: [], permissions: {} })); + } + + /** + * Encodes a record. + * + * @param {String} type Either "remote" or "local". + * @param {Object} record The record object to encode. + * @return {Promise} + */ + }, { + key: "_encodeRecord", + value: function _encodeRecord(type, record) { + if (!this[type + "Transformers"].length) { + return Promise.resolve(record); + } + return (0, _utils.waterfall)(this[type + "Transformers"].map(transformer => { + return record => transformer.encode(record); + }), record); + } + + /** + * Decodes a record. + * + * @param {String} type Either "remote" or "local". + * @param {Object} record The record object to decode. + * @return {Promise} + */ + }, { + key: "_decodeRecord", + value: function _decodeRecord(type, record) { + if (!this[type + "Transformers"].length) { + return Promise.resolve(record); + } + return (0, _utils.waterfall)(this[type + "Transformers"].reverse().map(transformer => { + return record => transformer.decode(record); + }), record); + } + + /** + * Adds a record to the local database. + * + * Note: If either the `useRecordId` or `synced` options are true, then the + * record object must contain the id field to be validated. If none of these + * options are true, an id is generated using the current IdSchema; in this + * case, the record passed must not have an id. + * + * Options: + * - {Boolean} synced Sets record status to "synced" (default: `false`). + * - {Boolean} useRecordId Forces the `id` field from the record to be used, + * instead of one that is generated automatically + * (default: `false`). + * + * @param {Object} record + * @param {Object} options + * @return {Promise} + */ + }, { + key: "create", + value: function create(record) { + var options = arguments.length <= 1 || arguments[1] === undefined ? { useRecordId: false, synced: false } : arguments[1]; + + var reject = msg => Promise.reject(new Error(msg)); + if (typeof record !== "object") { + return reject("Record is not an object."); + } + if ((options.synced || options.useRecordId) && !record.id) { + return reject("Missing required Id; synced and useRecordId options require one"); + } + if (!options.synced && !options.useRecordId && record.id) { + return reject("Extraneous Id; can't create a record having one set."); + } + var newRecord = Object.assign({}, record, { + id: options.synced || options.useRecordId ? record.id : this.idSchema.generate(), + _status: options.synced ? "synced" : "created" + }); + if (!this.idSchema.validate(newRecord.id)) { + return reject("Invalid Id: " + newRecord.id); + } + return this.db.create(newRecord).then(record => { + return { data: record, permissions: {} }; + }); + } + + /** + * Updates a record from the local database. + * + * Options: + * - {Boolean} synced: Sets record status to "synced" (default: false) + * + * @param {Object} record + * @param {Object} options + * @return {Promise} + */ + }, { + key: "update", + value: function update(record) { + var options = arguments.length <= 1 || arguments[1] === undefined ? { synced: false } : arguments[1]; + + if (typeof record !== "object") { + return Promise.reject(new Error("Record is not an object.")); + } + if (!record.id) { + return Promise.reject(new Error("Cannot update a record missing id.")); + } + if (!this.idSchema.validate(record.id)) { + return Promise.reject(new Error("Invalid Id: " + record.id)); + } + return this.get(record.id).then(_ => { + var newStatus = "updated"; + if (record._status === "deleted") { + newStatus = "deleted"; + } else if (options.synced) { + newStatus = "synced"; + } + var updatedRecord = Object.assign({}, record, { _status: newStatus }); + return this.db.update(updatedRecord).then(record => { + return { data: record, permissions: {} }; + }); + }); + } + + /** + * Retrieve a record by its id from the local database. + * + * @param {String} id + * @param {Object} options + * @return {Promise} + */ + }, { + key: "get", + value: function get(id) { + var options = arguments.length <= 1 || arguments[1] === undefined ? { includeDeleted: false } : arguments[1]; + + if (!this.idSchema.validate(id)) { + return Promise.reject(Error("Invalid Id: " + id)); + } + return this.db.get(id).then(record => { + if (!record || !options.includeDeleted && record._status === "deleted") { + throw new Error("Record with id=" + id + " not found."); + } else { + return { data: record, permissions: {} }; + } + }); + } + + /** + * Deletes a record from the local database. + * + * Options: + * - {Boolean} virtual: When set to `true`, doesn't actually delete the record, + * update its `_status` attribute to `deleted` instead. + * + * @param {String} id The record's Id. + * @param {Object} options The options object. + * @return {Promise} + */ + }, { + key: "delete", + value: function _delete(id) { + var options = arguments.length <= 1 || arguments[1] === undefined ? { virtual: true } : arguments[1]; + + if (!this.idSchema.validate(id)) { + return Promise.reject(new Error("Invalid Id: " + id)); + } + // Ensure the record actually exists. + return this.get(id, { includeDeleted: true }).then(res => { + if (options.virtual) { + if (res.data._status === "deleted") { + // Record is already deleted + return Promise.resolve({ + data: { id: id }, + permissions: {} + }); + } else { + return this.update(Object.assign({}, res.data, { + _status: "deleted" + })); + } + } + return this.db["delete"](id).then(id => { + return { data: { id: id }, permissions: {} }; + }); + }); + } + + /** + * Lists records from the local database. + * + * Params: + * - {Object} filters The filters to apply (default: `{}`). + * - {String} order The order to apply (default: `-last_modified`). + * + * Options: + * - {Boolean} includeDeleted: Include virtually deleted records. + * + * @param {Object} params The filters and order to apply to the results. + * @param {Object} options The options object. + * @return {Promise} + */ + }, { + key: "list", + value: function list() { + var params = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; + var options = arguments.length <= 1 || arguments[1] === undefined ? { includeDeleted: false } : arguments[1]; + + params = Object.assign({ order: "-last_modified", filters: {} }, params); + return this.db.list().then(results => { + var reduced = (0, _utils.reduceRecords)(params.filters, params.order, results); + if (!options.includeDeleted) { + reduced = reduced.filter(record => record._status !== "deleted"); + } + return { data: reduced, permissions: {} }; + }); + } + + /** + * Attempts to apply a remote change to its local matching record. Note that + * at this point, remote record data are already decoded. + * + * @param {Object} local The local record object. + * @param {Object} remote The remote change object. + * @return {Promise} + */ + }, { + key: "_processChangeImport", + value: function _processChangeImport(local, remote) { + var identical = (0, _utils.deepEquals)((0, _api.cleanRecord)(local), (0, _api.cleanRecord)(remote)); + if (local._status !== "synced") { + // Locally deleted, unsynced: scheduled for remote deletion. + if (local._status === "deleted") { + return { type: "skipped", data: local }; + } + if (identical) { + // If records are identical, import anyway, so we bump the + // local last_modified value from the server and set record + // status to "synced". + return this.update(remote, { synced: true }).then(res => { + return { type: "updated", data: res.data }; + }); + } + return { + type: "conflicts", + data: { type: "incoming", local: local, remote: remote } + }; + } + if (remote.deleted) { + return this["delete"](remote.id, { virtual: false }).then(res => { + return { type: "deleted", data: res.data }; + }); + } + return this.update(remote, { synced: true }).then(updated => { + // if identical, simply exclude it from all lists + var type = identical ? "void" : "updated"; + return { type: type, data: updated.data }; + }); + } + + /** + * Import a single change into the local database. + * + * @param {Object} change + * @return {Promise} + */ + }, { + key: "_importChange", + value: function _importChange(change) { + var _decodedChange, decodePromise; + // if change is a deletion, skip decoding + if (change.deleted) { + decodePromise = Promise.resolve(change); + } else { + decodePromise = this._decodeRecord("remote", change); + } + return decodePromise.then(change => { + _decodedChange = change; + return this.get(_decodedChange.id, { includeDeleted: true }); + }) + // Matching local record found + .then(res => this._processChangeImport(res.data, _decodedChange))["catch"](err => { + if (!/not found/i.test(err.message)) { + err.type = "incoming"; + return { type: "errors", data: err }; + } + // Not found locally but remote change is marked as deleted; skip to + // avoid recreation. + if (_decodedChange.deleted) { + return { type: "skipped", data: _decodedChange }; + } + return this.create(_decodedChange, { synced: true }) + // If everything went fine, expose created record data + .then(res => ({ type: "created", data: res.data })) + // Expose individual creation errors + ["catch"](err => ({ type: "errors", data: err })); + }); + } + + /** + * Import changes into the local database. + * + * @param {SyncResultObject} syncResultObject The sync result object. + * @param {Object} changeObject The change object. + * @return {Promise} + */ + }, { + key: "importChanges", + value: function importChanges(syncResultObject, changeObject) { + return Promise.all(changeObject.changes.map(change => { + return this._importChange(change); + })).then(imports => { + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = imports[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var imported = _step.value; + + if (imported.type !== "void") { + syncResultObject.add(imported.type, imported.data); + } + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator["return"]) { + _iterator["return"](); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + + return syncResultObject; + }).then(syncResultObject => { + syncResultObject.lastModified = changeObject.lastModified; + // Don't persist lastModified value if any conflict or error occured + if (!syncResultObject.ok) { + return syncResultObject; + } + // No conflict occured, persist collection's lastModified value + return this.db.saveLastModified(syncResultObject.lastModified).then(lastModified => { + this._lastModified = lastModified; + return syncResultObject; + }); + }); + } + + /** + * Resets the local records as if they were never synced; existing records are + * marked as newly created, deleted records are dropped. + * + * A next call to {@link Collection.sync} will thus republish the whole content of the + * local collection to the server. + * + * @return {Promise} Resolves with the number of processed records. + */ + }, { + key: "resetSyncStatus", + value: function resetSyncStatus() { + var _count; + return this.list({}, { includeDeleted: true }).then(res => { + return Promise.all(res.data.map(r => { + // Garbage collect deleted records. + if (r._status === "deleted") { + return this.db["delete"](r.id); + } + // Records that were synced become «created». + return this.db.update(Object.assign({}, r, { + last_modified: undefined, + _status: "created" + })); + })); + }).then(res => { + _count = res.length; + return this.db.saveLastModified(null); + }).then(_ => _count); + } + + /** + * Returns an object containing two lists: + * + * - `toDelete`: unsynced deleted records we can safely delete; + * - `toSync`: local updates to send to the server. + * + * @return {Object} + */ + }, { + key: "gatherLocalChanges", + value: function gatherLocalChanges() { + var _toDelete; + return this.list({}, { includeDeleted: true }).then(res => { + return res.data.reduce((acc, record) => { + if (record._status === "deleted" && !record.last_modified) { + acc.toDelete.push(record); + } else if (record._status !== "synced") { + acc.toSync.push(record); + } + return acc; + // rename toSync to toPush or toPublish + }, { toDelete: [], toSync: [] }); + }).then(_ref => { + var toDelete = _ref.toDelete; + var toSync = _ref.toSync; + + _toDelete = toDelete; + return Promise.all(toSync.map(this._encodeRecord.bind(this, "remote"))); + }).then(toSync => ({ toDelete: _toDelete, toSync: toSync })); + } + + /** + * Fetch remote changes, import them to the local database, and handle + * conflicts according to `options.strategy`. Then, updates the passed + * {@link SyncResultObject} with import results. + * + * Options: + * - {String} strategy: The selected sync strategy. + * + * @param {SyncResultObject} syncResultObject + * @param {Object} options + * @return {Promise} + */ + }, { + key: "pullChanges", + value: function pullChanges(syncResultObject) { + var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + + if (!syncResultObject.ok) { + return Promise.resolve(syncResultObject); + } + options = Object.assign({ + strategy: Collection.strategy.MANUAL, + lastModified: this.lastModified, + headers: {} + }, options); + // First fetch remote changes from the server + return this.api.fetchChangesSince(this.bucket, this.name, { + lastModified: options.lastModified, + headers: options.headers + }) + // Reflect these changes locally + .then(changes => this.importChanges(syncResultObject, changes)) + // Handle conflicts, if any + .then(result => this._handleConflicts(result, options.strategy)); + } + + /** + * Publish local changes to the remote server and updates the passed + * {@link SyncResultObject} with publication results. + * + * @param {SyncResultObject} syncResultObject The sync result object. + * @param {Object} options The options object. + * @return {Promise} + */ + }, { + key: "pushChanges", + value: function pushChanges(syncResultObject) { + var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + + if (!syncResultObject.ok) { + return Promise.resolve(syncResultObject); + } + var safe = options.strategy === Collection.SERVER_WINS; + options = Object.assign({ safe: safe }, options); + + // Fetch local changes + return this.gatherLocalChanges().then(_ref2 => { + var toDelete = _ref2.toDelete; + var toSync = _ref2.toSync; + + return Promise.all([ + // Delete never synced records marked for deletion + Promise.all(toDelete.map(record => { + return this["delete"](record.id, { virtual: false }); + })), + // Send batch update requests + this.api.batch(this.bucket, this.name, toSync, options)]); + }) + // Update published local records + .then(_ref3 => { + var _ref32 = _slicedToArray(_ref3, 2); + + var deleted = _ref32[0]; + var synced = _ref32[1]; + + // Merge outgoing errors into sync result object + syncResultObject.add("errors", synced.errors.map(error => { + error.type = "outgoing"; + return error; + })); + // Merge outgoing conflicts into sync result object + syncResultObject.add("conflicts", synced.conflicts); + // Process local updates following published changes + return Promise.all(synced.published.map(record => { + if (record.deleted) { + // Remote deletion was successful, refect it locally + return this["delete"](record.id, { virtual: false }).then(res => { + // Amend result data with the deleted attribute set + return { data: { id: res.data.id, deleted: true } }; + }); + } else { + // Remote create/update was successful, reflect it locally + return this._decodeRecord("remote", record).then(record => this.update(record, { synced: true })); + } + })).then(published => { + syncResultObject.add("published", published.map(res => res.data)); + return syncResultObject; + }); + }) + // Handle conflicts, if any + .then(result => this._handleConflicts(result, options.strategy)).then(result => { + var resolvedUnsynced = result.resolved.filter(record => record._status !== "synced"); + // No resolved conflict to reflect anywhere + if (resolvedUnsynced.length === 0 || options.resolved) { + return result; + } else if (options.strategy === Collection.strategy.CLIENT_WINS && !options.resolved) { + // We need to push local versions of the records to the server + return this.pushChanges(result, Object.assign({}, options, { resolved: true })); + } else if (options.strategy === Collection.strategy.SERVER_WINS) { + // If records have been automatically resolved according to strategy and + // are in non-synced status, mark them as synced. + return Promise.all(resolvedUnsynced.map(record => { + return this.update(record, { synced: true }); + })).then(_ => result); + } + }); + } + + /** + * Resolves a conflict, updating local record according to proposed + * resolution — keeping remote record `last_modified` value as a reference for + * further batch sending. + * + * @param {Object} conflict The conflict object. + * @param {Object} resolution The proposed record. + * @return {Promise} + */ + }, { + key: "resolve", + value: function resolve(conflict, resolution) { + return this.update(Object.assign({}, resolution, { + // Ensure local record has the latest authoritative timestamp + last_modified: conflict.remote.last_modified + })); + } + + /** + * Handles synchronization conflicts according to specified strategy. + * + * @param {SyncResultObject} result The sync result object. + * @param {String} strategy The {@link Collection.strategy}. + * @return {Promise} + */ + }, { + key: "_handleConflicts", + value: function _handleConflicts(result) { + var strategy = arguments.length <= 1 || arguments[1] === undefined ? Collection.strategy.MANUAL : arguments[1]; + + if (strategy === Collection.strategy.MANUAL || result.conflicts.length === 0) { + return Promise.resolve(result); + } + return Promise.all(result.conflicts.map(conflict => { + var resolution = strategy === Collection.strategy.CLIENT_WINS ? conflict.local : conflict.remote; + return this.resolve(conflict, resolution); + })).then(imports => { + return result.reset("conflicts").add("resolved", imports.map(res => res.data)); + }); + } + + /** + * Synchronize remote and local data. The promise will resolve with a + * {@link SyncResultObject}, though will reject: + * + * - if the server is currently backed off; + * - if the server has been detected flushed. + * + * Options: + * - {Object} headers: HTTP headers to attach to outgoing requests. + * - {Collection.strategy} strategy: See {@link Collection.strategy}. + * - {Boolean} ignoreBackoff: Force synchronization even if server is currently + * backed off. + * + * @param {Object} options Options. + * @return {Promise} + */ + }, { + key: "sync", + value: function sync() { + var options = arguments.length <= 0 || arguments[0] === undefined ? { strategy: Collection.strategy.MANUAL, headers: {}, ignoreBackoff: false } : arguments[0]; + + if (!options.ignoreBackoff && this.api.backoff > 0) { + var seconds = Math.ceil(this.api.backoff / 1000); + return Promise.reject(new Error("Server is backed off; retry in " + seconds + "s or use the ignoreBackoff option.")); + } + var result = new SyncResultObject(); + return this.db.getLastModified().then(lastModified => this._lastModified = lastModified).then(_ => this.pullChanges(result, options)).then(result => this.pushChanges(result, options)).then(result => { + // Avoid performing a last pull if nothing has been published. + if (result.published.length === 0) { + return result; + } + return this.pullChanges(result, options); + }); + } + }, { + key: "name", + get: function get() { + return this._name; + } + + /** + * The bucket name. + * @type {String} + */ + }, { + key: "bucket", + get: function get() { + return this._bucket; + } + + /** + * The last modified timestamp. + * @type {Number} + */ + }, { + key: "lastModified", + get: function get() { + return this._lastModified; + } + + /** + * Synchronization strategies. Available strategies are: + * + * - `MANUAL`: Conflicts will be reported in a dedicated array. + * - `SERVER_WINS`: Conflicts are resolved using remote data. + * - `CLIENT_WINS`: Conflicts are resolved using local data. + * + * @type {Object} + */ + }], [{ + key: "strategy", + get: function get() { + return { + CLIENT_WINS: "client_wins", + SERVER_WINS: "server_wins", + MANUAL: "manual" + }; + } + }]); + + return Collection; +})(); + +exports["default"] = Collection; + +},{"./adapters/base":11,"./api":12,"./utils":16,"uuid":9}],14:[function(require,module,exports){ +/** + * Kinto server error code descriptors. + * @type {Object} + */ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports["default"] = { + 104: "Missing Authorization Token", + 105: "Invalid Authorization Token", + 106: "Request body was not valid JSON", + 107: "Invalid request parameter", + 108: "Missing request parameter", + 109: "Invalid posted data", + 110: "Invalid Token / id", + 111: "Missing Token / id", + 112: "Content-Length header was not provided", + 113: "Request body too large", + 114: "Resource was modified meanwhile", + 115: "Method not allowed on this end point", + 116: "Requested version not available on this server", + 117: "Client has sent too many requests", + 121: "Resource access is forbidden for this user", + 122: "Another resource violates constraint", + 201: "Service Temporary unavailable due to high load", + 202: "Service deprecated", + 999: "Internal Server Error" +}; +module.exports = exports["default"]; + +},{}],15:[function(require,module,exports){ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var _errorsJs = require("./errors.js"); + +var _errorsJs2 = _interopRequireDefault(_errorsJs); + +/** + * Enhanced HTTP client for the Kinto protocol. + */ + +var HTTP = (function () { + _createClass(HTTP, null, [{ + key: "DEFAULT_REQUEST_HEADERS", + + /** + * Default HTTP request headers applied to each outgoing request. + * + * @type {Object} + */ + get: function get() { + return { + "Accept": "application/json", + "Content-Type": "application/json" + }; + } + + /** + * Constructor. + * + * Options: + * - {String} requestMode The HTTP request mode (default: `"cors"`). + * + * @param {EventEmitter} events The event handler. + * @param {Object} options The options object. + */ + }]); + + function HTTP(events) { + var options = arguments.length <= 1 || arguments[1] === undefined ? { requestMode: "cors" } : arguments[1]; + + _classCallCheck(this, HTTP); + + // public properties + /** + * The event emitter instance. + * @type {EventEmitter} + */ + if (!events) { + throw new Error("No events handler provided"); + } + this.events = events; + + /** + * The request mode. + * @see https://fetch.spec.whatwg.org/#requestmode + * @type {String} + */ + this.requestMode = options.requestMode; + } + + /** + * Performs an HTTP request to the Kinto server. + * + * Options: + * - `{Object} headers` The request headers object (default: {}) + * + * Resolves with an objet containing the following HTTP response properties: + * - `{Number} status` The HTTP status code. + * - `{Object} json` The JSON response body. + * - `{Headers} headers` The response headers object; see the ES6 fetch() spec. + * + * @param {String} url The URL. + * @param {Object} options The fetch() options object. + * @return {Promise} + */ + + _createClass(HTTP, [{ + key: "request", + value: function request(url) { + var options = arguments.length <= 1 || arguments[1] === undefined ? { headers: {} } : arguments[1]; + + var response, status, statusText, headers; + // Ensure default request headers are always set + options.headers = Object.assign({}, HTTP.DEFAULT_REQUEST_HEADERS, options.headers); + options.mode = this.requestMode; + return fetch(url, options).then(res => { + response = res; + headers = res.headers; + status = res.status; + statusText = res.statusText; + this._checkForDeprecationHeader(headers); + this._checkForBackoffHeader(status, headers); + return res.text(); + }) + // Check if we have a body; if so parse it as JSON. + .then(text => { + if (text.length === 0) { + return null; + } + // Note: we can't consume the response body twice. + return JSON.parse(text); + })["catch"](err => { + var error = new Error("HTTP " + (status || 0) + "; " + err); + error.response = response; + error.stack = err.stack; + throw error; + }).then(json => { + if (json && status >= 400) { + var message = "HTTP " + status + "; "; + if (json.errno && json.errno in _errorsJs2["default"]) { + message += _errorsJs2["default"][json.errno]; + if (json.message) { + message += ": " + json.message; + } + } else { + message += statusText || ""; + } + var error = new Error(message.trim()); + error.response = response; + error.data = json; + throw error; + } + return { status: status, json: json, headers: headers }; + }); + } + }, { + key: "_checkForDeprecationHeader", + value: function _checkForDeprecationHeader(headers) { + var alertHeader = headers.get("Alert"); + if (!alertHeader) { + return; + } + var alert; + try { + alert = JSON.parse(alertHeader); + } catch (err) { + console.warn("Unable to parse Alert header message", alertHeader); + return; + } + console.warn(alert.message, alert.url); + this.events.emit("deprecated", alert); + } + }, { + key: "_checkForBackoffHeader", + value: function _checkForBackoffHeader(status, headers) { + var backoffMs; + var backoffSeconds = parseInt(headers.get("Backoff"), 10); + if (backoffSeconds > 0) { + backoffMs = new Date().getTime() + backoffSeconds * 1000; + } else { + backoffMs = 0; + } + this.events.emit("backoff", backoffMs); + } + }]); + + return HTTP; +})(); + +exports["default"] = HTTP; +module.exports = exports["default"]; + +},{"./errors.js":14}],16:[function(require,module,exports){ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.deepEquals = deepEquals; +exports.quote = quote; +exports.unquote = unquote; +exports.sortObjects = sortObjects; +exports.filterObjects = filterObjects; +exports.reduceRecords = reduceRecords; +exports.partition = partition; +exports.isUUID4 = isUUID4; +exports.waterfall = waterfall; + +var _assert = require("assert"); + +var RE_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; + +/** + * Deeply checks if two structures are equals. + * + * @param {Any} a + * @param {Any} b + * @return {Boolean} + */ + +function deepEquals(a, b) { + try { + (0, _assert.deepEqual)(a, b); + } catch (err) { + return false; + } + return true; +} + +/** + * Returns the specified string with double quotes. + * + * @param {String} str A string to quote. + * @return {String} + */ + +function quote(str) { + return "\"" + str + "\""; +} + +/** + * Trim double quotes from specified string. + * + * @param {String} str A string to unquote. + * @return {String} + */ + +function unquote(str) { + return str.replace(/^"/, "").replace(/"$/, ""); +} + +/** + * Checks if a value is undefined. + * @param {Any} value + * @return {Boolean} + */ +function _isUndefined(value) { + return typeof value === "undefined"; +} + +/** + * Sorts records in a list according to a given ordering. + * + * @param {String} order The ordering, eg. `-last_modified`. + * @param {Array} list The collection to order. + * @return {Array} + */ + +function sortObjects(order, list) { + var hasDash = order[0] === "-"; + var field = hasDash ? order.slice(1) : order; + var direction = hasDash ? -1 : 1; + return list.slice().sort((a, b) => { + if (a[field] && _isUndefined(b[field])) { + return direction; + } + if (b[field] && _isUndefined(a[field])) { + return -direction; + } + if (_isUndefined(a[field]) && _isUndefined(b[field])) { + return 0; + } + return a[field] > b[field] ? direction : -direction; + }); +} + +/** + * Filters records in a list matching all given filters. + * + * @param {String} filters The filters object. + * @param {Array} list The collection to order. + * @return {Array} + */ + +function filterObjects(filters, list) { + return list.filter(entry => { + return Object.keys(filters).every(filter => { + return entry[filter] === filters[filter]; + }); + }); +} + +/** + * Filter and sort list against provided filters and order. + * + * @param {Object} filters The filters to apply. + * @param {String} order The order to apply. + * @param {Array} list The list to reduce. + * @return {Array} + */ + +function reduceRecords(filters, order, list) { + return sortObjects(order, filterObjects(filters, list)); +} + +/** + * Chunks an array into n pieces. + * + * @param {Array} array + * @param {Number} n + * @return {Array} + */ + +function partition(array, n) { + if (n <= 0) { + return array; + } + return array.reduce((acc, x, i) => { + if (i === 0 || i % n === 0) { + acc.push([x]); + } else { + acc[acc.length - 1].push(x); + } + return acc; + }, []); +} + +/** + * Checks if a string is an UUID, according to RFC4122. + * + * @param {String} uuid The uuid to validate. + * @return {Boolean} + */ + +function isUUID4(uuid) { + return RE_UUID.test(uuid); +} + +/** + * Resolves a list of functions sequentially, which can be sync or async; in + * case of async, functions must return a promise. + * + * @param {Array} fns The list of functions. + * @param {Any} init The initial value. + * @return {Promise} + */ + +function waterfall(fns, init) { + if (!fns.length) { + return Promise.resolve(init); + } + return fns.reduce((promise, nextFn) => { + return promise.then(nextFn); + }, Promise.resolve(init)); +} + +},{"assert":3}]},{},[2])(2) +}); \ No newline at end of file diff --git a/services/common/moz.build b/services/common/moz.build index e1737935cd..4a3c4a80f3 100644 --- a/services/common/moz.build +++ b/services/common/moz.build @@ -14,19 +14,25 @@ EXTRA_COMPONENTS += [ ] EXTRA_JS_MODULES['services-common'] += [ - 'hawkclient.js', - 'hawkrequest.js', + 'async.js', 'logmanager.js', + 'moz-kinto-client.js', + 'observers.js', + 'rest.js', 'stringbundle.js', - 'tokenserverclient.js', 'utils.js', ] -EXTRA_PP_JS_MODULES['services-common'] += [ - 'async.js', - 'observers.js', - 'rest.js', -] +if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android': + EXTRA_JS_MODULES['services-common'] += [ + 'hawkclient.js', + 'hawkrequest.js', + 'tokenserverclient.js', + ] + + TESTING_JS_MODULES.services.common += [ + 'modules-testing/storageserver.js', + ] TESTING_JS_MODULES.services.common += [ 'modules-testing/logging.js', diff --git a/services/common/observers.js b/services/common/observers.js index 8b04e5ff6d..c0b7710482 100644 --- a/services/common/observers.js +++ b/services/common/observers.js @@ -2,8 +2,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef MERGED_COMPARTMENT - this.EXPORTED_SYMBOLS = ["Observers"]; var Cc = Components.classes; @@ -11,8 +9,6 @@ var Ci = Components.interfaces; var Cr = Components.results; var Cu = Components.utils; -#endif - Cu.import("resource://gre/modules/XPCOMUtils.jsm"); /** diff --git a/services/common/rest.js b/services/common/rest.js index df27318859..b63708b718 100644 --- a/services/common/rest.js +++ b/services/common/rest.js @@ -2,8 +2,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef MERGED_COMPARTMENT - var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; this.EXPORTED_SYMBOLS = [ @@ -12,10 +10,9 @@ this.EXPORTED_SYMBOLS = [ "TokenAuthenticatedRESTRequest", ]; -#endif - Cu.import("resource://gre/modules/Preferences.jsm"); Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Log.jsm"); Cu.import("resource://services-common/utils.js"); @@ -305,14 +302,9 @@ RESTRequest.prototype = { } // Create and initialize HTTP channel. - let channel = Services.io.newChannelFromURI2(this.uri, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER) - .QueryInterface(Ci.nsIRequest) - .QueryInterface(Ci.nsIHttpChannel); + let channel = NetUtil.newChannel({uri: this.uri, loadUsingSystemPrincipal: true}) + .QueryInterface(Ci.nsIRequest) + .QueryInterface(Ci.nsIHttpChannel); this.channel = channel; channel.loadFlags |= this.loadFlags; channel.notificationCallbacks = this; @@ -358,10 +350,10 @@ RESTRequest.prototype = { // Blast off! try { - channel.asyncOpen(this, null); + channel.asyncOpen2(this); } catch (ex) { // asyncOpen can throw in a bunch of cases -- e.g., a forbidden port. - this._log.warn("Caught an error in asyncOpen: " + CommonUtils.exceptionStr(ex)); + this._log.warn("Caught an error in asyncOpen", ex); CommonUtils.nextTick(onComplete.bind(this, ex)); } this.status = this.SENT; @@ -517,8 +509,7 @@ RESTRequest.prototype = { } } catch (ex) { this._log.warn("Exception thrown reading " + count + " bytes from " + - "the channel."); - this._log.warn(CommonUtils.exceptionStr(ex)); + "the channel", ex); throw ex; } } else { @@ -538,8 +529,7 @@ RESTRequest.prototype = { this.onProgress(); } catch (ex) { this._log.warn("Got exception calling onProgress handler, aborting " + - this.method + " " + channel.URI.spec); - this._log.debug("Exception: " + CommonUtils.exceptionStr(ex)); + this.method + " " + channel.URI.spec, ex); this.abort(); if (!this.onComplete) { @@ -606,7 +596,7 @@ RESTRequest.prototype = { } } } catch (ex) { - this._log.error("Error copying headers: " + CommonUtils.exceptionStr(ex)); + this._log.error("Error copying headers", ex); } this.channel = newChannel; @@ -642,8 +632,7 @@ RESTResponse.prototype = { try { status = this.request.channel.responseStatus; } catch (ex) { - this._log.debug("Caught exception fetching HTTP status code:" + - CommonUtils.exceptionStr(ex)); + this._log.debug("Caught exception fetching HTTP status code", ex); return null; } Object.defineProperty(this, "status", {value: status}); @@ -658,8 +647,7 @@ RESTResponse.prototype = { try { statusText = this.request.channel.responseStatusText; } catch (ex) { - this._log.debug("Caught exception fetching HTTP status text:" + - CommonUtils.exceptionStr(ex)); + this._log.debug("Caught exception fetching HTTP status text", ex); return null; } Object.defineProperty(this, "statusText", {value: statusText}); @@ -674,8 +662,7 @@ RESTResponse.prototype = { try { success = this.request.channel.requestSucceeded; } catch (ex) { - this._log.debug("Caught exception fetching HTTP success flag:" + - CommonUtils.exceptionStr(ex)); + this._log.debug("Caught exception fetching HTTP success flag", ex); return null; } Object.defineProperty(this, "success", {value: success}); @@ -694,8 +681,7 @@ RESTResponse.prototype = { headers[header.toLowerCase()] = value; }); } catch (ex) { - this._log.debug("Caught exception processing response headers:" + - CommonUtils.exceptionStr(ex)); + this._log.debug("Caught exception processing response headers", ex); return null; } diff --git a/services/common/tests/unit/head_helpers.js b/services/common/tests/unit/head_helpers.js index 41c245c7a6..b54045ec1d 100644 --- a/services/common/tests/unit/head_helpers.js +++ b/services/common/tests/unit/head_helpers.js @@ -79,7 +79,7 @@ function httpd_setup (handlers, port=-1) { } catch (ex) { _("=========================================="); _("Got exception starting HTTP server on port " + port); - _("Error: " + CommonUtils.exceptionStr(ex)); + _("Error: " + Log.exceptionStr(ex)); _("Is there a process already listening on port " + port + "?"); _("=========================================="); do_throw(ex); diff --git a/services/common/tests/unit/test_hawkclient.js b/services/common/tests/unit/test_hawkclient.js index 465cad2d8b..95375c7aff 100644 --- a/services/common/tests/unit/test_hawkclient.js +++ b/services/common/tests/unit/test_hawkclient.js @@ -44,7 +44,7 @@ add_task(function test_updateClockOffset() { do_check_true(Math.abs(client.localtimeOffsetMsec + HOUR_MS) <= SECOND_MS); }); -add_task(function test_authenticated_get_request() { +add_task(function* test_authenticated_get_request() { let message = "{\"msg\": \"Great Success!\"}"; let method = "GET"; @@ -66,7 +66,7 @@ add_task(function test_authenticated_get_request() { yield deferredStop(server); }); -function check_authenticated_request(method) { +function* check_authenticated_request(method) { let server = httpd_setup({"/foo": (request, response) => { do_check_true(request.hasHeader("Authorization")); @@ -98,7 +98,7 @@ add_task(function test_authenticated_patch_request() { check_authenticated_request("PATCH"); }); -add_task(function test_credentials_optional() { +add_task(function* test_credentials_optional() { let method = "GET"; let server = httpd_setup({ "/foo": (request, response) => { @@ -118,7 +118,7 @@ add_task(function test_credentials_optional() { yield deferredStop(server); }); -add_task(function test_server_error() { +add_task(function* test_server_error() { let message = "Ohai!"; let method = "GET"; @@ -141,7 +141,7 @@ add_task(function test_server_error() { yield deferredStop(server); }); -add_task(function test_server_error_json() { +add_task(function* test_server_error_json() { let message = JSON.stringify({error: "Cannot get ye flask."}); let method = "GET"; @@ -163,7 +163,7 @@ add_task(function test_server_error_json() { yield deferredStop(server); }); -add_task(function test_offset_after_request() { +add_task(function* test_offset_after_request() { let message = "Ohai!"; let method = "GET"; @@ -186,7 +186,7 @@ add_task(function test_offset_after_request() { yield deferredStop(server); }); -add_task(function test_offset_in_hawk_header() { +add_task(function* test_offset_in_hawk_header() { let message = "Ohai!"; let method = "GET"; @@ -233,7 +233,7 @@ add_task(function test_offset_in_hawk_header() { yield deferredStop(server); }); -add_task(function test_2xx_success() { +add_task(function* test_2xx_success() { // Just to ensure that we're not biased toward 200 OK for success let credentials = { id: "eyJleHBpcmVzIjogMTM2NTAxMDg5OC4x", @@ -257,7 +257,7 @@ add_task(function test_2xx_success() { yield deferredStop(server); }); -add_task(function test_retry_request_on_fail() { +add_task(function* test_retry_request_on_fail() { let attempts = 0; let credentials = { id: "eyJleHBpcmVzIjogMTM2NTAxMDg5OC4x", @@ -312,7 +312,7 @@ add_task(function test_retry_request_on_fail() { yield deferredStop(server); }); -add_task(function test_multiple_401_retry_once() { +add_task(function* test_multiple_401_retry_once() { // Like test_retry_request_on_fail, but always return a 401 // and ensure that the client only retries once. let attempts = 0; @@ -361,7 +361,7 @@ add_task(function test_multiple_401_retry_once() { yield deferredStop(server); }); -add_task(function test_500_no_retry() { +add_task(function* test_500_no_retry() { // If we get a 500 error, the client should not retry (as it would with a // 401) let credentials = { @@ -401,7 +401,7 @@ add_task(function test_500_no_retry() { yield deferredStop(server); }); -add_task(function test_401_then_500() { +add_task(function* test_401_then_500() { // Like test_multiple_401_retry_once, but return a 500 to the // second request, ensuring that the promise is properly rejected // in client.request. @@ -465,7 +465,7 @@ add_task(function test_401_then_500() { yield deferredStop(server); }); -add_task(function throw_if_not_json_body() { +add_task(function* throw_if_not_json_body() { let client = new HawkClient("https://example.com"); try { yield client.request("/bogus", "GET", {}, "I am not json"); diff --git a/services/common/tests/unit/test_storage_adapter.js b/services/common/tests/unit/test_storage_adapter.js new file mode 100644 index 0000000000..6220d80311 --- /dev/null +++ b/services/common/tests/unit/test_storage_adapter.js @@ -0,0 +1,183 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +Cu.import("resource://services-common/moz-kinto-client.js"); + +// set up what we need to make storage adapters +const Kinto = loadKinto(); +const FirefoxAdapter = Kinto.adapters.FirefoxAdapter; +const kintoFilename = "kinto.sqlite"; + +let gFirefoxAdapter = null; + +function do_get_kinto_adapter() { + if (gFirefoxAdapter == null) { + gFirefoxAdapter = new FirefoxAdapter("test"); + } + return gFirefoxAdapter; +} + +function do_get_kinto_db() { + let profile = do_get_profile(); + let kintoDB = profile.clone(); + kintoDB.append(kintoFilename); + return kintoDB; +} + +function cleanup_kinto() { + add_test(function cleanup_kinto_files(){ + let kintoDB = do_get_kinto_db(); + // clean up the db + kintoDB.remove(false); + // force re-creation of the adapter + gFirefoxAdapter = null; + run_next_test(); + }); +} + +function test_collection_operations() { + add_task(function* test_kinto_clear() { + let adapter = do_get_kinto_adapter(); + yield adapter.open(); + yield adapter.clear(); + yield adapter.close(); + }); + + // test creating new records... and getting them again + add_task(function* test_kinto_create_new_get_existing() { + let adapter = do_get_kinto_adapter(); + yield adapter.open(); + let record = {id:"test-id", foo:"bar"}; + yield adapter.create(record); + let newRecord = yield adapter.get("test-id"); + // ensure the record is the same as when it was added + deepEqual(record, newRecord); + yield adapter.close(); + }); + + // test removing records + add_task(function* test_kinto_create_new_get_existing() { + let adapter = do_get_kinto_adapter(); + yield adapter.open(); + // create a second record + let record = {id:"test-id-2", foo:"baz"}; + yield adapter.create(record); + let newRecord = yield adapter.get("test-id-2"); + deepEqual(record, newRecord); + // delete the record + let id = yield adapter.delete(record.id); + // ensure the delete resolved with the record id + do_check_eq(record.id, id); + newRecord = yield adapter.get(record.id); + // ... and ensure it's no longer there + do_check_eq(newRecord, undefined); + // ensure the other record still exists + newRecord = yield adapter.get("test-id"); + do_check_neq(newRecord, undefined); + yield adapter.close(); + }); + + // test getting records that don't exist + add_task(function* test_kinto_get_non_existant() { + let adapter = do_get_kinto_adapter(); + yield adapter.open(); + // Kinto expects adapters to either: + let newRecord = yield adapter.get("missing-test-id"); + // resolve with an undefined record + do_check_eq(newRecord, undefined); + yield adapter.close(); + }); + + // test updating records... and getting them again + add_task(function* test_kinto_update_get_existing() { + let adapter = do_get_kinto_adapter(); + yield adapter.open(); + let originalRecord = {id:"test-id", foo:"bar"}; + let updatedRecord = {id:"test-id", foo:"baz"}; + yield adapter.clear(); + yield adapter.create(originalRecord); + yield adapter.update(updatedRecord); + // ensure the record exists + let newRecord = yield adapter.get("test-id"); + // ensure the record is the same as when it was added + deepEqual(updatedRecord, newRecord); + yield adapter.close(); + }); + + // test listing records + add_task(function* test_kinto_list() { + let adapter = do_get_kinto_adapter(); + yield adapter.open(); + let originalRecord = {id:"test-id-1", foo:"bar"}; + let records = yield adapter.list(); + do_check_eq(records.length, 1); + yield adapter.create(originalRecord); + records = yield adapter.list(); + do_check_eq(records.length, 2); + yield adapter.close(); + }); + + // test save and get last modified + add_task(function* test_kinto_last_modified() { + const initialValue = 0; + const intendedValue = 12345678; + + let adapter = do_get_kinto_adapter(); + yield adapter.open(); + let lastModified = yield adapter.getLastModified(); + do_check_eq(lastModified, initialValue); + let result = yield adapter.saveLastModified(intendedValue); + do_check_eq(result, intendedValue); + lastModified = yield adapter.getLastModified(); + do_check_eq(lastModified, intendedValue); + + // test saveLastModified parses values correctly + result = yield adapter.saveLastModified(" " + intendedValue + " blah"); + // should resolve with the parsed int + do_check_eq(result, intendedValue); + // and should have saved correctly + lastModified = yield adapter.getLastModified(); + do_check_eq(lastModified, intendedValue); + yield adapter.close(); + }); +} + +// test kinto db setup and operations in various scenarios +// test from scratch - no current existing database +add_test(function test_db_creation() { + add_test(function test_create_from_scratch() { + // ensure the file does not exist in the profile + let kintoDB = do_get_kinto_db(); + do_check_false(kintoDB.exists()); + run_next_test(); + }); + + test_collection_operations(); + + cleanup_kinto(); + run_next_test(); +}); + +// this is the closest we can get to a schema version upgrade at v1 - test an +// existing database +add_test(function test_creation_from_empty_db() { + add_test(function test_create_from_empty_db() { + // place an empty kinto db file in the profile + let profile = do_get_profile(); + let kintoDB = do_get_kinto_db(); + + let emptyDB = do_get_file("test_storage_adapter/empty.sqlite"); + emptyDB.copyTo(profile,kintoFilename); + + run_next_test(); + }); + + test_collection_operations(); + + cleanup_kinto(); + run_next_test(); +}); + +function run_test() { + run_next_test(); +} diff --git a/services/common/tests/unit/test_storage_adapter/empty.sqlite b/services/common/tests/unit/test_storage_adapter/empty.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..7f295b414656e46e58d6ee28ab7f1c0b338b89e2 GIT binary patch literal 2048 zcmWFz^vNtqRY=P(%1ta$FlJz3U}R))P*7lCU|>SRj8HZUkcI(}7$LyKp! literal 0 HcmV?d00001 diff --git a/services/common/tests/unit/test_utils_stackTrace.js b/services/common/tests/unit/test_utils_stackTrace.js deleted file mode 100644 index 6c71d07535..0000000000 --- a/services/common/tests/unit/test_utils_stackTrace.js +++ /dev/null @@ -1,33 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -_("Define some functions in well defined line positions for the test"); -function foo(v) { return bar(v + 1); } // line 2 -function bar(v) { return baz(v + 1); } // line 3 -function baz(v) { throw new Error(v + 1); } // line 4 - -_("Make sure lazy constructor calling/assignment works"); -Cu.import("resource://services-common/utils.js"); - -function run_test() { - _("Make sure functions, arguments, files are pretty printed in the trace"); - let trace = ""; - try { - foo(0); - } - catch(ex) { - trace = CommonUtils.stackTrace(ex); - } - _("Got trace:", trace); - do_check_neq(trace, ""); - - let bazPos = trace.indexOf("baz@test_utils_stackTrace.js:7"); - let barPos = trace.indexOf("bar@test_utils_stackTrace.js:6"); - let fooPos = trace.indexOf("foo@test_utils_stackTrace.js:5"); - _("String positions:", bazPos, barPos, fooPos); - - _("Make sure the desired messages show up"); - do_check_true(bazPos >= 0); - do_check_true(barPos > bazPos); - do_check_true(fooPos > barPos); -} diff --git a/services/common/tests/unit/xpcshell.ini b/services/common/tests/unit/xpcshell.ini index 7229596fb7..aadab9fd4e 100644 --- a/services/common/tests/unit/xpcshell.ini +++ b/services/common/tests/unit/xpcshell.ini @@ -3,10 +3,14 @@ head = head_global.js head_helpers.js head_http.js tail = firefox-appdir = browser skip-if = toolkit == 'gonk' +support-files = + test_storage_adapter/** # Test load modules first so syntax failures are caught early. [test_load_modules.js] +[test_storage_adapter.js] + [test_utils_atob.js] [test_utils_convert_string.js] [test_utils_dateprefs.js] @@ -19,7 +23,6 @@ skip-if = toolkit == 'gonk' [test_utils_makeURI.js] [test_utils_namedTimer.js] [test_utils_sets.js] -[test_utils_stackTrace.js] [test_utils_utf8.js] [test_utils_uuid.js] diff --git a/services/common/tokenserverclient.js b/services/common/tokenserverclient.js index 69341d5aaf..a0771319f2 100644 --- a/services/common/tokenserverclient.js +++ b/services/common/tokenserverclient.js @@ -16,7 +16,6 @@ var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Log.jsm"); Cu.import("resource://services-common/rest.js"); -Cu.import("resource://services-common/utils.js"); Cu.import("resource://services-common/observers.js"); const PREF_LOG_LEVEL = "services.common.log.logger.tokenserverclient"; @@ -285,8 +284,7 @@ TokenServerClient.prototype = { try { cb(error, result); } catch (ex) { - self._log.warn("Exception when calling user-supplied callback: " + - CommonUtils.exceptionStr(ex)); + self._log.warn("Exception when calling user-supplied callback", ex); } cb = null; @@ -295,8 +293,7 @@ TokenServerClient.prototype = { try { client._processTokenResponse(this.response, callCallback); } catch (ex) { - this._log.warn("Error processing token server response: " + - CommonUtils.exceptionStr(ex)); + this._log.warn("Error processing token server response", ex); let error = new TokenServerClientError(ex); error.response = this.response; diff --git a/services/common/utils.js b/services/common/utils.js index 16a18a98a3..f0f57d14af 100644 --- a/services/common/utils.js +++ b/services/common/utils.js @@ -69,10 +69,6 @@ this.CommonUtils = { return true; }, - // Import these from Log.jsm for backward compatibility - exceptionStr: Log.exceptionStr, - stackTrace: Log.stackTrace, - /** * Encode byte string as base64URL (RFC 4648). * @@ -102,7 +98,7 @@ this.CommonUtils = { return Services.io.newURI(URIString, null, null); } catch (e) { let log = Log.repository.getLogger("Common.Utils"); - log.debug("Could not create URI: " + CommonUtils.exceptionStr(e)); + log.debug("Could not create URI", e); return null; } }, diff --git a/services/sync/modules/addonsreconciler.js b/services/sync/modules/addonsreconciler.js index 58605f55d5..21b9eaeac2 100644 --- a/services/sync/modules/addonsreconciler.js +++ b/services/sync/modules/addonsreconciler.js @@ -488,8 +488,7 @@ AddonsReconciler.prototype = { try { listener.changeListener.call(listener, date, change, state); } catch (ex) { - this._log.warn("Exception calling change listener: " + - Utils.exceptionStr(ex)); + this._log.warn("Exception calling change listener", ex); } } }, @@ -635,7 +634,7 @@ AddonsReconciler.prototype = { } } catch (ex) { - this._log.warn("Exception: " + Utils.exceptionStr(ex)); + this._log.warn("Exception", ex); } }, diff --git a/services/sync/modules/addonutils.js b/services/sync/modules/addonutils.js index 3a29901c13..43512a1704 100644 --- a/services/sync/modules/addonutils.js +++ b/services/sync/modules/addonutils.js @@ -38,21 +38,10 @@ AddonUtilsInternal.prototype = { * Function to be called with result of operation. */ getInstallFromSearchResult: - function getInstallFromSearchResult(addon, cb, requireSecureURI=true) { + function getInstallFromSearchResult(addon, cb) { this._log.debug("Obtaining install for " + addon.id); - // Verify that the source URI uses TLS. We don't allow installs from - // insecure sources for security reasons. The Addon Manager ensures that - // cert validation, etc is performed. - if (requireSecureURI) { - let scheme = addon.sourceURI.scheme; - if (scheme != "https") { - cb(new Error("Insecure source URI scheme: " + scheme), addon.install); - return; - } - } - // We should theoretically be able to obtain (and use) addon.install if // it is available. However, the addon.sourceURI rewriting won't be // reflected in the AddonInstall, so we can't use it. If we ever get rid @@ -80,8 +69,6 @@ AddonUtilsInternal.prototype = { * syncGUID - Sync GUID to use for the new add-on. * enabled - Boolean indicating whether the add-on should be enabled upon * install. - * requireSecureURI - Boolean indicating whether to require a secure - * URI to install from. This defaults to true. * * When complete it calls a callback with 2 arguments, error and result. * @@ -105,10 +92,6 @@ AddonUtilsInternal.prototype = { function installAddonFromSearchResult(addon, options, cb) { this._log.info("Trying to install add-on from search result: " + addon.id); - if (options.requireSecureURI === undefined) { - options.requireSecureURI = true; - } - this.getInstallFromSearchResult(addon, function onResult(error, install) { if (error) { cb(error, null); @@ -164,10 +147,10 @@ AddonUtilsInternal.prototype = { install.install(); } catch (ex) { - this._log.error("Error installing add-on: " + Utils.exceptionstr(ex)); + this._log.error("Error installing add-on", ex); cb(ex, null); } - }.bind(this), options.requireSecureURI); + }.bind(this)); }, /** @@ -261,6 +244,7 @@ AddonUtilsInternal.prototype = { installedIDs: [], installs: [], addons: [], + skipped: [], errors: [] }; @@ -299,14 +283,20 @@ AddonUtilsInternal.prototype = { // ideally send proper URLs, but this solution was deemed too // complicated at the time the functionality was implemented. for (let addon of addons) { - // sourceURI presence isn't enforced by AddonRepository. So, we skip - // add-ons without a sourceURI. - if (!addon.sourceURI) { - this._log.info("Skipping install of add-on because missing " + - "sourceURI: " + addon.id); + // Find the specified options for this addon. + let options; + for (let install of installs) { + if (install.id == addon.id) { + options = install; + break; + } + } + if (!this.canInstallAddon(addon, options)) { + ourResult.skipped.push(addon.id); continue; } + // We can go ahead and attempt to install it. toInstall.push(addon); // We should always be able to QI the nsIURI to nsIURL. If not, we @@ -362,10 +352,49 @@ AddonUtilsInternal.prototype = { }); }, + /** + * Returns true if we are able to install the specified addon, false + * otherwise. It is expected that this will log the reason if it returns + * false. + * + * @param addon + * (Addon) Add-on instance to check. + * @param options + * (object) The options specified for this addon. See installAddons() + * for the valid elements. + */ + canInstallAddon(addon, options) { + // sourceURI presence isn't enforced by AddonRepository. So, we skip + // add-ons without a sourceURI. + if (!addon.sourceURI) { + this._log.info("Skipping install of add-on because missing " + + "sourceURI: " + addon.id); + return false; + } + // Verify that the source URI uses TLS. We don't allow installs from + // insecure sources for security reasons. The Addon Manager ensures + // that cert validation etc is performed. + let requireSecureURI = true; + if (options && options.requireSecureURI !== undefined) { + requireSecureURI = options.requireSecureURI; + } + + if (requireSecureURI) { + let scheme = addon.sourceURI.scheme; + if (scheme != "https") { + this._log.info(`Skipping install of add-on "${addon.id}" because sourceURI's scheme of "${scheme}" is not trusted`); + return false; + } + } + this._log.info(`Add-on "${addon.id}" is able to be installed`); + return true; + }, + + /** * Update the user disabled flag for an add-on. * - * The supplied callback will ba called when the operation is + * The supplied callback will be called when the operation is * complete. If the new flag matches the existing or if the add-on * isn't currently active, the function will fire the callback * immediately. Else, the callback is invoked when the AddonManager diff --git a/services/sync/modules/engines.js b/services/sync/modules/engines.js index aa7f33e8d8..46ea7724df 100644 --- a/services/sync/modules/engines.js +++ b/services/sync/modules/engines.js @@ -15,7 +15,6 @@ var {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components; Cu.import("resource://services-common/async.js"); Cu.import("resource://gre/modules/Log.jsm"); Cu.import("resource://services-common/observers.js"); -Cu.import("resource://services-common/utils.js"); Cu.import("resource://services-sync/constants.js"); Cu.import("resource://services-sync/identity.js"); Cu.import("resource://services-sync/record.js"); @@ -308,8 +307,7 @@ Store.prototype = { // ex.cause will carry its stack with it when rethrown. throw ex.cause; } catch (ex if !Async.isShutdownException(ex)) { - this._log.warn("Failed to apply incoming record " + record.id); - this._log.warn("Encountered exception: " + Utils.exceptionStr(ex)); + this._log.warn("Failed to apply incoming record " + record.id, ex); failed.push(record.id); } }; @@ -577,16 +575,11 @@ EngineManager.prototype = { this._engines[name] = engine; } } catch (ex) { - this._log.error(CommonUtils.exceptionStr(ex)); - - let mesg = ex.message ? ex.message : ex; let name = engineObject || ""; name = name.prototype || ""; name = name.name || ""; - let out = "Could not initialize engine '" + name + "': " + mesg; - this._log.error(out); - + this._log.error(`Could not initialize engine ${name}`, ex); return engineObject; } }, @@ -821,7 +814,7 @@ SyncEngine.prototype = { return this._previousFailed; }, set previousFailed(val) { - let cb = (error) => this._log.error(Utils.exceptionStr(error)); + let cb = (error) => this._log.error("Failed to set previousFailed", error); // Coerce the array to a string for more efficient comparison. if (val + "" == this._previousFailed) { return; @@ -1004,8 +997,7 @@ SyncEngine.prototype = { } catch (ex if !Async.isShutdownException(ex)) { // Catch any error that escapes from applyIncomingBatch. At present // those will all be abort events. - this._log.warn("Got exception " + Utils.exceptionStr(ex) + - ", aborting processIncoming."); + this._log.warn("Got exception, aborting processIncoming", ex); aborting = ex; } this._tracker.ignoreAll = false; @@ -1073,7 +1065,7 @@ SyncEngine.prototype = { self._log.debug("Ignoring second retry suggestion."); // Fall through to error case. case SyncEngine.kRecoveryStrategy.error: - self._log.warn("Error decrypting record: " + Utils.exceptionStr(ex)); + self._log.warn("Error decrypting record", ex); failed.push(item.id); return; case SyncEngine.kRecoveryStrategy.ignore: @@ -1083,7 +1075,7 @@ SyncEngine.prototype = { } } } catch (ex) { - self._log.warn("Error decrypting record: " + Utils.exceptionStr(ex)); + self._log.warn("Error decrypting record", ex); failed.push(item.id); return; } @@ -1096,8 +1088,7 @@ SyncEngine.prototype = { failed.push(item.id); aborting = ex.cause; } catch (ex if !Async.isShutdownException(ex)) { - self._log.warn("Failed to reconcile incoming record " + item.id); - self._log.warn("Encountered exception: " + Utils.exceptionStr(ex)); + self._log.warn("Failed to reconcile incoming record " + item.id, ex); failed.push(item.id); return; } @@ -1469,7 +1460,7 @@ SyncEngine.prototype = { out.encrypt(this.service.collectionKeys.keyForCollection(this.name)); up.pushData(out); } catch (ex if !Async.isShutdownException(ex)) { - this._log.warn("Error creating record: " + Utils.exceptionStr(ex)); + this._log.warn("Error creating record", ex); } // Partial upload @@ -1560,7 +1551,7 @@ SyncEngine.prototype = { this._log.trace("Trying to decrypt a record from the server.."); test.get(); } catch (ex if !Async.isShutdownException(ex)) { - this._log.debug("Failed test decrypt: " + Utils.exceptionStr(ex)); + this._log.debug("Failed test decrypt", ex); } return canDecrypt; diff --git a/services/sync/modules/engines/addons.js b/services/sync/modules/engines/addons.js index eabc66a86a..c8d1a5d514 100644 --- a/services/sync/modules/engines/addons.js +++ b/services/sync/modules/engines/addons.js @@ -298,6 +298,14 @@ AddonsStore.prototype = { // engine and the record will try to be applied later. let results = cb.wait(); + if (results.skipped.includes(record.addonID)) { + this._log.info("Add-on skipped: " + record.addonID); + // Just early-return for skipped addons - we don't want to arrange to + // try again next time because the condition that caused up to skip + // will remain true for this addon forever. + return; + } + let addon; for (let a of results.addons) { if (a.id == record.addonID) { diff --git a/services/sync/modules/engines/bookmarks.js b/services/sync/modules/engines/bookmarks.js index d59f3b26da..f9c5165b77 100644 --- a/services/sync/modules/engines/bookmarks.js +++ b/services/sync/modules/engines/bookmarks.js @@ -426,8 +426,7 @@ BookmarksEngine.prototype = { // Failure to create a backup is somewhat bad, but probably not bad // enough to prevent syncing of bookmarks - so just log the error and // continue. - this._log.warn("Got exception \"" + Utils.exceptionStr(ex) + - "\" backing up bookmarks, but continuing with sync."); + this._log.warn("Error while backing up bookmarks, but continuing with sync", ex); cb(); } ); @@ -442,9 +441,7 @@ BookmarksEngine.prototype = { try { guidMap = this._buildGUIDMap(); } catch (ex if !Async.isShutdownException(ex)) { - this._log.warn("Got exception \"" + Utils.exceptionStr(ex) + - "\" building GUID map." + - " Skipping all other incoming items."); + this._log.warn("Error while building GUID map, skipping all other incoming items", ex); throw {code: Engine.prototype.eEngineAbortApplyIncoming, cause: ex}; } @@ -691,7 +688,7 @@ BookmarksStore.prototype = { return true; } } catch(ex) { - this._log.debug("Failed to reparent item. " + Utils.exceptionStr(ex)); + this._log.debug("Failed to reparent item", ex); } return false; }, @@ -1323,8 +1320,7 @@ BookmarksStore.prototype = { let u = PlacesUtils.bookmarks.getBookmarkURI(itemID); this._tagURI(u, tags); } catch (e) { - this._log.warn("Got exception fetching URI for " + itemID + ": not tagging. " + - Utils.exceptionStr(e)); + this._log.warn(`Got exception fetching URI for ${itemID} not tagging`, e); // I guess it doesn't have a URI. Don't try to tag it. return; diff --git a/services/sync/modules/engines/passwords.js b/services/sync/modules/engines/passwords.js index b570ab654f..b6d155556d 100644 --- a/services/sync/modules/engines/passwords.js +++ b/services/sync/modules/engines/passwords.js @@ -69,7 +69,7 @@ PasswordEngine.prototype = { Svc.Prefs.set("deletePwdFxA", true); Svc.Prefs.reset("deletePwd"); // The old prefname we previously used. } catch (ex if !Async.isShutdownException(ex)) { - this._log.debug("Password deletes failed: " + Utils.exceptionStr(ex)); + this._log.debug("Password deletes failed", ex); } } }, @@ -233,8 +233,7 @@ PasswordStore.prototype = { try { Services.logins.addLogin(login); } catch(ex) { - this._log.debug("Adding record " + record.id + - " resulted in exception " + Utils.exceptionStr(ex)); + this._log.debug(`Adding record ${record.id} resulted in exception`, ex); } }, @@ -266,9 +265,7 @@ PasswordStore.prototype = { try { Services.logins.modifyLogin(loginItem, newinfo); } catch(ex) { - this._log.debug("Modifying record " + record.id + - " resulted in exception " + Utils.exceptionStr(ex) + - ". Not modifying."); + this._log.debug(`Modifying record ${record.id} resulted in exception; not modifying`, ex); } }, diff --git a/services/sync/modules/identity.js b/services/sync/modules/identity.js index c52938703f..6f0c52c825 100644 --- a/services/sync/modules/identity.js +++ b/services/sync/modules/identity.js @@ -338,7 +338,7 @@ IdentityManager.prototype = { try { this._syncKeyBundle = new SyncKeyBundle(this.username, this.syncKey); } catch (ex) { - this._log.warn(Utils.exceptionStr(ex)); + this._log.warn("Failed to create sync bundle", ex); return null; } } diff --git a/services/sync/modules/policies.js b/services/sync/modules/policies.js index aa85bd22e7..2f654b852a 100644 --- a/services/sync/modules/policies.js +++ b/services/sync/modules/policies.js @@ -620,7 +620,7 @@ ErrorHandler.prototype = { this.checkServerError(exception); Status.engines = [engine_name, exception.failureCode || ENGINE_UNKNOWN_FAIL]; - this._log.debug(engine_name + " failed: " + Utils.exceptionStr(exception)); + this._log.debug(engine_name + " failed", exception); break; case "weave:service:login:error": this.resetFileLog(this._logManager.REASON_ERROR); diff --git a/services/sync/modules/record.js b/services/sync/modules/record.js index 2812b1ffae..a301ec6ee1 100644 --- a/services/sync/modules/record.js +++ b/services/sync/modules/record.js @@ -239,7 +239,7 @@ RecordManager.prototype = { return this.set(url, record); } catch (ex if !Async.isShutdownException(ex)) { - this._log.debug("Failed to import record: " + Utils.exceptionStr(ex)); + this._log.debug("Failed to import record", ex); return null; } }, diff --git a/services/sync/modules/resource.js b/services/sync/modules/resource.js index 9505b7c1de..39e2e21148 100644 --- a/services/sync/modules/resource.js +++ b/services/sync/modules/resource.js @@ -13,6 +13,7 @@ var Cr = Components.results; var Cu = Components.utils; Cu.import("resource://gre/modules/Preferences.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); Cu.import("resource://services-common/async.js"); Cu.import("resource://gre/modules/Log.jsm"); Cu.import("resource://services-common/observers.js"); @@ -148,16 +149,9 @@ AsyncResource.prototype = { // to obtain a request channel. // _createRequest: function Res__createRequest(method) { - let channel = Services.io.newChannel2(this.spec, - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER) - .QueryInterface(Ci.nsIRequest) - .QueryInterface(Ci.nsIHttpChannel); + let channel = NetUtil.newChannel({uri: this.spec, loadUsingSystemPrincipal: true}) + .QueryInterface(Ci.nsIRequest) + .QueryInterface(Ci.nsIHttpChannel); channel.loadFlags |= DEFAULT_LOAD_FLAGS; @@ -230,10 +224,10 @@ AsyncResource.prototype = { this._log, this.ABORT_TIMEOUT); channel.requestMethod = action; try { - channel.asyncOpen(listener, null); + channel.asyncOpen2(listener); } catch (ex) { - // asyncOpen can throw in a bunch of cases -- e.g., a forbidden port. - this._log.warn("Caught an error in asyncOpen: " + CommonUtils.exceptionStr(ex)); + // asyncOpen2 can throw in a bunch of cases -- e.g., a forbidden port. + this._log.warn("Caught an error in asyncOpen2", ex); CommonUtils.nextTick(callback.bind(this, ex)); } }, @@ -280,9 +274,7 @@ AsyncResource.prototype = { } catch(ex) { // Got a response, but an exception occurred during processing. // This shouldn't occur. - this._log.warn("Caught unexpected exception " + CommonUtils.exceptionStr(ex) + - " in _onComplete."); - this._log.debug(CommonUtils.stackTrace(ex)); + this._log.warn("Caught unexpected exception in _oncomplete", ex); } // Process headers. They can be empty, or the call can otherwise fail, so @@ -320,9 +312,7 @@ AsyncResource.prototype = { contentLength + "."); } } catch (ex) { - this._log.debug("Caught exception " + CommonUtils.exceptionStr(ex) + - " visiting headers in _onComplete."); - this._log.debug(CommonUtils.stackTrace(ex)); + this._log.debug("Caught exception visiting headers in _onComplete", ex); } let ret = new String(data); @@ -337,7 +327,7 @@ AsyncResource.prototype = { try { return JSON.parse(ret); } catch (ex) { - this._log.warn("Got exception parsing response body: \"" + CommonUtils.exceptionStr(ex)); + this._log.warn("Got exception parsing response body", ex); // Stringify to avoid possibly printing non-printable characters. this._log.debug("Parse fail: Response body starts: \"" + JSON.stringify((ret + "").slice(0, 100)) + @@ -543,7 +533,7 @@ ChannelListener.prototype = { siStream = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(Ci.nsIScriptableInputStream); siStream.init(stream); } catch (ex) { - this._log.warn("Exception creating nsIScriptableInputStream." + CommonUtils.exceptionStr(ex)); + this._log.warn("Exception creating nsIScriptableInputStream", ex); this._log.debug("Parameters: " + req.URI.spec + ", " + stream + ", " + off + ", " + count); // Cannot proceed, so rethrow and allow the channel to cancel itself. throw ex; @@ -560,8 +550,7 @@ ChannelListener.prototype = { this._onProgress(); } catch (ex if !Async.isShutdownException(ex)) { this._log.warn("Got exception calling onProgress handler during fetch of " - + req.URI.spec); - this._log.debug(CommonUtils.exceptionStr(ex)); + + req.URI.spec, ex); this._log.trace("Rethrowing; expect a failure code from the HTTP channel."); throw ex; } @@ -576,7 +565,7 @@ ChannelListener.prototype = { try { CommonUtils.namedTimer(this.abortRequest, this._timeout, this, "abortTimer"); } catch (ex) { - this._log.warn("Got exception extending abort timer: " + CommonUtils.exceptionStr(ex)); + this._log.warn("Got exception extending abort timer", ex); } }, @@ -670,14 +659,14 @@ ChannelNotificationListener.prototype = { } } } catch (ex) { - this._log.error("Error copying headers: " + CommonUtils.exceptionStr(ex)); + this._log.error("Error copying headers", ex); } // We let all redirects proceed. try { callback.onRedirectVerifyCallback(Cr.NS_OK); } catch (ex) { - this._log.error("onRedirectVerifyCallback threw!" + CommonUtils.exceptionStr(ex)); + this._log.error("onRedirectVerifyCallback threw!", ex); } } }; diff --git a/services/sync/modules/service.js b/services/sync/modules/service.js index 43012a3b9f..73819f444f 100644 --- a/services/sync/modules/service.js +++ b/services/sync/modules/service.js @@ -21,7 +21,6 @@ const KEYS_WBO = "keys"; Cu.import("resource://gre/modules/Preferences.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Log.jsm"); -Cu.import("resource://services-common/utils.js"); Cu.import("resource://services-sync/constants.js"); Cu.import("resource://services-sync/engines.js"); Cu.import("resource://services-sync/engines/clients.js"); @@ -484,8 +483,7 @@ Sync11Service.prototype = { this.engineManager.register(ns[engineName]); } catch (ex) { - this._log.warn("Could not register engine " + name + ": " + - CommonUtils.exceptionStr(ex)); + this._log.warn("Could not register engine " + name, ex); } } @@ -680,8 +678,7 @@ Sync11Service.prototype = { } catch (ex) { // This means no keys are present, or there's a network error. - this._log.debug("Failed to fetch and verify keys: " - + Utils.exceptionStr(ex)); + this._log.debug("Failed to fetch and verify keys", ex); this.errorHandler.checkServerError(ex); return false; } @@ -782,7 +779,7 @@ Sync11Service.prototype = { } } catch (ex) { // Must have failed on some network issue - this._log.debug("verifyLogin failed: " + Utils.exceptionStr(ex)); + this._log.debug("verifyLogin failed", ex); this.status.login = LOGIN_FAILED_NETWORK_ERROR; this.errorHandler.checkServerError(ex); return false; @@ -855,8 +852,7 @@ Sync11Service.prototype = { try { cb.wait(); } catch (ex) { - this._log.debug("Password change failed: " + - CommonUtils.exceptionStr(ex)); + this._log.debug("Password change failed", ex); return false; } @@ -902,8 +898,7 @@ Sync11Service.prototype = { try { engine.removeClientData(); } catch(ex) { - this._log.warn("Deleting client data for " + engine.name + " failed:" - + Utils.exceptionStr(ex)); + this._log.warn(`Deleting client data for ${engine.name} failed`, ex); } } this._log.debug("Finished deleting client data."); @@ -1532,7 +1527,7 @@ Sync11Service.prototype = { try { response = res.delete(); } catch (ex) { - this._log.debug("Failed to wipe server: " + CommonUtils.exceptionStr(ex)); + this._log.debug("Failed to wipe server", ex); throw ex; } if (response.status != 200 && response.status != 404) { @@ -1549,8 +1544,7 @@ Sync11Service.prototype = { try { response = this.resource(url).delete(); } catch (ex) { - this._log.debug("Failed to wipe '" + name + "' collection: " + - Utils.exceptionStr(ex)); + this._log.debug("Failed to wipe '" + name + "' collection", ex); throw ex; } @@ -1696,8 +1690,7 @@ Sync11Service.prototype = { return this.getStorageRequest(url).get(function onComplete(error) { // Note: 'this' is the request. if (error) { - this._log.debug("Failed to retrieve '" + info_type + "': " + - Utils.exceptionStr(error)); + this._log.debug("Failed to retrieve '" + info_type + "'", error); return callback(error); } if (this.response.status != 200) { diff --git a/services/sync/modules/stages/enginesync.js b/services/sync/modules/stages/enginesync.js index ce7fce94dd..85d80d4f4c 100644 --- a/services/sync/modules/stages/enginesync.js +++ b/services/sync/modules/stages/enginesync.js @@ -136,8 +136,7 @@ EngineSynchronizer.prototype = { try { this._updateEnabledEngines(); } catch (ex) { - this._log.debug("Updating enabled engines failed: " + - Utils.exceptionStr(ex)); + this._log.debug("Updating enabled engines failed", ex); this.service.errorHandler.checkServerError(ex); this.onComplete(ex); return; diff --git a/services/sync/modules/util.js b/services/sync/modules/util.js index 7f1bf44c7e..f2604bab62 100644 --- a/services/sync/modules/util.js +++ b/services/sync/modules/util.js @@ -35,8 +35,6 @@ this.Utils = { // In the ideal world, references to these would be removed. nextTick: CommonUtils.nextTick, namedTimer: CommonUtils.namedTimer, - exceptionStr: CommonUtils.exceptionStr, - stackTrace: CommonUtils.stackTrace, makeURI: CommonUtils.makeURI, encodeUTF8: CommonUtils.encodeUTF8, decodeUTF8: CommonUtils.decodeUTF8, @@ -77,7 +75,7 @@ this.Utils = { return func.call(thisArg); } catch(ex) { - thisArg._log.debug("Exception: " + Utils.exceptionStr(ex)); + thisArg._log.debug("Exception", ex); if (exceptionCallback) { return exceptionCallback.call(thisArg, ex); } @@ -342,8 +340,7 @@ this.Utils = { // Ignore non-existent files. } catch (e) { if (that._log) { - that._log.debug("Failed to load json: " + - CommonUtils.exceptionStr(e)); + that._log.debug("Failed to load json", e); } } diff --git a/services/sync/tests/unit/head_http_server.js b/services/sync/tests/unit/head_http_server.js index 688ba78912..b227b6858e 100644 --- a/services/sync/tests/unit/head_http_server.js +++ b/services/sync/tests/unit/head_http_server.js @@ -586,7 +586,7 @@ SyncServer.prototype = { } catch (ex) { _("=========================================="); _("Got exception starting Sync HTTP server."); - _("Error: " + Utils.exceptionStr(ex)); + _("Error: " + Log.exceptionStr(ex)); _("Is there a process already listening on port " + port + "?"); _("=========================================="); do_throw(ex); diff --git a/services/sync/tests/unit/test_addon_utils.js b/services/sync/tests/unit/test_addon_utils.js index 189b414d71..0931162c70 100644 --- a/services/sync/tests/unit/test_addon_utils.js +++ b/services/sync/tests/unit/test_addon_utils.js @@ -3,6 +3,7 @@ "use strict"; +Cu.import("resource://gre/modules/Log.jsm"); Cu.import("resource://gre/modules/Preferences.jsm"); Cu.import("resource://services-sync/addonutils.js"); Cu.import("resource://services-sync/util.js"); @@ -35,7 +36,7 @@ function createAndStartHTTPServer(port=HTTP_PORT) { return server; } catch (ex) { _("Got exception starting HTTP server on port " + port); - _("Error: " + Utils.exceptionStr(ex)); + _("Error: " + Log.exceptionStr(ex)); do_throw(ex); } } @@ -60,6 +61,9 @@ add_test(function test_handle_empty_source_uri() { do_check_true("installedIDs" in result); do_check_eq(0, result.installedIDs.length); + do_check_true("skipped" in result); + do_check_true(result.skipped.includes(ID)); + server.stop(run_next_test); }); @@ -79,44 +83,18 @@ add_test(function test_ignore_untrusted_source_uris() { let sourceURI = ioService.newURI(s, null, null); let addon = {sourceURI: sourceURI, name: "bad", id: "bad"}; - try { - let cb = Async.makeSpinningCallback(); - AddonUtils.getInstallFromSearchResult(addon, cb, true); - cb.wait(); - } catch (ex) { - do_check_neq(null, ex); - do_check_eq(0, ex.message.indexOf("Insecure source URI")); - continue; - } - - // We should never get here if an exception is thrown. - do_check_true(false); + let canInstall = AddonUtils.canInstallAddon(addon); + do_check_false(canInstall, "Correctly rejected a bad URL"); } - let count = 0; for (let s of good) { let sourceURI = ioService.newURI(s, null, null); let addon = {sourceURI: sourceURI, name: "good", id: "good"}; - // Despite what you might think, we don't get an error in the callback. - // The install won't work because the underlying Addon instance wasn't - // proper. But, that just results in an AddonInstall that is missing - // certain values. We really just care that the callback is being invoked - // anyway. - let callback = function onInstall(error, install) { - do_check_null(error); - do_check_neq(null, install); - do_check_eq(sourceURI.spec, install.sourceURI.spec); - - count += 1; - - if (count >= good.length) { - run_next_test(); - } - }; - - AddonUtils.getInstallFromSearchResult(addon, callback, true); + let canInstall = AddonUtils.canInstallAddon(addon); + do_check_true(canInstall, "Correctly accepted a good URL"); } + run_next_test(); }); add_test(function test_source_uri_rewrite() { @@ -151,7 +129,11 @@ add_test(function test_source_uri_rewrite() { let server = createAndStartHTTPServer(); let installCallback = Async.makeSpinningCallback(); - AddonUtils.installAddons([{id: "rewrite@tests.mozilla.org"}], installCallback); + let installOptions = { + id: "rewrite@tests.mozilla.org", + requireSecureURI: false, + } + AddonUtils.installAddons([installOptions], installCallback); installCallback.wait(); do_check_true(installCalled); diff --git a/services/sync/tests/unit/test_addons_store.js b/services/sync/tests/unit/test_addons_store.js index 691ec97b67..1ea4025f0d 100644 --- a/services/sync/tests/unit/test_addons_store.js +++ b/services/sync/tests/unit/test_addons_store.js @@ -3,6 +3,7 @@ "use strict"; +Cu.import("resource://gre/modules/Log.jsm"); Cu.import("resource://gre/modules/Preferences.jsm"); Cu.import("resource://services-sync/addonutils.js"); Cu.import("resource://services-sync/engines/addons.js"); @@ -60,7 +61,7 @@ function createAndStartHTTPServer(port) { return server; } catch (ex) { _("Got exception starting HTTP server on port " + port); - _("Error: " + Utils.exceptionStr(ex)); + _("Error: " + Log.exceptionStr(ex)); do_throw(ex); } } @@ -412,8 +413,9 @@ add_test(function test_create_bad_install() { let record = createRecordForThisApp(guid, id, true, false); let failed = store.applyIncomingBatch([record]); - do_check_eq(1, failed.length); - do_check_eq(guid, failed[0]); + // This addon had no source URI so was skipped - but it's not treated as + // failure. + do_check_eq(0, failed.length); let addon = getAddonFromAddonManagerByID(id); do_check_eq(null, addon); diff --git a/services/sync/tests/unit/test_bookmark_engine.js b/services/sync/tests/unit/test_bookmark_engine.js index a27d1b2b63..f20b696677 100644 --- a/services/sync/tests/unit/test_bookmark_engine.js +++ b/services/sync/tests/unit/test_bookmark_engine.js @@ -71,7 +71,7 @@ add_test(function test_ID_caching() { _("New mobile ID: " + newMobileID); } catch (ex) { err = ex; - _("Error: " + Utils.exceptionStr(err)); + _("Error: " + Log.exceptionStr(err)); } do_check_true(!err); @@ -220,7 +220,7 @@ add_task(function test_restorePromptsReupload() { engine.sync(); } catch(ex) { error = ex; - _("Got error: " + Utils.exceptionStr(ex)); + _("Got error: " + Log.exceptionStr(ex)); } do_check_true(!error); @@ -264,7 +264,7 @@ add_task(function test_restorePromptsReupload() { engine.sync(); } catch(ex) { error = ex; - _("Got error: " + Utils.exceptionStr(ex)); + _("Got error: " + Log.exceptionStr(ex)); } do_check_true(!error); diff --git a/services/sync/tests/unit/test_node_reassignment.js b/services/sync/tests/unit/test_node_reassignment.js index 7fe5ed7eda..c9dd12e939 100644 --- a/services/sync/tests/unit/test_node_reassignment.js +++ b/services/sync/tests/unit/test_node_reassignment.js @@ -92,11 +92,12 @@ function prepareServer() { function getReassigned() { try { return Services.prefs.getBoolPref("services.sync.lastSyncReassigned"); - } catch (ex if (ex.result == Cr.NS_ERROR_UNEXPECTED)) { - return false; } catch (ex) { + if (ex.result == Cr.NS_ERROR_UNEXPECTED) { + return false; + } do_throw("Got exception retrieving lastSyncReassigned: " + - Utils.exceptionStr(ex)); + Log.exceptionStr(ex)); } } @@ -106,7 +107,7 @@ function getReassigned() { * Runs `between` between the two. This can be used to undo deliberate failure * setup, detach observers, etc. */ -function syncAndExpectNodeReassignment(server, firstNotification, between, +function* syncAndExpectNodeReassignment(server, firstNotification, between, secondNotification, url) { let deferred = Promise.defer(); function onwards() { @@ -160,7 +161,7 @@ function syncAndExpectNodeReassignment(server, firstNotification, between, yield deferred.promise; } -add_task(function test_momentary_401_engine() { +add_task(function* test_momentary_401_engine() { _("Test a failure for engine URLs that's resolved by reassignment."); let server = yield prepareServer(); let john = server.user("johndoe"); @@ -212,7 +213,7 @@ add_task(function test_momentary_401_engine() { }); // This test ends up being a failing fetch *after we're already logged in*. -add_task(function test_momentary_401_info_collections() { +add_task(function* test_momentary_401_info_collections() { _("Test a failure for info/collections that's resolved by reassignment."); let server = yield prepareServer(); @@ -235,7 +236,7 @@ add_task(function test_momentary_401_info_collections() { Service.infoURL); }); -add_task(function test_momentary_401_storage_loggedin() { +add_task(function* test_momentary_401_storage_loggedin() { _("Test a failure for any storage URL, not just engine parts. " + "Resolved by reassignment."); let server = yield prepareServer(); @@ -260,7 +261,7 @@ add_task(function test_momentary_401_storage_loggedin() { Service.storageURL + "meta/global"); }); -add_task(function test_momentary_401_storage_loggedout() { +add_task(function* test_momentary_401_storage_loggedout() { _("Test a failure for any storage URL, not just engine parts. " + "Resolved by reassignment."); let server = yield prepareServer(); @@ -282,7 +283,7 @@ add_task(function test_momentary_401_storage_loggedout() { Service.storageURL + "meta/global"); }); -add_task(function test_loop_avoidance_storage() { +add_task(function* test_loop_avoidance_storage() { _("Test that a repeated failure doesn't result in a sync loop " + "if node reassignment cannot resolve the failure."); @@ -382,7 +383,7 @@ add_task(function test_loop_avoidance_storage() { yield deferred.promise; }); -add_task(function test_loop_avoidance_engine() { +add_task(function* test_loop_avoidance_engine() { _("Test that a repeated 401 in an engine doesn't result in a sync loop " + "if node reassignment cannot resolve the failure."); let server = yield prepareServer(); diff --git a/services/sync/tests/unit/test_service_sync_locked.js b/services/sync/tests/unit/test_service_sync_locked.js index e2cbbfa92e..8d24a5133e 100644 --- a/services/sync/tests/unit/test_service_sync_locked.js +++ b/services/sync/tests/unit/test_service_sync_locked.js @@ -11,8 +11,10 @@ function run_test() { function augmentLogger(old) { let d = old.debug; let i = old.info; - old.debug = function(m) { debug.push(m); d.call(old, m); } - old.info = function(m) { info.push(m); i.call(old, m); } + // For the purposes of this test we don't need to do full formatting + // of the 2nd param, as the ones we care about are always strings. + old.debug = function(m, p) { debug.push(p ? m + ": " + p : m); d.call(old, m, p); } + old.info = function(m, p) { info.push(p ? m + ": " + p : m); i.call(old, m, p); } return old; } @@ -28,9 +30,7 @@ function run_test() { Service.sync(); Service._locked = false; - do_check_eq(debug[debug.length - 2], - "Exception: Could not acquire lock. Label: \"service.js: login\". No traceback available"); - do_check_eq(info[info.length - 1], - "Cannot start sync: already syncing?"); + do_check_true(debug[debug.length - 2].startsWith("Exception: Could not acquire lock. Label: \"service.js: login\".")); + do_check_eq(info[info.length - 1], "Cannot start sync: already syncing?"); } diff --git a/services/sync/tests/unit/test_utils_catch.js b/services/sync/tests/unit/test_utils_catch.js index 302e20e2c3..5f50bf7e43 100644 --- a/services/sync/tests/unit/test_utils_catch.js +++ b/services/sync/tests/unit/test_utils_catch.js @@ -8,7 +8,7 @@ function run_test() { catch: Utils.catch, _log: { debug: function(str) { - didThrow = str.search(/^Exception: /) == 0; + didThrow = str.search(/^Exception/) == 0; }, info: function(str) { wasLocked = str.indexOf("Cannot start sync: already syncing?") == 0; diff --git a/services/sync/tps/extensions/tps/resource/auth/fxaccounts.jsm b/services/sync/tps/extensions/tps/resource/auth/fxaccounts.jsm index f5daa14be7..837a6b3993 100644 --- a/services/sync/tps/extensions/tps/resource/auth/fxaccounts.jsm +++ b/services/sync/tps/extensions/tps/resource/auth/fxaccounts.jsm @@ -67,7 +67,7 @@ var Authentication = { Logger.AssertTrue(account["username"], "Username has been found"); Logger.AssertTrue(account["password"], "Password has been found"); - Logger.logInfo("Login user: " + account["username"] + '\n'); + Logger.logInfo("Login user: " + account["username"]); let client = new FxAccountsClient(); client.signIn(account["username"], account["password"], true).then(credentials => { diff --git a/services/sync/tps/extensions/tps/resource/modules/history.jsm b/services/sync/tps/extensions/tps/resource/modules/history.jsm index e03b301b5f..3e750a5f0f 100644 --- a/services/sync/tps/extensions/tps/resource/modules/history.jsm +++ b/services/sync/tps/extensions/tps/resource/modules/history.jsm @@ -161,7 +161,7 @@ var HistoryEntry = { } let all_items_found = true; - for (let itemvisit in item.visits) { + for (let itemvisit of item.visits) { all_items_found = all_items_found && "found" in itemvisit; Logger.logInfo("History entry for " + item.uri + ", type:" + itemvisit.type + ", date:" + itemvisit.date + diff --git a/services/sync/tps/extensions/tps/resource/tps.jsm b/services/sync/tps/extensions/tps/resource/tps.jsm index 71066094dd..ee0d64b938 100644 --- a/services/sync/tps/extensions/tps/resource/tps.jsm +++ b/services/sync/tps/extensions/tps/resource/tps.jsm @@ -14,6 +14,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components; var module = this; // Global modules +Cu.import("resource://gre/modules/Log.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://services-common/async.js"); @@ -108,6 +109,7 @@ var TPS = { _test: null, _triggeredSync: false, _usSinceEpoch: 0, + _requestedQuit: false, _init: function TPS__init() { // Check if Firefox Accounts is enabled @@ -122,6 +124,8 @@ var TPS = { Services.obs.addObserver(this, aTopic, true); }, this); + // Configure some logging prefs for Sync itself. + Weave.Svc.Prefs.set("log.appender.dump", "Debug"); // Import the appropriate authentication module if (this.fxaccounts_enabled) { Cu.import("resource://tps/auth/fxaccounts.jsm", module); @@ -131,9 +135,16 @@ var TPS = { } }, - DumpError: function TPS__DumpError(msg) { + DumpError(msg, exc = null) { this._errors++; - Logger.logError("[phase" + this._currentPhase + "] " + msg); + let errInfo; + if (exc) { + errInfo = Log.exceptionStr(exc); // includes details and stack-trace. + } else { + // always write a stack even if no error passed. + errInfo = Log.stackTrace(new Error()); + } + Logger.logError(`[phase ${this._currentPhase}] ${msg} - ${errInfo}`); this.quit(); }, @@ -232,7 +243,7 @@ var TPS = { } } catch (e) { - this.DumpError("Exception caught: " + Utils.exceptionStr(e)); + this.DumpError("Observer failed", e); return; } }, @@ -265,6 +276,7 @@ var TPS = { }, quit: function TPS__quit() { + this._requestedQuit = true; this.goQuitApplication(); }, @@ -414,26 +426,26 @@ var TPS = { let password_id = -1; Logger.logInfo("executing action " + action.toUpperCase() + " on password " + JSON.stringify(password)); - var password = new Password(password); + let passwordOb = new Password(password); switch (action) { case ACTION_ADD: - Logger.AssertTrue(password.Create() > -1, "error adding password"); + Logger.AssertTrue(passwordOb.Create() > -1, "error adding password"); break; case ACTION_VERIFY: - Logger.AssertTrue(password.Find() != -1, "password not found"); + Logger.AssertTrue(passwordOb.Find() != -1, "password not found"); break; case ACTION_VERIFY_NOT: - Logger.AssertTrue(password.Find() == -1, + Logger.AssertTrue(passwordOb.Find() == -1, "password found, but it shouldn't exist"); break; case ACTION_DELETE: - Logger.AssertTrue(password.Find() != -1, "password not found"); - password.Remove(); + Logger.AssertTrue(passwordOb.Find() != -1, "password not found"); + passwordOb.Remove(); break; case ACTION_MODIFY: - if (password.updateProps != null) { - Logger.AssertTrue(password.Find() != -1, "password not found"); - password.Update(); + if (passwordOb.updateProps != null) { + Logger.AssertTrue(passwordOb.Find() != -1, "password not found"); + passwordOb.Update(); } break; default: @@ -601,7 +613,15 @@ var TPS = { this._currentAction++; } catch(e) { - this.DumpError("Exception caught: " + Utils.exceptionStr(e)); + if (Async.isShutdownException(e)) { + if (this._requestedQuit) { + Logger.logInfo("Sync aborted due to requested shutdown"); + } else { + this.DumpError("Sync aborted due to shutdown, but we didn't request it"); + } + } else { + this.DumpError("RunNextTestAction failed", e); + } return; } this.RunNextTestAction(); @@ -658,7 +678,7 @@ var TPS = { // executed. Utils.nextTick(this._executeTestPhase.bind(this, file, phase, settings)); } catch(e) { - this.DumpError("Exception caught: " + Utils.exceptionStr(e)); + this.DumpError("RunTestPhase failed", e); return; } }, @@ -743,7 +763,7 @@ var TPS = { this._currentAction = 0; } catch(e) { - this.DumpError("Exception caught: " + Utils.exceptionStr(e)); + this.DumpError("_executeTestPhase failed", e); return; } }, @@ -819,10 +839,6 @@ var TPS = { cb.wait(); Svc.Obs.remove(aEventName, cb); Logger.logInfo(aEventName + " observed!"); - - cb = Async.makeSpinningCallback(); - Utils.nextTick(cb); - cb.wait(); }, @@ -868,6 +884,12 @@ var TPS = { this.waitForSetupComplete(); Logger.AssertEqual(Weave.Status.service, Weave.STATUS_OK, "Weave status OK"); this.waitForTracking(); + // If fxaccounts is enabled we get an initial sync at login time - let + // that complete. + if (this.fxaccounts_enabled) { + this._triggeredSync = true; + this.waitForSyncFinished(); + } }, /** @@ -896,6 +918,7 @@ var TPS = { this._triggeredSync = true; this.StartAsyncOperation(); Weave.Service.sync(); + Logger.logInfo("Sync is complete"); }, WipeServer: function TPS__WipeServer() { diff --git a/storage/StorageBaseStatementInternal.cpp b/storage/StorageBaseStatementInternal.cpp index 72ea84fb03..f2c2bb5222 100644 --- a/storage/StorageBaseStatementInternal.cpp +++ b/storage/StorageBaseStatementInternal.cpp @@ -45,10 +45,12 @@ public: NS_IMETHOD Run() { if (mStatement->mAsyncStatement) { - (void)::sqlite3_finalize(mStatement->mAsyncStatement); + sqlite3_finalize(mStatement->mAsyncStatement); mStatement->mAsyncStatement = nullptr; } - (void)::NS_ProxyRelease(mConnection->threadOpenedOn, mStatement); + + nsCOMPtr targetThread(mConnection->threadOpenedOn); + NS_ProxyRelease(targetThread, mStatement.forget()); return NS_OK; } private: @@ -91,13 +93,8 @@ public: (void)::sqlite3_finalize(mAsyncStatement); mAsyncStatement = nullptr; - // Because of our ambiguous nsISupports we cannot use the NS_ProxyRelease - // template helpers. - Connection *rawConnection = nullptr; - mConnection.swap(rawConnection); - (void)::NS_ProxyRelease( - rawConnection->threadOpenedOn, - NS_ISUPPORTS_CAST(mozIStorageConnection *, rawConnection)); + nsCOMPtr target(mConnection->threadOpenedOn); + (void)::NS_ProxyRelease(target, mConnection.forget()); return NS_OK; } private: diff --git a/storage/mozStorageAsyncStatement.cpp b/storage/mozStorageAsyncStatement.cpp index 7b4029b55c..2df196fb8e 100644 --- a/storage/mozStorageAsyncStatement.cpp +++ b/storage/mozStorageAsyncStatement.cpp @@ -220,10 +220,8 @@ AsyncStatement::~AsyncStatement() if (!onCallingThread) { // NS_ProxyRelase only magic forgets for us if mDBConnection is an // nsCOMPtr. Which it is not; it's an nsRefPtr. - Connection *forgottenConn = nullptr; - mDBConnection.swap(forgottenConn); - (void)::NS_ProxyRelease(forgottenConn->threadOpenedOn, - static_cast(forgottenConn)); + nsCOMPtr targetThread(mDBConnection->threadOpenedOn); + NS_ProxyRelease(targetThread, mDBConnection.forget()); } } diff --git a/storage/mozStorageConnection.cpp b/storage/mozStorageConnection.cpp index 31ead70cf0..fc508f41d8 100644 --- a/storage/mozStorageConnection.cpp +++ b/storage/mozStorageConnection.cpp @@ -381,15 +381,8 @@ public: } ~AsyncCloseConnection() { - nsCOMPtr thread; - (void)NS_GetMainThread(getter_AddRefs(thread)); - // Handle ambiguous nsISupports inheritance. - Connection *rawConnection = nullptr; - mConnection.swap(rawConnection); - (void)NS_ProxyRelease(thread, - NS_ISUPPORTS_CAST(mozIStorageConnection *, - rawConnection)); - (void)NS_ProxyRelease(thread, mCallbackEvent); + NS_ReleaseOnMainThread(mConnection.forget()); + NS_ReleaseOnMainThread(mCallbackEvent.forget()); } private: RefPtr mConnection; @@ -451,22 +444,13 @@ private: MOZ_ASSERT(NS_SUCCEEDED(rv)); // Handle ambiguous nsISupports inheritance. - Connection *rawConnection = nullptr; - mConnection.swap(rawConnection); - (void)NS_ProxyRelease(thread, NS_ISUPPORTS_CAST(mozIStorageConnection *, - rawConnection)); - - Connection *rawClone = nullptr; - mClone.swap(rawClone); - (void)NS_ProxyRelease(thread, NS_ISUPPORTS_CAST(mozIStorageConnection *, - rawClone)); + NS_ProxyRelease(thread, mConnection.forget()); + NS_ProxyRelease(thread, mClone.forget()); // Generally, the callback will be released by CallbackComplete. // However, if for some reason Run() is not executed, we still // need to ensure that it is released here. - mozIStorageCompletionCallback *rawCallback = nullptr; - mCallback.swap(rawCallback); - (void)NS_ProxyRelease(thread, rawCallback); + NS_ProxyRelease(thread, mCallback.forget()); } RefPtr mConnection; diff --git a/storage/mozStorageService.cpp b/storage/mozStorageService.cpp index 5ebc03ef07..449ddc4266 100644 --- a/storage/mozStorageService.cpp +++ b/storage/mozStorageService.cpp @@ -332,8 +332,7 @@ Service::unregisterConnection(Connection *aConnection) // Ensure the connection is released on its opening thread. Note, we // must use .forget().take() so that we can manually cast to an // unambiguous nsISupports type. - NS_ProxyRelease(thread, - static_cast(mConnections[i].forget().take())); + NS_ProxyRelease(thread, mConnections[i].forget()); mConnections.RemoveElementAt(i); return; @@ -733,23 +732,13 @@ private: ~AsyncInitDatabase() { - nsCOMPtr thread; - DebugOnly rv = NS_GetMainThread(getter_AddRefs(thread)); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - (void)NS_ProxyRelease(thread, mStorageFile); - - // Handle ambiguous nsISupports inheritance. - Connection *rawConnection = nullptr; - mConnection.swap(rawConnection); - (void)NS_ProxyRelease(thread, NS_ISUPPORTS_CAST(mozIStorageConnection *, - rawConnection)); + NS_ReleaseOnMainThread(mStorageFile.forget()); + NS_ReleaseOnMainThread(mConnection.forget()); // Generally, the callback will be released by CallbackComplete. // However, if for some reason Run() is not executed, we still // need to ensure that it is released here. - mozIStorageCompletionCallback *rawCallback = nullptr; - mCallback.swap(rawCallback); - (void)NS_ProxyRelease(thread, rawCallback); + NS_ReleaseOnMainThread(mCallback.forget()); } RefPtr mConnection; diff --git a/storage/mozStorageStatementData.h b/storage/mozStorageStatementData.h index b9d6b45c97..8baaf2fa73 100644 --- a/storage/mozStorageStatementData.h +++ b/storage/mozStorageStatementData.h @@ -52,8 +52,7 @@ public: // We need to ensure that mParamsArray is released on the main thread, // as the binding arguments may be XPConnect values, which are safe // to release only on the main thread. - nsCOMPtr mainThread = do_GetMainThread(); - (void)NS_ProxyRelease(mainThread, mParamsArray); + NS_ReleaseOnMainThread(mParamsArray.forget()); } /** diff --git a/testing/tps/.gitignore b/testing/tps/.gitignore new file mode 100644 index 0000000000..65f8b6e053 --- /dev/null +++ b/testing/tps/.gitignore @@ -0,0 +1,4 @@ +# These files are added by running the TPS test suite. +build/ +dist/ +tps.egg-info/ diff --git a/testing/tps/.hgignore b/testing/tps/.hgignore new file mode 100644 index 0000000000..65f8b6e053 --- /dev/null +++ b/testing/tps/.hgignore @@ -0,0 +1,4 @@ +# These files are added by running the TPS test suite. +build/ +dist/ +tps.egg-info/ diff --git a/testing/tps/setup.py b/testing/tps/setup.py index 0b12464379..4d870913fe 100644 --- a/testing/tps/setup.py +++ b/testing/tps/setup.py @@ -13,9 +13,9 @@ deps = ['httplib2 == 0.7.3', 'mozinfo == 0.7', 'mozinstall == 1.10', 'mozprocess == 0.19', - 'mozprofile == 0.21', + 'mozprofile == 0.27', 'mozrunner == 6.0', - 'mozversion == 0.6', + 'mozversion == 1.4', ] # we only support python 2.6+ right now diff --git a/testing/tps/tps/testrunner.py b/testing/tps/tps/testrunner.py index c41c73e432..8495a2fa2e 100644 --- a/testing/tps/tps/testrunner.py +++ b/testing/tps/tps/testrunner.py @@ -72,6 +72,8 @@ class TPSTestRunner(object): 'services.sync.firstSync': 'notReady', 'services.sync.lastversion': '1.0', 'toolkit.startup.max_resumed_crashes': -1, + # hrm - not sure what the release/beta channels will do? + 'xpinstall.signatures.required': False, } debug_preferences = { @@ -338,11 +340,11 @@ class TPSTestRunner(object): if self.mobile: self.preferences.update({'services.sync.client.type' : 'mobile'}) - # Set a dummy username to force the correct authentication type. For the - # old sync, the username is not allowed to contain a '@'. - dummy = {'fx_account': 'dummy@somewhere', 'sync_account': 'dummy'} - auth_type = self.config.get('auth_type', 'fx_account') - self.preferences.update({'services.sync.username': dummy[auth_type]}) + # If we are using legacy Sync, then set a dummy username to force the + # correct authentication type. Without this pref set to a value + # without an '@' character, Sync will initialize for FxA. + if self.config.get('auth_type', 'fx_account') != "fx_account": + self.preferences.update({'services.sync.username': "dummy"}) if self.debug: self.preferences.update(self.debug_preferences) diff --git a/testing/web-platform/mozilla/meta/MANIFEST.json b/testing/web-platform/mozilla/meta/MANIFEST.json index 5787b59caf..5aad985824 100644 --- a/testing/web-platform/mozilla/meta/MANIFEST.json +++ b/testing/web-platform/mozilla/meta/MANIFEST.json @@ -433,36 +433,6 @@ "url": "/_mozilla/service-workers/service-worker/resource-timing.https.html" } ], - "service-workers/service-worker/resources/fetch-request-resources-iframe.https.html": [ - { - "path": "service-workers/service-worker/resources/fetch-request-resources-iframe.https.html", - "url": "/_mozilla/service-workers/service-worker/resources/fetch-request-resources-iframe.https.html" - } - ], - "service-workers/service-worker/resources/fetch-request-xhr-iframe.https.html": [ - { - "path": "service-workers/service-worker/resources/fetch-request-xhr-iframe.https.html", - "url": "/_mozilla/service-workers/service-worker/resources/fetch-request-xhr-iframe.https.html" - } - ], - "service-workers/service-worker/resources/fetch-response-xhr-iframe.https.html": [ - { - "path": "service-workers/service-worker/resources/fetch-response-xhr-iframe.https.html", - "url": "/_mozilla/service-workers/service-worker/resources/fetch-response-xhr-iframe.https.html" - } - ], - "service-workers/service-worker/resources/invalid-blobtype-iframe.https.html": [ - { - "path": "service-workers/service-worker/resources/invalid-blobtype-iframe.https.html", - "url": "/_mozilla/service-workers/service-worker/resources/invalid-blobtype-iframe.https.html" - } - ], - "service-workers/service-worker/resources/invalid-header-iframe.https.html": [ - { - "path": "service-workers/service-worker/resources/invalid-header-iframe.https.html", - "url": "/_mozilla/service-workers/service-worker/resources/invalid-header-iframe.https.html" - } - ], "service-workers/service-worker/service-worker-csp-connect.https.html": [ { "path": "service-workers/service-worker/service-worker-csp-connect.https.html", @@ -615,4 +585,4 @@ "rev": null, "url_base": "/_mozilla/", "version": 2 -} +} \ No newline at end of file diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-csp.https.html.ini b/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-csp.https.html.ini new file mode 100644 index 0000000000..d203be567b --- /dev/null +++ b/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-csp.https.html.ini @@ -0,0 +1,5 @@ +[fetch-csp.https.html] + type: testharness + [Verify CSP control of fetch() in a Service Worker] + expected: FAIL + diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-event-respond-with-stops-propagation.https.html.ini b/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-event-respond-with-stops-propagation.https.html.ini new file mode 100644 index 0000000000..df5d339beb --- /dev/null +++ b/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-event-respond-with-stops-propagation.https.html.ini @@ -0,0 +1,6 @@ +[fetch-event-respond-with-stops-propagation.https.html] + type: testharness + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1194881 + [respondWith() invokes stopImmediatePropagation()] + expected: FAIL + diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-event.https.html.ini b/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-event.https.html.ini index 62be44dea1..da57577afd 100644 --- a/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-event.https.html.ini +++ b/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-event.https.html.ini @@ -1,5 +1,6 @@ [fetch-event.https.html] type: testharness + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1194881 [Service Worker responds to fetch event with POST form] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-css-base-url.https.html.ini b/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-css-base-url.https.html.ini index b4d7bdf446..278dd4f659 100644 --- a/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-css-base-url.https.html.ini +++ b/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-css-base-url.https.html.ini @@ -1,6 +1,5 @@ [fetch-request-css-base-url.https.html] type: testharness - expected: TIMEOUT [CSS's base URL must be the request URL even when fetched from other URL.] - expected: TIMEOUT + expected: FAIL diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-css-images.https.html.ini b/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-css-images.https.html.ini new file mode 100644 index 0000000000..110a928078 --- /dev/null +++ b/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-css-images.https.html.ini @@ -0,0 +1,5 @@ +[fetch-request-css-images.https.html] + type: testharness + [Verify FetchEvent for css images.] + expected: FAIL + diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-resources.https.html.ini b/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-resources.https.html.ini deleted file mode 100644 index 162d1bf209..0000000000 --- a/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-resources.https.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[fetch-request-resources.https.html] - type: testharness - [Verify FetchEvent for resources.] - expected: FAIL - diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-xhr.https.html.ini b/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-xhr.https.html.ini deleted file mode 100644 index ea93b4494c..0000000000 --- a/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-xhr.https.html.ini +++ /dev/null @@ -1,10 +0,0 @@ -[fetch-request-xhr.https.html] - type: testharness - expected: - if debug and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): CRASH - if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): CRASH - if debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): CRASH - TIMEOUT - [Verify the body of FetchEvent using XMLHttpRequest] - expected: TIMEOUT - diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/invalid-blobtype.https.html.ini b/testing/web-platform/mozilla/meta/service-workers/service-worker/invalid-blobtype.https.html.ini new file mode 100644 index 0000000000..c950fe24cb --- /dev/null +++ b/testing/web-platform/mozilla/meta/service-workers/service-worker/invalid-blobtype.https.html.ini @@ -0,0 +1,5 @@ +[invalid-blobtype.https.html] + type: testharness + [Verify the response of FetchEvent using XMLHttpRequest] + expected: FAIL + diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/invalid-header.https.html.ini b/testing/web-platform/mozilla/meta/service-workers/service-worker/invalid-header.https.html.ini deleted file mode 100644 index 69a938735e..0000000000 --- a/testing/web-platform/mozilla/meta/service-workers/service-worker/invalid-header.https.html.ini +++ /dev/null @@ -1,10 +0,0 @@ -[invalid-header.https.html] - type: testharness - expected: - if debug and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): CRASH - if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): CRASH - if debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): CRASH - TIMEOUT - [Verify the response of FetchEvent using XMLHttpRequest] - expected: TIMEOUT - diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/referer.https.html.ini b/testing/web-platform/mozilla/meta/service-workers/service-worker/referer.https.html.ini index 234b86e6b0..a9cfabc1c9 100644 --- a/testing/web-platform/mozilla/meta/service-workers/service-worker/referer.https.html.ini +++ b/testing/web-platform/mozilla/meta/service-workers/service-worker/referer.https.html.ini @@ -1,10 +1,5 @@ [referer.https.html] type: testharness - expected: - if debug and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): CRASH - if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): CRASH - if debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): CRASH - TIMEOUT [Verify the referer] - expected: TIMEOUT + expected: FAIL diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/resources/fetch-request-resources-iframe.https.html.ini b/testing/web-platform/mozilla/meta/service-workers/service-worker/resources/fetch-request-resources-iframe.https.html.ini deleted file mode 100644 index 881566d885..0000000000 --- a/testing/web-platform/mozilla/meta/service-workers/service-worker/resources/fetch-request-resources-iframe.https.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[fetch-request-resources-iframe.https.html] - type: testharness - expected: TIMEOUT diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/resources/fetch-request-xhr-iframe.https.html.ini b/testing/web-platform/mozilla/meta/service-workers/service-worker/resources/fetch-request-xhr-iframe.https.html.ini deleted file mode 100644 index 7efc2f00c3..0000000000 --- a/testing/web-platform/mozilla/meta/service-workers/service-worker/resources/fetch-request-xhr-iframe.https.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[fetch-request-xhr-iframe.https.html] - type: testharness - expected: TIMEOUT diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/resources/fetch-response-xhr-iframe.https.html.ini b/testing/web-platform/mozilla/meta/service-workers/service-worker/resources/fetch-response-xhr-iframe.https.html.ini deleted file mode 100644 index e4ecfd5fd1..0000000000 --- a/testing/web-platform/mozilla/meta/service-workers/service-worker/resources/fetch-response-xhr-iframe.https.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[fetch-response-xhr-iframe.https.html] - type: testharness - expected: TIMEOUT diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/resources/invalid-blobtype-iframe.https.html.ini b/testing/web-platform/mozilla/meta/service-workers/service-worker/resources/invalid-blobtype-iframe.https.html.ini deleted file mode 100644 index 43bd110a56..0000000000 --- a/testing/web-platform/mozilla/meta/service-workers/service-worker/resources/invalid-blobtype-iframe.https.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[invalid-blobtype-iframe.https.html] - type: testharness - expected: TIMEOUT diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/resources/invalid-header-iframe.https.html.ini b/testing/web-platform/mozilla/meta/service-workers/service-worker/resources/invalid-header-iframe.https.html.ini deleted file mode 100644 index 2853b577e6..0000000000 --- a/testing/web-platform/mozilla/meta/service-workers/service-worker/resources/invalid-header-iframe.https.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[invalid-header-iframe.https.html] - type: testharness - expected: TIMEOUT diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-csp.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-csp.https.html index ed50b49f49..37833787ac 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-csp.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-csp.https.html @@ -22,7 +22,7 @@ async_test(function(t) { service_worker_unregister_and_done(t, SCOPE); }); frame.contentWindow.postMessage({}, - host_info['HTTP_ORIGIN'], + host_info['HTTPS_ORIGIN'], [channel.port2]); }); }) diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-css-base-url.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-css-base-url.https.html index 9af97c1b4e..056366ac32 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-css-base-url.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-css-base-url.https.html @@ -25,7 +25,7 @@ async_test(function(t) { return; } var result = msg.data; - var base = get_host_info()['HTTP_ORIGIN'] + base_path(); + var base = get_host_info()['HTTPS_ORIGIN'] + base_path(); assert_equals( result.url, base + 'resources/dummy.png', diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-css-images.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-css-images.https.html new file mode 100644 index 0000000000..2bfd9c4193 --- /dev/null +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-css-images.https.html @@ -0,0 +1,99 @@ + +Service Worker: FetchEvent for css image + + + + + diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-resources.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-resources.https.html index d2fc9c6dd9..6affec9761 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-resources.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-resources.https.html @@ -8,12 +8,11 @@ var url_count = 0; var expected_results = {}; -function image_test(frame, url, cross_origin, expected_context, - expexted_mode, expected_credentials) { +function image_test(frame, url, cross_origin, expexted_mode, + expected_credentials) { var actual_url = url + (++url_count); expected_results[actual_url] = { cross_origin: cross_origin, - context: expected_context, mode: expexted_mode, credentials: expected_credentials, message: 'Image load (url:' + @@ -22,12 +21,11 @@ function image_test(frame, url, cross_origin, expected_context, return frame.contentWindow.load_image(actual_url, cross_origin); } -function script_test(frame, url, cross_origin, expected_context, - expexted_mode, expected_credentials) { +function script_test(frame, url, cross_origin, expexted_mode, + expected_credentials) { var actual_url = url + (++url_count); expected_results[actual_url] = { cross_origin: cross_origin, - context: expected_context, mode: expexted_mode, credentials: expected_credentials, message: 'Script load (url:' + @@ -36,12 +34,11 @@ function script_test(frame, url, cross_origin, expected_context, return frame.contentWindow.load_script(actual_url, cross_origin); } -function css_test(frame, url, cross_origin, expected_context, - expexted_mode, expected_credentials) { +function css_test(frame, url, cross_origin, expexted_mode, + expected_credentials) { var actual_url = url + (++url_count); expected_results[actual_url] = { cross_origin: cross_origin, - context: expected_context, mode: expexted_mode, credentials: expected_credentials, message: 'CSS load (url:' + @@ -50,12 +47,10 @@ function css_test(frame, url, cross_origin, expected_context, return frame.contentWindow.load_css(actual_url, cross_origin); } -function font_face_test(frame, url, expected_context, expexted_mode, - expected_credentials) { +function font_face_test(frame, url, expexted_mode, expected_credentials) { var actual_url = url + (++url_count); expected_results[actual_url] = { url: actual_url, - context: expected_context, mode: expexted_mode, credentials: expected_credentials, message: 'FontFace load (url:' + actual_url + ')' @@ -63,40 +58,14 @@ function font_face_test(frame, url, expected_context, expexted_mode, return frame.contentWindow.load_font(actual_url); } -function css_image_test(frame, url, type, expected_context, - expexted_mode, expected_credentials) { - var actual_url = url + (++url_count); - expected_results[actual_url] = { - url: actual_url, - context: expected_context, - mode: expexted_mode, - credentials: expected_credentials, - message: 'CSSImage load (url:' + actual_url + ' type:' + type + ')' - }; - return frame.contentWindow.load_css_image(actual_url, type); -} - -function css_image_set_test(frame, url, type, expected_context, - expexted_mode, expected_credentials) { - var actual_url = url + (++url_count); - expected_results[actual_url] = { - url: actual_url, - context: expected_context, - mode: expexted_mode, - credentials: expected_credentials, - message: 'CSSImageSet load (url:' + actual_url + ' type:' + type + ')' - }; - return frame.contentWindow.load_css_image_set(actual_url, type); -} - async_test(function(t) { var SCOPE = 'resources/fetch-request-resources-iframe.https.html'; var SCRIPT = 'resources/fetch-request-resources-worker.js'; var host_info = get_host_info(); var LOCAL_URL = - host_info['HTTP_ORIGIN'] + base_path() + 'resources/dummy?test'; + host_info['HTTPS_ORIGIN'] + base_path() + 'resources/dummy?test'; var REMOTE_URL = - host_info['HTTP_REMOTE_ORIGIN'] + base_path() + 'resources/dummy?test'; + host_info['HTTPS_REMOTE_ORIGIN'] + base_path() + 'resources/dummy?test'; var worker; var frame; service_worker_unregister_and_register(t, SCRIPT, SCOPE) @@ -117,17 +86,6 @@ async_test(function(t) { if (!expected) { return; } - assert_equals( - result.context, expected.context, - 'context of ' + expected.message + ' must be ' + - expected.context + '.'); - assert_equals( - result.context_clone, expected.context, - 'context of clone() of' + expected.message + - ' must be ' + expected.context + '.'); - assert_equals( - result.context_new, '', - 'context of new Request() must be the empty string'); assert_equals( result.mode, expected.mode, 'mode of ' + expected.message + ' must be ' + @@ -150,53 +108,33 @@ async_test(function(t) { .then(function() { return with_iframe(SCOPE); }) .then(function(f) { frame = f; - image_test(f, LOCAL_URL, '', 'image', 'no-cors', 'same-origin'); - image_test(f, LOCAL_URL, 'anonymous', 'image', 'cors', 'omit'); - image_test(f, LOCAL_URL, 'use-credentials', 'image', 'cors', - 'include'); - image_test(f, REMOTE_URL, '', 'image', 'no-cors', 'same-origin'); - image_test(f, REMOTE_URL, 'anonymous', 'image', 'cors', 'omit'); - image_test(f, REMOTE_URL, 'use-credentials', 'image', 'cors', - 'include'); - script_test(f, LOCAL_URL, '', 'script', 'no-cors', 'same-origin'); - script_test(f, LOCAL_URL, 'anonymous', 'script', 'cors', 'omit'); - script_test(f, LOCAL_URL, 'use-credentials', 'script', 'cors', - 'include'); - script_test(f, REMOTE_URL, '', 'script', 'no-cors', 'same-origin'); - script_test(f, REMOTE_URL, 'anonymous', 'script', 'cors', 'omit'); - script_test(f, REMOTE_URL, 'use-credentials', 'script', 'cors', - 'include'); + // TODO: Disable 'no-cors' tests for image and stylesheet until + // AsyncOpen2 and cookie policy is supported. + // image_test(f, LOCAL_URL, '', 'no-cors', 'include'); + // image_test(f, REMOTE_URL, '', 'no-cors', 'include'); + // css_test(f, LOCAL_URL, '', 'no-cors', 'include'); + // css_test(f, REMOTE_URL, '', 'no-cors', 'include'); - css_test(f, LOCAL_URL, '', 'style', 'no-cors', 'same-origin'); - css_test(f, LOCAL_URL, 'anonymous', 'style', 'cors', 'omit'); - css_test(f, LOCAL_URL, 'use-credentials', 'style', 'cors', - 'include'); - css_test(f, REMOTE_URL, '', 'style', 'no-cors', 'same-origin'); - css_test(f, REMOTE_URL, 'anonymous', 'style', 'cors', 'omit'); - css_test(f, REMOTE_URL, 'use-credentials', 'style', 'cors', - 'include'); + image_test(f, LOCAL_URL, 'anonymous', 'cors', 'same-origin'); + image_test(f, LOCAL_URL, 'use-credentials', 'cors', 'include'); + image_test(f, REMOTE_URL, 'anonymous', 'cors', 'omit'); + image_test(f, REMOTE_URL, 'use-credentials', 'cors', 'include'); - font_face_test(f, LOCAL_URL, 'font', 'no-cors', 'same-origin'); - font_face_test(f, REMOTE_URL, 'font', 'cors', 'omit'); + script_test(f, LOCAL_URL, '', 'no-cors', 'include'); + script_test(f, LOCAL_URL, 'anonymous', 'cors', 'same-origin'); + script_test(f, LOCAL_URL, 'use-credentials', 'cors', 'include'); + script_test(f, REMOTE_URL, '', 'no-cors', 'include'); + script_test(f, REMOTE_URL, 'anonymous', 'cors', 'same-origin'); + script_test(f, REMOTE_URL, 'use-credentials', 'cors', 'include'); - css_image_test(f, LOCAL_URL, 'backgroundImage', 'image', - 'no-cors', 'same-origin'); - css_image_test(f, REMOTE_URL, 'backgroundImage', 'image', - 'no-cors', 'same-origin'); - css_image_test(f, LOCAL_URL, 'shapeOutside', 'image', 'cors', - 'omit'); - css_image_test(f, REMOTE_URL, 'shapeOutside', 'image', 'cors', - 'omit'); + css_test(f, LOCAL_URL, 'anonymous', 'cors', 'same-origin'); + css_test(f, LOCAL_URL, 'use-credentials', 'cors', 'include'); + css_test(f, REMOTE_URL, 'anonymous', 'cors', 'omit'); + css_test(f, REMOTE_URL, 'use-credentials', 'cors', 'include'); - css_image_set_test(f, LOCAL_URL, 'backgroundImage', 'image', - 'no-cors', 'same-origin'); - css_image_set_test(f, REMOTE_URL, 'backgroundImage', 'image', - 'no-cors', 'same-origin'); - css_image_set_test(f, LOCAL_URL, 'shapeOutside', 'image', 'cors', - 'omit'); - css_image_set_test(f, REMOTE_URL, 'shapeOutside', 'image', 'cors', - 'omit'); + font_face_test(f, LOCAL_URL, 'cors', 'same-origin'); + font_face_test(f, REMOTE_URL, 'cors', 'omit'); }) .catch(unreached_rejection(t)); diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-xhr.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-xhr.https.html index efbcd72f0d..1b80985a35 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-xhr.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-request-xhr.https.html @@ -17,12 +17,15 @@ async_test(function(t) { .then(function(frame) { var channel = new MessageChannel(); channel.port1.onmessage = t.step_func(function(e) { - assert_equals(e.data.results, 'finish'); - frame.remove(); - service_worker_unregister_and_done(t, SCOPE); + if (e.data.results === 'finish') { + frame.remove(); + service_worker_unregister_and_done(t, SCOPE); + } else if (e.data.results == 'equals') { + assert_equals(e.data.got, e.data.expected); + } }); frame.contentWindow.postMessage({}, - host_info['HTTP_ORIGIN'], + host_info['HTTPS_ORIGIN'], [channel.port2]); }) .catch(unreached_rejection(t)); diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-response-xhr.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-response-xhr.https.html index 6125b38c3b..1fd66bb2ac 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-response-xhr.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-response-xhr.https.html @@ -9,6 +9,13 @@ async_test(function(t) { var SCOPE = 'resources/fetch-response-xhr-iframe.https.html'; var SCRIPT = 'resources/fetch-response-xhr-worker.js'; var host_info = get_host_info(); + + window.addEventListener('message', t.step_func(on_message), false); + function on_message(e) { + assert_equals(e.data.results, 'foo, bar'); + t.done(); + } + service_worker_unregister_and_register(t, SCRIPT, SCOPE) .then(function(registration) { return wait_for_state(t, registration.installing, 'activated'); diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/invalid-blobtype.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/invalid-blobtype.https.html index 73bc3449b4..f1f2d1bdc6 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/invalid-blobtype.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/invalid-blobtype.https.html @@ -22,7 +22,7 @@ async_test(function(t) { service_worker_unregister_and_done(t, SCOPE); }); frame.contentWindow.postMessage({}, - host_info['HTTP_ORIGIN'], + host_info['HTTPS_ORIGIN'], [channel.port2]); }) .catch(unreached_rejection(t)); diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/invalid-header.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/invalid-header.https.html index 9d43b9a9db..7946952649 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/invalid-header.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/invalid-header.https.html @@ -22,7 +22,7 @@ async_test(function(t) { service_worker_unregister_and_done(t, SCOPE); }); frame.contentWindow.postMessage({}, - host_info['HTTP_ORIGIN'], + host_info['HTTPS_ORIGIN'], [channel.port2]); }) .catch(unreached_rejection(t)); diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/referer.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/referer.https.html index 19cfe6da77..9b3565329e 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/referer.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/referer.https.html @@ -22,7 +22,7 @@ async_test(function(t) { service_worker_unregister_and_done(t, SCOPE); }); frame.contentWindow.postMessage({}, - host_info['HTTP_ORIGIN'], + host_info['HTTPS_ORIGIN'], [channel.port2]); }) .catch(unreached_rejection(t)); diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/dummy-worker-interceptor.js b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/dummy-worker-interceptor.js index 7173e8ad5d..43244e1d99 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/dummy-worker-interceptor.js +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/dummy-worker-interceptor.js @@ -1,8 +1,21 @@ +importScripts('get-host-info.sub.js'); + var worker_text = 'postMessage("worker loading intercepted by service worker"); '; self.onfetch = function(event) { if (event.request.url.indexOf('synthesized') != -1) { event.respondWith(new Response(worker_text)); + } else if (event.request.url.indexOf('same-origin') != -1) { + event.respondWith(fetch('dummy-worker-script.py')); + } else if (event.request.url.indexOf('cors') != -1) { + var path = (new URL('dummy-worker-script.py', self.location)).pathname; + var url = get_host_info()['HTTPS_REMOTE_ORIGIN'] + path; + var mode = "no-cors"; + if (event.request.url.indexOf('no-cors') == -1) { + url += '?ACAOrigin=*'; + mode = "cors"; + } + event.respondWith(fetch(url, { mode: mode })); } }; diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/dummy-worker-script.py b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/dummy-worker-script.py new file mode 100644 index 0000000000..6f40b5ed60 --- /dev/null +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/dummy-worker-script.py @@ -0,0 +1,9 @@ +def main(request, response): + headers = [] + + if "ACAOrigin" in request.GET: + for item in request.GET["ACAOrigin"].split(","): + headers.append(("Access-Control-Allow-Origin", item)) + + return headers, "postMessage('dummy-worker-script loaded');" + diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-csp-iframe.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-csp-iframe.html index 1ca11c2566..c6b3e022ea 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-csp-iframe.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-csp-iframe.html @@ -8,7 +8,7 @@ var port = undefined; var meta = document.createElement('meta'); meta.setAttribute('http-equiv', 'Content-Security-Policy'); -meta.setAttribute('content', 'img-src ' + host_info['HTTP_ORIGIN'] + +meta.setAttribute('content', 'img-src ' + host_info['HTTPS_ORIGIN'] + '; script-src \'unsafe-inline\''); document.head.appendChild(meta); @@ -22,7 +22,7 @@ function test1() { results += 'FAIL(1)'; test2(); }; - img.src = host_info['HTTP_ORIGIN'] + image_path; + img.src = host_info['HTTPS_ORIGIN'] + image_path; } function test2() { @@ -35,7 +35,7 @@ function test2() { img.onerror = function() { test3(); }; - img.src = host_info['HTTP_REMOTE_ORIGIN'] + image_path; + img.src = host_info['HTTPS_REMOTE_ORIGIN'] + image_path; } function test3() { @@ -49,7 +49,7 @@ function test3() { test4(); }; img.src = './dummy?url=' + - encodeURIComponent(host_info['HTTP_ORIGIN'] + image_path); + encodeURIComponent(host_info['HTTPS_ORIGIN'] + image_path); } function test4() { @@ -63,7 +63,7 @@ function test4() { finish(); }; img.src = './dummy?mode=no-cors&url=' + - encodeURIComponent(host_info['HTTP_REMOTE_ORIGIN'] + image_path); + encodeURIComponent(host_info['HTTPS_REMOTE_ORIGIN'] + image_path); } function finish() { diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-css-base-url-worker.js b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-css-base-url-worker.js index 3218a2b3dd..0d9244ec75 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-css-base-url-worker.js +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-css-base-url-worker.js @@ -15,7 +15,7 @@ self.addEventListener('fetch', function(event) { var url = event.request.url; if (url.indexOf('fetch-request-css-base-url-style.css') != -1) { event.respondWith(fetch( - get_host_info()['HTTP_REMOTE_ORIGIN'] + base_path() + + get_host_info()['HTTPS_REMOTE_ORIGIN'] + base_path() + 'fetch-request-css-base-url-style.css', {mode: 'no-cors'})); } else if (url.indexOf('dummy.png') != -1) { diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-resources-iframe.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-resources-iframe.https.html index 57b80d1ffa..cadbff45ca 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-resources-iframe.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-request-resources-iframe.https.html @@ -1,4 +1,3 @@ - + + + + diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-response-xhr-iframe.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-response-xhr-iframe.https.html index d421ca3a2a..3391381e38 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-response-xhr-iframe.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-response-xhr-iframe.https.html @@ -1,6 +1,7 @@ - + + + diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/worker-load-interceptor.js b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/worker-load-interceptor.js new file mode 100644 index 0000000000..9d53f59239 --- /dev/null +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/worker-load-interceptor.js @@ -0,0 +1,14 @@ +importScripts('get-host-info.sub.js'); + +var response_text = "This load was successfully intercepted."; +var response_script = "postMessage(\"This load was successfully intercepted.\");"; + +self.onfetch = function(event) { + var url = event.request.url; + if (url.indexOf("synthesized-response.txt") != -1) { + event.respondWith(new Response(response_text)); + } else if (url.indexOf("synthesized-response.js") != -1) { + event.respondWith(new Response(response_script)); + } +}; + diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/worker-interception.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/worker-interception.https.html index f4199bba20..96fdf8e15c 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/worker-interception.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/worker-interception.https.html @@ -32,5 +32,123 @@ promise_test(function(t) { }); }, 'Verify worker script from uncontrolled document is intercepted by Service Worker'); +promise_test(function(t) { + var worker_url = 'resources/dummy-same-origin-worker.js'; + var service_worker = 'resources/dummy-worker-interceptor.js'; + var scope = worker_url; + + return service_worker_unregister_and_register(t, service_worker, scope) + .then(function(r) { + return wait_for_state(t, r.installing, 'activated'); + }) + .then(function() { + return new Promise(function(resolve, reject) { + var w = new Worker(worker_url); + w.onmessage = function(e) { + resolve(e.data); + } + + w.onerror = function(e) { + reject(e.message); + } + }); + }) + .then(function(data) { + assert_equals(data, 'dummy-worker-script loaded'); + service_worker_unregister_and_done(t, scope); + }); + }, 'Verify worker script intercepted by same-origin response succeeds'); + +promise_test(function(t) { + var worker_url = 'resources/dummy-cors-worker.js'; + var service_worker = 'resources/dummy-worker-interceptor.js'; + var scope = worker_url; + + return service_worker_unregister_and_register(t, service_worker, scope) + .then(function(r) { + return wait_for_state(t, r.installing, 'activated'); + }) + .then(function() { + return new Promise(function(resolve, reject) { + var w = new Worker(worker_url); + w.onmessage = function(e) { + resolve(e.data); + } + + w.onerror = function(e) { + reject(e.message); + } + }); + }) + .then(function(data) { + assert_equals(data, 'dummy-worker-script loaded'); + service_worker_unregister_and_done(t, scope); + }); + }, 'Verify worker script intercepted by cors response succeeds'); + +promise_test(function(t) { + var worker_url = 'resources/dummy-no-cors-worker.js'; + var service_worker = 'resources/dummy-worker-interceptor.js'; + var scope = worker_url; + + return service_worker_unregister_and_register(t, service_worker, scope) + .then(function(r) { + return wait_for_state(t, r.installing, 'activated'); + }) + .then(function() { + return new Promise(function(resolve, reject) { + var w = new Worker(worker_url); + w.onmessage = function(e) { + resolve(e.data); + } + + w.onerror = function(e) { + reject(e); + return true; + } + }); + }) + .then(function(data) { + assert_unreached('intercepted no-cors worker load should fail'); + service_worker_unregister_and_done(t, scope); + }) + .catch(function(e) { + assert_true(true, 'intercepted no-cors worker load should fail'); + service_worker_unregister_and_done(t, scope); + }); + }, 'Verify worker script intercepted by no-cors cross-origin response fails'); + +promise_test(function(t) { + var subdoc_url = 'resources/worker-interception-iframe.https.html?bypass'; + var service_worker = 'resources/worker-load-interceptor.js'; + var scope = 'resources/'; + + window.addEventListener('message', t.step_func(on_message), false); + function on_message(e) { + assert_equals(e.data.results, "This load was successfully intercepted."); + t.done(); + } + + return service_worker_unregister_and_register(t, service_worker, scope) + .then(function(r) { + return wait_for_state(t, r.installing, 'activated'); + }) + .then(function() { return with_iframe(subdoc_url); }) + .then(function(frame) { + return new Promise(function(resolve, reject) { + var channel = new MessageChannel(); + channel.port1.onmessage = function(e) { + resolve(e.data); + } + + frame.contentWindow.postMessage("GO", "*", [channel.port2]); + }); + }) + .then(function(data) { + assert_equals(data.results, 'finish'); + service_worker_unregister_and_done(t, scope); + }); + }, 'Verify worker loads from controlled document are intercepted by Service Worker'); + diff --git a/toolkit/components/osfile/NativeOSFileInternals.cpp b/toolkit/components/osfile/NativeOSFileInternals.cpp index 571c24c6be..4738f68fe8 100644 --- a/toolkit/components/osfile/NativeOSFileInternals.cpp +++ b/toolkit/components/osfile/NativeOSFileInternals.cpp @@ -529,8 +529,7 @@ public: // Last ditch attempt to release on the main thread - some of // the members of event are not thread-safe, so letting the // pointer go out of scope would cause a crash. - nsCOMPtr main = do_GetMainThread(); - NS_ProxyRelease(main, event); + NS_ReleaseOnMainThread(event.forget()); } } @@ -547,8 +546,7 @@ public: // Last ditch attempt to release on the main thread - some of // the members of event are not thread-safe, so letting the // pointer go out of scope would cause a crash. - nsCOMPtr main = do_GetMainThread(); - NS_ProxyRelease(main, event); + NS_ReleaseOnMainThread(event.forget()); } } @@ -749,8 +747,7 @@ public: if (!mResult) { return; } - nsCOMPtr main = do_GetMainThread(); - (void)NS_ProxyRelease(main, mResult); + NS_ReleaseOnMainThread(mResult.forget()); } protected: @@ -787,8 +784,7 @@ public: if (!mResult) { return; } - nsCOMPtr main = do_GetMainThread(); - (void)NS_ProxyRelease(main, mResult); + NS_ReleaseOnMainThread(mResult.forget()); } protected: diff --git a/toolkit/components/places/AsyncFaviconHelpers.cpp b/toolkit/components/places/AsyncFaviconHelpers.cpp index 1153c95c48..6e340bb598 100644 --- a/toolkit/components/places/AsyncFaviconHelpers.cpp +++ b/toolkit/components/places/AsyncFaviconHelpers.cpp @@ -372,10 +372,8 @@ AsyncFaviconHelperBase::AsyncFaviconHelperBase( AsyncFaviconHelperBase::~AsyncFaviconHelperBase() { - nsCOMPtr thread; - (void)NS_GetMainThread(getter_AddRefs(thread)); if (mCallback) { - (void)NS_ProxyRelease(thread, mCallback, true); + NS_ReleaseOnMainThread(mCallback.forget(), true); } } diff --git a/toolkit/components/places/Helpers.h b/toolkit/components/places/Helpers.h index 3d7ff6c40a..8bd3161ae6 100644 --- a/toolkit/components/places/Helpers.h +++ b/toolkit/components/places/Helpers.h @@ -196,7 +196,7 @@ public: { mStatementCache.FinalizeStatements(); // Release the owner back on the calling thread. - (void)NS_ProxyRelease(mCallingThread, mOwner); + NS_ProxyRelease(mCallingThread, mOwner.forget()); return NS_OK; } diff --git a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp index 47e9d4faf7..0220005e07 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp +++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp @@ -798,11 +798,8 @@ NS_IMPL_ISUPPORTS(nsUrlClassifierLookupCallback, nsUrlClassifierLookupCallback::~nsUrlClassifierLookupCallback() { - nsCOMPtr thread; - (void)NS_GetMainThread(getter_AddRefs(thread)); - if (mCallback) { - (void)NS_ProxyRelease(thread, mCallback, false); + NS_ReleaseOnMainThread(mCallback.forget()); } } diff --git a/toolkit/modules/tests/xpcshell/test_Log_stackTrace.js b/toolkit/modules/tests/xpcshell/test_Log_stackTrace.js new file mode 100644 index 0000000000..f6aac6decd --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_Log_stackTrace.js @@ -0,0 +1,30 @@ +print("Define some functions in well defined line positions for the test"); +function foo(v) { return bar(v + 1); } // line 2 +function bar(v) { return baz(v + 1); } // line 3 +function baz(v) { throw new Error(v + 1); } // line 4 + +print("Make sure lazy constructor calling/assignment works"); +Components.utils.import("resource://gre/modules/Log.jsm"); + +function run_test() { + print("Make sure functions, arguments, files are pretty printed in the trace"); + let trace = ""; + try { + foo(0); + } + catch(ex) { + trace = Log.stackTrace(ex); + } + print(`Got trace: ${trace}`); + do_check_neq(trace, ""); + + let bazPos = trace.indexOf("baz@test_Log_stackTrace.js:4"); + let barPos = trace.indexOf("bar@test_Log_stackTrace.js:3"); + let fooPos = trace.indexOf("foo@test_Log_stackTrace.js:2"); + print(`String positions: ${bazPos} ${barPos} ${fooPos}`); + + print("Make sure the desired messages show up"); + do_check_true(bazPos >= 0); + do_check_true(barPos > bazPos); + do_check_true(fooPos > barPos); +} diff --git a/toolkit/modules/tests/xpcshell/xpcshell.ini b/toolkit/modules/tests/xpcshell/xpcshell.ini index 6a104538db..05307b25a0 100644 --- a/toolkit/modules/tests/xpcshell/xpcshell.ini +++ b/toolkit/modules/tests/xpcshell/xpcshell.ini @@ -31,3 +31,5 @@ support-files = [test_web_channel.js] [test_web_channel_broker.js] [test_ZipUtils.js] +skip-if = toolkit == 'android' +[test_Log_stackTrace.js] diff --git a/widget/gonk/nsScreenManagerGonk.cpp b/widget/gonk/nsScreenManagerGonk.cpp index 73a54ce92e..458228c6ac 100644 --- a/widget/gonk/nsScreenManagerGonk.cpp +++ b/widget/gonk/nsScreenManagerGonk.cpp @@ -552,7 +552,7 @@ nsScreenGonk::UpdateMirroringWidget(already_AddRefed& aWindow) if (mMirroringWidget) { nsCOMPtr widget = mMirroringWidget.forget(); - NS_ReleaseOnMainThread(widget); + NS_ReleaseOnMainThread(widget.forget()); } mMirroringWidget = aWindow; } diff --git a/xpcom/base/nsConsoleService.cpp b/xpcom/base/nsConsoleService.cpp index 722f71a1ac..17ed1fa6ef 100644 --- a/xpcom/base/nsConsoleService.cpp +++ b/xpcom/base/nsConsoleService.cpp @@ -314,7 +314,7 @@ nsConsoleService::LogMessageWithMode(nsIConsoleMessage* aMessage, // Release |retiredMessage| on the main thread in case it is an instance of // a mainthread-only class like nsScriptErrorWithStack and we're off the // main thread. - NS_ReleaseOnMainThread(retiredMessage); + NS_ReleaseOnMainThread(retiredMessage.forget()); } if (r) { diff --git a/xpcom/base/nsInterfaceRequestorAgg.cpp b/xpcom/base/nsInterfaceRequestorAgg.cpp index 0f87a74c66..7e5cd83da8 100644 --- a/xpcom/base/nsInterfaceRequestorAgg.cpp +++ b/xpcom/base/nsInterfaceRequestorAgg.cpp @@ -54,16 +54,8 @@ nsInterfaceRequestorAgg::GetInterface(const nsIID& aIID, void** aResult) nsInterfaceRequestorAgg::~nsInterfaceRequestorAgg() { - nsIInterfaceRequestor* iir = nullptr; - mFirst.swap(iir); - if (iir) { - NS_ProxyRelease(mConsumerTarget, iir); - } - iir = nullptr; - mSecond.swap(iir); - if (iir) { - NS_ProxyRelease(mConsumerTarget, iir); - } + NS_ProxyRelease(mConsumerTarget, mFirst.forget()); + NS_ProxyRelease(mConsumerTarget, mSecond.forget()); } nsresult diff --git a/xpcom/glue/nsProxyRelease.cpp b/xpcom/glue/nsProxyRelease.cpp deleted file mode 100644 index c61bd2009f..0000000000 --- a/xpcom/glue/nsProxyRelease.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* -*- 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 "nsProxyRelease.h" -#include "nsThreadUtils.h" -#include "nsAutoPtr.h" - -class nsProxyReleaseEvent : public nsRunnable -{ -public: - explicit nsProxyReleaseEvent(nsISupports* aDoomed) : mDoomed(aDoomed) {} - - NS_IMETHOD Run() - { - mDoomed->Release(); - return NS_OK; - } - -private: - nsISupports* MOZ_OWNING_REF mDoomed; -}; - -nsresult -NS_ProxyRelease(nsIEventTarget* aTarget, nsISupports* aDoomed, - bool aAlwaysProxy) -{ - nsresult rv; - - if (!aDoomed) { - // nothing to do - return NS_OK; - } - - if (!aTarget) { - NS_RELEASE(aDoomed); - return NS_OK; - } - - if (!aAlwaysProxy) { - bool onCurrentThread = false; - rv = aTarget->IsOnCurrentThread(&onCurrentThread); - if (NS_SUCCEEDED(rv) && onCurrentThread) { - NS_RELEASE(aDoomed); - return NS_OK; - } - } - - nsCOMPtr ev = new nsProxyReleaseEvent(aDoomed); - if (!ev) { - // we do not release aDoomed here since it may cause a delete on the - // wrong thread. better to leak than crash. - return NS_ERROR_OUT_OF_MEMORY; - } - - rv = aTarget->Dispatch(ev, NS_DISPATCH_NORMAL); - if (NS_FAILED(rv)) { - NS_WARNING("failed to post proxy release event"); - // again, it is better to leak the aDoomed object than risk crashing as - // a result of deleting it on the wrong thread. - } - return rv; -} diff --git a/xpcom/glue/nsProxyRelease.h b/xpcom/glue/nsProxyRelease.h index f1461be677..dfc0fa9464 100644 --- a/xpcom/glue/nsProxyRelease.h +++ b/xpcom/glue/nsProxyRelease.h @@ -12,44 +12,34 @@ #include "nsCOMPtr.h" #include "nsAutoPtr.h" #include "MainThreadUtils.h" +#include "nsThreadUtils.h" #include "mozilla/Likely.h" +#include "mozilla/Move.h" #ifdef XPCOM_GLUE_AVOID_NSPR #error NS_ProxyRelease implementation depends on NSPR. #endif -/** - * Ensure that a nsCOMPtr is released on the target thread. - * - * @see NS_ProxyRelease(nsIEventTarget*, nsISupports*, bool) - */ + template -inline NS_HIDDEN_(nsresult) -NS_ProxyRelease(nsIEventTarget* aTarget, nsCOMPtr& aDoomed, - bool aAlwaysProxy = false) +class nsProxyReleaseEvent : public nsRunnable { - T* raw = nullptr; - aDoomed.swap(raw); - return NS_ProxyRelease(aTarget, raw, aAlwaysProxy); -} +public: + explicit nsProxyReleaseEvent(already_AddRefed aDoomed) + : mDoomed(aDoomed.take()) {} + + NS_IMETHOD Run() + { + NS_IF_RELEASE(mDoomed); + return NS_OK; + } + +private: + T* MOZ_OWNING_REF mDoomed; +}; /** - * Ensure that a nsRefPtr is released on the target thread. - * - * @see NS_ProxyRelease(nsIEventTarget*, nsISupports*, bool) - */ -template -inline NS_HIDDEN_(nsresult) -NS_ProxyRelease(nsIEventTarget* aTarget, RefPtr& aDoomed, - bool aAlwaysProxy = false) -{ - T* raw = nullptr; - aDoomed.swap(raw); - return NS_ProxyRelease(aTarget, raw, aAlwaysProxy); -} - -/** - * Ensures that the delete of a nsISupports object occurs on the target thread. + * Ensures that the delete of a smart pointer occurs on the target thread. * * @param aTarget * the target thread where the doomed object should be released. @@ -61,42 +51,39 @@ NS_ProxyRelease(nsIEventTarget* aTarget, RefPtr& aDoomed, * true, then an event will always be posted to the target thread for * asynchronous release. */ -nsresult -NS_ProxyRelease(nsIEventTarget* aTarget, nsISupports* aDoomed, - bool aAlwaysProxy = false); - -/** - * Ensure that a nsCOMPtr is released on the main thread. - * - * @see NS_ReleaseOnMainThread( nsISupports*, bool) - */ template -inline NS_HIDDEN_(nsresult) -NS_ReleaseOnMainThread(nsCOMPtr& aDoomed, - bool aAlwaysProxy = false) +inline NS_HIDDEN_(void) +NS_ProxyRelease(nsIEventTarget* aTarget, already_AddRefed aDoomed, + bool aAlwaysProxy = false) { - T* raw = nullptr; - aDoomed.swap(raw); - return NS_ReleaseOnMainThread(raw, aAlwaysProxy); + // Auto-managing release of the pointer. + RefPtr doomed = aDoomed; + nsresult rv; + + if (!doomed || !aTarget) { + return; + } + + if (!aAlwaysProxy) { + bool onCurrentThread = false; + rv = aTarget->IsOnCurrentThread(&onCurrentThread); + if (NS_SUCCEEDED(rv) && onCurrentThread) { + return; + } + } + + nsCOMPtr ev = new nsProxyReleaseEvent(doomed.forget()); + + rv = aTarget->Dispatch(ev, NS_DISPATCH_NORMAL); + if (NS_FAILED(rv)) { + NS_WARNING("failed to post proxy release event, leaking!"); + // It is better to leak the aDoomed object than risk crashing as + // a result of deleting it on the wrong thread. + } } /** - * Ensure that a nsRefPtr is released on the main thread. - * - * @see NS_ReleaseOnMainThread(nsISupports*, bool) - */ -template -inline NS_HIDDEN_(nsresult) -NS_ReleaseOnMainThread(RefPtr& aDoomed, - bool aAlwaysProxy = false) -{ - T* raw = nullptr; - aDoomed.swap(raw); - return NS_ReleaseOnMainThread(raw, aAlwaysProxy); -} - -/** - * Ensures that the delete of a nsISupports object occurs on the main thread. + * Ensures that the delete of a smart pointer occurs on the main thread. * * @param aDoomed * the doomed object; the object to be released on the main thread. @@ -106,8 +93,9 @@ NS_ReleaseOnMainThread(RefPtr& aDoomed, * parameter is true, then an event will always be posted to the main * thread for asynchronous release. */ -inline nsresult -NS_ReleaseOnMainThread(nsISupports* aDoomed, +template +inline NS_HIDDEN_(void) +NS_ReleaseOnMainThread(already_AddRefed aDoomed, bool aAlwaysProxy = false) { // NS_ProxyRelease treats a null event target as "the current thread". So a @@ -115,10 +103,15 @@ NS_ReleaseOnMainThread(nsISupports* aDoomed, // main thread or the release must happen asynchronously. nsCOMPtr mainThread; if (!NS_IsMainThread() || aAlwaysProxy) { - NS_GetMainThread(getter_AddRefs(mainThread)); + nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread)); + + if (NS_FAILED(rv)) { + NS_WARNING("Could not get main thread! Leaking."); + return; + } } - return NS_ProxyRelease(mainThread, aDoomed, aAlwaysProxy); + NS_ProxyRelease(mainThread, mozilla::Move(aDoomed), aAlwaysProxy); } /** @@ -185,13 +178,7 @@ private: if (NS_IsMainThread()) { NS_IF_RELEASE(mRawPtr); } else if (mRawPtr) { - nsCOMPtr mainThread; - NS_GetMainThread(getter_AddRefs(mainThread)); - if (!mainThread) { - NS_WARNING("Couldn't get main thread! Leaking pointer."); - return; - } - NS_ProxyRelease(mainThread, mRawPtr); + NS_ReleaseOnMainThread(dont_AddRef(mRawPtr)); } } diff --git a/xpcom/glue/objs.mozbuild b/xpcom/glue/objs.mozbuild index 9c211cc789..641ae8aa45 100644 --- a/xpcom/glue/objs.mozbuild +++ b/xpcom/glue/objs.mozbuild @@ -39,7 +39,6 @@ xpcom_glue_src_cppsrcs = [ xpcom_gluens_src_lcppsrcs = [ 'BlockingResourceBase.cpp', 'GenericFactory.cpp', - 'nsProxyRelease.cpp', 'nsTextFormatter.cpp', ] diff --git a/xpcom/libxpcomrt/moz.build b/xpcom/libxpcomrt/moz.build index cb58cc5a72..29b4322e5d 100644 --- a/xpcom/libxpcomrt/moz.build +++ b/xpcom/libxpcomrt/moz.build @@ -61,7 +61,6 @@ xpcom_glue_src = [ 'nsID.cpp', 'nsISupportsImpl.cpp', 'nsMemory.cpp', - 'nsProxyRelease.cpp', 'nsQuickSort.cpp', 'nsTArray.cpp', 'nsTObserverArray.cpp',