1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/AlertNotification.h"
9 #include "imgIContainer.h"
10 #include "imgINotificationObserver.h"
11 #include "imgIRequest.h"
12 #include "imgLoader.h"
13 #include "nsAlertsUtils.h"
14 #include "nsComponentManagerUtils.h"
15 #include "nsContentUtils.h"
16 #include "nsNetUtil.h"
17 #include "nsServiceManagerUtils.h"
19 #include "mozilla/Unused.h"
23 NS_IMPL_ISUPPORTS(AlertNotification
, nsIAlertNotification
)
25 AlertNotification::AlertNotification()
26 : mTextClickable(false), mInPrivateBrowsing(false) {}
28 AlertNotification::~AlertNotification() = default;
31 AlertNotification::Init(const nsAString
& aName
, const nsAString
& aImageURL
,
32 const nsAString
& aTitle
, const nsAString
& aText
,
33 bool aTextClickable
, const nsAString
& aCookie
,
34 const nsAString
& aDir
, const nsAString
& aLang
,
35 const nsAString
& aData
, nsIPrincipal
* aPrincipal
,
36 bool aInPrivateBrowsing
, bool aRequireInteraction
,
37 bool aSilent
, const nsTArray
<uint32_t>& aVibrate
) {
39 mImageURL
= aImageURL
;
42 mTextClickable
= aTextClickable
;
47 mPrincipal
= aPrincipal
;
48 mInPrivateBrowsing
= aInPrivateBrowsing
;
49 mRequireInteraction
= aRequireInteraction
;
51 mVibrate
= aVibrate
.Clone();
56 AlertNotification::SetActions(
57 const nsTArray
<RefPtr
<nsIAlertAction
>>& aActions
) {
58 mActions
= aActions
.Clone();
63 AlertNotification::GetName(nsAString
& aName
) {
69 AlertNotification::GetImageURL(nsAString
& aImageURL
) {
70 aImageURL
= mImageURL
;
75 AlertNotification::GetTitle(nsAString
& aTitle
) {
81 AlertNotification::GetText(nsAString
& aText
) {
87 AlertNotification::GetTextClickable(bool* aTextClickable
) {
88 *aTextClickable
= mTextClickable
;
93 AlertNotification::GetCookie(nsAString
& aCookie
) {
99 AlertNotification::GetDir(nsAString
& aDir
) {
105 AlertNotification::GetLang(nsAString
& aLang
) {
111 AlertNotification::GetRequireInteraction(bool* aRequireInteraction
) {
112 *aRequireInteraction
= mRequireInteraction
;
117 AlertNotification::GetData(nsAString
& aData
) {
123 AlertNotification::GetPrincipal(nsIPrincipal
** aPrincipal
) {
124 NS_IF_ADDREF(*aPrincipal
= mPrincipal
);
129 AlertNotification::GetURI(nsIURI
** aURI
) {
130 if (!nsAlertsUtils::IsActionablePrincipal(mPrincipal
)) {
134 auto* basePrin
= BasePrincipal::Cast(mPrincipal
);
135 return basePrin
->GetURI(aURI
);
139 AlertNotification::GetInPrivateBrowsing(bool* aInPrivateBrowsing
) {
140 *aInPrivateBrowsing
= mInPrivateBrowsing
;
145 AlertNotification::GetActionable(bool* aActionable
) {
146 *aActionable
= nsAlertsUtils::IsActionablePrincipal(mPrincipal
);
151 AlertNotification::GetSilent(bool* aSilent
) {
157 AlertNotification::GetVibrate(nsTArray
<uint32_t>& aVibrate
) {
158 aVibrate
= mVibrate
.Clone();
163 AlertNotification::GetActions(nsTArray
<RefPtr
<nsIAlertAction
>>& aActions
) {
164 aActions
= mActions
.Clone();
169 AlertNotification::GetSource(nsAString
& aSource
) {
170 nsAlertsUtils::GetSourceHostPort(mPrincipal
, aSource
);
175 AlertNotification::GetOpaqueRelaunchData(nsAString
& aOpaqueRelaunchData
) {
176 aOpaqueRelaunchData
= mOpaqueRelaunchData
;
181 AlertNotification::SetOpaqueRelaunchData(const nsAString
& aOpaqueRelaunchData
) {
182 mOpaqueRelaunchData
= aOpaqueRelaunchData
;
187 AlertNotification::LoadImage(uint32_t aTimeout
,
188 nsIAlertNotificationImageListener
* aListener
,
189 nsISupports
* aUserData
, nsICancelable
** aRequest
) {
190 NS_ENSURE_ARG(aListener
);
191 NS_ENSURE_ARG_POINTER(aRequest
);
194 // Exit early if this alert doesn't have an image.
195 if (mImageURL
.IsEmpty()) {
196 return aListener
->OnImageMissing(aUserData
);
198 nsCOMPtr
<nsIURI
> imageURI
;
199 NS_NewURI(getter_AddRefs(imageURI
), mImageURL
);
201 return aListener
->OnImageMissing(aUserData
);
204 RefPtr
<AlertImageRequest
> request
= new AlertImageRequest(
205 imageURI
, mPrincipal
, mInPrivateBrowsing
, aTimeout
, aListener
, aUserData
);
207 request
.forget(aRequest
);
211 NS_IMPL_CYCLE_COLLECTION(AlertImageRequest
, mURI
, mPrincipal
, mListener
,
214 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AlertImageRequest
)
215 NS_INTERFACE_MAP_ENTRY(imgINotificationObserver
)
216 NS_INTERFACE_MAP_ENTRY(nsICancelable
)
217 NS_INTERFACE_MAP_ENTRY(nsITimerCallback
)
218 NS_INTERFACE_MAP_ENTRY(nsINamed
)
219 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, imgINotificationObserver
)
222 NS_IMPL_CYCLE_COLLECTING_ADDREF(AlertImageRequest
)
223 NS_IMPL_CYCLE_COLLECTING_RELEASE(AlertImageRequest
)
225 AlertImageRequest::AlertImageRequest(
226 nsIURI
* aURI
, nsIPrincipal
* aPrincipal
, bool aInPrivateBrowsing
,
227 uint32_t aTimeout
, nsIAlertNotificationImageListener
* aListener
,
228 nsISupports
* aUserData
)
230 mPrincipal(aPrincipal
),
231 mInPrivateBrowsing(aInPrivateBrowsing
),
233 mListener(aListener
),
234 mUserData(aUserData
) {}
236 AlertImageRequest::~AlertImageRequest() {
238 mRequest
->CancelAndForgetObserver(NS_BINDING_ABORTED
);
242 void AlertImageRequest::Notify(imgIRequest
* aRequest
, int32_t aType
,
243 const nsIntRect
* aData
) {
244 MOZ_ASSERT(aRequest
== mRequest
);
246 uint32_t imgStatus
= imgIRequest::STATUS_ERROR
;
247 nsresult rv
= aRequest
->GetImageStatus(&imgStatus
);
248 if (NS_WARN_IF(NS_FAILED(rv
)) || (imgStatus
& imgIRequest::STATUS_ERROR
)) {
253 // If the image is already decoded, `FRAME_COMPLETE` will fire before
254 // `LOAD_COMPLETE`, so we can notify the listener immediately. Otherwise,
255 // we'll need to request a decode when `LOAD_COMPLETE` fires, and wait
256 // for the first frame.
258 if (aType
== imgINotificationObserver::LOAD_COMPLETE
) {
259 if (!(imgStatus
& imgIRequest::STATUS_FRAME_COMPLETE
)) {
260 nsCOMPtr
<imgIContainer
> image
;
261 rv
= aRequest
->GetImage(getter_AddRefs(image
));
262 if (NS_WARN_IF(NS_FAILED(rv
) || !image
)) {
267 // Ask the image to decode at its intrinsic size.
268 int32_t width
= 0, height
= 0;
269 image
->GetWidth(&width
);
270 image
->GetHeight(&height
);
271 image
->RequestDecodeForSize(gfx::IntSize(width
, height
),
272 imgIContainer::FLAG_HIGH_QUALITY_SCALING
);
277 if (aType
== imgINotificationObserver::FRAME_COMPLETE
) {
278 return NotifyComplete();
283 AlertImageRequest::Notify(nsITimer
* aTimer
) {
284 MOZ_ASSERT(aTimer
== mTimer
);
285 return NotifyMissing();
289 AlertImageRequest::GetName(nsACString
& aName
) {
290 aName
.AssignLiteral("AlertImageRequest");
295 AlertImageRequest::Cancel(nsresult aReason
) {
297 mRequest
->Cancel(aReason
);
299 // We call `NotifyMissing` here because we won't receive a `LOAD_COMPLETE`
300 // notification if we cancel the request before it loads (bug 1233086,
301 // comment 33). Once that's fixed, `nsIAlertNotification::loadImage` could
302 // return the underlying `imgIRequest` instead of the wrapper.
303 return NotifyMissing();
306 nsresult
AlertImageRequest::Start() {
307 // Keep the request alive until we notify the image listener.
312 rv
= NS_NewTimerWithCallback(getter_AddRefs(mTimer
), this, mTimeout
,
313 nsITimer::TYPE_ONE_SHOT
);
314 if (NS_WARN_IF(NS_FAILED(rv
))) {
315 return NotifyMissing();
319 // Begin loading the image.
320 imgLoader
* il
= imgLoader::NormalLoader();
322 return NotifyMissing();
325 // Bug 1237405: `LOAD_ANONYMOUS` disables cookies, but we want to use a
326 // temporary cookie jar instead. We should also use
327 // `imgLoader::PrivateBrowsingLoader()` instead of the normal loader.
328 // Unfortunately, the PB loader checks the load group, and asserts if its
329 // load context's PB flag isn't set. The fix is to pass the load group to
330 // `nsIAlertNotification::loadImage`.
331 int32_t loadFlags
= nsIRequest::LOAD_NORMAL
;
332 if (mInPrivateBrowsing
) {
333 loadFlags
= nsIRequest::LOAD_ANONYMOUS
;
336 rv
= il
->LoadImageXPCOM(
337 mURI
, nullptr, nullptr, mPrincipal
, nullptr, this, nullptr, loadFlags
,
338 nullptr, nsIContentPolicy::TYPE_INTERNAL_IMAGE
, getter_AddRefs(mRequest
));
339 if (NS_WARN_IF(NS_FAILED(rv
))) {
340 return NotifyMissing();
346 nsresult
AlertImageRequest::NotifyMissing() {
351 if (nsCOMPtr
<nsIAlertNotificationImageListener
> listener
=
352 std::move(mListener
)) {
353 nsresult rv
= listener
->OnImageMissing(mUserData
);
361 void AlertImageRequest::NotifyComplete() {
366 if (nsCOMPtr
<nsIAlertNotificationImageListener
> listener
=
367 std::move(mListener
)) {
368 listener
->OnImageReady(mUserData
, mRequest
);
373 } // namespace mozilla