Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/url/URLWorker.cpp
Line
Count
Source (jump to first uncovered line)
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/. */
6
7
#include "URLWorker.h"
8
9
#include "mozilla/dom/Blob.h"
10
#include "mozilla/dom/BlobURLProtocolHandler.h"
11
#include "mozilla/dom/WorkerPrivate.h"
12
#include "mozilla/dom/WorkerRunnable.h"
13
#include "mozilla/dom/WorkerScope.h"
14
#include "mozilla/Unused.h"
15
#include "nsProxyRelease.h"
16
#include "nsStandardURL.h"
17
#include "nsURLHelper.h"
18
19
namespace mozilla {
20
21
using net::nsStandardURL;
22
23
namespace dom {
24
25
// This class creates an URL from a DOM Blob on the main thread.
26
class CreateURLRunnable : public WorkerMainThreadRunnable
27
{
28
private:
29
  BlobImpl* mBlobImpl;
30
  nsAString& mURL;
31
32
public:
33
  CreateURLRunnable(WorkerPrivate* aWorkerPrivate, BlobImpl* aBlobImpl,
34
                    nsAString& aURL)
35
  : WorkerMainThreadRunnable(aWorkerPrivate,
36
                             NS_LITERAL_CSTRING("URL :: CreateURL"))
37
  , mBlobImpl(aBlobImpl)
38
  , mURL(aURL)
39
0
  {
40
0
    MOZ_ASSERT(aBlobImpl);
41
0
42
0
    DebugOnly<bool> isMutable;
43
0
    MOZ_ASSERT(NS_SUCCEEDED(aBlobImpl->GetMutable(&isMutable)));
44
0
    MOZ_ASSERT(!isMutable);
45
0
  }
46
47
  bool
48
  MainThreadRun() override
49
0
  {
50
0
    using namespace mozilla::ipc;
51
0
52
0
    AssertIsOnMainThread();
53
0
54
0
    DebugOnly<bool> isMutable;
55
0
    MOZ_ASSERT(NS_SUCCEEDED(mBlobImpl->GetMutable(&isMutable)));
56
0
    MOZ_ASSERT(!isMutable);
57
0
58
0
    nsCOMPtr<nsIPrincipal> principal = mWorkerPrivate->GetPrincipal();
59
0
60
0
    nsAutoCString url;
61
0
    nsresult rv =
62
0
      BlobURLProtocolHandler::AddDataEntry(mBlobImpl, principal, url);
63
0
64
0
    if (NS_FAILED(rv)) {
65
0
      NS_WARNING("Failed to add data entry for the blob!");
66
0
      SetDOMStringToNull(mURL);
67
0
      return false;
68
0
    }
69
0
70
0
    if (!mWorkerPrivate->IsSharedWorker() &&
71
0
        !mWorkerPrivate->IsServiceWorker()) {
72
0
      // Walk up to top worker object.
73
0
      WorkerPrivate* wp = mWorkerPrivate;
74
0
      while (WorkerPrivate* parent = wp->GetParent()) {
75
0
        wp = parent;
76
0
      }
77
0
78
0
      nsCOMPtr<nsIScriptContext> sc = wp->GetScriptContext();
79
0
      // We could not have a ScriptContext in JSM code. In this case, we leak.
80
0
      if (sc) {
81
0
        nsCOMPtr<nsIGlobalObject> global = sc->GetGlobalObject();
82
0
        MOZ_ASSERT(global);
83
0
84
0
        global->RegisterHostObjectURI(url);
85
0
      }
86
0
    }
87
0
88
0
    mURL = NS_ConvertUTF8toUTF16(url);
89
0
    return true;
90
0
  }
91
};
92
93
// This class revokes an URL on the main thread.
94
class RevokeURLRunnable : public WorkerMainThreadRunnable
95
{
96
private:
97
  const nsString mURL;
98
99
public:
100
  RevokeURLRunnable(WorkerPrivate* aWorkerPrivate,
101
                    const nsAString& aURL)
102
  : WorkerMainThreadRunnable(aWorkerPrivate,
103
                             NS_LITERAL_CSTRING("URL :: RevokeURL"))
104
  , mURL(aURL)
105
0
  {}
106
107
  bool
108
  MainThreadRun() override
109
0
  {
110
0
    AssertIsOnMainThread();
111
0
112
0
    NS_ConvertUTF16toUTF8 url(mURL);
113
0
114
0
    nsIPrincipal* urlPrincipal =
115
0
      BlobURLProtocolHandler::GetDataEntryPrincipal(url);
116
0
117
0
    nsCOMPtr<nsIPrincipal> principal = mWorkerPrivate->GetPrincipal();
118
0
119
0
    bool subsumes;
120
0
    if (urlPrincipal &&
121
0
        NS_SUCCEEDED(principal->Subsumes(urlPrincipal, &subsumes)) &&
122
0
        subsumes) {
123
0
      BlobURLProtocolHandler::RemoveDataEntry(url);
124
0
    }
125
0
126
0
    if (!mWorkerPrivate->IsSharedWorker() &&
127
0
        !mWorkerPrivate->IsServiceWorker()) {
128
0
      // Walk up to top worker object.
129
0
      WorkerPrivate* wp = mWorkerPrivate;
130
0
      while (WorkerPrivate* parent = wp->GetParent()) {
131
0
        wp = parent;
132
0
      }
133
0
134
0
      nsCOMPtr<nsIScriptContext> sc = wp->GetScriptContext();
135
0
      // We could not have a ScriptContext in JSM code. In this case, we leak.
136
0
      if (sc) {
137
0
        nsCOMPtr<nsIGlobalObject> global = sc->GetGlobalObject();
138
0
        MOZ_ASSERT(global);
139
0
140
0
        global->UnregisterHostObjectURI(url);
141
0
      }
142
0
    }
143
0
144
0
    return true;
145
0
  }
146
};
147
148
// This class checks if an URL is valid on the main thread.
149
class IsValidURLRunnable : public WorkerMainThreadRunnable
150
{
151
private:
152
  const nsString mURL;
153
  bool mValid;
154
155
public:
156
  IsValidURLRunnable(WorkerPrivate* aWorkerPrivate,
157
                     const nsAString& aURL)
158
  : WorkerMainThreadRunnable(aWorkerPrivate,
159
                             NS_LITERAL_CSTRING("URL :: IsValidURL"))
160
  , mURL(aURL)
161
  , mValid(false)
162
0
  {}
163
164
  bool
165
  MainThreadRun() override
166
0
  {
167
0
    AssertIsOnMainThread();
168
0
169
0
    NS_ConvertUTF16toUTF8 url(mURL);
170
0
    mValid = BlobURLProtocolHandler::HasDataEntry(url);
171
0
172
0
    return true;
173
0
  }
174
175
  bool
176
  IsValidURL() const
177
0
  {
178
0
    return mValid;
179
0
  }
180
};
181
182
// This class creates a URL object on the main thread.
183
class ConstructorRunnable : public WorkerMainThreadRunnable
184
{
185
private:
186
  const nsString mURL;
187
188
  nsString mBase; // IsVoid() if we have no base URI string.
189
190
  nsCOMPtr<nsIURI> mRetval;
191
192
public:
193
  ConstructorRunnable(WorkerPrivate* aWorkerPrivate,
194
                      const nsAString& aURL, const Optional<nsAString>& aBase)
195
  : WorkerMainThreadRunnable(aWorkerPrivate,
196
                             NS_LITERAL_CSTRING("URL :: Constructor"))
197
  , mURL(aURL)
198
0
  {
199
0
    if (aBase.WasPassed()) {
200
0
      mBase = aBase.Value();
201
0
    } else {
202
0
      mBase.SetIsVoid(true);
203
0
    }
204
0
    mWorkerPrivate->AssertIsOnWorkerThread();
205
0
  }
206
207
  bool
208
  MainThreadRun() override
209
0
  {
210
0
    AssertIsOnMainThread();
211
0
212
0
    nsCOMPtr<nsIURI> baseUri;
213
0
    if (!mBase.IsVoid()) {
214
0
      nsresult rv = NS_NewURI(getter_AddRefs(baseUri), mBase, nullptr, nullptr,
215
0
                              nsContentUtils::GetIOService());
216
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
217
0
        return true;
218
0
      }
219
0
    }
220
0
221
0
    nsCOMPtr<nsIURI> uri;
222
0
    nsresult rv = NS_NewURI(getter_AddRefs(uri), mURL, nullptr, baseUri,
223
0
                            nsContentUtils::GetIOService());
224
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
225
0
      return true;
226
0
    }
227
0
228
0
    mRetval = std::move(uri);
229
0
    return true;
230
0
  }
231
232
  nsIURI*
233
  GetURI(ErrorResult& aRv) const
234
0
  {
235
0
    MOZ_ASSERT(mWorkerPrivate);
236
0
    mWorkerPrivate->AssertIsOnWorkerThread();
237
0
238
0
    if (!mRetval) {
239
0
      aRv.ThrowTypeError<MSG_INVALID_URL>(mURL);
240
0
    }
241
0
242
0
    return mRetval;
243
0
  }
244
};
245
246
class OriginGetterRunnable : public WorkerMainThreadRunnable
247
{
248
public:
249
  OriginGetterRunnable(WorkerPrivate* aWorkerPrivate,
250
                       nsAString& aValue,
251
                       nsIURI* aURI)
252
  : WorkerMainThreadRunnable(aWorkerPrivate,
253
                             // We can have telemetry keys for each getter when
254
                             // needed.
255
                             NS_LITERAL_CSTRING("URL :: getter"))
256
  , mValue(aValue)
257
  , mURI(aURI)
258
0
  {
259
0
    mWorkerPrivate->AssertIsOnWorkerThread();
260
0
  }
261
262
  bool
263
  MainThreadRun() override
264
0
  {
265
0
    AssertIsOnMainThread();
266
0
    ErrorResult rv;
267
0
    nsContentUtils::GetUTFOrigin(mURI, mValue);
268
0
    return true;
269
0
  }
270
271
  void
272
  Dispatch(ErrorResult& aRv)
273
0
  {
274
0
    WorkerMainThreadRunnable::Dispatch(Canceling, aRv);
275
0
  }
276
277
private:
278
  nsAString& mValue;
279
  nsCOMPtr<nsIURI> mURI;
280
};
281
282
class ProtocolSetterRunnable : public WorkerMainThreadRunnable
283
{
284
public:
285
  ProtocolSetterRunnable(WorkerPrivate* aWorkerPrivate,
286
                         const nsACString& aValue,
287
                         nsIURI* aURI)
288
  : WorkerMainThreadRunnable(aWorkerPrivate,
289
                             NS_LITERAL_CSTRING("ProtocolSetterRunnable"))
290
  , mValue(aValue)
291
  , mURI(aURI)
292
0
  {
293
0
    mWorkerPrivate->AssertIsOnWorkerThread();
294
0
  }
295
296
  bool
297
  MainThreadRun() override
298
0
  {
299
0
    AssertIsOnMainThread();
300
0
301
0
    nsCOMPtr<nsIURI> clone;
302
0
    nsresult rv = NS_MutateURI(mURI)
303
0
                    .SetScheme(mValue)
304
0
                    .Finalize(clone);
305
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
306
0
      return true;
307
0
    }
308
0
309
0
    nsAutoCString href;
310
0
    rv = clone->GetSpec(href);
311
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
312
0
      return true;
313
0
    }
314
0
315
0
    nsCOMPtr<nsIURI> uri;
316
0
    rv = NS_NewURI(getter_AddRefs(uri), href);
317
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
318
0
      return true;
319
0
    }
320
0
321
0
    mRetval = std::move(uri);
322
0
    return true;
323
0
  }
324
325
  void
326
  Dispatch(ErrorResult& aRv)
327
0
  {
328
0
    WorkerMainThreadRunnable::Dispatch(Canceling, aRv);
329
0
  }
330
331
  nsIURI*
332
  GetRetval() const
333
0
  {
334
0
    return mRetval;
335
0
  }
336
337
private:
338
  const nsCString mValue;
339
  nsCOMPtr<nsIURI> mURI;
340
  nsCOMPtr<nsIURI> mRetval;
341
};
342
343
/* static */ already_AddRefed<URLWorker>
344
URLWorker::Constructor(const GlobalObject& aGlobal, const nsAString& aURL,
345
                       const Optional<nsAString>& aBase, ErrorResult& aRv)
346
0
{
347
0
  JSContext* cx = aGlobal.Context();
348
0
  WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
349
0
350
0
  RefPtr<URLWorker> url = new URLWorker(workerPrivate);
351
0
  url->Init(aURL, aBase, aRv);
352
0
353
0
  return aRv.Failed() ? nullptr : url.forget();
354
0
}
355
356
/* static */ already_AddRefed<URLWorker>
357
URLWorker::Constructor(const GlobalObject& aGlobal, const nsAString& aURL,
358
                       const nsAString& aBase, ErrorResult& aRv)
359
0
{
360
0
  Optional<nsAString> base;
361
0
  base = &aBase;
362
0
363
0
  return Constructor(aGlobal, aURL, base, aRv);
364
0
}
365
366
/* static */ void
367
URLWorker::CreateObjectURL(const GlobalObject& aGlobal, Blob& aBlob,
368
                           nsAString& aResult, mozilla::ErrorResult& aRv)
369
0
{
370
0
  JSContext* cx = aGlobal.Context();
371
0
  WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
372
0
373
0
  RefPtr<BlobImpl> blobImpl = aBlob.Impl();
374
0
  MOZ_ASSERT(blobImpl);
375
0
376
0
  aRv = blobImpl->SetMutable(false);
377
0
  if (NS_WARN_IF(aRv.Failed())) {
378
0
    return;
379
0
  }
380
0
381
0
  RefPtr<CreateURLRunnable> runnable =
382
0
    new CreateURLRunnable(workerPrivate, blobImpl, aResult);
383
0
384
0
  runnable->Dispatch(Canceling, aRv);
385
0
  if (NS_WARN_IF(aRv.Failed())) {
386
0
    return;
387
0
  }
388
0
389
0
  if (workerPrivate->IsSharedWorker() || workerPrivate->IsServiceWorker()) {
390
0
    WorkerGlobalScope* scope = workerPrivate->GlobalScope();
391
0
    MOZ_ASSERT(scope);
392
0
393
0
    scope->RegisterHostObjectURI(NS_ConvertUTF16toUTF8(aResult));
394
0
  }
395
0
}
396
397
/* static */ void
398
URLWorker::RevokeObjectURL(const GlobalObject& aGlobal, const nsAString& aUrl,
399
                           ErrorResult& aRv)
400
0
{
401
0
  JSContext* cx = aGlobal.Context();
402
0
  WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
403
0
404
0
  RefPtr<RevokeURLRunnable> runnable =
405
0
    new RevokeURLRunnable(workerPrivate, aUrl);
406
0
407
0
  runnable->Dispatch(Canceling, aRv);
408
0
  if (NS_WARN_IF(aRv.Failed())) {
409
0
    return;
410
0
  }
411
0
412
0
  if (workerPrivate->IsSharedWorker() || workerPrivate->IsServiceWorker()) {
413
0
    WorkerGlobalScope* scope = workerPrivate->GlobalScope();
414
0
    MOZ_ASSERT(scope);
415
0
416
0
    scope->UnregisterHostObjectURI(NS_ConvertUTF16toUTF8(aUrl));
417
0
  }
418
0
}
419
420
/* static */ bool
421
URLWorker::IsValidURL(const GlobalObject& aGlobal, const nsAString& aUrl,
422
                      ErrorResult& aRv)
423
0
{
424
0
  JSContext* cx = aGlobal.Context();
425
0
  WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
426
0
427
0
  RefPtr<IsValidURLRunnable> runnable =
428
0
    new IsValidURLRunnable(workerPrivate, aUrl);
429
0
430
0
  runnable->Dispatch(Canceling, aRv);
431
0
  if (NS_WARN_IF(aRv.Failed())) {
432
0
    return false;
433
0
  }
434
0
435
0
  return runnable->IsValidURL();
436
0
}
437
438
URLWorker::URLWorker(WorkerPrivate* aWorkerPrivate)
439
  : URL(nullptr)
440
  , mWorkerPrivate(aWorkerPrivate)
441
0
{}
442
443
void
444
URLWorker::Init(const nsAString& aURL, const Optional<nsAString>& aBase,
445
                ErrorResult& aRv)
446
0
{
447
0
  nsAutoCString scheme;
448
0
  nsresult rv = net_ExtractURLScheme(NS_ConvertUTF16toUTF8(aURL), scheme);
449
0
  if (NS_FAILED(rv)) {
450
0
    // this may be a relative URL, check baseURL
451
0
    if (!aBase.WasPassed()) {
452
0
      aRv.ThrowTypeError<MSG_INVALID_URL>(aURL);
453
0
      return;
454
0
    }
455
0
    rv = net_ExtractURLScheme(NS_ConvertUTF16toUTF8(aBase.Value()), scheme);
456
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
457
0
      aRv.ThrowTypeError<MSG_INVALID_URL>(aURL);
458
0
      return;
459
0
    }
460
0
  }
461
0
462
0
  // create url proxy
463
0
  RefPtr<ConstructorRunnable> runnable =
464
0
    new ConstructorRunnable(mWorkerPrivate, aURL, aBase);
465
0
  runnable->Dispatch(Canceling, aRv);
466
0
  if (NS_WARN_IF(aRv.Failed())) {
467
0
    return;
468
0
  }
469
0
470
0
  nsCOMPtr<nsIURI> uri = runnable->GetURI(aRv);
471
0
  if (NS_WARN_IF(aRv.Failed())) {
472
0
    return;
473
0
  }
474
0
475
0
  SetURI(uri.forget());
476
0
}
477
478
0
URLWorker::~URLWorker() = default;
479
480
void
481
URLWorker::SetHref(const nsAString& aHref, ErrorResult& aRv)
482
0
{
483
0
  nsAutoCString scheme;
484
0
  nsresult rv = net_ExtractURLScheme(NS_ConvertUTF16toUTF8(aHref), scheme);
485
0
  if (NS_FAILED(rv)) {
486
0
    aRv.ThrowTypeError<MSG_INVALID_URL>(aHref);
487
0
    return;
488
0
  }
489
0
490
0
  RefPtr<ConstructorRunnable> runnable =
491
0
    new ConstructorRunnable(mWorkerPrivate, aHref, Optional<nsAString>());
492
0
  runnable->Dispatch(Canceling, aRv);
493
0
  if (NS_WARN_IF(aRv.Failed())) {
494
0
    return;
495
0
  }
496
0
497
0
  nsCOMPtr<nsIURI> uri = runnable->GetURI(aRv);
498
0
  if (NS_WARN_IF(aRv.Failed())) {
499
0
    return;
500
0
  }
501
0
502
0
  SetURI(uri.forget());
503
0
  UpdateURLSearchParams();
504
0
}
505
506
void
507
URLWorker::GetOrigin(nsAString& aOrigin, ErrorResult& aRv) const
508
0
{
509
0
  RefPtr<OriginGetterRunnable> runnable =
510
0
    new OriginGetterRunnable(mWorkerPrivate, aOrigin, GetURI());
511
0
512
0
  runnable->Dispatch(aRv);
513
0
}
514
515
void
516
URLWorker::SetProtocol(const nsAString& aProtocol, ErrorResult& aRv)
517
0
{
518
0
  nsAString::const_iterator start, end;
519
0
  aProtocol.BeginReading(start);
520
0
  aProtocol.EndReading(end);
521
0
  nsAString::const_iterator iter(start);
522
0
523
0
  FindCharInReadable(':', iter, end);
524
0
  NS_ConvertUTF16toUTF8 scheme(Substring(start, iter));
525
0
526
0
  RefPtr<ProtocolSetterRunnable> runnable =
527
0
    new ProtocolSetterRunnable(mWorkerPrivate, scheme, GetURI());
528
0
  runnable->Dispatch(aRv);
529
0
  if (NS_WARN_IF(aRv.Failed())) {
530
0
    return;
531
0
  }
532
0
533
0
  nsCOMPtr<nsIURI> uri = runnable->GetRetval();
534
0
  if (NS_WARN_IF(!uri)) {
535
0
    return;
536
0
  }
537
0
538
0
  SetURI(uri.forget());
539
0
}
540
541
} // namespace dom
542
} // namespace mozilla