Coverage Report

Created: 2018-09-25 14:53

/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