From c4388cb7dc45af02fec8622149f839d2c70a01a0 Mon Sep 17 00:00:00 2001 From: roytam1 Date: Thu, 29 Jun 2023 15:40:43 +0800 Subject: [PATCH] import changes from `dev' branch of rmottola/Arctic-Fox: - Bug 1229987: P2. Drain decoder when encountering gap. r=cpearce This allows for all buffered frames to be playable. (7ac0b2ddec) - Bug 1229987: P3. Update mochitests and add new one verifying behavior. r=cpearce (f4a231e9c8) - Bug 1229987: P4. Stop pre-rolling when encountering WAITING_FOR_DATA. r=cpearce (721df00361) - Bug 1205470: [MSE] Remove assertion. r=cpearce (857f4bfcd0) - fix comment (2e6e5f800c) - Bug 1226707: P2 Ensure we won't operate on a decoder that failed to initialize. r=cpearce (fa86fc1bb3) - Bug 1226707: P3. Only create the type of decoder we will need. r=cpearce (49a3d28e6c) - Bug 1229987: P5. Drop frames during internal seeking early. r=cpearce (9aeb23d3e6) - Bug 1197075: P4. Reject skip promise on cancellation or shutdown. r=edwin (b3d7af2cdd) - remove ifdef (3524eedb5a) - Bug 1229028 - remove #ifdef NS_BUILD_REFCNT_LOGGING methods from nsXULPrototypeNode; r=bz (5b87939fd6) - Bug 1214295 - Fix up entry points for ClickWithInputSource. r=bz (d7fda37d34) - Bug 1217307 - Remove some unnecessary null checks in rest of dom/xul/. r=njn (87486950c6) - Bug 1186811 (part 1) - Replace nsBaseHashtable::EnumerateRead() calls in dom/storage/ with iterators. r=baku. (0c35317169) - Bug 1186811 (part 2) - Replace nsBaseHashtable::EnumerateRead() calls in dom/storage/ with iterators. r=baku. (4a367a23e8) - Bug 1186811 (part 3) - Replace nsBaseHashtable::EnumerateRead() calls in dom/storage/ with iterators. r=baku. (e9861ab1ab) - Bug 1186811 (part 4) - Replace nsBaseHashtable::EnumerateRead() calls in dom/storage/ with iterators. r=baku. (34037caa14) - Bug 1189426 - Don't assert false in DOMStorageDBThread::InsertDBOp when we failed to open the database. r=smaug (b40612f549) - Bug 1192194 - Make DOMStorageDBThead::mDBReady atomic. r=smaug (ce34bfd1a6) - Bug 1208897 - Fix an initialization order bug in DOMStorageDBThread; r=baku (7efba6dea3) - Bug 1209349 - Audit the callers of the two-argument OriginAttributes. r=janv (5bdc041a87) - Bug 1189998, Part 1 - Consolidate Push client interfaces. r=mt,dragana (bd0eec93cb) - Bug 1222619 - about:serviceworkers should show the correct cache entries, r=bkelly (ee9b3f1740) - Bug 1189998, Part 2 - Migrate Push service callers. r=mt (392d6aa21d) - Bug 1230672 part 1 - Make '-moz-column-fill:auto' work also when 'overflow' isn't 'visible'. r=bz (c10d71c206) - Bug 1230672 part 2 - Add reftest with -moz-column-fill:auto and overflow:hidden. (8062b9e222) - Bug 1227162 - [css-grid][css-align] Make the scrolled pseudo frame inherit more CSS Align / Grid properties. r=dholbert (2f6e04f3f8) - Bug 1164783 - Removing trailing spaces. r=dbaron (980a19fb5d) - Bug 1230207 - Add support for display:grid/flex layout on
elements. r=bz,dholbert (9e1ba66f34) - bug 1230025 remove declaration of non-existant NS_NewThumbFrame r=dholbert (8b1f7cba54) - Bug 1230672 part 3 - Add support for multicol layout on
elements. r=bz (475f8b329f) - Bug 1232271 - initialize local scalar mSolidColor with default value - NS_RGBA(0, 0, 0, 0). r=matt.woodrow (7b8cd28617) - Bug 1144534 - If we have tiling, don't reduce layer resolution for large transforms. r=mattwoodrow (96785125bb) - Bug 1221842 - Don't reallocate DLBI geometry objects if nothing has changed. r=roc (69cbb41714) - Bug 1232635 - since presContext is always a valid pointer remove the useless null check that also confused Coverity. r=bz (8426c6a2ff) --- b2g/installer/package-manifest.in | 6 +- browser/base/content/sanitize.js | 10 +- browser/installer/package-manifest.in | 3 +- dom/interfaces/push/moz.build | 3 +- dom/interfaces/push/nsIPushClient.idl | 58 --- .../push/nsIPushNotificationService.idl | 77 --- .../push/nsIPushObserverNotification.idl | 4 +- dom/interfaces/push/nsIPushService.idl | 134 +++++ dom/media/MediaDecoder.cpp | 1 + dom/media/MediaDecoderStateMachine.cpp | 13 +- dom/media/MediaFormatReader.cpp | 304 ++++++----- dom/media/MediaFormatReader.h | 27 +- dom/media/mediasource/MediaSourceDemuxer.cpp | 10 +- dom/media/mediasource/MediaSourceDemuxer.h | 1 - dom/media/mediasource/test/mochitest.ini | 5 + .../mediasource/test/test_BufferingWait.html | 9 +- .../test/test_BufferingWait_mp4.html | 6 +- .../test/test_DrainOnMissingData_mp4.html | 60 +++ .../mediasource/test/test_PlayEvents.html | 15 +- .../test/test_WaitingOnMissingData_mp4.html | 5 +- .../test_WaitingToEndedTransition_mp4.html | 56 ++ dom/notification/Notification.cpp | 4 +- dom/push/Push.js | 55 +- dom/push/Push.manifest | 15 +- dom/push/PushClient.js | 167 ------ dom/push/PushComponents.js | 483 ++++++++++++++++++ dom/push/PushManager.cpp | 123 +++-- dom/push/PushNotificationService.js | 114 ----- dom/push/PushRecord.jsm | 6 +- dom/push/PushService.jsm | 170 ++---- dom/push/moz.build | 3 +- dom/storage/DOMStorageCache.cpp | 79 +-- dom/storage/DOMStorageDBThread.cpp | 88 ++-- dom/storage/DOMStorageDBThread.h | 9 +- dom/xul/nsXULElement.cpp | 16 +- dom/xul/nsXULElement.h | 27 +- dom/xul/templates/nsRDFBinding.cpp | 6 - .../templates/nsRDFConInstanceTestNode.cpp | 4 - dom/xul/templates/nsRDFConMemberTestNode.cpp | 12 - dom/xul/templates/nsRDFPropertyTestNode.cpp | 8 - dom/xul/templates/nsRDFQuery.cpp | 3 - dom/xul/templates/nsResourceSet.cpp | 3 - dom/xul/templates/nsRuleNetwork.cpp | 8 - dom/xul/templates/nsXULContentBuilder.cpp | 3 - dom/xul/templates/nsXULSortService.cpp | 3 - dom/xul/templates/nsXULTemplateBuilder.cpp | 17 - .../nsXULTemplateQueryProcessorRDF.cpp | 34 +- .../nsXULTemplateQueryProcessorStorage.cpp | 11 - dom/xul/templates/nsXULTreeBuilder.cpp | 3 - layout/base/FrameLayerBuilder.cpp | 41 +- layout/base/nsCSSFrameConstructor.cpp | 76 ++- .../reftests/columns/columnfill-auto-ref.html | 62 ++- layout/reftests/columns/columnfill-auto.html | 67 ++- layout/style/forms.css | 37 +- layout/style/nsRuleNode.cpp | 19 + layout/style/ua.css | 12 +- toolkit/content/aboutServiceWorkers.js | 27 +- toolkit/forgetaboutsite/ForgetAboutSite.jsm | 14 +- .../test/unit/test_removeDataFromDomain.js | 19 +- 59 files changed, 1507 insertions(+), 1148 deletions(-) delete mode 100644 dom/interfaces/push/nsIPushClient.idl delete mode 100644 dom/interfaces/push/nsIPushNotificationService.idl create mode 100644 dom/interfaces/push/nsIPushService.idl create mode 100644 dom/media/mediasource/test/test_DrainOnMissingData_mp4.html create mode 100644 dom/media/mediasource/test/test_WaitingToEndedTransition_mp4.html delete mode 100644 dom/push/PushClient.js create mode 100644 dom/push/PushComponents.js delete mode 100644 dom/push/PushNotificationService.js diff --git a/b2g/installer/package-manifest.in b/b2g/installer/package-manifest.in index c2c2e4628c..2225c801f4 100644 --- a/b2g/installer/package-manifest.in +++ b/b2g/installer/package-manifest.in @@ -665,9 +665,11 @@ @RESPATH@/components/AppsService.manifest @RESPATH@/components/Push.js @RESPATH@/components/Push.manifest -@RESPATH@/components/PushClient.js -@RESPATH@/components/PushNotificationService.js +#ifdef MOZ_SIMPLEPUSH +@RESPATH@/components/PushComponents.js +#else @RESPATH@/components/PushServiceLauncher.js +#endif @RESPATH@/components/InterAppComm.manifest @RESPATH@/components/InterAppCommService.js diff --git a/browser/base/content/sanitize.js b/browser/base/content/sanitize.js index 7bc0b8b7e6..048bfc6bd4 100644 --- a/browser/base/content/sanitize.js +++ b/browser/base/content/sanitize.js @@ -409,9 +409,13 @@ Sanitizer.prototype = { sss.clearAll(); // Clear all push notification subscriptions - var push = Cc["@mozilla.org/push/NotificationService;1"] - .getService(Ci.nsIPushNotificationService); - push.clearAll(); + var push = Cc["@mozilla.org/push/Service;1"] + .getService(Ci.nsIPushService); + push.clearForDomain("*", status => { + if (!Components.isSuccessCode(status)) { + dump("Error clearing Web Push data: " + status + "\n"); + } + }); }, get canClear() diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index e219d1e9ad..e44e79b13b 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -576,8 +576,7 @@ @RESPATH@/components/AlarmsManager.manifest @RESPATH@/components/Push.js @RESPATH@/components/Push.manifest -@RESPATH@/components/PushClient.js -@RESPATH@/components/PushNotificationService.js +@RESPATH@/components/PushComponents.js @RESPATH@/components/SlowScriptDebug.manifest @RESPATH@/components/SlowScriptDebug.js diff --git a/dom/interfaces/push/moz.build b/dom/interfaces/push/moz.build index 541fd1af53..370ef0cd3a 100644 --- a/dom/interfaces/push/moz.build +++ b/dom/interfaces/push/moz.build @@ -5,9 +5,8 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. XPIDL_SOURCES += [ - 'nsIPushClient.idl', - 'nsIPushNotificationService.idl', 'nsIPushObserverNotification.idl', + 'nsIPushService.idl', ] XPIDL_MODULE = 'dom_push' diff --git a/dom/interfaces/push/nsIPushClient.idl b/dom/interfaces/push/nsIPushClient.idl deleted file mode 100644 index 9575ebf6ae..0000000000 --- a/dom/interfaces/push/nsIPushClient.idl +++ /dev/null @@ -1,58 +0,0 @@ -/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsISupports.idl" - -interface nsIPrincipal; - -/** - * Satisfies contracts similar to the Push API specification. - * - * If status is not NS_OK, endpoint should be ignored. When subscribing to - * a new endpoint, endpoint will be a valid URL on success, when querying for - * the presence of an existing subscription, this will be an empty string if - * the calling {scope+principal} does not currently have an associated - * endpoint. - */ - -[scriptable, uuid(d83e398f-9920-4451-b23a-6d5a5ad2fa26)] -interface nsIPushEndpointCallback : nsISupports -{ - void onPushEndpoint(in nsresult status, - in DOMString endpoint, - in uint32_t keyLen, - [array, size_is(keyLen)] in octet key, - in uint32_t authSecretLen, - [array, size_is(authSecretLen)] in octet authSecret); -}; - -/** - * Satisfies contracts similar to the Push API specification. - * - * If status is not NS_OK, there was a problem unsubscribing and success should - * be ignored. success is true if unsubscribing was successful and false if - * there was no subscription. - */ -[scriptable, uuid(9522934d-e844-4f2f-81e8-48c3947b44de)] -interface nsIUnsubscribeResultCallback : nsISupports -{ - void onUnsubscribe(in nsresult status, in bool success); -}; - -/** - * Provides an XPIDL component to interact with the PushService from content - * processes. Unlike PushManager, this has no relationship to the DOM and is - * not exposed to web content. This was added to allow ServiceWorkers to use - * it by dispatching appropriate runnables to the main thread. - */ -[scriptable, uuid(6622d599-439e-4ad1-af32-c941bd2b9968)] -interface nsIPushClient : nsISupports -{ - void subscribe(in DOMString scope, in nsIPrincipal principal, in nsIPushEndpointCallback callback); - - void unsubscribe(in DOMString scope, in nsIPrincipal principal, in nsIUnsubscribeResultCallback callback); - - void getSubscription(in DOMString scope, in nsIPrincipal principal, in nsIPushEndpointCallback callback); -}; diff --git a/dom/interfaces/push/nsIPushNotificationService.idl b/dom/interfaces/push/nsIPushNotificationService.idl deleted file mode 100644 index 0ce0ecea4e..0000000000 --- a/dom/interfaces/push/nsIPushNotificationService.idl +++ /dev/null @@ -1,77 +0,0 @@ -/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsISupports.idl" - -/** - * A service for components to subscribe and receive push messages from web - * services. This functionality is exposed to content via the Push API, which - * uses service workers to notify applications. This interface exists to allow - * privileged code to receive messages without migrating to service workers. - */ -[scriptable, uuid(74586476-d73f-4867-bece-87c1dea35750)] -interface nsIPushNotificationService : nsISupports -{ - /** - * Creates a push subscription for the given |scope| URL and |pageURL|. - * Returns a promise for the new subscription record, or the existing - * record if this |scope| already has a subscription. - * - * The |pushEndpoint| property of the subscription record is a URL string - * that can be used to send push messages to subscribers. For details, - * please see the Simple Push protocol docs. - * - * Each incoming message fires a `push-notification` observer - * notification, with an `nsIPushObserverNotification` as the subject and - * the |scope| as the data. - * - * If the server drops a subscription, a `push-subscription-change` observer - * will be fired, with the subject set to `null` and the data set to |scope|. - * Servers may drop subscriptions at any time, so callers should recreate - * subscriptions if desired. - */ - jsval register(in string scope, in jsval originAttributes); - - /** - * Revokes a push subscription for the given |scope|. Returns a promise - * for the revoked subscription record, or `null` if the |scope| is not - * subscribed to receive notifications. - */ - jsval unregister(in string scope, in jsval originAttributes); - - /** - * Returns a promise for the subscription record associated with the - * given |scope|, or `null` if the |scope| does not have a subscription. - */ - jsval registration(in string scope, in jsval originAttributes); - - /** - * Clear all subscriptions. - */ - jsval clearAll(); - - /** - * Clear subscriptions for a domain. - */ - jsval clearForDomain(in string domain); -}; - -[scriptable, uuid(a2555e70-46f8-4b52-bf02-d978b979d143)] -interface nsIPushQuotaManager : nsISupports -{ - /** - * Informs the quota manager that a notification - * for the given origin has been shown. Used to - * determine if push quota should be relaxed. - */ - void notificationForOriginShown(in string origin); - - /** - * Informs the quota manager that a notification - * for the given origin has been closed. Used to - * determine if push quota should be relaxed. - */ - void notificationForOriginClosed(in string origin); -}; diff --git a/dom/interfaces/push/nsIPushObserverNotification.idl b/dom/interfaces/push/nsIPushObserverNotification.idl index 4876dcf910..169ae4233c 100644 --- a/dom/interfaces/push/nsIPushObserverNotification.idl +++ b/dom/interfaces/push/nsIPushObserverNotification.idl @@ -6,8 +6,8 @@ #include "nsISupports.idl" /** - * A push message received by an `nsIPushNotificationService`, used as the - * subject of a `push-notification` observer notification. + * A push message received by an `nsIPushService`, used as the subject of a + * `push-notification` observer notification. */ [scriptable, uuid(56f57607-28b6-44b0-aa56-3d4d3c88be15)] interface nsIPushObserverNotification : nsISupports diff --git a/dom/interfaces/push/nsIPushService.idl b/dom/interfaces/push/nsIPushService.idl new file mode 100644 index 0000000000..1345b99829 --- /dev/null +++ b/dom/interfaces/push/nsIPushService.idl @@ -0,0 +1,134 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsIPrincipal; + +/** + * A push subscription, passed as an argument to a subscription callback. + * Similar to the `PushSubscription` WebIDL interface. + */ +[scriptable, uuid(1de32d5c-ea88-4c9e-9626-b032bd87f415)] +interface nsIPushSubscription : nsISupports +{ + readonly attribute DOMString endpoint; + readonly attribute long long pushCount; + readonly attribute long long lastPush; + readonly attribute long quota; + + bool quotaApplies(); + bool isExpired(); + + void getKey(in DOMString name, + [optional] out uint32_t keyLen, + [array, size_is(keyLen), retval] out uint8_t key); +}; + +/** + * Called by methods that return a push subscription. A non-success + * |status| indicates that there was a problem returning the + * subscription, and the |subscription| argument should be ignored. Otherwise, + * |subscription| will point to a valid push subscription, or |null| if the + * subscription does not exist. + */ + [scriptable, uuid(1799c074-9d52-46b0-ab3c-c09790732f6f), function] + interface nsIPushSubscriptionCallback : nsISupports + { + void onPushSubscription(in nsresult status, + in nsIPushSubscription subscription); + }; + +/** + * Called by |unsubscribe|. A non-success |status| indicates that there was + * a problem unsubscribing, and the |success| argument should be ignored. + * Otherwise, |success| is true if unsubscribing was successful, and false if + * the subscription does not exist. + */ +[scriptable, uuid(d574118f-61a9-4270-b1f6-4461aa85c4f5), function] +interface nsIUnsubscribeResultCallback : nsISupports +{ + void onUnsubscribe(in nsresult status, in bool success); +}; + +/** + * Called by |clearForDomain|. A non-success |status| indicates that there was + * a problem clearing subscriptions for the given domain. + */ +[scriptable, uuid(bd47b38e-8bfa-4f92-834e-832a4431e05e), function] +interface nsIPushClearResultCallback : nsISupports +{ + void onClear(in nsresult status); +}; + +/** + * A service for components to subscribe and receive push messages from web + * services. This functionality is exposed to content via the Push DOM API, + * which uses service workers. This interface exists to support the DOM API, + * and allows privileged code to receive messages without migrating to service + * workers. + */ +[scriptable, uuid(678ef584-bf25-47aa-ac84-03efc0865b68)] +interface nsIPushService : nsISupports +{ + /** + * Creates a push subscription for the given |scope| URL and |principal|. + * If a subscription already exists for this |(scope, principal)| pair, + * the callback will receive the existing record as the second argument. + * + * The |endpoint| property of the subscription record is a URL string + * that can be used to send push messages to subscribers. + * + * Each incoming message fires a `push-notification` observer + * notification, with an `nsIPushObserverNotification` as the subject and + * the |scope| as the data. + * + * If the server drops a subscription, a `push-subscription-change` observer + * will be fired, with the subject set to `null` and the data set to |scope|. + * Servers may drop subscriptions at any time, so callers should recreate + * subscriptions if desired. + */ + void subscribe(in DOMString scope, in nsIPrincipal principal, + in nsIPushSubscriptionCallback callback); + + /** + * Removes a push subscription for the given |scope|. + */ + void unsubscribe(in DOMString scope, in nsIPrincipal principal, + in nsIUnsubscribeResultCallback callback); + + /** + * Retrieves the subscription record associated with the given + * |(scope, principal)| pair. If the subscription does not exist, the + * callback will receive |null| as the second argument. + */ + void getSubscription(in DOMString scope, in nsIPrincipal principal, + in nsIPushSubscriptionCallback callback); + + /** + * Drops every subscription for the given |domain|, or all domains if + * |domain| is "*". + */ + void clearForDomain(in DOMString domain, + in nsIPushClearResultCallback callback); +}; + +[scriptable, uuid(a2555e70-46f8-4b52-bf02-d978b979d143)] +interface nsIPushQuotaManager : nsISupports +{ + /** + * Informs the quota manager that a notification + * for the given origin has been shown. Used to + * determine if push quota should be relaxed. + */ + void notificationForOriginShown(in string origin); + + /** + * Informs the quota manager that a notification + * for the given origin has been closed. Used to + * determine if push quota should be relaxed. + */ + void notificationForOriginClosed(in string origin); +}; diff --git a/dom/media/MediaDecoder.cpp b/dom/media/MediaDecoder.cpp index a7a9b2f6a6..a7410385eb 100644 --- a/dom/media/MediaDecoder.cpp +++ b/dom/media/MediaDecoder.cpp @@ -787,6 +787,7 @@ MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType) UpdateDormantState(false /* aDormantTimeout */, true /* aActivity */); + MOZ_ASSERT(!mIsDormant, "should be out of dormant by now"); MOZ_ASSERT(aTime >= 0.0, "Cannot seek to a negative value."); int64_t timeUsecs = 0; diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp index 6d3c515be7..bde75c42b3 100644 --- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -783,6 +783,17 @@ MediaDecoderStateMachine::OnNotDecoded(MediaData::Type aType, self->WaitRequestRef(aRejection.mType).Complete(); })); + // We are out of data to decode and will enter buffering mode soon. + // We want to play the frames we have already decoded, so we stop pre-rolling + // and ensure that loadeddata is fired as required. + if (isAudio) { + StopPrerollingAudio(); + } else { + StopPrerollingVideo(); + } + if (mState == DECODER_STATE_BUFFERING || mState == DECODER_STATE_DECODING) { + MaybeFinishDecodeFirstFrame(); + } return; } @@ -1480,7 +1491,7 @@ MediaDecoderStateMachine::Seek(SeekTarget aTarget) return MediaDecoder::SeekPromise::CreateAndReject(/* aIgnored = */ true, __func__); } - NS_ASSERTION(mState > DECODER_STATE_DECODING_METADATA, + MOZ_ASSERT(mState > DECODER_STATE_DECODING_METADATA, "We should have got duration already"); if (mState < DECODER_STATE_DECODING || diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp index 085686d37b..aea7bcdb8e 100644 --- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -35,7 +35,6 @@ static mozilla::LazyLogModule sFormatDecoderLog("MediaFormatReader"); namespace mozilla { -#ifdef PR_LOGGING static const char* TrackTypeToStr(TrackInfo::TrackType aTrack) { @@ -53,7 +52,6 @@ TrackTypeToStr(TrackInfo::TrackType aTrack) return "Unknown"; } } -#endif MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder, MediaDataDemuxer* aDemuxer, @@ -347,10 +345,16 @@ MediaFormatReader::OnDemuxerInitFailed(DemuxerFailureReason aFailure) } bool -MediaFormatReader::EnsureDecodersCreated() +MediaFormatReader::EnsureDecoderCreated(TrackType aTrack) { MOZ_ASSERT(OnTaskQueue()); + auto& decoder = GetDecoderData(aTrack); + + if (decoder.mDecoder) { + return true; + } + if (!mPlatform) { mPlatform = new PDMFactory(); NS_ENSURE_TRUE(mPlatform, false); @@ -365,34 +369,34 @@ MediaFormatReader::EnsureDecodersCreated() } } - if (HasAudio() && !mAudio.mDecoder) { - mAudio.mDecoderInitialized = false; - mAudio.mDecoder = - mPlatform->CreateDecoder(mAudio.mInfo ? - *mAudio.mInfo->GetAsAudioInfo() : - mInfo.mAudio, - mAudio.mTaskQueue, - mAudio.mCallback); - NS_ENSURE_TRUE(mAudio.mDecoder != nullptr, false); - } + decoder.mDecoderInitialized = false; - if (HasVideo() && !mVideo.mDecoder) { - mVideo.mDecoderInitialized = false; - // Decoders use the layers backend to decide if they can use hardware decoding, - // so specify LAYERS_NONE if we want to forcibly disable it. - mVideo.mDecoder = - mPlatform->CreateDecoder(mVideo.mInfo ? - *mVideo.mInfo->GetAsVideoInfo() : - mInfo.mVideo, - mVideo.mTaskQueue, - mVideo.mCallback, - mHardwareAccelerationDisabled ? LayersBackend::LAYERS_NONE : - mLayersBackendType, - GetImageContainer()); - NS_ENSURE_TRUE(mVideo.mDecoder != nullptr, false); + switch (aTrack) { + case TrackType::kAudioTrack: + decoder.mDecoder = + mPlatform->CreateDecoder(decoder.mInfo ? + *decoder.mInfo->GetAsAudioInfo() : + mInfo.mAudio, + decoder.mTaskQueue, + decoder.mCallback); + break; + case TrackType::kVideoTrack: + // Decoders use the layers backend to decide if they can use hardware decoding, + // so specify LAYERS_NONE if we want to forcibly disable it. + decoder.mDecoder = + mPlatform->CreateDecoder(mVideo.mInfo ? + *mVideo.mInfo->GetAsVideoInfo() : + mInfo.mVideo, + decoder.mTaskQueue, + decoder.mCallback, + mHardwareAccelerationDisabled ? LayersBackend::LAYERS_NONE : + mLayersBackendType, + GetImageContainer()); + break; + default: + break; } - - return true; + return decoder.mDecoder != nullptr; } bool @@ -420,6 +424,8 @@ MediaFormatReader::EnsureDecoderInitialized(TrackType aTrack) [self, aTrack] (MediaDataDecoder::DecoderFailureReason aResult) { auto& decoder = self->GetDecoderData(aTrack); decoder.mInitPromise.Complete(); + decoder.mDecoder->Shutdown(); + decoder.mDecoder = nullptr; self->NotifyError(aTrack); })); return false; @@ -451,7 +457,7 @@ MediaFormatReader::DisableHardwareAcceleration() Flush(TrackInfo::kVideoTrack); mVideo.mDecoder->Shutdown(); mVideo.mDecoder = nullptr; - if (!EnsureDecodersCreated()) { + if (!EnsureDecoderCreated(TrackType::kVideoTrack)) { LOG("Unable to re-create decoder, aborting"); NotifyError(TrackInfo::kVideoTrack); return; @@ -540,6 +546,9 @@ MediaFormatReader::OnDemuxFailed(TrackType aTrack, DemuxerFailureReason aFailure NotifyError(aTrack); break; case DemuxerFailureReason::WAITING_FOR_DATA: + if (!decoder.mWaitingForData) { + decoder.mNeedDraining = true; + } NotifyWaitingForData(aTrack); break; case DemuxerFailureReason::CANCELED: @@ -685,6 +694,9 @@ MediaFormatReader::NotifyWaitingForData(TrackType aTrack) MOZ_ASSERT(OnTaskQueue()); auto& decoder = GetDecoderData(aTrack); decoder.mWaitingForData = true; + if (decoder.mTimeThreshold) { + decoder.mTimeThreshold.ref().mWaiting = true; + } ScheduleUpdate(aTrack); } @@ -754,18 +766,32 @@ MediaFormatReader::UpdateReceivedNewData(TrackType aTrack) if (!decoder.mReceivedNewData) { return false; } - decoder.mReceivedNewData = false; - decoder.mWaitingForData = false; - bool hasLastEnd; - media::TimeUnit lastEnd = decoder.mTimeRanges.GetEnd(&hasLastEnd); + // Update our cached TimeRange. decoder.mTimeRanges = decoder.mTrackDemuxer->GetBuffered(); - if (decoder.mTimeRanges.Length() && - (!hasLastEnd || decoder.mTimeRanges.GetEnd() < lastEnd)) { - // New data was added after our previous end, we can clear the EOS flag. - decoder.mDemuxEOS = false; + + if (decoder.mDrainComplete || decoder.mDraining) { + // We do not want to clear mWaitingForData or mDemuxEOS while + // a drain is in progress in order to properly complete the operation. + return false; } + bool hasLastEnd; + media::TimeUnit lastEnd = decoder.mTimeRanges.GetEnd(&hasLastEnd); + if (hasLastEnd) { + if (decoder.mLastTimeRangesEnd && decoder.mLastTimeRangesEnd.ref() > lastEnd) { + // New data was added after our previous end, we can clear the EOS flag. + decoder.mDemuxEOS = false; + } + decoder.mLastTimeRangesEnd = Some(lastEnd); + } + + decoder.mReceivedNewData = false; + if (decoder.mTimeThreshold) { + decoder.mTimeThreshold.ref().mWaiting = false; + } + decoder.mWaitingForData = false; + if (decoder.mError) { return false; } @@ -802,6 +828,8 @@ MediaFormatReader::RequestDemuxSamples(TrackType aTrack) if (decoder.mDemuxEOS) { // Nothing left to demux. + // We do not want to attempt to demux while in waiting for data mode + // as it would retrigger an unecessary drain. return; } @@ -837,7 +865,7 @@ MediaFormatReader::HandleDemuxedSamples(TrackType aTrack, return; } - if (!EnsureDecodersCreated()) { + if (!EnsureDecoderCreated(aTrack)) { NS_WARNING("Error constructing decoders"); NotifyError(aTrack); return; @@ -877,6 +905,7 @@ MediaFormatReader::HandleDemuxedSamples(TrackType aTrack, info->GetID()); decoder.mInfo = info; decoder.mLastStreamSourceID = info->GetID(); + decoder.mNextStreamSourceID.reset(); // Flush will clear our array of queued samples. So make a copy now. nsTArray> samples{decoder.mQueuedSamples}; Flush(aTrack); @@ -886,38 +915,11 @@ MediaFormatReader::HandleDemuxedSamples(TrackType aTrack, decoder.mQueuedSamples.AppendElements(Move(samples)); NotifyDecodingRequested(aTrack); } else { - MOZ_ASSERT(decoder.mTimeThreshold.isNothing()); + SeekTarget seekTarget = + decoder.mTimeThreshold.refOr(SeekTarget(TimeUnit::FromMicroseconds(sample->mTime), false)); LOG("Stream change occurred on a non-keyframe. Seeking to:%lld", - sample->mTime); - decoder.mTimeThreshold = Some(TimeUnit::FromMicroseconds(sample->mTime)); - RefPtr self = this; - decoder.ResetDemuxer(); - decoder.mSeekRequest.Begin(decoder.mTrackDemuxer->Seek(decoder.mTimeThreshold.ref()) - ->Then(OwnerThread(), __func__, - [self, aTrack] (media::TimeUnit aTime) { - auto& decoder = self->GetDecoderData(aTrack); - decoder.mSeekRequest.Complete(); - self->NotifyDecodingRequested(aTrack); - }, - [self, aTrack] (DemuxerFailureReason aResult) { - auto& decoder = self->GetDecoderData(aTrack); - decoder.mSeekRequest.Complete(); - switch (aResult) { - case DemuxerFailureReason::WAITING_FOR_DATA: - self->NotifyWaitingForData(aTrack); - break; - case DemuxerFailureReason::END_OF_STREAM: - self->NotifyEndOfStream(aTrack); - break; - case DemuxerFailureReason::CANCELED: - case DemuxerFailureReason::SHUTDOWN: - break; - default: - self->NotifyError(aTrack); - break; - } - decoder.mTimeThreshold.reset(); - })); + seekTarget.mTime.ToMicroseconds()); + InternalSeek(aTrack, seekTarget); } return; } @@ -951,6 +953,42 @@ MediaFormatReader::HandleDemuxedSamples(TrackType aTrack, decoder.mInputExhausted = false; } +void +MediaFormatReader::InternalSeek(TrackType aTrack, const SeekTarget& aTarget) +{ + MOZ_ASSERT(OnTaskQueue()); + auto& decoder = GetDecoderData(aTrack); + decoder.mTimeThreshold = Some(aTarget); + RefPtr self = this; + decoder.ResetDemuxer(); + decoder.mSeekRequest.Begin(decoder.mTrackDemuxer->Seek(decoder.mTimeThreshold.ref().mTime) + ->Then(OwnerThread(), __func__, + [self, aTrack] (media::TimeUnit aTime) { + auto& decoder = self->GetDecoderData(aTrack); + decoder.mSeekRequest.Complete(); + self->NotifyDecodingRequested(aTrack); + }, + [self, aTrack] (DemuxerFailureReason aResult) { + auto& decoder = self->GetDecoderData(aTrack); + decoder.mSeekRequest.Complete(); + switch (aResult) { + case DemuxerFailureReason::WAITING_FOR_DATA: + self->NotifyWaitingForData(aTrack); + break; + case DemuxerFailureReason::END_OF_STREAM: + self->NotifyEndOfStream(aTrack); + break; + case DemuxerFailureReason::CANCELED: + case DemuxerFailureReason::SHUTDOWN: + break; + default: + self->NotifyError(aTrack); + break; + } + decoder.mTimeThreshold.reset(); + })); +} + void MediaFormatReader::DrainDecoder(TrackType aTrack) { @@ -986,7 +1024,6 @@ MediaFormatReader::Update(TrackType aTrack) LOGV("Processing update for %s", TrackTypeToStr(aTrack)); - bool needInput = false; bool needOutput = false; auto& decoder = GetDecoderData(aTrack); decoder.mUpdateScheduled = false; @@ -1000,65 +1037,77 @@ MediaFormatReader::Update(TrackType aTrack) return; } - if (!decoder.HasPromise() && decoder.mWaitingForData) { - // Nothing more we can do at present. - LOGV("Still waiting for data."); - return; - } - // Record number of frames decoded and parsed. Automatically update the // stats counters using the AutoNotifyDecoded stack-based class. AbstractMediaDecoder::AutoNotifyDecoded a(mDecoder); - if (aTrack == TrackInfo::kVideoTrack) { - uint64_t delta = - decoder.mNumSamplesOutputTotal - mLastReportedNumDecodedFrames; - a.mDecoded = static_cast(delta); - mLastReportedNumDecodedFrames = decoder.mNumSamplesOutputTotal; + // Drop any frames found prior our internal seek target. + while (decoder.mTimeThreshold && decoder.mOutput.Length()) { + RefPtr& output = decoder.mOutput[0]; + SeekTarget target = decoder.mTimeThreshold.ref(); + media::TimeUnit time = media::TimeUnit::FromMicroseconds(output->mTime); + if (time >= target.mTime) { + // We have reached our internal seek target. + decoder.mTimeThreshold.reset(); + } + if (time < target.mTime || target.mDropTarget) { + LOGV("Internal Seeking: Dropping %s frame time:%f wanted:%f (kf:%d)", + TrackTypeToStr(aTrack), + media::TimeUnit::FromMicroseconds(output->mTime).ToSeconds(), + target.mTime.ToSeconds(), + output->mKeyframe); + decoder.mOutput.RemoveElementAt(0); + } } if (decoder.HasPromise()) { needOutput = true; - if (!decoder.mOutput.IsEmpty()) { + if (decoder.mOutput.Length()) { // We have a decoded sample ready to be returned. if (aTrack == TrackType::kVideoTrack) { + uint64_t delta = + decoder.mNumSamplesOutputTotal - mLastReportedNumDecodedFrames; + a.mDecoded = static_cast(delta); + mLastReportedNumDecodedFrames = decoder.mNumSamplesOutputTotal; nsCString error; mVideo.mIsHardwareAccelerated = mVideo.mDecoder && mVideo.mDecoder->IsHardwareAccelerated(error); } - while (decoder.mOutput.Length()) { - RefPtr output = decoder.mOutput[0]; - decoder.mOutput.RemoveElementAt(0); - decoder.mSizeOfQueue -= 1; - if (decoder.mTimeThreshold.isNothing() || - media::TimeUnit::FromMicroseconds(output->mTime) >= decoder.mTimeThreshold.ref()) { - ReturnOutput(output, aTrack); - decoder.mTimeThreshold.reset(); - break; - } else { - LOGV("Internal Seeking: Dropping frame time:%f wanted:%f (kf:%d)", - media::TimeUnit::FromMicroseconds(output->mTime).ToSeconds(), - decoder.mTimeThreshold.ref().ToSeconds(), - output->mKeyframe); - } - } - } else if (decoder.mDrainComplete) { - decoder.mDrainComplete = false; - decoder.mDraining = false; - if (decoder.mError) { - LOG("Decoding Error"); - decoder.RejectPromise(DECODE_ERROR, __func__); - return; - } else if (decoder.mDemuxEOS) { - decoder.RejectPromise(END_OF_STREAM, __func__); - } + RefPtr output = decoder.mOutput[0]; + decoder.mOutput.RemoveElementAt(0); + decoder.mSizeOfQueue -= 1; + decoder.mLastSampleTime = + Some(media::TimeUnit::FromMicroseconds(output->mTime)); + ReturnOutput(output, aTrack); } else if (decoder.mError) { + LOG("Rejecting %s promise: DECODE_ERROR", TrackTypeToStr(aTrack)); decoder.RejectPromise(DECODE_ERROR, __func__); return; - } else if (decoder.mWaitingForData) { - if (!decoder.mReceivedNewData) { - LOG("Waiting For Data"); + } else if (decoder.mDrainComplete) { + bool wasDraining = decoder.mDraining; + decoder.mDrainComplete = false; + decoder.mDraining = false; + if (decoder.mDemuxEOS) { + LOG("Rejecting %s promise: EOS", TrackTypeToStr(aTrack)); + decoder.RejectPromise(END_OF_STREAM, __func__); + } else if (decoder.mWaitingForData) { + if (wasDraining && decoder.mLastSampleTime && + !decoder.mNextStreamSourceID) { + // We have completed draining the decoder following WaitingForData. + // Set up the internal seek machinery to be able to resume from the + // last sample decoded. + LOG("Seeking to last sample time: %lld", + decoder.mLastSampleTime.ref().ToMicroseconds()); + InternalSeek(aTrack, SeekTarget(decoder.mLastSampleTime.ref(), true)); + } + LOG("Rejecting %s promise: WAITING_FOR_DATA", TrackTypeToStr(aTrack)); decoder.RejectPromise(WAITING_FOR_DATA, __func__); + } + // Now that draining has completed, we check if we have received + // new data again as the result may now be different from the earlier + // run. + if (UpdateReceivedNewData(aTrack)) { + LOGV("Nothing more to do"); return; } } @@ -1069,20 +1118,27 @@ MediaFormatReader::Update(TrackType aTrack) return; } - if (!NeedInput(decoder)) { + bool needInput = NeedInput(decoder); + + LOGV("Update(%s) ni=%d no=%d ie=%d, in:%llu out:%llu qs=%u pending:%u waiting:%d ahead:%d sid:%u", + TrackTypeToStr(aTrack), needInput, needOutput, decoder.mInputExhausted, + decoder.mNumSamplesInput, decoder.mNumSamplesOutput, + uint32_t(size_t(decoder.mSizeOfQueue)), uint32_t(decoder.mOutput.Length()), + decoder.mWaitingForData, !decoder.HasPromise(), decoder.mLastStreamSourceID); + + if (decoder.mWaitingForData && + (!decoder.mTimeThreshold || decoder.mTimeThreshold.ref().mWaiting)) { + // Nothing more we can do at present. + LOGV("Still waiting for data."); + return; + } + + if (!needInput) { LOGV("No need for additional input (pending:%u)", uint32_t(decoder.mOutput.Length())); return; } - needInput = true; - - LOGV("Update(%s) ni=%d no=%d ie=%d, in:%llu out:%llu qs=%u pending:%u ahead:%d sid:%u", - TrackTypeToStr(aTrack), needInput, needOutput, decoder.mInputExhausted, - decoder.mNumSamplesInput, decoder.mNumSamplesOutput, - uint32_t(size_t(decoder.mSizeOfQueue)), uint32_t(decoder.mOutput.Length()), - !decoder.HasPromise(), decoder.mLastStreamSourceID); - // Demux samples if we don't have some. RequestDemuxSamples(aTrack); @@ -1305,18 +1361,18 @@ MediaFormatReader::OnVideoSkipFailed(MediaTrackDemuxer::SkipFailureHolder aFailu switch (aFailure.mFailure) { case DemuxerFailureReason::END_OF_STREAM: NotifyEndOfStream(TrackType::kVideoTrack); - mVideo.RejectPromise(END_OF_STREAM, __func__); break; case DemuxerFailureReason::WAITING_FOR_DATA: NotifyWaitingForData(TrackType::kVideoTrack); - mVideo.RejectPromise(WAITING_FOR_DATA, __func__); break; case DemuxerFailureReason::CANCELED: case DemuxerFailureReason::SHUTDOWN: + if (mVideo.HasPromise()) { + mVideo.RejectPromise(CANCELED, __func__); + } break; default: NotifyError(TrackType::kVideoTrack); - mVideo.RejectPromise(DECODE_ERROR, __func__); break; } } diff --git a/dom/media/MediaFormatReader.h b/dom/media/MediaFormatReader.h index 98dccf05e2..5a49f8bafa 100644 --- a/dom/media/MediaFormatReader.h +++ b/dom/media/MediaFormatReader.h @@ -109,7 +109,7 @@ private: void NotifyDemuxer(); void ReturnOutput(MediaData* aData, TrackType aTrack); - bool EnsureDecodersCreated(); + bool EnsureDecoderCreated(TrackType aTrack); bool EnsureDecoderInitialized(TrackType aTrack); // Enqueues a task to call Update(aTrack) on the decoder task queue. @@ -127,6 +127,22 @@ private: // Decode any pending already demuxed samples. bool DecodeDemuxedSamples(TrackType aTrack, MediaRawData* aSample); + + struct SeekTarget { + SeekTarget(const media::TimeUnit& aTime, bool aDropTarget) + : mTime(aTime) + , mDropTarget(aDropTarget) + , mWaiting(false) + {} + + media::TimeUnit mTime; + bool mDropTarget; + bool mWaiting; + }; + // Perform an internal seek to aTime. If aDropTarget is true then + // the first sample past the target will be dropped. + void InternalSeek(TrackType aTrack, const SeekTarget& aTarget); + // Drain the current decoder. void DrainDecoder(TrackType aTrack); void NotifyNewOutput(TrackType aTrack, MediaData* aSample); @@ -261,8 +277,11 @@ private: bool mDraining; bool mDrainComplete; // If set, all decoded samples prior mTimeThreshold will be dropped. - // Used for internal seeking when a change of stream is detected. - Maybe mTimeThreshold; + // Used for internal seeking when a change of stream is detected or when + // encountering data discontinuity. + Maybe mTimeThreshold; + // Time of last sample returned. + Maybe mLastSampleTime; // Decoded samples returned my mDecoder awaiting being returned to // state machine upon request. @@ -300,6 +319,7 @@ private: mDraining = false; mDrainComplete = false; mTimeThreshold.reset(); + mLastSampleTime.reset(); mOutput.Clear(); mNumSamplesInput = 0; mNumSamplesOutput = 0; @@ -316,6 +336,7 @@ private: uint32_t mLastStreamSourceID; Maybe mNextStreamSourceID; media::TimeIntervals mTimeRanges; + Maybe mLastTimeRangesEnd; RefPtr mInfo; }; diff --git a/dom/media/mediasource/MediaSourceDemuxer.cpp b/dom/media/mediasource/MediaSourceDemuxer.cpp index 8833819eb1..348198d73f 100644 --- a/dom/media/mediasource/MediaSourceDemuxer.cpp +++ b/dom/media/mediasource/MediaSourceDemuxer.cpp @@ -22,14 +22,13 @@ using media::TimeIntervals; MediaSourceDemuxer::MediaSourceDemuxer() : mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK), /* aSupportsTailDispatch = */ false)) - , mInitDone(false) , mMonitor("MediaSourceDemuxer") { MOZ_ASSERT(NS_IsMainThread()); } -// Gap allowed between frames. Due to inaccuracies in determining buffer end -// frames (see Mozilla bug 1065207). This value is based on the end of frame +// Due to inaccuracies in determining buffer end +// frames (Bug 1065207). This value is based on the end of frame // default value used in Blink, kDefaultBufferDurationInMs. const TimeUnit MediaSourceDemuxer::EOS_FUZZ = media::TimeUnit::FromMicroseconds(125000); @@ -46,7 +45,6 @@ MediaSourceDemuxer::AttemptInit() MOZ_ASSERT(OnTaskQueue()); if (ScanSourceBuffersForContent()) { - mInitDone = true; return InitPromise::CreateAndResolve(NS_OK, __func__); } @@ -78,12 +76,10 @@ void MediaSourceDemuxer::NotifyDataArrived() RefPtr self = this; nsCOMPtr task = NS_NewRunnableFunction([self] () { - if (self->mInitDone) { + if (self->mInitPromise.IsEmpty()) { return; } - MOZ_ASSERT(!self->mInitPromise.IsEmpty()); if (self->ScanSourceBuffersForContent()) { - self->mInitDone = true; self->mInitPromise.ResolveIfExists(NS_OK, __func__); } }); diff --git a/dom/media/mediasource/MediaSourceDemuxer.h b/dom/media/mediasource/MediaSourceDemuxer.h index 033d8af32d..8be870cbf6 100644 --- a/dom/media/mediasource/MediaSourceDemuxer.h +++ b/dom/media/mediasource/MediaSourceDemuxer.h @@ -79,7 +79,6 @@ private: nsTArray> mSourceBuffers; MozPromiseHolder mInitPromise; - bool mInitDone; // Monitor to protect members below across multiple threads. mutable Monitor mMonitor; diff --git a/dom/media/mediasource/test/mochitest.ini b/dom/media/mediasource/test/mochitest.ini index 9fed5e0047..bcd7501340 100644 --- a/dom/media/mediasource/test/mochitest.ini +++ b/dom/media/mediasource/test/mochitest.ini @@ -28,6 +28,8 @@ skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) skip-if = true # bug 1182946 [test_BufferingWait_mp4.html] skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ +[test_DrainOnMissingData_mp4.html] +skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ [test_EndOfStream.html] skip-if = (true || toolkit == 'android' || buildapp == 'mulet') #timeout android/mulet only bug 1101187 and bug 1182946 [test_EndOfStream_mp4.html] @@ -98,3 +100,6 @@ skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) skip-if = true # Disabled due to bug 1124493 and friends. WebM MSE is deprioritized. [test_WaitingOnMissingData_mp4.html] skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ +[test_WaitingToEndedTransition_mp4.html] +skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ + diff --git a/dom/media/mediasource/test/test_BufferingWait.html b/dom/media/mediasource/test/test_BufferingWait.html index b428319a5d..972e25945c 100644 --- a/dom/media/mediasource/test/test_BufferingWait.html +++ b/dom/media/mediasource/test/test_BufferingWait.html @@ -41,18 +41,15 @@ runWithMSE(function(ms, v) { /* Note - Missing |46712, 67833 - 46712| segment here corresponding to (0.8, 1.2] */ /* Note - Missing |67833, 88966 - 67833| segment here corresponding to (1.2, 1.6] */ loadSegment.bind(null, sb, new Uint8Array(arrayBuffer, 88966))).then(function() { - // Some decoders (Windows in particular) may keep up to 25 frames queued - // before returning a sample. 0.7 is 1.62s - 25 * 0.03333 - var promise = waitUntilTime(0.7); + // 0.767 is the time of the last video sample +- 40ms. + var promise = waitUntilTime(.767-0.04); info("Playing video. It should play for a bit, then fire 'waiting'"); v.play(); return promise; }).then(function() { window.firstStop = Date.now(); loadSegment(sb, new Uint8Array(arrayBuffer, 46712, 67833 - 46712)); - // Some decoders (Windows in particular) may keep up to 25 frames queued - // before returning a sample. 1.5 is 2.41s - 25 * 0.03333 - return waitUntilTime(1.5); + return waitUntilTime(1.167-0.04); }).then(function() { var waitDuration = (Date.now() - window.firstStop) / 1000; ok(waitDuration < 15, "Should not spend an inordinate amount of time buffering: " + waitDuration); diff --git a/dom/media/mediasource/test/test_BufferingWait_mp4.html b/dom/media/mediasource/test/test_BufferingWait_mp4.html index 361fa9d365..db04f4e0b1 100644 --- a/dom/media/mediasource/test/test_BufferingWait_mp4.html +++ b/dom/media/mediasource/test/test_BufferingWait_mp4.html @@ -41,14 +41,16 @@ runWithMSE(function(ms, v) { /* Note - Missing |bipbop4| segment here corresponding to (2.41, 3.20] */ .then(fetchAndLoad.bind(null, sb, 'bipbop/bipbop', ['5'], '.m4s')) .then(function() { - var promise = waitUntilTime(1.4); + // last audio sample has a start time of 1.578956s + var promise = waitUntilTime(1.57895); info("Playing video. It should play for a bit, then fire 'waiting'"); v.play(); return promise; }).then(function() { window.firstStop = Date.now(); fetchAndLoad(sb, 'bipbop/bipbop', ['3'], '.m4s'); - return waitUntilTime(2.2); + // last audio sample has a start time of 2.368435 + return waitUntilTime(2.36843); }).then(function() { var waitDuration = (Date.now() - window.firstStop) / 1000; ok(waitDuration < 15, "Should not spend an inordinate amount of time buffering: " + waitDuration); diff --git a/dom/media/mediasource/test/test_DrainOnMissingData_mp4.html b/dom/media/mediasource/test/test_DrainOnMissingData_mp4.html new file mode 100644 index 0000000000..f2284377f6 --- /dev/null +++ b/dom/media/mediasource/test/test_DrainOnMissingData_mp4.html @@ -0,0 +1,60 @@ + + + + MSE: |waiting| event when source data is missing + + + + + +

+
+ + diff --git a/dom/media/mediasource/test/test_PlayEvents.html b/dom/media/mediasource/test/test_PlayEvents.html index 1f6f1ad11a..a390ae247b 100644 --- a/dom/media/mediasource/test/test_PlayEvents.html +++ b/dom/media/mediasource/test/test_PlayEvents.html @@ -24,6 +24,18 @@ SimpleTest.waitForExplicitFinish(); runWithMSE(function(ms, el) { el.controls = true; once(ms, 'sourceopen').then(function() { + // Log events for debugging. + var events = ["suspend", "play", "canplay", "canplaythrough", "loadstart", "loadedmetadata", + "loadeddata", "playing", "ended", "error", "stalled", "emptied", "abort", + "waiting", "pause", "durationchange", "seeking", "seeked"]; + function logEvent(e) { + var v = e.target; + info("got " + e.type + " event"); + } + events.forEach(function(e) { + el.addEventListener(e, logEvent, false); + }); + ok(true, "Receive a sourceopen event"); var videosb = ms.addSourceBuffer("video/mp4"); el.addEventListener("error", function(e) { @@ -37,9 +49,6 @@ runWithMSE(function(ms, el) { var promises = []; promises.push(once(el, 'loadeddata')); promises.push(once(el, 'canplay')); - // Load [0, 1.601666). We must ensure that we load over 25 frames as the - // windows H264 decoder will not produce a sample until then - // (bug 1191138). promises.push(fetchAndLoad(videosb, 'bipbop/bipbop_video', range(1, 3), '.m4s')); return Promise.all(promises); }) diff --git a/dom/media/mediasource/test/test_WaitingOnMissingData_mp4.html b/dom/media/mediasource/test/test_WaitingOnMissingData_mp4.html index ae6c9f904d..79344c772b 100644 --- a/dom/media/mediasource/test/test_WaitingOnMissingData_mp4.html +++ b/dom/media/mediasource/test/test_WaitingOnMissingData_mp4.html @@ -40,8 +40,9 @@ runWithMSE(function(ms, el) { // currentTime is based on the current video frame, so if the audio ends just before // the next video frame, currentTime can be up to 1 frame's worth earlier than // min(audioEnd, videoEnd). - isfuzzy(el.currentTime, Math.min(audiosb.buffered.end(0), videosb.buffered.end(0)) - 1/60, - 1/30, "Got a waiting event at " + el.currentTime); + // 0.0465 is the length of the last audio frame. + ok(el.currentTime >= (Math.min(audiosb.buffered.end(0), videosb.buffered.end(0)) - 0.0465), + "Got a waiting event at " + el.currentTime); info("Loading more data"); var p = once(el, 'ended'); var loads = Promise.all([fetchAndLoad(audiosb, 'bipbop/bipbop_audio', [5], '.m4s'), diff --git a/dom/media/mediasource/test/test_WaitingToEndedTransition_mp4.html b/dom/media/mediasource/test/test_WaitingToEndedTransition_mp4.html new file mode 100644 index 0000000000..f932b3d4e7 --- /dev/null +++ b/dom/media/mediasource/test/test_WaitingToEndedTransition_mp4.html @@ -0,0 +1,56 @@ + + + + MSE: |waiting| event when source data is missing + + + + + +

+
+ + diff --git a/dom/notification/Notification.cpp b/dom/notification/Notification.cpp index a49d04ceb9..76e67cb99c 100644 --- a/dom/notification/Notification.cpp +++ b/dom/notification/Notification.cpp @@ -52,7 +52,7 @@ #endif #ifndef MOZ_SIMPLEPUSH -#include "nsIPushNotificationService.h" +#include "nsIPushService.h" #endif namespace mozilla { @@ -1194,7 +1194,7 @@ NotificationObserver::AdjustPushQuota(const char* aTopic) return NS_ERROR_NOT_IMPLEMENTED; #else nsCOMPtr pushQuotaManager = - do_GetService("@mozilla.org/push/NotificationService;1"); + do_GetService("@mozilla.org/push/Service;1"); if (!pushQuotaManager) { return NS_ERROR_FAILURE; } diff --git a/dom/push/Push.js b/dom/push/Push.js index f6098d4725..0c148d96d1 100644 --- a/dom/push/Push.js +++ b/dom/push/Push.js @@ -21,6 +21,9 @@ XPCOMUtils.defineLazyGetter(this, "console", () => { }); }); +XPCOMUtils.defineLazyServiceGetter(this, "PushService", + "@mozilla.org/push/Service;1", "nsIPushService"); + const PUSH_CID = Components.ID("{cde1d019-fad8-4044-b141-65fb4fb7a245}"); /** @@ -51,8 +54,6 @@ Push.prototype = { this.initDOMRequestHelper(aWindow); this._principal = aWindow.document.nodePrincipal; - - this._client = Cc["@mozilla.org/push/PushClient;1"].createInstance(Ci.nsIPushClient); }, setScope: function(scope){ @@ -96,8 +97,8 @@ Push.prototype = { histogram.add(true); return this.askPermission().then(() => this.createPromise((resolve, reject) => { - let callback = new PushEndpointCallback(this, resolve, reject); - this._client.subscribe(this._scope, this._principal, callback); + let callback = new PushSubscriptionCallback(this, resolve, reject); + PushService.subscribe(this._scope, this._principal, callback); }) ); }, @@ -106,8 +107,8 @@ Push.prototype = { console.debug("getSubscription()", this._scope); return this.createPromise((resolve, reject) => { - let callback = new PushEndpointCallback(this, resolve, reject); - this._client.getSubscription(this._scope, this._principal, callback); + let callback = new PushSubscriptionCallback(this, resolve, reject); + PushService.getSubscription(this._scope, this._principal, callback); }); }, @@ -172,16 +173,16 @@ Push.prototype = { }, }; -function PushEndpointCallback(pushManager, resolve, reject) { +function PushSubscriptionCallback(pushManager, resolve, reject) { this.pushManager = pushManager; this.resolve = resolve; this.reject = reject; } -PushEndpointCallback.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPushEndpointCallback]), - onPushEndpoint: function(ok, endpoint, keyLen, key, - authSecretLen, authSecretIn) { +PushSubscriptionCallback.prototype = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPushSubscriptionCallback]), + + onPushSubscription: function(ok, subscription) { let {pushManager} = this; if (!Components.isSuccessCode(ok)) { this.reject(new pushManager._window.DOMException( @@ -191,32 +192,32 @@ PushEndpointCallback.prototype = { return; } - if (!endpoint) { + if (!subscription) { this.resolve(null); return; } - let publicKey = null; - if (keyLen) { - publicKey = new ArrayBuffer(keyLen); - let keyView = new Uint8Array(publicKey); - keyView.set(key); - } - - let authSecret = null; - if (authSecretLen) { - authSecret = new ArrayBuffer(authSecretLen); - let secretView = new Uint8Array(authSecret); - secretView.set(authSecretIn); - } - - let sub = new pushManager._window.PushSubscription(endpoint, + let publicKey = this._getKey(subscription, "p256dh"); + let authSecret = this._getKey(subscription, "auth"); + let sub = new pushManager._window.PushSubscription(subscription.endpoint, pushManager._scope, publicKey, authSecret); sub.setPrincipal(pushManager._principal); this.resolve(sub); }, + + _getKey: function(subscription, name) { + let outKeyLen = {}; + let rawKey = subscription.getKey(name, outKeyLen); + if (!outKeyLen.value) { + return null; + } + let key = new ArrayBuffer(outKeyLen.value); + let keyView = new Uint8Array(key); + keyView.set(rawKey); + return key; + }, }; this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Push]); diff --git a/dom/push/Push.manifest b/dom/push/Push.manifest index 49ba27bf12..93c4879248 100644 --- a/dom/push/Push.manifest +++ b/dom/push/Push.manifest @@ -2,13 +2,10 @@ component {cde1d019-fad8-4044-b141-65fb4fb7a245} Push.js contract @mozilla.org/push/PushManager;1 {cde1d019-fad8-4044-b141-65fb4fb7a245} -# XPCOM component; initializes the PushService on startup. -component {32028e38-903b-4a64-a180-5857eb4cb3dd} PushNotificationService.js -contract @mozilla.org/push/NotificationService;1 {32028e38-903b-4a64-a180-5857eb4cb3dd} -category app-startup PushNotificationService @mozilla.org/push/NotificationService;1 +# XPCOM components. +component {daaa8d73-677e-4233-8acd-2c404bd01658} PushComponents.js +contract @mozilla.org/push/Service;1 {daaa8d73-677e-4233-8acd-2c404bd01658} +category app-startup PushServiceParent @mozilla.org/push/Service;1 -component {66a87970-6dc9-46e0-ac61-adb4a13791de} PushNotificationService.js -contract @mozilla.org/push/ObserverNotification;1 {66a87970-6dc9-46e0-ac61-adb4a13791de} - -component {16042199-bec0-484a-9640-25ecc0c0a149} PushClient.js -contract @mozilla.org/push/PushClient;1 {16042199-bec0-484a-9640-25ecc0c0a149} +component {e68997fd-8b92-49ee-af12-800830b023e8} PushComponents.js +contract @mozilla.org/push/ObserverNotification;1 {e68997fd-8b92-49ee-af12-800830b023e8} diff --git a/dom/push/PushClient.js b/dom/push/PushClient.js deleted file mode 100644 index 30bcb63d9a..0000000000 --- a/dom/push/PushClient.js +++ /dev/null @@ -1,167 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cu = Components.utils; -const Cr = Components.results; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -XPCOMUtils.defineLazyGetter(this, "console", () => { - let {ConsoleAPI} = Cu.import("resource://gre/modules/Console.jsm", {}); - return new ConsoleAPI({ - maxLogLevelPref: "dom.push.loglevel", - prefix: "PushClient", - }); -}); - -const kMessages = [ - "PushService:Register:OK", - "PushService:Register:KO", - "PushService:Registration:OK", - "PushService:Registration:KO", - "PushService:Unregister:OK", - "PushService:Unregister:KO", -]; - -this.PushClient = function PushClient() { - console.debug("PushClient()"); - this._cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"] - .getService(Ci.nsISyncMessageSender); - this._requests = {}; - this.addListeners(); -}; - -PushClient.prototype = { - classID: Components.ID("{16042199-bec0-484a-9640-25ecc0c0a149}"), - - contractID: "@mozilla.org/push/PushClient;1", - - QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference, - Ci.nsIPushClient, - Ci.nsIMessageListener,]), - - - _getRandomId: function() { - return Cc["@mozilla.org/uuid-generator;1"] - .getService(Ci.nsIUUIDGenerator).generateUUID().toString(); - }, - - addRequest: function(data) { - let id = this._getRandomId(); - this._requests[id] = data; - return id; - }, - - takeRequest: function(requestId) { - let d = this._requests[requestId]; - delete this._requests[requestId]; - return d; - }, - - addListeners: function() { - for (let message of kMessages) { - this._cpmm.addWeakMessageListener(message, this); - } - }, - - subscribe: function(scope, principal, callback) { - console.debug("subscribe()", scope); - let requestId = this.addRequest(callback); - this._cpmm.sendAsyncMessage("Push:Register", { - scope: scope, - requestID: requestId, - }, null, principal); - }, - - unsubscribe: function(scope, principal, callback) { - console.debug("unsubscribe()", scope); - let requestId = this.addRequest(callback); - this._cpmm.sendAsyncMessage("Push:Unregister", { - scope: scope, - requestID: requestId, - }, null, principal); - }, - - getSubscription: function(scope, principal, callback) { - console.debug("getSubscription()", scope); - let requestId = this.addRequest(callback); - console.debug("getSubscription: Going to send", scope, principal, - requestId); - this._cpmm.sendAsyncMessage("Push:Registration", { - scope: scope, - requestID: requestId, - }, null, principal); - }, - - _deliverPushEndpoint: function(request, registration) { - if (!registration) { - request.onPushEndpoint(Cr.NS_OK, "", 0, null, 0, null); - return; - } - - let key; - if (registration.p256dhKey) { - key = new Uint8Array(registration.p256dhKey); - } - - let authSecret; - if (registration.authSecret) { - authSecret = new Uint8Array(registration.authSecret); - } - - request.onPushEndpoint(Cr.NS_OK, - registration.pushEndpoint, - key ? key.length : 0, - key, - authSecret ? authSecret.length : 0, - authSecret); - }, - - receiveMessage: function(aMessage) { - console.debug("receiveMessage()", aMessage); - - let json = aMessage.data; - let request = this.takeRequest(json.requestID); - - if (!request) { - console.error("receiveMessage: Unknown request ID", json.requestID); - return; - } - - switch (aMessage.name) { - case "PushService:Register:OK": - case "PushService:Registration:OK": - this._deliverPushEndpoint(request, json.result); - break; - - case "PushService:Register:KO": - case "PushService:Registration:KO": - request.onPushEndpoint(Cr.NS_ERROR_FAILURE, "", 0, null, 0, null); - break; - - case "PushService:Unregister:OK": - if (typeof json.result !== "boolean") { - console.error("receiveMessage: Expected boolean for unregister response", - json.result); - request.onUnsubscribe(Cr.NS_ERROR_FAILURE, false); - return; - } - - request.onUnsubscribe(Cr.NS_OK, json.result); - break; - case "PushService:Unregister:KO": - request.onUnsubscribe(Cr.NS_ERROR_FAILURE, false); - break; - default: - console.error("receiveMessage: NOT IMPLEMENTED!", aMessage.name); - } - }, -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PushClient]); diff --git a/dom/push/PushComponents.js b/dom/push/PushComponents.js new file mode 100644 index 0000000000..c73715e7f7 --- /dev/null +++ b/dom/push/PushComponents.js @@ -0,0 +1,483 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +/** + * This file exports XPCOM components for C++ and chrome JavaScript callers to + * interact with the Push service. + */ + +const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +var isParent = Services.appinfo.processType === Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT; + +/** + * `PushServiceBase`, `PushServiceParent`, and `PushServiceContent` collectively + * implement the `nsIPushService` interface. This interface provides calls + * similar to the Push DOM API, but does not require service workers. + * + * Push service methods may be called from the parent or content process. The + * parent process implementation loads `PushService.jsm` at app startup, and + * calls its methods directly. The content implementation forwards calls to + * the parent Push service via IPC. + * + * The implementations share a class and contract ID. + */ +function PushServiceBase() { + this.wrappedJSObject = this; + this._addListeners(); +} + +PushServiceBase.prototype = { + classID: Components.ID("{daaa8d73-677e-4233-8acd-2c404bd01658}"), + contractID: "@mozilla.org/push/Service;1", + QueryInterface: XPCOMUtils.generateQI([ + Ci.nsIObserver, + Ci.nsISupportsWeakReference, + Ci.nsIPushService, + Ci.nsIPushQuotaManager, + ]), + + _handleReady() {}, + + _addListeners() { + for (let message of this._messages) { + this._mm.addMessageListener(message, this); + } + }, + + _isValidMessage(message) { + return this._messages.includes(message.name); + }, + + observe(subject, topic, data) { + if (topic === "app-startup") { + Services.obs.addObserver(this, "sessionstore-windows-restored", true); + return; + } + if (topic === "sessionstore-windows-restored") { + Services.obs.removeObserver(this, "sessionstore-windows-restored"); + this._handleReady(); + return; + } + }, + + _deliverSubscription(request, props) { + if (!props) { + request.onPushSubscription(Cr.NS_OK, null); + return; + } + request.onPushSubscription(Cr.NS_OK, new PushSubscription(props)); + }, +}; + +/** + * The parent process implementation of `nsIPushService`. This version loads + * `PushService.jsm` at startup and calls its methods directly. It also + * receives and responds to requests from the content process. + */ +function PushServiceParent() { + PushServiceBase.call(this); +} + +PushServiceParent.prototype = Object.create(PushServiceBase.prototype); + +XPCOMUtils.defineLazyServiceGetter(PushServiceParent.prototype, "_mm", + "@mozilla.org/parentprocessmessagemanager;1", "nsIMessageBroadcaster"); + +XPCOMUtils.defineLazyGetter(PushServiceParent.prototype, "_service", + function() { + const {PushService} = Cu.import("resource://gre/modules/PushService.jsm", + {}); + PushService.init(); + return PushService; +}); + +Object.assign(PushServiceParent.prototype, { + _xpcom_factory: XPCOMUtils.generateSingletonFactory(PushServiceParent), + + _messages: [ + "Push:Register", + "Push:Registration", + "Push:Unregister", + "Push:Clear", + "Push:RegisterEventNotificationListener", + "Push:NotificationForOriginShown", + "Push:NotificationForOriginClosed", + "child-process-shutdown", + ], + + // nsIPushService methods + + subscribe(scope, principal, callback) { + return this._handleRequest("Push:Register", principal, { + scope: scope, + }).then(result => { + this._deliverSubscription(callback, result); + }, error => { + callback.onPushSubscription(Cr.NS_ERROR_FAILURE, null); + }).catch(Cu.reportError); + }, + + unsubscribe(scope, principal, callback) { + this._handleRequest("Push:Unregister", principal, { + scope: scope, + }).then(result => { + callback.onUnsubscribe(Cr.NS_OK, result); + }, error => { + callback.onUnsubscribe(Cr.NS_ERROR_FAILURE, false); + }).catch(Cu.reportError); + }, + + getSubscription(scope, principal, callback) { + return this._handleRequest("Push:Registration", principal, { + scope: scope, + }).then(result => { + this._deliverSubscription(callback, result); + }, error => { + callback.onPushSubscription(Cr.NS_ERROR_FAILURE, null); + }).catch(Cu.reportError); + }, + + clearForDomain(domain, callback) { + let principal = Services.scriptSecurityManager.getSystemPrincipal(); + return this._handleRequest("Push:Clear", principal, { + domain: domain, + }).then(result => { + callback.onClear(Cr.NS_OK); + }, error => { + callback.onClear(Cr.NS_ERROR_FAILURE); + }).catch(Cu.reportError); + }, + + // nsIPushQuotaManager methods + + notificationForOriginShown(origin) { + this._service.notificationForOriginShown(origin); + }, + + notificationForOriginClosed(origin) { + this._service.notificationForOriginClosed(origin); + }, + + receiveMessage(message) { + if (!this._isValidMessage(message)) { + return; + } + let {name, principal, target, data} = message; + if (name === "Push:RegisterEventNotificationListener") { + this._service.registerListener(target); + return; + } + if (name === "child-process-shutdown") { + this._service.unregisterListener(target); + return; + } + if (name === "Push:NotificationForOriginShown") { + this.notificationForOriginShown(data); + return; + } + if (name === "Push:NotificationForOriginClosed") { + this.notificationForOriginClosed(data); + return; + } + if (!target.assertPermission("push")) { + return; + } + let sender = target.QueryInterface(Ci.nsIMessageSender); + return this._handleRequest(name, principal, data).then(result => { + sender.sendAsyncMessage(this._getResponseName(name, "OK"), { + requestID: data.requestID, + result: result + }); + }, error => { + sender.sendAsyncMessage(this._getResponseName(name, "KO"), { + requestID: data.requestID, + }); + }).catch(Cu.reportError); + }, + + _handleReady() { + this._service.init(); + }, + + _toPageRecord(principal, data) { + if (!data.scope) { + throw new Error("Invalid page record: missing scope"); + } + + data.originAttributes = + ChromeUtils.originAttributesToSuffix(principal.originAttributes); + + return data; + }, + + _handleRequest(name, principal, data) { + if (!principal) { + return Promise.reject(new Error("Invalid request: missing principal")); + } + + if (name == "Push:Clear") { + return this._service.clear(data); + } + + let pageRecord; + try { + pageRecord = this._toPageRecord(principal, data); + } catch (e) { + return Promise.reject(e); + } + + if (name === "Push:Register") { + return this._service.register(pageRecord); + } + if (name === "Push:Registration") { + return this._service.registration(pageRecord); + } + if (name === "Push:Unregister") { + return this._service.unregister(pageRecord); + } + + return Promise.reject(new Error("Invalid request: unknown name")); + }, + + _getResponseName(requestName, suffix) { + let name = requestName.slice("Push:".length); + return "PushService:" + name + ":" + suffix; + }, +}); + +/** + * The content process implementation of `nsIPushService`. This version + * uses the child message manager to forward calls to the parent process. + * The parent Push service instance handles the request, and responds with a + * message containing the result. + */ +function PushServiceContent() { + PushServiceBase.apply(this, arguments); + this._requests = new Map(); + this._requestId = 0; +} + +PushServiceContent.prototype = Object.create(PushServiceBase.prototype); + +XPCOMUtils.defineLazyServiceGetter(PushServiceContent.prototype, + "_mm", "@mozilla.org/childprocessmessagemanager;1", + "nsISyncMessageSender"); + +Object.assign(PushServiceContent.prototype, { + _xpcom_factory: XPCOMUtils.generateSingletonFactory(PushServiceContent), + + _messages: [ + "PushService:Register:OK", + "PushService:Register:KO", + "PushService:Registration:OK", + "PushService:Registration:KO", + "PushService:Unregister:OK", + "PushService:Unregister:KO", + "PushService:Clear:OK", + "PushService:Clear:KO", + ], + + // nsIPushService methods + + subscribe(scope, principal, callback) { + let requestId = this._addRequest(callback); + this._mm.sendAsyncMessage("Push:Register", { + scope: scope, + requestID: requestId, + }, null, principal); + }, + + unsubscribe(scope, principal, callback) { + let requestId = this._addRequest(callback); + this._mm.sendAsyncMessage("Push:Unregister", { + scope: scope, + requestID: requestId, + }, null, principal); + }, + + getSubscription(scope, principal, callback) { + let requestId = this._addRequest(callback); + this._mm.sendAsyncMessage("Push:Registration", { + scope: scope, + requestID: requestId, + }, null, principal); + }, + + clearForDomain(domain, callback) { + let requestId = this._addRequest(callback); + this._mm.sendAsyncMessage("Push:Clear", { + domain: domain, + requestID: requestId, + }); + }, + + // nsIPushQuotaManager methods + + notificationForOriginShown(origin) { + this._mm.sendAsyncMessage("Push:NotificationForOriginShown", origin); + }, + + notificationForOriginClosed(origin) { + this._mm.sendAsyncMessage("Push:NotificationForOriginClosed", origin); + }, + + _addRequest(data) { + let id = ++this._requestId; + this._requests.set(id, data); + return id; + }, + + _takeRequest(requestId) { + let d = this._requests.get(requestId); + this._requests.delete(requestId); + return d; + }, + + receiveMessage(message) { + if (!this._isValidMessage(message)) { + return; + } + let {name, data} = message; + let request = this._takeRequest(data.requestID); + + if (!request) { + return; + } + + switch (name) { + case "PushService:Register:OK": + case "PushService:Registration:OK": + this._deliverSubscription(request, data.result); + break; + + case "PushService:Register:KO": + case "PushService:Registration:KO": + request.onPushSubscription(Cr.NS_ERROR_FAILURE, null); + break; + + case "PushService:Unregister:OK": + if (typeof data.result === "boolean") { + request.onUnsubscribe(Cr.NS_OK, data.result); + } else { + request.onUnsubscribe(Cr.NS_ERROR_FAILURE, false); + } + break; + + case "PushService:Unregister:KO": + request.onUnsubscribe(Cr.NS_ERROR_FAILURE, false); + break; + + case "PushService:Clear:OK": + request.onClear(Cr.NS_OK); + break; + + case "PushService:Clear:KO": + request.onClear(Cr.NS_ERROR_FAILURE); + break; + + default: + break; + } + }, +}); + +/** `PushSubscription` instances are passed to all subscription callbacks. */ +function PushSubscription(props) { + this._props = props; +} + +PushSubscription.prototype = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPushSubscription]), + + /** The URL for sending messages to this subscription. */ + get endpoint() { + return this._props.endpoint; + }, + + /** The last time a message was sent to this subscription. */ + get lastPush() { + return this._props.lastPush; + }, + + /** The total number of messages sent to this subscription. */ + get pushCount() { + return this._props.pushCount; + }, + + /** The number of remaining background messages that can be sent to this + * subscription, or -1 of the subscription is exempt from the quota. + */ + get quota() { + return this._props.quota; + }, + + /** + * Indicates whether this subscription is subject to the background message + * quota. + */ + quotaApplies() { + return this.quota >= 0; + }, + + /** + * Indicates whether this subscription exceeded the background message quota, + * or the user revoked the notification permission. The caller must request a + * new subscription to continue receiving push messages. + */ + isExpired() { + return this.quota === 0; + }, + + /** + * Returns a key for encrypting messages sent to this subscription. JS + * callers receive the key buffer as a return value, while C++ callers + * receive the key size and buffer as out parameters. + */ + getKey(name, outKeyLen) { + if (name === "p256dh") { + return this._getRawKey(this._props.p256dhKey, outKeyLen); + } + if (name === "auth") { + return this._getRawKey(this._props.authenticationSecret, outKeyLen); + } + return null; + }, + + _getRawKey(key, outKeyLen) { + if (!key) { + return null; + } + let rawKey = new Uint8Array(key); + if (outKeyLen) { + outKeyLen.value = rawKey.length; + } + return rawKey; + }, +}; + +/** + * `PushObserverNotification` instances are passed to all + * `push-notification` observers. + */ +function PushObserverNotification() {} + +PushObserverNotification.prototype = { + classID: Components.ID("{e68997fd-8b92-49ee-af12-800830b023e8}"), + contractID: "@mozilla.org/push/ObserverNotification;1", + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPushObserverNotification]), +}; + +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ + PushObserverNotification, + + // Export the correct implementation depending on whether we're running in + // the parent or content process. + isParent ? PushServiceParent : PushServiceContent, +]); diff --git a/dom/push/PushManager.cpp b/dom/push/PushManager.cpp index 41dee1a27e..c4a50c952a 100644 --- a/dom/push/PushManager.cpp +++ b/dom/push/PushManager.cpp @@ -19,7 +19,7 @@ #include "nsIGlobalObject.h" #include "nsIPermissionManager.h" #include "nsIPrincipal.h" -#include "nsIPushClient.h" +#include "nsIPushService.h" #include "nsComponentManagerUtils.h" #include "nsFrameMessageManager.h" @@ -104,9 +104,9 @@ PushSubscription::Unsubscribe(ErrorResult& aRv) { MOZ_ASSERT(mPrincipal); - nsCOMPtr client = - do_CreateInstance("@mozilla.org/push/PushClient;1"); - if (NS_WARN_IF(!client)) { + nsCOMPtr service = + do_GetService("@mozilla.org/push/Service;1"); + if (NS_WARN_IF(!service)) { aRv = NS_ERROR_FAILURE; return nullptr; } @@ -118,7 +118,8 @@ PushSubscription::Unsubscribe(ErrorResult& aRv) RefPtr callback = new UnsubscribeResultCallback(p); - client->Unsubscribe(mScope, mPrincipal, callback); + Unused << NS_WARN_IF(NS_FAILED( + service->Unsubscribe(mScope, mPrincipal, callback))); return p.forget(); } @@ -457,15 +458,15 @@ public: RefPtr callback = new WorkerUnsubscribeResultCallback(mProxy); - nsCOMPtr client = - do_CreateInstance("@mozilla.org/push/PushClient;1"); - if (!client) { + nsCOMPtr service = + do_GetService("@mozilla.org/push/Service;1"); + if (!service) { callback->OnUnsubscribe(NS_ERROR_FAILURE, false); return NS_OK; } nsCOMPtr principal = mProxy->GetWorkerPrivate()->GetPrincipal(); - if (NS_WARN_IF(NS_FAILED(client->Unsubscribe(mScope, principal, callback)))) { + if (NS_WARN_IF(NS_FAILED(service->Unsubscribe(mScope, principal, callback)))) { callback->OnUnsubscribe(NS_ERROR_FAILURE, false); return NS_OK; } @@ -578,7 +579,7 @@ private: nsTArray mAuthSecret; }; -class GetSubscriptionCallback final : public nsIPushEndpointCallback +class GetSubscriptionCallback final : public nsIPushSubscriptionCallback { public: NS_DECL_ISUPPORTS @@ -590,15 +591,11 @@ public: {} NS_IMETHOD - OnPushEndpoint(nsresult aStatus, - const nsAString& aEndpoint, - uint32_t aKeyLen, - uint8_t* aKey, - uint32_t aAuthSecretLen, - uint8_t* aAuthSecret) override + OnPushSubscription(nsresult aStatus, + nsIPushSubscription* aSubscription) override { AssertIsOnMainThread(); - MOZ_ASSERT(mProxy, "OnPushEndpoint() called twice?"); + MOZ_ASSERT(mProxy, "OnPushSubscription() called twice?"); RefPtr proxy = mProxy.forget(); @@ -610,17 +607,17 @@ public: AutoJSAPI jsapi; jsapi.Init(); - nsTArray rawP256dhKey(aKeyLen); - rawP256dhKey.ReplaceElementsAt(0, aKeyLen, aKey, aKeyLen); - - nsTArray authSecret(aAuthSecretLen); - authSecret.ReplaceElementsAt(0, aAuthSecretLen, - aAuthSecret, aAuthSecretLen); + nsAutoString endpoint; + nsTArray rawP256dhKey, authSecret; + if (NS_SUCCEEDED(aStatus)) { + aStatus = GetSubscriptionParams(aSubscription, endpoint, rawP256dhKey, + authSecret); + } RefPtr r = new GetSubscriptionResultRunnable(proxy, aStatus, - aEndpoint, + endpoint, mScope, rawP256dhKey, authSecret); @@ -630,23 +627,73 @@ public: // Convenience method for use in this file. void - OnPushEndpointError(nsresult aStatus) + OnPushSubscriptionError(nsresult aStatus) { Unused << NS_WARN_IF(NS_FAILED( - OnPushEndpoint(aStatus, EmptyString(), 0, nullptr, 0, nullptr))); + OnPushSubscription(aStatus, nullptr))); } - protected: ~GetSubscriptionCallback() {} private: + inline nsresult + FreeKeys(nsresult aStatus, uint8_t* aKey, uint8_t* aAuthSecret) + { + NS_Free(aKey); + NS_Free(aAuthSecret); + return aStatus; + } + + nsresult + GetSubscriptionParams(nsIPushSubscription* aSubscription, + nsAString& aEndpoint, + nsTArray& aRawP256dhKey, + nsTArray& aAuthSecret) + { + if (!aSubscription) { + return NS_OK; + } + + nsresult rv = aSubscription->GetEndpoint(aEndpoint); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + uint8_t* key = nullptr; + uint8_t* authSecret = nullptr; + + uint32_t keyLen; + rv = aSubscription->GetKey(NS_LITERAL_STRING("p256dh"), &keyLen, &key); + if (NS_WARN_IF(NS_FAILED(rv))) { + return FreeKeys(rv, key, authSecret); + } + + uint32_t authSecretLen; + rv = aSubscription->GetKey(NS_LITERAL_STRING("auth"), &authSecretLen, + &authSecret); + if (NS_WARN_IF(NS_FAILED(rv))) { + return FreeKeys(rv, key, authSecret); + } + + if (!aRawP256dhKey.SetLength(keyLen, fallible) || + !aRawP256dhKey.ReplaceElementsAt(0, keyLen, key, keyLen, fallible) || + !aAuthSecret.SetLength(authSecretLen, fallible) || + !aAuthSecret.ReplaceElementsAt(0, authSecretLen, authSecret, + authSecretLen, fallible)) { + + return FreeKeys(NS_ERROR_OUT_OF_MEMORY, key, authSecret); + } + + return FreeKeys(NS_OK, key, authSecret); + } + RefPtr mProxy; nsString mScope; }; -NS_IMPL_ISUPPORTS(GetSubscriptionCallback, nsIPushEndpointCallback) +NS_IMPL_ISUPPORTS(GetSubscriptionCallback, nsIPushSubscriptionCallback) class GetSubscriptionRunnable final : public nsRunnable { @@ -674,35 +721,35 @@ public: PushPermissionState state; nsresult rv = GetPermissionState(principal, state); if (NS_FAILED(rv)) { - callback->OnPushEndpointError(NS_ERROR_FAILURE); + callback->OnPushSubscriptionError(NS_ERROR_FAILURE); return NS_OK; } if (state != PushPermissionState::Granted) { if (mAction == WorkerPushManager::GetSubscriptionAction) { - callback->OnPushEndpointError(NS_OK); + callback->OnPushSubscriptionError(NS_OK); return NS_OK; } - callback->OnPushEndpointError(NS_ERROR_FAILURE); + callback->OnPushSubscriptionError(NS_ERROR_FAILURE); return NS_OK; } - nsCOMPtr client = - do_CreateInstance("@mozilla.org/push/PushClient;1"); - if (!client) { - callback->OnPushEndpointError(NS_ERROR_FAILURE); + nsCOMPtr service = + do_GetService("@mozilla.org/push/Service;1"); + if (!service) { + callback->OnPushSubscriptionError(NS_ERROR_FAILURE); return NS_OK; } if (mAction == WorkerPushManager::SubscribeAction) { - rv = client->Subscribe(mScope, principal, callback); + rv = service->Subscribe(mScope, principal, callback); } else { MOZ_ASSERT(mAction == WorkerPushManager::GetSubscriptionAction); - rv = client->GetSubscription(mScope, principal, callback); + rv = service->GetSubscription(mScope, principal, callback); } if (NS_WARN_IF(NS_FAILED(rv))) { - callback->OnPushEndpointError(NS_ERROR_FAILURE); + callback->OnPushSubscriptionError(NS_ERROR_FAILURE); return NS_OK; } diff --git a/dom/push/PushNotificationService.js b/dom/push/PushNotificationService.js deleted file mode 100644 index ae946a625b..0000000000 --- a/dom/push/PushNotificationService.js +++ /dev/null @@ -1,114 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cu = Components.utils; -const Cr = Components.results; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -var isParent = Cc["@mozilla.org/xre/runtime;1"] - .getService(Ci.nsIXULRuntime) - .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT; - -XPCOMUtils.defineLazyGetter(this, "PushService", function() { - // Lazily initialize the PushService on - // `sessionstore-windows-restored` or first use. - const {PushService} = Cu.import("resource://gre/modules/PushService.jsm", {}); - if (isParent) { - PushService.init(); - } - return PushService; -}); - -this.PushNotificationService = function PushNotificationService() {}; - -PushNotificationService.prototype = { - classID: Components.ID("{32028e38-903b-4a64-a180-5857eb4cb3dd}"), - - contractID: "@mozilla.org/push/NotificationService;1", - - _xpcom_factory: XPCOMUtils.generateSingletonFactory(PushNotificationService), - QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, - Ci.nsISupportsWeakReference, - Ci.nsIPushNotificationService, - Ci.nsIPushQuotaManager,]), - - register: function register(scope, originAttributes) { - return PushService.register({ - scope: scope, - originAttributes: originAttributes, - maxQuota: Infinity, - }); - }, - - unregister: function unregister(scope, originAttributes) { - return PushService.unregister({scope, originAttributes}); - }, - - registration: function registration(scope, originAttributes) { - return PushService.registration({scope, originAttributes}); - }, - - clearAll: function clearAll() { - return PushService._clearAll(); - }, - - clearForDomain: function(domain) { - return PushService._clearForDomain(domain); - }, - - observe: function observe(subject, topic, data) { - switch (topic) { - case "app-startup": - Services.obs.addObserver(this, "sessionstore-windows-restored", true); - break; - case "sessionstore-windows-restored": - Services.obs.removeObserver(this, "sessionstore-windows-restored"); - if (isParent) { - PushService.init(); - } - break; - } - }, - - // nsIPushQuotaManager methods - - notificationForOriginShown: function(origin) { - if (!isParent) { - Services.cpmm.sendAsyncMessage("Push:NotificationForOriginShown", origin); - return; - } - - PushService._notificationForOriginShown(origin); - }, - - notificationForOriginClosed: function(origin) { - if (!isParent) { - Services.cpmm.sendAsyncMessage("Push:NotificationForOriginClosed", origin); - return; - } - - PushService._notificationForOriginClosed(origin); - } -}; - -this.PushObserverNotification = function PushObserverNotification() {}; - -PushObserverNotification.prototype = { - classID: Components.ID("{66a87970-6dc9-46e0-ac61-adb4a13791de}"), - - contractID: "@mozilla.org/push/ObserverNotification;1", - - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPushObserverNotification]) -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ - PushNotificationService, - PushObserverNotification -]); diff --git a/dom/push/PushRecord.jsm b/dom/push/PushRecord.jsm index df34102b58..b3742dff63 100644 --- a/dom/push/PushRecord.jsm +++ b/dom/push/PushRecord.jsm @@ -30,6 +30,9 @@ this.EXPORTED_SYMBOLS = ["PushRecord"]; const prefs = new Preferences("dom.push."); +/** + * The push subscription record, stored in IndexedDB. + */ function PushRecord(props) { this.pushEndpoint = props.pushEndpoint; this.scope = props.scope; @@ -227,11 +230,12 @@ PushRecord.prototype = { toSubscription() { return { - pushEndpoint: this.pushEndpoint, + endpoint: this.pushEndpoint, lastPush: this.lastPush, pushCount: this.pushCount, p256dhKey: this.p256dhPublicKey, authenticationSecret: this.authenticationSecret, + quota: this.quotaApplies() ? this.quota : -1, }; }, }; diff --git a/dom/push/PushService.jsm b/dom/push/PushService.jsm index ad1258dc0e..a4e0df97e6 100644 --- a/dom/push/PushService.jsm +++ b/dom/push/PushService.jsm @@ -43,12 +43,6 @@ XPCOMUtils.defineLazyGetter(this, "console", () => { const prefs = new Preferences("dom.push."); -const kCHILD_PROCESS_MESSAGES = ["Push:Register", "Push:Unregister", - "Push:Registration", "Push:RegisterEventNotificationListener", - "Push:NotificationForOriginShown", - "Push:NotificationForOriginClosed", - "child-process-shutdown"]; - const PUSH_SERVICE_UNINIT = 0; const PUSH_SERVICE_INIT = 1; // No serverURI const PUSH_SERVICE_ACTIVATING = 2;//activating db @@ -105,7 +99,7 @@ this.PushService = { // reduce the quota for a record. Used for testing purposes. _updateQuotaTestCallback: null, - _childListeners: [], + _childListeners: new Set(), // When serverURI changes (this is used for testing), db is cleaned up and a // a new db is started. This events must be sequential. @@ -379,7 +373,7 @@ this.PushService = { this._setState(PUSH_SERVICE_INIT); return Promise.resolve(); } - return this._startService(service, uri, event) + return this._startService(service, uri) .then(_ => this._stateChangeProcessEnqueue(_ => this._changeStateConnectionEnabledEvent(prefs.get("connection.enabled"))) ); @@ -390,7 +384,7 @@ this.PushService = { if (this._state == PUSH_SERVICE_INIT) { this._setState(PUSH_SERVICE_ACTIVATING); // The service has not been running - start it. - return this._startService(service, uri, STARTING_SERVICE_EVENT) + return this._startService(service, uri) .then(_ => this._stateChangeProcessEnqueue(_ => this._changeStateConnectionEnabledEvent(prefs.get("connection.enabled"))) ); @@ -402,7 +396,7 @@ this.PushService = { // check is called in changeStateConnectionEnabledEvent function) return this._stopService(CHANGING_SERVICE_EVENT) .then(_ => - this._startService(service, uri, CHANGING_SERVICE_EVENT) + this._startService(service, uri) ) .then(_ => this._stateChangeProcessEnqueue(_ => this._changeStateConnectionEnabledEvent(prefs.get("connection.enabled"))) @@ -458,7 +452,7 @@ this.PushService = { } // Start service. - this._startService(service, uri, false, options).then(_ => { + this._startService(service, uri, options).then(_ => { // Before completing the activation check prefs. This will first check // connection.enabled pref and then check offline state. this._changeStateConnectionEnabledEvent(prefs.get("connection.enabled")); @@ -515,24 +509,13 @@ this.PushService = { Services.obs.addObserver(this, "perm-changed", false); }, - _startService: function(service, serverURI, event, options = {}) { + _startService: function(service, serverURI, options = {}) { console.debug("startService()"); if (this._state != PUSH_SERVICE_ACTIVATING) { return; } - if (event != CHANGING_SERVICE_EVENT) { - // if only serverURL is changed we can keep listening for broadcast - // messages and queue them. - let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"] - .getService(Ci.nsIMessageBroadcaster); - - kCHILD_PROCESS_MESSAGES.forEach(msgName => - ppmm.addMessageListener(msgName, this) - ); - } - this._service = service; this._db = options.db; @@ -564,15 +547,6 @@ this.PushService = { this.stopAlarm(); this._stopObservers(); - if (event != CHANGING_SERVICE_EVENT) { - let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"] - .getService(Ci.nsIMessageBroadcaster); - - kCHILD_PROCESS_MESSAGES.forEach( - msgName => ppmm.removeMessageListener(msgName, this) - ); - } - this._service.disconnect(); this._service.uninit(); this._service = null; @@ -616,7 +590,7 @@ this.PushService = { uninit: function() { console.debug("uninit()"); - this._childListeners = []; + this._childListeners.clear(); if (this._state == PUSH_SERVICE_UNINIT) { return; @@ -725,14 +699,14 @@ this.PushService = { }, _notifyListeners: function(name, data) { - if (this._childListeners.length > 0) { + if (this._childListeners.size > 0) { // Try to send messages to all listeners, but remove any that fail since // the receiver is likely gone away. - for (var i = this._childListeners.length - 1; i >= 0; --i) { + for (let listener of this._childListeners) { try { - this._childListeners[i].sendAsyncMessage(name, data); + listener.sendAsyncMessage(name, data); } catch(e) { - this._childListeners.splice(i, 1); + this._childListeners.delete(listener); } } } else { @@ -944,7 +918,7 @@ this.PushService = { }); }, - _notificationForOriginShown(origin) { + notificationForOriginShown(origin) { console.debug("notificationForOriginShown()", origin); let count; if (this._visibleNotifications.has(origin)) { @@ -955,7 +929,7 @@ this.PushService = { this._visibleNotifications.set(origin, count + 1); }, - _notificationForOriginClosed(origin) { + notificationForOriginClosed(origin) { console.debug("notificationForOriginClosed()", origin); let count; if (this._visibleNotifications.has(origin)) { @@ -1106,99 +1080,27 @@ this.PushService = { throw reply.error; }, - receiveMessage: function(aMessage) { - console.debug("receiveMessage()", aMessage.name); - - if (kCHILD_PROCESS_MESSAGES.indexOf(aMessage.name) == -1) { - console.debug("receiveMessage: Invalid message from child", - aMessage.name); - return; - } - - if (aMessage.name === "Push:RegisterEventNotificationListener") { - console.debug("receiveMessage: Adding child listener"); - this._childListeners.push(aMessage.target); - return; - } - - if (aMessage.name === "child-process-shutdown") { - console.debug("receiveMessage: Possibly removing child listener"); - for (var i = this._childListeners.length - 1; i >= 0; --i) { - if (this._childListeners[i] == aMessage.target) { - console.debug("receiveMessage: Removed child listener"); - this._childListeners.splice(i, 1); - } - } - console.debug("receiveMessage: Clearing notifications from child"); - this._visibleNotifications.clear(); - return; - } - - if (aMessage.name === "Push:NotificationForOriginShown") { - console.debug("receiveMessage: Notification shown from child"); - this._notificationForOriginShown(aMessage.data); - return; - } - - if (aMessage.name === "Push:NotificationForOriginClosed") { - console.debug("receiveMessage: Notification closed from child"); - this._notificationForOriginClosed(aMessage.data); - return; - } - - if (!aMessage.target.assertPermission("push")) { - console.debug("receiveMessage: Got message from a child process that", - "does not have 'push' permission"); - return null; - } - - let mm = aMessage.target.QueryInterface(Ci.nsIMessageSender); - - let name = aMessage.name.slice("Push:".length); - Promise.resolve().then(_ => { - let pageRecord = this._validatePageRecord(aMessage); - return this[name.toLowerCase()](pageRecord); - }).then(result => { - mm.sendAsyncMessage("PushService:" + name + ":OK", { - requestID: aMessage.data.requestID, - result: result, - }); - }, error => { - console.error("receiveMessage: Error handling message", aMessage, error); - mm.sendAsyncMessage("PushService:" + name + ":KO", { - requestID: aMessage.data.requestID, - }); - }).catch(error => { - console.error("receiveMessage: Error sending reply", error); - }); + registerListener(listener) { + console.debug("registerListener: Adding child listener"); + this._childListeners.add(listener); }, - _validatePageRecord: function(aMessage) { - let principal = aMessage.principal; - if (!principal) { - throw new Error("Missing message principal"); - } + unregisterListener(listener) { + console.debug("unregisterListener: Possibly removing child listener"); + this._childListeners.delete(listener); + this._visibleNotifications.clear(); + }, - let pageRecord = aMessage.data; - if (!pageRecord.scope) { - throw new Error("Missing page record scope"); - } - - pageRecord.originAttributes = - ChromeUtils.originAttributesToSuffix(principal.originAttributes); - - return pageRecord; + _getByPageRecord(pageRecord) { + return this._checkActivated().then(_ => + this._db.getByIdentifiers(pageRecord) + ); }, register: function(aPageRecord) { console.debug("register()", aPageRecord); - if (!aPageRecord.scope || aPageRecord.originAttributes === undefined) { - return Promise.reject(new Error("Invalid page record")); - } - - return this._checkActivated() - .then(_ => this._db.getByIdentifiers(aPageRecord)) + return this._getByPageRecord(aPageRecord) .then(record => { if (!record) { return this._lookupOrPutPendingRequest(aPageRecord); @@ -1245,12 +1147,7 @@ this.PushService = { unregister: function(aPageRecord) { console.debug("unregister()", aPageRecord); - if (!aPageRecord.scope || aPageRecord.originAttributes === undefined) { - return Promise.reject(new Error("Invalid page record")); - } - - return this._checkActivated() - .then(_ => this._db.getByIdentifiers(aPageRecord)) + return this._getByPageRecord(aPageRecord) .then(record => { if (record === undefined) { return false; @@ -1263,6 +1160,13 @@ this.PushService = { }); }, + clear: function(info) { + if (info.domain == "*") { + return this._clearAll(); + } + return this._clearForDomain(info.domain); + }, + _clearAll: function _clearAll() { return this._checkActivated() .then(_ => this._db.drop()) @@ -1313,12 +1217,8 @@ this.PushService = { registration: function(aPageRecord) { console.debug("registration()"); - if (!aPageRecord.scope || aPageRecord.originAttributes === undefined) { - return Promise.reject(new Error("Invalid page record")); - } - return this._checkActivated() - .then(_ => this._db.getByIdentifiers(aPageRecord)) + return this._getByPageRecord(aPageRecord) .then(record => { if (!record) { return null; diff --git a/dom/push/moz.build b/dom/push/moz.build index 7148c1414f..a37f3040f7 100644 --- a/dom/push/moz.build +++ b/dom/push/moz.build @@ -6,8 +6,7 @@ EXTRA_COMPONENTS += [ 'Push.js', 'Push.manifest', - 'PushClient.js', - 'PushNotificationService.js', + 'PushComponents.js', ] EXTRA_JS_MODULES += [ diff --git a/dom/storage/DOMStorageCache.cpp b/dom/storage/DOMStorageCache.cpp index 376fe17d13..06142148ce 100644 --- a/dom/storage/DOMStorageCache.cpp +++ b/dom/storage/DOMStorageCache.cpp @@ -151,19 +151,6 @@ DOMStorageCache::Persist(const DOMStorage* aStorage) const !aStorage->IsPrivate(); } -namespace { - -PLDHashOperator -CloneSetData(const nsAString& aKey, const nsString aValue, void* aArg) -{ - DOMStorageCache::Data* target = static_cast(aArg); - target->mKeys.Put(aKey, aValue); - - return PL_DHASH_NEXT; -} - -} // namespace - DOMStorageCache::Data& DOMStorageCache::DataSet(const DOMStorage* aStorage) { @@ -178,7 +165,9 @@ DOMStorageCache::DataSet(const DOMStorage* aStorage) Data& defaultSet = mData[kDefaultSet]; Data& sessionSet = mData[kSessionSet]; - defaultSet.mKeys.EnumerateRead(CloneSetData, &sessionSet); + for (auto iter = defaultSet.mKeys.Iter(); !iter.Done(); iter.Next()) { + sessionSet.mKeys.Put(iter.Key(), iter.UserData()); + } mSessionOnlyDataSetActive = true; @@ -363,36 +352,6 @@ DOMStorageCache::GetLength(const DOMStorage* aStorage, uint32_t* aRetval) return NS_OK; } -namespace { - -class IndexFinderData -{ -public: - IndexFinderData(uint32_t aIndex, nsAString& aRetval) - : mIndex(aIndex), mKey(aRetval) - { - mKey.SetIsVoid(true); - } - - uint32_t mIndex; - nsAString& mKey; -}; - -PLDHashOperator -FindKeyOrder(const nsAString& aKey, const nsString aValue, void* aArg) -{ - IndexFinderData* data = static_cast(aArg); - - if (data->mIndex--) { - return PL_DHASH_NEXT; - } - - data->mKey = aKey; - return PL_DHASH_STOP; -} - -} // namespace - nsresult DOMStorageCache::GetKey(const DOMStorage* aStorage, uint32_t aIndex, nsAString& aRetval) { @@ -407,24 +366,18 @@ DOMStorageCache::GetKey(const DOMStorage* aStorage, uint32_t aIndex, nsAString& } } - IndexFinderData data(aIndex, aRetval); - DataSet(aStorage).mKeys.EnumerateRead(FindKeyOrder, &data); + aRetval.SetIsVoid(true); + for (auto iter = DataSet(aStorage).mKeys.Iter(); !iter.Done(); iter.Next()) { + if (aIndex == 0) { + aRetval = iter.Key(); + break; + } + aIndex--; + } + return NS_OK; } -namespace { - -static PLDHashOperator -KeysArrayBuilder(const nsAString& aKey, const nsString aValue, void* aArg) -{ - nsTArray* keys = static_cast* >(aArg); - - keys->AppendElement(aKey); - return PL_DHASH_NEXT; -} - -} // namespace - void DOMStorageCache::GetKeys(const DOMStorage* aStorage, nsTArray& aKeys) { @@ -436,7 +389,9 @@ DOMStorageCache::GetKeys(const DOMStorage* aStorage, nsTArray& aKeys) return; } - DataSet(aStorage).mKeys.EnumerateRead(KeysArrayBuilder, &aKeys); + for (auto iter = DataSet(aStorage).mKeys.Iter(); !iter.Done(); iter.Next()) { + aKeys.AppendElement(iter.Key()); + } } nsresult @@ -598,7 +553,9 @@ DOMStorageCache::CloneFrom(const DOMStorageCache* aThat) mSessionOnlyDataSetActive = aThat->mSessionOnlyDataSetActive; for (uint32_t i = 0; i < kDataSetCount; ++i) { - aThat->mData[i].mKeys.EnumerateRead(CloneSetData, &mData[i]); + for (auto it = aThat->mData[i].mKeys.ConstIter(); !it.Done(); it.Next()) { + mData[i].mKeys.Put(it.Key(), it.UserData()); + } ProcessUsageDelta(i, aThat->mData[i].mOriginQuotaUsage); } } diff --git a/dom/storage/DOMStorageDBThread.cpp b/dom/storage/DOMStorageDBThread.cpp index 4380667317..7c98119a49 100644 --- a/dom/storage/DOMStorageDBThread.cpp +++ b/dom/storage/DOMStorageDBThread.cpp @@ -187,18 +187,18 @@ DOMStorageDBThread::InsertDBOp(DOMStorageDBThread::DBOperation* aOperation) // Sentinel to don't forget to delete the operation when we exit early. nsAutoPtr opScope(aOperation); - if (mStopIOThread) { - // Thread use after shutdown demanded. - MOZ_ASSERT(false); - return NS_ERROR_NOT_INITIALIZED; - } - if (NS_FAILED(mStatus)) { MonitorAutoUnlock unlock(mThreadObserver->GetMonitor()); aOperation->Finalize(mStatus); return mStatus; } + if (mStopIOThread) { + // Thread use after shutdown demanded. + MOZ_ASSERT(false); + return NS_ERROR_NOT_INITIALIZED; + } + switch (aOperation->Type()) { case DBOperation::opPreload: case DBOperation::opPreloadUrgent: @@ -1256,40 +1256,25 @@ DOMStorageDBThread::PendingOperations::Finalize(nsresult aRv) namespace { -class FindPendingOperationForScopeData +bool +FindPendingClearForScope(const nsACString& aScope, + DOMStorageDBThread::DBOperation* aPendingOperation) { -public: - explicit FindPendingOperationForScopeData(const nsACString& aScope) : mScope(aScope), mFound(false) {} - nsCString mScope; - bool mFound; -}; - -PLDHashOperator -FindPendingClearForScope(const nsACString& aMapping, - DOMStorageDBThread::DBOperation* aPendingOperation, - void* aArg) -{ - FindPendingOperationForScopeData* data = - static_cast(aArg); - if (aPendingOperation->Type() == DOMStorageDBThread::DBOperation::opClearAll) { - data->mFound = true; - return PL_DHASH_STOP; + return true; } if (aPendingOperation->Type() == DOMStorageDBThread::DBOperation::opClear && - data->mScope == aPendingOperation->Scope()) { - data->mFound = true; - return PL_DHASH_STOP; + aScope == aPendingOperation->Scope()) { + return true; } if (aPendingOperation->Type() == DOMStorageDBThread::DBOperation::opClearMatchingScope && - StringBeginsWith(data->mScope, aPendingOperation->Scope())) { - data->mFound = true; - return PL_DHASH_STOP; + StringBeginsWith(aScope, aPendingOperation->Scope())) { + return true; } - return PL_DHASH_NEXT; + return false; } } // namespace @@ -1299,17 +1284,14 @@ DOMStorageDBThread::PendingOperations::IsScopeClearPending(const nsACString& aSc { // Called under the lock - FindPendingOperationForScopeData data(aScope); - mClears.EnumerateRead(FindPendingClearForScope, &data); - if (data.mFound) { - return true; + for (auto iter = mClears.Iter(); !iter.Done(); iter.Next()) { + if (FindPendingClearForScope(aScope, iter.UserData())) { + return true; + } } for (uint32_t i = 0; i < mExecList.Length(); ++i) { - DOMStorageDBThread::DBOperation* task = mExecList[i]; - FindPendingClearForScope(EmptyCString(), task, &data); - - if (data.mFound) { + if (FindPendingClearForScope(aScope, mExecList[i])) { return true; } } @@ -1319,23 +1301,18 @@ DOMStorageDBThread::PendingOperations::IsScopeClearPending(const nsACString& aSc namespace { -PLDHashOperator -FindPendingUpdateForScope(const nsACString& aMapping, - DOMStorageDBThread::DBOperation* aPendingOperation, - void* aArg) +bool +FindPendingUpdateForScope(const nsACString& aScope, + DOMStorageDBThread::DBOperation* aPendingOperation) { - FindPendingOperationForScopeData* data = - static_cast(aArg); - if ((aPendingOperation->Type() == DOMStorageDBThread::DBOperation::opAddItem || aPendingOperation->Type() == DOMStorageDBThread::DBOperation::opUpdateItem || aPendingOperation->Type() == DOMStorageDBThread::DBOperation::opRemoveItem) && - data->mScope == aPendingOperation->Scope()) { - data->mFound = true; - return PL_DHASH_STOP; + aScope == aPendingOperation->Scope()) { + return true; } - return PL_DHASH_NEXT; + return false; } } // namespace @@ -1345,17 +1322,14 @@ DOMStorageDBThread::PendingOperations::IsScopeUpdatePending(const nsACString& aS { // Called under the lock - FindPendingOperationForScopeData data(aScope); - mUpdates.EnumerateRead(FindPendingUpdateForScope, &data); - if (data.mFound) { - return true; + for (auto iter = mUpdates.Iter(); !iter.Done(); iter.Next()) { + if (FindPendingUpdateForScope(aScope, iter.UserData())) { + return true; + } } for (uint32_t i = 0; i < mExecList.Length(); ++i) { - DOMStorageDBThread::DBOperation* task = mExecList[i]; - FindPendingUpdateForScope(EmptyCString(), task, &data); - - if (data.mFound) { + if (FindPendingUpdateForScope(aScope, mExecList[i])) { return true; } } diff --git a/dom/storage/DOMStorageDBThread.h b/dom/storage/DOMStorageDBThread.h index d046ee7034..07b737b71a 100644 --- a/dom/storage/DOMStorageDBThread.h +++ b/dom/storage/DOMStorageDBThread.h @@ -10,6 +10,7 @@ #include "prthread.h" #include "prinrval.h" #include "nsTArray.h" +#include "mozilla/Atomics.h" #include "mozilla/Monitor.h" #include "mozilla/storage/StatementCache.h" #include "nsString.h" @@ -292,7 +293,7 @@ private: // Whether DB has already been open, avoid races between main thread reads // and pending DB init in the background I/O thread - bool mDBReady; + Atomic mDBReady; // State of the database initiation nsresult mStatus; @@ -300,15 +301,15 @@ private: // List of scopes having data, for optimization purposes only nsTHashtable mScopesHavingData; - StatementCache mWorkerStatements; - StatementCache mReaderStatements; - // Connection used by the worker thread for all read and write ops nsCOMPtr mWorkerConnection; // Connection used only on the main thread for sync read operations nsCOMPtr mReaderConnection; + StatementCache mWorkerStatements; + StatementCache mReaderStatements; + // Time the first pending operation has been added to the pending operations // list PRIntervalTime mDirtyEpoch; diff --git a/dom/xul/nsXULElement.cpp b/dom/xul/nsXULElement.cpp index 65c52dd8fe..2280fc388d 100644 --- a/dom/xul/nsXULElement.cpp +++ b/dom/xul/nsXULElement.cpp @@ -685,7 +685,7 @@ nsXULElement::PerformAccesskey(bool aKeyCausesActivation, } if (aKeyCausesActivation && !content->IsAnyOfXULElements(nsGkAtoms::textbox, nsGkAtoms::menulist)) { - elm->ClickWithInputSource(nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD); + elm->ClickWithInputSource(nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD, aIsTrustedEvent); } } else { return content->PerformAccesskey(aKeyCausesActivation, aIsTrustedEvent); @@ -1731,17 +1731,17 @@ nsXULElement::Blur(ErrorResult& rv) NS_IMETHODIMP nsXULElement::Click() { - return ClickWithInputSource(nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN); + return ClickWithInputSource(nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN, /* aIsTrusted = */ true); } void nsXULElement::Click(ErrorResult& rv) { - rv = Click(); + rv = ClickWithInputSource(nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN, nsContentUtils::IsCallerChrome()); } nsresult -nsXULElement::ClickWithInputSource(uint16_t aInputSource) +nsXULElement::ClickWithInputSource(uint16_t aInputSource, bool aIsTrustedEvent) { if (BoolAttrIsTrue(nsGkAtoms::disabled)) return NS_OK; @@ -1753,13 +1753,11 @@ nsXULElement::ClickWithInputSource(uint16_t aInputSource) // strong ref to PresContext so events don't destroy it RefPtr context = shell->GetPresContext(); - bool isCallerChrome = nsContentUtils::IsCallerChrome(); - - WidgetMouseEvent eventDown(isCallerChrome, eMouseDown, + WidgetMouseEvent eventDown(aIsTrustedEvent, eMouseDown, nullptr, WidgetMouseEvent::eReal); - WidgetMouseEvent eventUp(isCallerChrome, eMouseUp, + WidgetMouseEvent eventUp(aIsTrustedEvent, eMouseUp, nullptr, WidgetMouseEvent::eReal); - WidgetMouseEvent eventClick(isCallerChrome, eMouseClick, nullptr, + WidgetMouseEvent eventClick(aIsTrustedEvent, eMouseClick, nullptr, WidgetMouseEvent::eReal); eventDown.inputSource = eventUp.inputSource = eventClick.inputSource = aInputSource; diff --git a/dom/xul/nsXULElement.h b/dom/xul/nsXULElement.h index bca1265a0f..9a4dd8ac79 100644 --- a/dom/xul/nsXULElement.h +++ b/dom/xul/nsXULElement.h @@ -124,11 +124,6 @@ public: nsIURI* aDocumentURI, const nsTArray> *aNodeInfos) = 0; -#ifdef NS_BUILD_REFCNT_LOGGING - virtual const char* ClassName() = 0; - virtual uint32_t ClassSize() = 0; -#endif - /** * The prototype document must call ReleaseSubtree when it is going * away. This makes the parents through the tree stop owning their @@ -166,11 +161,6 @@ public: Unlink(); } -#ifdef NS_BUILD_REFCNT_LOGGING - virtual const char* ClassName() override { return "nsXULPrototypeElement"; } - virtual uint32_t ClassSize() override { return sizeof(*this); } -#endif - virtual void ReleaseSubtree() override { for (int32_t i = mChildren.Length() - 1; i >= 0; i--) { @@ -219,11 +209,6 @@ public: nsXULPrototypeScript(uint32_t aLineNo, uint32_t version); virtual ~nsXULPrototypeScript(); -#ifdef NS_BUILD_REFCNT_LOGGING - virtual const char* ClassName() override { return "nsXULPrototypeScript"; } - virtual uint32_t ClassSize() override { return sizeof(*this); } -#endif - virtual nsresult Serialize(nsIObjectOutputStream* aStream, nsXULPrototypeDocument* aProtoDoc, const nsTArray> *aNodeInfos) override; @@ -298,11 +283,6 @@ public: { } -#ifdef NS_BUILD_REFCNT_LOGGING - virtual const char* ClassName() override { return "nsXULPrototypeText"; } - virtual uint32_t ClassSize() override { return sizeof(*this); } -#endif - virtual nsresult Serialize(nsIObjectOutputStream* aStream, nsXULPrototypeDocument* aProtoDoc, const nsTArray> *aNodeInfos) override; @@ -326,11 +306,6 @@ public: { } -#ifdef NS_BUILD_REFCNT_LOGGING - virtual const char* ClassName() override { return "nsXULPrototypePI"; } - virtual uint32_t ClassSize() override { return sizeof(*this); } -#endif - virtual nsresult Serialize(nsIObjectOutputStream* aStream, nsXULPrototypeDocument* aProtoDoc, const nsTArray> *aNodeInfos) override; @@ -401,7 +376,7 @@ public: virtual bool PerformAccesskey(bool aKeyCausesActivation, bool aIsTrustedEvent) override; - nsresult ClickWithInputSource(uint16_t aInputSource); + nsresult ClickWithInputSource(uint16_t aInputSource, bool aIsTrustedEvent); virtual nsIContent *GetBindingParent() const override; virtual bool IsNodeOfType(uint32_t aFlags) const override; diff --git a/dom/xul/templates/nsRDFBinding.cpp b/dom/xul/templates/nsRDFBinding.cpp index ab492660c1..120adfa79c 100644 --- a/dom/xul/templates/nsRDFBinding.cpp +++ b/dom/xul/templates/nsRDFBinding.cpp @@ -26,9 +26,6 @@ nsresult RDFBindingSet::AddBinding(nsIAtom* aVar, nsIAtom* aRef, nsIRDFResource* aPredicate) { RDFBinding* newbinding = new RDFBinding(aRef, aPredicate, aVar); - if (! newbinding) - return NS_ERROR_OUT_OF_MEMORY; - if (mFirst) { RDFBinding* binding = mFirst; @@ -208,9 +205,6 @@ nsBindingValues::SetBindingSet(RDFBindingSet* aBindings) int32_t count = aBindings->Count(); if (count) { mValues = new nsCOMPtr[count]; - if (!mValues) - return NS_ERROR_OUT_OF_MEMORY; - mBindings = aBindings; } else { diff --git a/dom/xul/templates/nsRDFConInstanceTestNode.cpp b/dom/xul/templates/nsRDFConInstanceTestNode.cpp index f80c6f5f6c..a968097436 100644 --- a/dom/xul/templates/nsRDFConInstanceTestNode.cpp +++ b/dom/xul/templates/nsRDFConInstanceTestNode.cpp @@ -209,10 +209,6 @@ nsRDFConInstanceTestNode::FilterInstantiations(InstantiationSet& aInstantiations { Element* element = new nsRDFConInstanceTestNode::Element(valueres, container, empty); - - if (! element) - return NS_ERROR_OUT_OF_MEMORY; - inst->AddSupportingElement(element); } else { diff --git a/dom/xul/templates/nsRDFConMemberTestNode.cpp b/dom/xul/templates/nsRDFConMemberTestNode.cpp index b256ebd6d2..0bb96a5b5a 100644 --- a/dom/xul/templates/nsRDFConMemberTestNode.cpp +++ b/dom/xul/templates/nsRDFConMemberTestNode.cpp @@ -179,10 +179,6 @@ nsRDFConMemberTestNode::FilterInstantiations(InstantiationSet& aInstantiations, Element* element = new nsRDFConMemberTestNode::Element(containerRes, memberValue); - - if (! element) - return NS_ERROR_OUT_OF_MEMORY; - inst->AddSupportingElement(element); } else { @@ -231,10 +227,6 @@ nsRDFConMemberTestNode::FilterInstantiations(InstantiationSet& aInstantiations, Element* element = new nsRDFConMemberTestNode::Element(containerRes, node); - - if (! element) - return NS_ERROR_OUT_OF_MEMORY; - newinst.AddSupportingElement(element); aInstantiations.Insert(inst, newinst); } @@ -320,10 +312,6 @@ nsRDFConMemberTestNode::FilterInstantiations(InstantiationSet& aInstantiations, Element* element = new nsRDFConMemberTestNode::Element(source, memberValue); - - if (! element) - return NS_ERROR_OUT_OF_MEMORY; - newinst.AddSupportingElement(element); aInstantiations.Insert(inst, newinst); diff --git a/dom/xul/templates/nsRDFPropertyTestNode.cpp b/dom/xul/templates/nsRDFPropertyTestNode.cpp index 7ff5ece28d..07bd0a8d06 100644 --- a/dom/xul/templates/nsRDFPropertyTestNode.cpp +++ b/dom/xul/templates/nsRDFPropertyTestNode.cpp @@ -180,10 +180,6 @@ nsRDFPropertyTestNode::FilterInstantiations(InstantiationSet& aInstantiations, Element* element = new nsRDFPropertyTestNode::Element(sourceRes, mProperty, targetValue); - - if (! element) - return NS_ERROR_OUT_OF_MEMORY; - inst->AddSupportingElement(element); } else { @@ -274,10 +270,6 @@ nsRDFPropertyTestNode::FilterInstantiations(InstantiationSet& aInstantiations, Element* element = new nsRDFPropertyTestNode::Element(sourceRes, mProperty, targetValue); - - if (! element) - return NS_ERROR_OUT_OF_MEMORY; - newinst.AddSupportingElement(element); aInstantiations.Insert(inst, newinst); diff --git a/dom/xul/templates/nsRDFQuery.cpp b/dom/xul/templates/nsRDFQuery.cpp index b659824316..b4eb706ed2 100644 --- a/dom/xul/templates/nsRDFQuery.cpp +++ b/dom/xul/templates/nsRDFQuery.cpp @@ -33,9 +33,6 @@ nsRDFQuery::SetCachedResults(nsXULTemplateQueryProcessorRDF* aProcessor, const InstantiationSet& aInstantiations) { mCachedResults = new nsXULTemplateResultSetRDF(aProcessor, this, &aInstantiations); - if (! mCachedResults) - return NS_ERROR_OUT_OF_MEMORY; - return NS_OK; } diff --git a/dom/xul/templates/nsResourceSet.cpp b/dom/xul/templates/nsResourceSet.cpp index dc9448c3c1..64f4aad197 100644 --- a/dom/xul/templates/nsResourceSet.cpp +++ b/dom/xul/templates/nsResourceSet.cpp @@ -56,9 +56,6 @@ nsResourceSet::Add(nsIRDFResource* aResource) if (mCount >= mCapacity) { int32_t capacity = mCapacity + 4; nsIRDFResource** resources = new nsIRDFResource*[capacity]; - if (! resources) - return NS_ERROR_OUT_OF_MEMORY; - for (int32_t i = mCount - 1; i >= 0; --i) resources[i] = mResources[i]; diff --git a/dom/xul/templates/nsRuleNetwork.cpp b/dom/xul/templates/nsRuleNetwork.cpp index 601a6e71ce..c2cee5bbd0 100644 --- a/dom/xul/templates/nsRuleNetwork.cpp +++ b/dom/xul/templates/nsRuleNetwork.cpp @@ -53,9 +53,6 @@ MemoryElementSet::Add(MemoryElement* aElement) } List* list = new List; - if (! list) - return NS_ERROR_OUT_OF_MEMORY; - list->mElement = aElement; list->mRefCnt = 1; list->mNext = mElements; @@ -78,9 +75,6 @@ nsAssignmentSet::Add(const nsAssignment& aAssignment) return NS_ERROR_UNEXPECTED; List* list = new List(aAssignment); - if (! list) - return NS_ERROR_OUT_OF_MEMORY; - list->mRefCnt = 1; list->mNext = mAssignments; @@ -321,8 +315,6 @@ TestNode::Propagate(InstantiationSet& aInstantiations, bool owned = false; InstantiationSet* instantiations = new InstantiationSet(aInstantiations); - if (!instantiations) - return NS_ERROR_OUT_OF_MEMORY; rv = kid->Propagate(*instantiations, aIsUpdate, owned); if (!owned) delete instantiations; diff --git a/dom/xul/templates/nsXULContentBuilder.cpp b/dom/xul/templates/nsXULContentBuilder.cpp index a98e32989a..95b644fa60 100644 --- a/dom/xul/templates/nsXULContentBuilder.cpp +++ b/dom/xul/templates/nsXULContentBuilder.cpp @@ -348,9 +348,6 @@ NS_NewXULContentBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult) nsresult rv; nsXULContentBuilder* result = new nsXULContentBuilder(); - if (!result) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(result); // stabilize rv = result->InitGlobals(); diff --git a/dom/xul/templates/nsXULSortService.cpp b/dom/xul/templates/nsXULSortService.cpp index 353cb1cd6c..291ab5c43a 100644 --- a/dom/xul/templates/nsXULSortService.cpp +++ b/dom/xul/templates/nsXULSortService.cpp @@ -502,9 +502,6 @@ nsresult NS_NewXULSortService(nsIXULSortService** sortService) { *sortService = new XULSortServiceImpl(); - if (!*sortService) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(*sortService); return NS_OK; } diff --git a/dom/xul/templates/nsXULTemplateBuilder.cpp b/dom/xul/templates/nsXULTemplateBuilder.cpp index c0aa5920fb..bf7985da86 100644 --- a/dom/xul/templates/nsXULTemplateBuilder.cpp +++ b/dom/xul/templates/nsXULTemplateBuilder.cpp @@ -1200,15 +1200,12 @@ nsXULTemplateBuilder::LoadDataSources(nsIDocument* aDocument, if (querytype.EqualsLiteral("rdf")) { isRDFQuery = true; mQueryProcessor = new nsXULTemplateQueryProcessorRDF(); - NS_ENSURE_TRUE(mQueryProcessor, NS_ERROR_OUT_OF_MEMORY); } else if (querytype.EqualsLiteral("xml")) { mQueryProcessor = new nsXULTemplateQueryProcessorXML(); - NS_ENSURE_TRUE(mQueryProcessor, NS_ERROR_OUT_OF_MEMORY); } else if (querytype.EqualsLiteral("storage")) { mQueryProcessor = new nsXULTemplateQueryProcessorStorage(); - NS_ENSURE_TRUE(mQueryProcessor, NS_ERROR_OUT_OF_MEMORY); } else { nsAutoCString cid(NS_QUERY_PROCESSOR_CONTRACTID_PREFIX); @@ -1739,9 +1736,6 @@ nsXULTemplateBuilder::CompileQueries() mMemberVariable = do_GetAtom(membervar); nsTemplateQuerySet* queryset = new nsTemplateQuerySet(0); - if (!queryset) - return NS_ERROR_OUT_OF_MEMORY; - if (!mQuerySets.AppendElement(queryset)) { delete queryset; return NS_ERROR_OUT_OF_MEMORY; @@ -1802,8 +1796,6 @@ nsXULTemplateBuilder::CompileTemplate(nsIContent* aTemplate, // first one is always created by CompileQueries if (hasQuerySet) { aQuerySet = new nsTemplateQuerySet(++*aPriority); - if (!aQuerySet) - return NS_ERROR_OUT_OF_MEMORY; // once the queryset is appended to the mQuerySets list, it // will be removed by CompileQueries if an error occurs @@ -1882,9 +1874,6 @@ nsXULTemplateBuilder::CompileTemplate(nsIContent* aTemplate, // create a new queryset if one hasn't been created already if (hasQuerySet) { aQuerySet = new nsTemplateQuerySet(++*aPriority); - if (! aQuerySet) - return NS_ERROR_OUT_OF_MEMORY; - if (!mQuerySets.AppendElement(aQuerySet)) { delete aQuerySet; return NS_ERROR_OUT_OF_MEMORY; @@ -1926,9 +1915,6 @@ nsXULTemplateBuilder::CompileTemplate(nsIContent* aTemplate, // a new queryset must always be created in this case if (hasQuerySet) { aQuerySet = new nsTemplateQuerySet(++*aPriority); - if (! aQuerySet) - return NS_ERROR_OUT_OF_MEMORY; - if (!mQuerySets.AppendElement(aQuerySet)) { delete aQuerySet; return NS_ERROR_OUT_OF_MEMORY; @@ -2266,9 +2252,6 @@ nsXULTemplateBuilder::CompileWhereCondition(nsTemplateRule* aRule, return NS_OK; } - if (! condition) - return NS_ERROR_OUT_OF_MEMORY; - if (*aCurrentCondition) { (*aCurrentCondition)->SetNext(condition); } diff --git a/dom/xul/templates/nsXULTemplateQueryProcessorRDF.cpp b/dom/xul/templates/nsXULTemplateQueryProcessorRDF.cpp index 1cc43f4444..cb71a2b1e4 100644 --- a/dom/xul/templates/nsXULTemplateQueryProcessorRDF.cpp +++ b/dom/xul/templates/nsXULTemplateQueryProcessorRDF.cpp @@ -382,8 +382,6 @@ nsXULTemplateQueryProcessorRDF::CompileQuery(nsIXULTemplateBuilder* aBuilder, query->SetQueryNode(aQueryNode); nsInstantiationNode* instnode = new nsInstantiationNode(this, query); - if (!instnode) - return NS_ERROR_OUT_OF_MEMORY; // this and other functions always add nodes to mAllTests first. That // way if something fails, the node will just sit harmlessly in mAllTests @@ -481,8 +479,6 @@ nsXULTemplateQueryProcessorRDF::GenerateResults(nsISupports* aDatasource, seed.AddAssignment(query->mRefVariable, refResource); InstantiationSet* instantiations = new InstantiationSet(); - if (!instantiations) - return NS_ERROR_OUT_OF_MEMORY; instantiations->Append(seed); // if the propagation caused a match, then the results will be @@ -506,8 +502,6 @@ nsXULTemplateQueryProcessorRDF::GenerateResults(nsISupports* aDatasource, if (! results) { // no results were found so create an empty set results = new nsXULTemplateResultSetRDF(this, query, nullptr); - if (! results) - return NS_ERROR_OUT_OF_MEMORY; } results.swap(*aResults); @@ -892,8 +886,6 @@ nsXULTemplateQueryProcessorRDF::Propagate(nsIRDFResource* aSource, rdftestnode->CanPropagate(aSource, aProperty, aTarget, seed); InstantiationSet* instantiations = new InstantiationSet(); - if (!instantiations) - return NS_ERROR_OUT_OF_MEMORY; instantiations->Append(seed); rv = rdftestnode->Constrain(*instantiations); @@ -1165,11 +1157,8 @@ nsXULTemplateQueryProcessorRDF::CompileExtendedQuery(nsRDFQuery* aQuery, TestNode** aLastNode) { // Compile an extended query's children - nsContentTestNode* idnode = new nsContentTestNode(this, aQuery->mRefVariable); - if (! idnode) - return NS_ERROR_OUT_OF_MEMORY; aQuery->SetRoot(idnode); nsresult rv = mAllTests.Add(idnode); @@ -1372,11 +1361,9 @@ nsXULTemplateQueryProcessorRDF::CompileTripleCondition(nsRDFQuery* aQuery, return NS_OK; } - if (! testnode) - return NS_ERROR_OUT_OF_MEMORY; - // add testnode to mAllTests first. If adding to mRDFTests fails, just // leave it in the list so that it can be deleted later. + MOZ_ASSERT(testnode); nsresult rv = mAllTests.Add(testnode); if (NS_FAILED(rv)) { delete testnode; @@ -1430,9 +1417,6 @@ nsXULTemplateQueryProcessorRDF::CompileMemberCondition(nsRDFQuery* aQuery, containervar, childvar); - if (! testnode) - return NS_ERROR_OUT_OF_MEMORY; - // add testnode to mAllTests first. If adding to mRDFTests fails, just // leave it in the list so that it can be deleted later. nsresult rv = mAllTests.Add(testnode); @@ -1457,8 +1441,6 @@ nsXULTemplateQueryProcessorRDF::AddDefaultSimpleRules(nsRDFQuery* aQuery, nsContentTestNode* idnode = new nsContentTestNode(this, aQuery->mRefVariable); - if (! idnode) - return NS_ERROR_OUT_OF_MEMORY; // Create (?container ^member ?member) nsRDFConMemberTestNode* membernode = @@ -1467,11 +1449,6 @@ nsXULTemplateQueryProcessorRDF::AddDefaultSimpleRules(nsRDFQuery* aQuery, aQuery->mRefVariable, aQuery->mMemberVariable); - if (! membernode) { - delete idnode; - return NS_ERROR_OUT_OF_MEMORY; - } - // add nodes to mAllTests first. If later calls fail, just leave them in // the list so that they can be deleted later. nsresult rv = mAllTests.Add(idnode); @@ -1580,9 +1557,6 @@ nsXULTemplateQueryProcessorRDF::CompileSimpleQuery(nsRDFQuery* aQuery, iscontainer, isempty); - if (! testnode) - return NS_ERROR_OUT_OF_MEMORY; - rv = mAllTests.Add(testnode); if (NS_FAILED(rv)) { delete testnode; @@ -1620,9 +1594,6 @@ nsXULTemplateQueryProcessorRDF::CompileSimpleQuery(nsRDFQuery* aQuery, testnode = new nsRDFPropertyTestNode(prevnode, this, aQuery->mMemberVariable, property, target); - if (! testnode) - return NS_ERROR_OUT_OF_MEMORY; - rv = mAllTests.Add(testnode); if (NS_FAILED(rv)) { delete testnode; @@ -1702,9 +1673,6 @@ nsXULTemplateQueryProcessorRDF::AddMemoryElements(const Instantiation& aInst, nsCOMArray* arr; if (!mMemoryElementToResultMap.Get(hash, &arr)) { arr = new nsCOMArray(); - if (!arr) - return NS_ERROR_OUT_OF_MEMORY; - mMemoryElementToResultMap.Put(hash, arr); } diff --git a/dom/xul/templates/nsXULTemplateQueryProcessorStorage.cpp b/dom/xul/templates/nsXULTemplateQueryProcessorStorage.cpp index 06568dd949..ba2d974079 100644 --- a/dom/xul/templates/nsXULTemplateQueryProcessorStorage.cpp +++ b/dom/xul/templates/nsXULTemplateQueryProcessorStorage.cpp @@ -79,10 +79,6 @@ nsXULTemplateResultSetStorage::GetNext(nsISupports **aResult) { nsXULTemplateResultStorage* result = new nsXULTemplateResultStorage(this); - - if (!result) - return NS_ERROR_OUT_OF_MEMORY; - *aResult = result; NS_ADDREF(result); return NS_OK; @@ -405,10 +401,6 @@ nsXULTemplateQueryProcessorStorage::GenerateResults(nsISupports* aDatasource, nsXULTemplateResultSetStorage* results = new nsXULTemplateResultSetStorage(statement); - - if (!results) - return NS_ERROR_OUT_OF_MEMORY; - *aResults = results; NS_ADDREF(*aResults); @@ -431,9 +423,6 @@ nsXULTemplateQueryProcessorStorage::TranslateRef(nsISupports* aDatasource, { nsXULTemplateResultStorage* result = new nsXULTemplateResultStorage(nullptr); - if (!result) - return NS_ERROR_OUT_OF_MEMORY; - *aRef = result; NS_ADDREF(*aRef); return NS_OK; diff --git a/dom/xul/templates/nsXULTreeBuilder.cpp b/dom/xul/templates/nsXULTreeBuilder.cpp index 440390db68..e67f5e74d9 100644 --- a/dom/xul/templates/nsXULTreeBuilder.cpp +++ b/dom/xul/templates/nsXULTreeBuilder.cpp @@ -262,9 +262,6 @@ NS_NewXULTreeBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult) nsresult rv; nsXULTreeBuilder* result = new nsXULTreeBuilder(); - if (! result) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(result); // stabilize rv = result->InitGlobals(); diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index 100909626b..576850c942 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -174,8 +174,11 @@ FrameLayerBuilder::DisplayItemData::EndUpdate(nsAutoPtr a { MOZ_RELEASE_ASSERT(mLayer); MOZ_ASSERT(mItem); + MOZ_ASSERT(mGeometry || aGeometry); - mGeometry = aGeometry; + if (aGeometry) { + mGeometry = aGeometry; + } mClip = mItem->GetClip(); mFrameListChanges.Clear(); @@ -411,6 +414,7 @@ public: mFixedPosFrameForLayerData(nullptr), mReferenceFrame(nullptr), mLayer(nullptr), + mSolidColor(NS_RGBA(0, 0, 0, 0)), mIsSolidColorInVisibleRegion(false), mFontSmoothingBackgroundColor(NS_RGBA(0,0,0,0)), mExclusiveToOneItem(false), @@ -4298,7 +4302,7 @@ FrameLayerBuilder::ComputeGeometryChangeForItem(DisplayItemData* aData) PaintedLayerItemsEntry* entry = mPaintedLayerItems.GetEntry(paintedLayer); - nsAutoPtr geometry(item->AllocateGeometry(mDisplayListBuilder)); + nsAutoPtr geometry; PaintedDisplayItemLayerUserData* layerData = static_cast(aData->mLayer->GetUserData(&gPaintedDisplayItemLayerUserData)); @@ -4306,7 +4310,7 @@ FrameLayerBuilder::ComputeGeometryChangeForItem(DisplayItemData* aData) const DisplayItemClip& clip = item->GetClip(); - // If the frame is marked as invalidated, and didn't specify a rect to invalidate then we want to + // If the frame is marked as invalidated, and didn't specify a rect to invalidate then we want to // invalidate both the old and new bounds, otherwise we only want to invalidate the changed areas. // If we do get an invalid rect, then we want to add this on top of the change areas. nsRect invalid; @@ -4314,7 +4318,7 @@ FrameLayerBuilder::ComputeGeometryChangeForItem(DisplayItemData* aData) bool notifyRenderingChanged = true; if (!aData->mGeometry) { // This item is being added for the first time, invalidate its entire area. - //TODO: We call GetGeometry again in AddPaintedDisplayItem, we should reuse this. + geometry = item->AllocateGeometry(mDisplayListBuilder); combined = clip.ApplyNonRoundedIntersection(geometry->ComputeInvalidationRegion()); #ifdef MOZ_DUMP_PAINTING if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { @@ -4322,7 +4326,8 @@ FrameLayerBuilder::ComputeGeometryChangeForItem(DisplayItemData* aData) } #endif } else if (aData->mIsInvalid || (item->IsInvalid(invalid) && invalid.IsEmpty())) { - // Either layout marked item as needing repainting, invalidate the entire old and new areas. + // Layout marked item/frame as needing repainting (without an explicit rect), invalidate the entire old and new areas. + geometry = item->AllocateGeometry(mDisplayListBuilder); combined = aData->mClip.ApplyNonRoundedIntersection(aData->mGeometry->ComputeInvalidationRegion()); combined.MoveBy(shift); combined.Or(combined, clip.ApplyNonRoundedIntersection(geometry->ComputeInvalidationRegion())); @@ -4336,8 +4341,10 @@ FrameLayerBuilder::ComputeGeometryChangeForItem(DisplayItemData* aData) // repainted. const nsTArray& changedFrames = aData->GetFrameListChanges(); + aData->mGeometry->MoveBy(shift); + item->ComputeInvalidationRegion(mDisplayListBuilder, aData->mGeometry, &combined); - // We have an optimization to cache the drawing background-attachment: fixed canvas + // We have an optimization to cache the drawing of background-attachment: fixed canvas // background images so we can scroll and just blit them when they are flattened into // the same layer as scrolling content. NotifyRenderingChanged is only used to tell // the canvas bg image item to purge this cache. We want to be careful not to accidentally @@ -4346,16 +4353,16 @@ FrameLayerBuilder::ComputeGeometryChangeForItem(DisplayItemData* aData) // AddOffsetAndComputeDifference is the only thing that will invalidate we skip the // NotifyRenderingChanged call (ComputeInvalidationRegion for background images also calls // NotifyRenderingChanged if anything changes). - if (aData->mGeometry->ComputeInvalidationRegion() == geometry->ComputeInvalidationRegion() && - aData->mClip == clip && invalid.IsEmpty() && changedFrames.Length() == 0) { + if (!combined.IsEmpty()) { + geometry = item->AllocateGeometry(mDisplayListBuilder); + } else if (aData->mClip == clip && invalid.IsEmpty() && changedFrames.Length() == 0) { notifyRenderingChanged = false; } - - aData->mGeometry->MoveBy(shift); - item->ComputeInvalidationRegion(mDisplayListBuilder, aData->mGeometry, &combined); aData->mClip.AddOffsetAndComputeDifference(entry->mCommonClipCount, shift, aData->mGeometry->ComputeInvalidationRegion(), - clip, entry->mLastCommonClipCount, geometry->ComputeInvalidationRegion(), + clip, entry->mLastCommonClipCount, + geometry ? geometry->ComputeInvalidationRegion() : + aData->mGeometry->ComputeInvalidationRegion(), &combined); // Add in any rect that the frame specified @@ -5171,9 +5178,11 @@ ChooseScaleAndSetTransform(FrameLayerBuilder* aLayerBuilder, scale = gfxSize(1.0, 1.0); } // If this is a transform container layer, then pre-rendering might - // mean we try render a layer bigger than the max texture size. Apply - // clmaping to prevent this. - if (aTransform) { + // mean we try render a layer bigger than the max texture size. If we have + // tiling, that's not a problem, since we'll automatically choose a tiled + // layer for layers of that size. If not, we need to apply clamping to + // prevent this. + if (aTransform && !gfxPrefs::LayersTilesEnabled()) { RestrictScaleToMaxLayerSize(scale, aVisibleRect, aContainerFrame, aLayer); } } else { @@ -5902,7 +5911,7 @@ FrameLayerBuilder::DrawPaintedLayer(PaintedLayer* aLayer, FlashPaint(aContext); } - if (presContext && presContext->GetDocShell() && isActiveLayerManager) { + if (presContext->GetDocShell() && isActiveLayerManager) { nsDocShell* docShell = static_cast(presContext->GetDocShell()); RefPtr timelines = TimelineConsumers::Get(); diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 06eb1abfb1..1eb12884b3 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -230,9 +230,6 @@ NS_NewRootBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); nsContainerFrame* NS_NewDocElementBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); -nsIFrame* -NS_NewThumbFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); - nsIFrame* NS_NewDeckFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); @@ -3159,10 +3156,56 @@ nsCSSFrameConstructor::ConstructFieldSetFrame(nsFrameConstructorState& aState, false, scrollFrame); } - nsContainerFrame* blockFrame = - NS_NewBlockFormattingContext(mPresShell, fieldsetContentStyle); - InitAndRestoreFrame(aState, content, - scrollFrame ? scrollFrame : fieldsetFrame, blockFrame); + nsContainerFrame* absPosContainer = nullptr; + if (fieldsetFrame->IsAbsPosContaininingBlock()) { + absPosContainer = fieldsetFrame; + } + + // Create the inner ::-moz-fieldset-content frame. + nsContainerFrame* contentFrameTop; + nsContainerFrame* contentFrame; + auto parent = scrollFrame ? scrollFrame : fieldsetFrame; + switch (fieldsetContentDisplay->mDisplay) { + case NS_STYLE_DISPLAY_FLEX: + contentFrame = NS_NewFlexContainerFrame(mPresShell, fieldsetContentStyle); + InitAndRestoreFrame(aState, content, parent, contentFrame); + contentFrameTop = contentFrame; + break; + case NS_STYLE_DISPLAY_GRID: + contentFrame = NS_NewGridContainerFrame(mPresShell, fieldsetContentStyle); + InitAndRestoreFrame(aState, content, parent, contentFrame); + contentFrameTop = contentFrame; + break; + default: { + MOZ_ASSERT(fieldsetContentDisplay->mDisplay == NS_STYLE_DISPLAY_BLOCK, + "bug in nsRuleNode::ComputeDisplayData?"); + + nsContainerFrame* columnSetFrame = nullptr; + RefPtr innerSC = fieldsetContentStyle; + const nsStyleColumn* columns = fieldsetContentStyle->StyleColumn(); + if (columns->mColumnCount != NS_STYLE_COLUMN_COUNT_AUTO || + columns->mColumnWidth.GetUnit() != eStyleUnit_Auto) { + columnSetFrame = + NS_NewColumnSetFrame(mPresShell, fieldsetContentStyle, nsFrameState(0)); + InitAndRestoreFrame(aState, content, parent, columnSetFrame); + innerSC = mPresShell->StyleSet()->ResolveAnonymousBoxStyle( + nsCSSAnonBoxes::columnContent, fieldsetContentStyle); + if (absPosContainer) { + absPosContainer = columnSetFrame; + } + } + contentFrame = NS_NewBlockFrame(mPresShell, innerSC); + if (columnSetFrame) { + InitAndRestoreFrame(aState, content, columnSetFrame, contentFrame); + SetInitialSingleChild(columnSetFrame, contentFrame); + contentFrameTop = columnSetFrame; + } else { + InitAndRestoreFrame(aState, content, parent, contentFrame); + contentFrameTop = contentFrame; + } + break; + } + } aState.AddChild(fieldsetFrame, aFrameItems, content, styleContext, aParentFrame); @@ -3170,16 +3213,16 @@ nsCSSFrameConstructor::ConstructFieldSetFrame(nsFrameConstructorState& aState, nsFrameConstructorSaveState absoluteSaveState; nsFrameItems childItems; - blockFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); - if (fieldsetFrame->IsAbsPosContaininingBlock()) { - aState.PushAbsoluteContainingBlock(blockFrame, fieldsetFrame, absoluteSaveState); + contentFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); + if (absPosContainer) { + aState.PushAbsoluteContainingBlock(contentFrame, absPosContainer, absoluteSaveState); } - ProcessChildren(aState, content, styleContext, blockFrame, true, + ProcessChildren(aState, content, styleContext, contentFrame, true, childItems, true, aItem.mPendingBinding); nsFrameItems fieldsetKids; - fieldsetKids.AddChild(scrollFrame ? scrollFrame : blockFrame); + fieldsetKids.AddChild(scrollFrame ? scrollFrame : contentFrameTop); for (nsFrameList::Enumerator e(childItems); !e.AtEnd(); e.Next()) { nsIFrame* child = e.get(); @@ -3195,18 +3238,18 @@ nsCSSFrameConstructor::ConstructFieldSetFrame(nsFrameConstructorState& aState, fieldsetKids.InsertFrame(fieldsetFrame, nullptr, child); if (scrollFrame) { StickyScrollContainer::NotifyReparentedFrameAcrossScrollFrameBoundary( - child, blockFrame); + child, contentFrame); } break; } } if (isScrollable) { - FinishBuildingScrollFrame(scrollFrame, blockFrame); + FinishBuildingScrollFrame(scrollFrame, contentFrameTop); } // Set the inner frame's initial child lists - blockFrame->SetInitialChildList(kPrincipalList, childItems); + contentFrame->SetInitialChildList(kPrincipalList, childItems); // Set the outer frame's initial child list fieldsetFrame->SetInitialChildList(kPrincipalList, fieldsetKids); @@ -3383,7 +3426,8 @@ IsFrameForFieldSet(nsIFrame* aFrame, nsIAtom* aFrameType) { nsIAtom* pseudo = aFrame->StyleContext()->GetPseudo(); if (pseudo == nsCSSAnonBoxes::fieldsetContent || - pseudo == nsCSSAnonBoxes::scrolledContent) { + pseudo == nsCSSAnonBoxes::scrolledContent || + pseudo == nsCSSAnonBoxes::columnContent) { return IsFrameForFieldSet(aFrame->GetParent(), aFrame->GetParent()->GetType()); } return aFrameType == nsGkAtoms::fieldSetFrame; diff --git a/layout/reftests/columns/columnfill-auto-ref.html b/layout/reftests/columns/columnfill-auto-ref.html index 619ef7f7b5..6e0046c309 100644 --- a/layout/reftests/columns/columnfill-auto-ref.html +++ b/layout/reftests/columns/columnfill-auto-ref.html @@ -3,8 +3,37 @@ -
-
- Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed feugiat libero vel diam. Pellentesque pulvinar commodo lacus. Sed fringilla. Sed lectus. Praesent laoreet orci vitae nisi. Duis venenatis tristique massa. Sed commodo diam at mauris. +
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed
+ +
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed
+ +
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed +
+ +
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed +
+ +
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed +
+ + diff --git a/layout/style/forms.css b/layout/style/forms.css index 58135b96ed..9b1527fe61 100644 --- a/layout/style/forms.css +++ b/layout/style/forms.css @@ -2,19 +2,20 @@ * 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/. */ -/** +/** Styles for old GFX form widgets - **/ - + **/ + @namespace url(http://www.w3.org/1999/xhtml); /* set default namespace to HTML */ @namespace xul url(http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul); *|*::-moz-fieldset-content { - display: block; + display: block; /* nsRuleNode::ComputeDisplayData overrules this in some cases */ unicode-bidi: inherit; text-overflow: inherit; overflow: inherit; + overflow-clip-box: inherit; padding: inherit; block-size: 100%; /* Need this so percentage block-sizes of kids work right */ /* Please keep the Multicol/Flex/Align sections below in sync with @@ -24,9 +25,19 @@ -moz-column-width: inherit; -moz-column-gap: inherit; -moz-column-rule: inherit; + -moz-column-fill: inherit; /* Flex container */ flex-direction: inherit; flex-wrap: inherit; + /* Grid container */ + grid-auto-columns: inherit; + grid-auto-rows: inherit; + grid-auto-flow: inherit; + grid-column-gap: inherit; + grid-row-gap: inherit; + grid-template-areas: inherit; + grid-template-columns: inherit; + grid-template-rows: inherit; /* CSS Align */ align-content: inherit; align-items: inherit; @@ -63,7 +74,7 @@ label { /* default inputs, text inputs, and selects */ -/* Note: Values in nsNativeTheme IsWidgetStyled function +/* Note: Values in nsNativeTheme IsWidgetStyled function need to match textfield background/border values here */ input { @@ -221,7 +232,7 @@ select { line-height: normal !important; white-space: nowrap !important; word-wrap: normal !important; - text-align: start; + text-align: start; cursor: default; box-sizing: border-box; -moz-user-select: none; @@ -239,7 +250,7 @@ select { /* Need the "select[size][multiple]" selector to override the settings on 'select[size="1"]', eg if one has