/src/mozilla-central/toolkit/components/alerts/AlertNotification.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* This Source Code Form is subject to the terms of the Mozilla Pub |
2 | | * License, v. 2.0. If a copy of the MPL was not distributed with t |
3 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
4 | | |
5 | | #include "mozilla/AlertNotification.h" |
6 | | |
7 | | #include "imgIContainer.h" |
8 | | #include "imgINotificationObserver.h" |
9 | | #include "imgIRequest.h" |
10 | | #include "imgLoader.h" |
11 | | #include "nsAlertsUtils.h" |
12 | | #include "nsComponentManagerUtils.h" |
13 | | #include "nsContentUtils.h" |
14 | | #include "nsNetUtil.h" |
15 | | #include "nsServiceManagerUtils.h" |
16 | | |
17 | | #include "mozilla/Unused.h" |
18 | | |
19 | | namespace mozilla { |
20 | | |
21 | | NS_IMPL_ISUPPORTS(AlertNotification, nsIAlertNotification) |
22 | | |
23 | | AlertNotification::AlertNotification() |
24 | | : mTextClickable(false) |
25 | | , mInPrivateBrowsing(false) |
26 | 0 | {} |
27 | | |
28 | | AlertNotification::~AlertNotification() |
29 | 0 | {} |
30 | | |
31 | | NS_IMETHODIMP |
32 | | AlertNotification::Init(const nsAString& aName, const nsAString& aImageURL, |
33 | | const nsAString& aTitle, const nsAString& aText, |
34 | | bool aTextClickable, const nsAString& aCookie, |
35 | | const nsAString& aDir, const nsAString& aLang, |
36 | | const nsAString& aData, nsIPrincipal* aPrincipal, |
37 | | bool aInPrivateBrowsing, bool aRequireInteraction) |
38 | 0 | { |
39 | 0 | mName = aName; |
40 | 0 | mImageURL = aImageURL; |
41 | 0 | mTitle = aTitle; |
42 | 0 | mText = aText; |
43 | 0 | mTextClickable = aTextClickable; |
44 | 0 | mCookie = aCookie; |
45 | 0 | mDir = aDir; |
46 | 0 | mLang = aLang; |
47 | 0 | mData = aData; |
48 | 0 | mPrincipal = aPrincipal; |
49 | 0 | mInPrivateBrowsing = aInPrivateBrowsing; |
50 | 0 | mRequireInteraction = aRequireInteraction; |
51 | 0 | return NS_OK; |
52 | 0 | } |
53 | | |
54 | | NS_IMETHODIMP |
55 | | AlertNotification::GetName(nsAString& aName) |
56 | 0 | { |
57 | 0 | aName = mName; |
58 | 0 | return NS_OK; |
59 | 0 | } |
60 | | |
61 | | NS_IMETHODIMP |
62 | | AlertNotification::GetImageURL(nsAString& aImageURL) |
63 | 0 | { |
64 | 0 | aImageURL = mImageURL; |
65 | 0 | return NS_OK; |
66 | 0 | } |
67 | | |
68 | | NS_IMETHODIMP |
69 | | AlertNotification::GetTitle(nsAString& aTitle) |
70 | 0 | { |
71 | 0 | aTitle = mTitle; |
72 | 0 | return NS_OK; |
73 | 0 | } |
74 | | |
75 | | NS_IMETHODIMP |
76 | | AlertNotification::GetText(nsAString& aText) |
77 | 0 | { |
78 | 0 | aText = mText; |
79 | 0 | return NS_OK; |
80 | 0 | } |
81 | | |
82 | | NS_IMETHODIMP |
83 | | AlertNotification::GetTextClickable(bool* aTextClickable) |
84 | 0 | { |
85 | 0 | *aTextClickable = mTextClickable; |
86 | 0 | return NS_OK; |
87 | 0 | } |
88 | | |
89 | | NS_IMETHODIMP |
90 | | AlertNotification::GetCookie(nsAString& aCookie) |
91 | 0 | { |
92 | 0 | aCookie = mCookie; |
93 | 0 | return NS_OK; |
94 | 0 | } |
95 | | |
96 | | NS_IMETHODIMP |
97 | | AlertNotification::GetDir(nsAString& aDir) |
98 | 0 | { |
99 | 0 | aDir = mDir; |
100 | 0 | return NS_OK; |
101 | 0 | } |
102 | | |
103 | | NS_IMETHODIMP |
104 | | AlertNotification::GetLang(nsAString& aLang) |
105 | 0 | { |
106 | 0 | aLang = mLang; |
107 | 0 | return NS_OK; |
108 | 0 | } |
109 | | |
110 | | NS_IMETHODIMP |
111 | | AlertNotification::GetRequireInteraction(bool* aRequireInteraction) |
112 | 0 | { |
113 | 0 | *aRequireInteraction = mRequireInteraction; |
114 | 0 | return NS_OK; |
115 | 0 | } |
116 | | |
117 | | NS_IMETHODIMP |
118 | | AlertNotification::GetData(nsAString& aData) |
119 | 0 | { |
120 | 0 | aData = mData; |
121 | 0 | return NS_OK; |
122 | 0 | } |
123 | | |
124 | | NS_IMETHODIMP |
125 | | AlertNotification::GetPrincipal(nsIPrincipal** aPrincipal) |
126 | 0 | { |
127 | 0 | NS_IF_ADDREF(*aPrincipal = mPrincipal); |
128 | 0 | return NS_OK; |
129 | 0 | } |
130 | | |
131 | | NS_IMETHODIMP |
132 | | AlertNotification::GetURI(nsIURI** aURI) |
133 | 0 | { |
134 | 0 | if (!nsAlertsUtils::IsActionablePrincipal(mPrincipal)) { |
135 | 0 | *aURI = nullptr; |
136 | 0 | return NS_OK; |
137 | 0 | } |
138 | 0 | return mPrincipal->GetURI(aURI); |
139 | 0 | } |
140 | | |
141 | | NS_IMETHODIMP |
142 | | AlertNotification::GetInPrivateBrowsing(bool* aInPrivateBrowsing) |
143 | 0 | { |
144 | 0 | *aInPrivateBrowsing = mInPrivateBrowsing; |
145 | 0 | return NS_OK; |
146 | 0 | } |
147 | | |
148 | | NS_IMETHODIMP |
149 | | AlertNotification::GetActionable(bool* aActionable) |
150 | 0 | { |
151 | 0 | *aActionable = nsAlertsUtils::IsActionablePrincipal(mPrincipal); |
152 | 0 | return NS_OK; |
153 | 0 | } |
154 | | |
155 | | NS_IMETHODIMP |
156 | | AlertNotification::GetSource(nsAString& aSource) |
157 | 0 | { |
158 | 0 | nsAlertsUtils::GetSourceHostPort(mPrincipal, aSource); |
159 | 0 | return NS_OK; |
160 | 0 | } |
161 | | |
162 | | NS_IMETHODIMP |
163 | | AlertNotification::LoadImage(uint32_t aTimeout, |
164 | | nsIAlertNotificationImageListener* aListener, |
165 | | nsISupports* aUserData, |
166 | | nsICancelable** aRequest) |
167 | 0 | { |
168 | 0 | NS_ENSURE_ARG(aListener); |
169 | 0 | NS_ENSURE_ARG_POINTER(aRequest); |
170 | 0 | *aRequest = nullptr; |
171 | 0 |
|
172 | 0 | // Exit early if this alert doesn't have an image. |
173 | 0 | if (mImageURL.IsEmpty()) { |
174 | 0 | return aListener->OnImageMissing(aUserData); |
175 | 0 | } |
176 | 0 | nsCOMPtr<nsIURI> imageURI; |
177 | 0 | NS_NewURI(getter_AddRefs(imageURI), mImageURL); |
178 | 0 | if (!imageURI) { |
179 | 0 | return aListener->OnImageMissing(aUserData); |
180 | 0 | } |
181 | 0 | |
182 | 0 | RefPtr<AlertImageRequest> request = new AlertImageRequest(imageURI, mPrincipal, |
183 | 0 | mInPrivateBrowsing, |
184 | 0 | aTimeout, aListener, |
185 | 0 | aUserData); |
186 | 0 | nsresult rv = request->Start(); |
187 | 0 | request.forget(aRequest); |
188 | 0 | return rv; |
189 | 0 | } |
190 | | |
191 | | NS_IMPL_CYCLE_COLLECTION(AlertImageRequest, mURI, mPrincipal, mListener, |
192 | | mUserData) |
193 | | |
194 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AlertImageRequest) |
195 | 0 | NS_INTERFACE_MAP_ENTRY(imgINotificationObserver) |
196 | 0 | NS_INTERFACE_MAP_ENTRY(nsICancelable) |
197 | 0 | NS_INTERFACE_MAP_ENTRY(nsITimerCallback) |
198 | 0 | NS_INTERFACE_MAP_ENTRY(nsINamed) |
199 | 0 | NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, imgINotificationObserver) |
200 | 0 | NS_INTERFACE_MAP_END |
201 | | |
202 | | NS_IMPL_CYCLE_COLLECTING_ADDREF(AlertImageRequest) |
203 | | NS_IMPL_CYCLE_COLLECTING_RELEASE(AlertImageRequest) |
204 | | |
205 | | AlertImageRequest::AlertImageRequest(nsIURI* aURI, nsIPrincipal* aPrincipal, |
206 | | bool aInPrivateBrowsing, uint32_t aTimeout, |
207 | | nsIAlertNotificationImageListener* aListener, |
208 | | nsISupports* aUserData) |
209 | | : mURI(aURI) |
210 | | , mPrincipal(aPrincipal) |
211 | | , mInPrivateBrowsing(aInPrivateBrowsing) |
212 | | , mTimeout(aTimeout) |
213 | | , mListener(aListener) |
214 | | , mUserData(aUserData) |
215 | 0 | {} |
216 | | |
217 | | AlertImageRequest::~AlertImageRequest() |
218 | 0 | { |
219 | 0 | if (mRequest) { |
220 | 0 | mRequest->CancelAndForgetObserver(NS_BINDING_ABORTED); |
221 | 0 | } |
222 | 0 | } |
223 | | |
224 | | NS_IMETHODIMP |
225 | | AlertImageRequest::Notify(imgIRequest* aRequest, int32_t aType, |
226 | | const nsIntRect* aData) |
227 | 0 | { |
228 | 0 | MOZ_ASSERT(aRequest == mRequest); |
229 | 0 |
|
230 | 0 | uint32_t imgStatus = imgIRequest::STATUS_ERROR; |
231 | 0 | nsresult rv = aRequest->GetImageStatus(&imgStatus); |
232 | 0 | if (NS_WARN_IF(NS_FAILED(rv)) || |
233 | 0 | (imgStatus & imgIRequest::STATUS_ERROR)) { |
234 | 0 | return NotifyMissing(); |
235 | 0 | } |
236 | 0 | |
237 | 0 | // If the image is already decoded, `FRAME_COMPLETE` will fire before |
238 | 0 | // `LOAD_COMPLETE`, so we can notify the listener immediately. Otherwise, |
239 | 0 | // we'll need to request a decode when `LOAD_COMPLETE` fires, and wait |
240 | 0 | // for the first frame. |
241 | 0 | |
242 | 0 | if (aType == imgINotificationObserver::LOAD_COMPLETE) { |
243 | 0 | if (!(imgStatus & imgIRequest::STATUS_FRAME_COMPLETE)) { |
244 | 0 | nsCOMPtr<imgIContainer> image; |
245 | 0 | rv = aRequest->GetImage(getter_AddRefs(image)); |
246 | 0 | if (NS_WARN_IF(NS_FAILED(rv) || !image)) { |
247 | 0 | return NotifyMissing(); |
248 | 0 | } |
249 | 0 | |
250 | 0 | // Ask the image to decode at its intrinsic size. |
251 | 0 | int32_t width = 0, height = 0; |
252 | 0 | image->GetWidth(&width); |
253 | 0 | image->GetHeight(&height); |
254 | 0 | image->RequestDecodeForSize(gfx::IntSize(width, height), imgIContainer::FLAG_NONE); |
255 | 0 | } |
256 | 0 | return NS_OK; |
257 | 0 | } |
258 | 0 | |
259 | 0 | if (aType == imgINotificationObserver::FRAME_COMPLETE) { |
260 | 0 | return NotifyComplete(); |
261 | 0 | } |
262 | 0 | |
263 | 0 | return NS_OK; |
264 | 0 | } |
265 | | |
266 | | NS_IMETHODIMP |
267 | | AlertImageRequest::Notify(nsITimer* aTimer) |
268 | 0 | { |
269 | 0 | MOZ_ASSERT(aTimer == mTimer); |
270 | 0 | return NotifyMissing(); |
271 | 0 | } |
272 | | |
273 | | NS_IMETHODIMP |
274 | | AlertImageRequest::GetName(nsACString& aName) |
275 | 0 | { |
276 | 0 | aName.AssignLiteral("AlertImageRequest"); |
277 | 0 | return NS_OK; |
278 | 0 | } |
279 | | |
280 | | NS_IMETHODIMP |
281 | | AlertImageRequest::Cancel(nsresult aReason) |
282 | 0 | { |
283 | 0 | if (mRequest) { |
284 | 0 | mRequest->Cancel(aReason); |
285 | 0 | } |
286 | 0 | // We call `NotifyMissing` here because we won't receive a `LOAD_COMPLETE` |
287 | 0 | // notification if we cancel the request before it loads (bug 1233086, |
288 | 0 | // comment 33). Once that's fixed, `nsIAlertNotification::loadImage` could |
289 | 0 | // return the underlying `imgIRequest` instead of the wrapper. |
290 | 0 | return NotifyMissing(); |
291 | 0 | } |
292 | | |
293 | | nsresult |
294 | | AlertImageRequest::Start() |
295 | 0 | { |
296 | 0 | // Keep the request alive until we notify the image listener. |
297 | 0 | NS_ADDREF_THIS(); |
298 | 0 |
|
299 | 0 | nsresult rv; |
300 | 0 | if (mTimeout > 0) { |
301 | 0 | rv = NS_NewTimerWithCallback(getter_AddRefs(mTimer), |
302 | 0 | this, mTimeout, |
303 | 0 | nsITimer::TYPE_ONE_SHOT); |
304 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
305 | 0 | return NotifyMissing(); |
306 | 0 | } |
307 | 0 | } |
308 | 0 | |
309 | 0 | // Begin loading the image. |
310 | 0 | imgLoader* il = imgLoader::NormalLoader(); |
311 | 0 | if (!il) { |
312 | 0 | return NotifyMissing(); |
313 | 0 | } |
314 | 0 | |
315 | 0 | // Bug 1237405: `LOAD_ANONYMOUS` disables cookies, but we want to use a |
316 | 0 | // temporary cookie jar instead. We should also use |
317 | 0 | // `imgLoader::PrivateBrowsingLoader()` instead of the normal loader. |
318 | 0 | // Unfortunately, the PB loader checks the load group, and asserts if its |
319 | 0 | // load context's PB flag isn't set. The fix is to pass the load group to |
320 | 0 | // `nsIAlertNotification::loadImage`. |
321 | 0 | int32_t loadFlags = mInPrivateBrowsing ? nsIRequest::LOAD_ANONYMOUS : |
322 | 0 | nsIRequest::LOAD_NORMAL; |
323 | 0 |
|
324 | 0 | rv = il->LoadImageXPCOM(mURI, nullptr, nullptr, |
325 | 0 | NS_LITERAL_STRING("default"), mPrincipal, nullptr, |
326 | 0 | this, nullptr, loadFlags, nullptr, |
327 | 0 | nsIContentPolicy::TYPE_INTERNAL_IMAGE, |
328 | 0 | getter_AddRefs(mRequest)); |
329 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
330 | 0 | return NotifyMissing(); |
331 | 0 | } |
332 | 0 | |
333 | 0 | return NS_OK; |
334 | 0 | } |
335 | | |
336 | | nsresult |
337 | | AlertImageRequest::NotifyMissing() |
338 | 0 | { |
339 | 0 | if (mTimer) { |
340 | 0 | mTimer->Cancel(); |
341 | 0 | mTimer = nullptr; |
342 | 0 | } |
343 | 0 | if (nsCOMPtr<nsIAlertNotificationImageListener> listener = mListener.forget()) { |
344 | 0 | nsresult rv = listener->OnImageMissing(mUserData); |
345 | 0 | NS_RELEASE_THIS(); |
346 | 0 | return rv; |
347 | 0 | } |
348 | 0 | return NS_OK; |
349 | 0 | } |
350 | | |
351 | | nsresult |
352 | | AlertImageRequest::NotifyComplete() |
353 | 0 | { |
354 | 0 | if (mTimer) { |
355 | 0 | mTimer->Cancel(); |
356 | 0 | mTimer = nullptr; |
357 | 0 | } |
358 | 0 | if (nsCOMPtr<nsIAlertNotificationImageListener> listener = mListener.forget()) { |
359 | 0 | nsresult rv = listener->OnImageReady(mUserData, mRequest); |
360 | 0 | NS_RELEASE_THIS(); |
361 | 0 | return rv; |
362 | 0 | } |
363 | 0 | return NS_OK; |
364 | 0 | } |
365 | | |
366 | | } // namespace mozilla |