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