Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/protocol/res/SubstitutingProtocolHandler.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 file,
5
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "mozilla/chrome/RegistryMessageUtils.h"
8
#include "mozilla/dom/ContentParent.h"
9
#include "mozilla/Unused.h"
10
11
#include "SubstitutingProtocolHandler.h"
12
#include "nsIChannel.h"
13
#include "nsIIOService.h"
14
#include "nsIFile.h"
15
#include "nsNetCID.h"
16
#include "nsNetUtil.h"
17
#include "nsReadableUtils.h"
18
#include "nsURLHelper.h"
19
#include "nsEscape.h"
20
21
using mozilla::dom::ContentParent;
22
23
namespace mozilla {
24
namespace net {
25
26
// Log module for Substituting Protocol logging. We keep the pre-existing module
27
// name of "nsResProtocol" to avoid disruption.
28
static LazyLogModule gResLog("nsResProtocol");
29
30
static NS_DEFINE_CID(kSubstitutingURLCID, NS_SUBSTITUTINGURL_CID);
31
32
//---------------------------------------------------------------------------------
33
// SubstitutingURL : overrides nsStandardURL::GetFile to provide nsIFile resolution
34
//---------------------------------------------------------------------------------
35
36
// The list of interfaces should be in sync with nsStandardURL
37
// Queries this list of interfaces. If none match, it queries mURI.
38
NS_IMPL_NSIURIMUTATOR_ISUPPORTS(SubstitutingURL::Mutator,
39
                                nsIURISetters,
40
                                nsIURIMutator,
41
                                nsIStandardURLMutator,
42
                                nsIURLMutator,
43
                                nsIFileURLMutator,
44
                                nsISerializable)
45
46
nsresult
47
SubstitutingURL::EnsureFile()
48
4
{
49
4
  nsAutoCString ourScheme;
50
4
  nsresult rv = GetScheme(ourScheme);
51
4
  NS_ENSURE_SUCCESS(rv, rv);
52
4
53
4
  // Get the handler associated with this scheme. It would be nice to just
54
4
  // pass this in when constructing SubstitutingURLs, but we need a generic
55
4
  // factory constructor.
56
4
  nsCOMPtr<nsIIOService> io = do_GetIOService(&rv);
57
4
  nsCOMPtr<nsIProtocolHandler> handler;
58
4
  rv = io->GetProtocolHandler(ourScheme.get(), getter_AddRefs(handler));
59
4
  NS_ENSURE_SUCCESS(rv, rv);
60
4
  nsCOMPtr<nsISubstitutingProtocolHandler> substHandler = do_QueryInterface(handler);
61
4
  MOZ_ASSERT(substHandler);
62
4
63
4
  nsAutoCString spec;
64
4
  rv = substHandler->ResolveURI(this, spec);
65
4
  if (NS_FAILED(rv))
66
4
    return rv;
67
4
68
4
  nsAutoCString scheme;
69
4
  rv = net_ExtractURLScheme(spec, scheme);
70
4
  if (NS_FAILED(rv))
71
4
    return rv;
72
4
73
4
  // Bug 585869:
74
4
  // In most cases, the scheme is jar if it's not file.
75
4
  // Regardless, net_GetFileFromURLSpec should be avoided
76
4
  // when the scheme isn't file.
77
4
  if (!scheme.EqualsLiteral("file"))
78
4
    return NS_ERROR_NO_INTERFACE;
79
0
80
0
  return net_GetFileFromURLSpec(spec, getter_AddRefs(mFile));
81
0
}
82
83
/* virtual */ nsStandardURL*
84
SubstitutingURL::StartClone()
85
0
{
86
0
  SubstitutingURL *clone = new SubstitutingURL();
87
0
  return clone;
88
0
}
89
90
NS_IMETHODIMP
91
SubstitutingURL::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
92
0
{
93
0
  *aClassIDNoAlloc = kSubstitutingURLCID;
94
0
  return NS_OK;
95
0
}
96
97
SubstitutingProtocolHandler::SubstitutingProtocolHandler(const char* aScheme, uint32_t aFlags,
98
                                                         bool aEnforceFileOrJar)
99
  : mScheme(aScheme)
100
  , mSubstitutions(16)
101
  , mEnforceFileOrJar(aEnforceFileOrJar)
102
3
{
103
3
  mFlags.emplace(aFlags);
104
3
  ConstructInternal();
105
3
}
106
107
SubstitutingProtocolHandler::SubstitutingProtocolHandler(const char* aScheme)
108
  : mScheme(aScheme)
109
  , mSubstitutions(16)
110
  , mEnforceFileOrJar(true)
111
0
{
112
0
  ConstructInternal();
113
0
}
114
115
void
116
SubstitutingProtocolHandler::ConstructInternal()
117
3
{
118
3
  nsresult rv;
119
3
  mIOService = do_GetIOService(&rv);
120
3
  MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv) && mIOService);
121
3
}
122
123
//
124
// IPC marshalling.
125
//
126
127
nsresult
128
SubstitutingProtocolHandler::CollectSubstitutions(InfallibleTArray<SubstitutionMapping>& aMappings)
129
0
{
130
0
  for (auto iter = mSubstitutions.ConstIter(); !iter.Done(); iter.Next()) {
131
0
    SubstitutionEntry& entry = iter.Data();
132
0
    nsCOMPtr<nsIURI> uri = entry.baseURI;
133
0
    SerializedURI serialized;
134
0
    if (uri) {
135
0
      nsresult rv = uri->GetSpec(serialized.spec);
136
0
      NS_ENSURE_SUCCESS(rv, rv);
137
0
    }
138
0
    SubstitutionMapping substitution = { mScheme, nsCString(iter.Key()), serialized, entry.flags };
139
0
    aMappings.AppendElement(substitution);
140
0
  }
141
0
142
0
  return NS_OK;
143
0
}
144
145
nsresult
146
SubstitutingProtocolHandler::SendSubstitution(const nsACString& aRoot, nsIURI* aBaseURI, uint32_t aFlags)
147
30
{
148
30
  if (GeckoProcessType_Content == XRE_GetProcessType()) {
149
0
    return NS_OK;
150
0
  }
151
30
152
30
  nsTArray<ContentParent*> parents;
153
30
  ContentParent::GetAll(parents);
154
30
  if (!parents.Length()) {
155
30
    return NS_OK;
156
30
  }
157
0
158
0
  SubstitutionMapping mapping;
159
0
  mapping.scheme = mScheme;
160
0
  mapping.path = aRoot;
161
0
  if (aBaseURI) {
162
0
    nsresult rv = aBaseURI->GetSpec(mapping.resolvedURI.spec);
163
0
    NS_ENSURE_SUCCESS(rv, rv);
164
0
  }
165
0
  mapping.flags = aFlags;
166
0
167
0
  for (uint32_t i = 0; i < parents.Length(); i++) {
168
0
    Unused << parents[i]->SendRegisterChromeItem(mapping);
169
0
  }
170
0
171
0
  return NS_OK;
172
0
}
173
174
//----------------------------------------------------------------------------
175
// nsIProtocolHandler
176
//----------------------------------------------------------------------------
177
178
nsresult
179
SubstitutingProtocolHandler::GetScheme(nsACString &result)
180
0
{
181
0
  result = mScheme;
182
0
  return NS_OK;
183
0
}
184
185
nsresult
186
SubstitutingProtocolHandler::GetDefaultPort(int32_t *result)
187
0
{
188
0
  *result = -1;
189
0
  return NS_OK;
190
0
}
191
192
nsresult
193
SubstitutingProtocolHandler::GetProtocolFlags(uint32_t *result)
194
19
{
195
19
  if (mFlags.isNothing()) {
196
0
    NS_WARNING("Trying to get protocol flags the wrong way - use nsIProtocolHandlerWithDynamicFlags instead");
197
0
    return NS_ERROR_NOT_AVAILABLE;
198
0
  }
199
19
200
19
  *result = mFlags.ref();
201
19
  return NS_OK;
202
19
}
203
204
nsresult
205
SubstitutingProtocolHandler::NewURI(const nsACString &aSpec,
206
                                    const char *aCharset,
207
                                    nsIURI *aBaseURI,
208
                                    nsIURI **result)
209
2.57k
{
210
2.57k
  // unescape any %2f and %2e to make sure nsStandardURL coalesces them.
211
2.57k
  // Later net_GetFileFromURLSpec() will do a full unescape and we want to
212
2.57k
  // treat them the same way the file system will. (bugs 380994, 394075)
213
2.57k
  nsAutoCString spec;
214
2.57k
  const char *src = aSpec.BeginReading();
215
2.57k
  const char *end = aSpec.EndReading();
216
2.57k
  const char *last = src;
217
2.57k
218
2.57k
  spec.SetCapacity(aSpec.Length()+1);
219
160k
  for ( ; src < end; ++src) {
220
158k
    if (*src == '%' && (src < end-2) && *(src+1) == '2') {
221
2.06k
      char ch = '\0';
222
2.06k
      if (*(src+2) == 'f' || *(src+2) == 'F') {
223
283
        ch = '/';
224
1.78k
      } else if (*(src+2) == 'e' || *(src+2) == 'E') {
225
1.47k
        ch = '.';
226
1.47k
      }
227
2.06k
228
2.06k
      if (ch) {
229
1.75k
        if (last < src) {
230
1.38k
          spec.Append(last, src-last);
231
1.38k
        }
232
1.75k
        spec.Append(ch);
233
1.75k
        src += 2;
234
1.75k
        last = src+1; // src will be incremented by the loop
235
1.75k
      }
236
2.06k
    }
237
158k
  }
238
2.57k
  if (last < src)
239
2.35k
    spec.Append(last, src-last);
240
2.57k
241
2.57k
  nsCOMPtr<nsIURI> base(aBaseURI);
242
2.57k
  return NS_MutateURI(new SubstitutingURL::Mutator())
243
2.57k
    .Apply(NS_MutatorMethod(&nsIStandardURLMutator::Init,
244
2.57k
                            nsIStandardURL::URLTYPE_STANDARD,
245
2.57k
                            -1, spec, aCharset, base, nullptr))
246
2.57k
    .Finalize(result);
247
2.57k
}
248
249
nsresult
250
SubstitutingProtocolHandler::NewChannel2(nsIURI* uri,
251
                                         nsILoadInfo* aLoadInfo,
252
                                         nsIChannel** result)
253
4
{
254
4
  NS_ENSURE_ARG_POINTER(uri);
255
4
  NS_ENSURE_ARG_POINTER(aLoadInfo);
256
4
257
4
  nsAutoCString spec;
258
4
  nsresult rv = ResolveURI(uri, spec);
259
4
  NS_ENSURE_SUCCESS(rv, rv);
260
4
261
4
  nsCOMPtr<nsIURI> newURI;
262
4
  rv = NS_NewURI(getter_AddRefs(newURI), spec);
263
4
  NS_ENSURE_SUCCESS(rv, rv);
264
4
265
4
  // We don't want to allow the inner protocol handler to modify the result
266
4
  // principal URI since we want either |uri| or anything pre-set by upper
267
4
  // layers to prevail.
268
4
  nsCOMPtr<nsIURI> savedResultPrincipalURI;
269
4
  rv = aLoadInfo->GetResultPrincipalURI(getter_AddRefs(savedResultPrincipalURI));
270
4
  NS_ENSURE_SUCCESS(rv, rv);
271
4
272
4
  rv = NS_NewChannelInternal(result, newURI, aLoadInfo);
273
4
  NS_ENSURE_SUCCESS(rv, rv);
274
4
275
4
  rv = aLoadInfo->SetResultPrincipalURI(savedResultPrincipalURI);
276
4
  NS_ENSURE_SUCCESS(rv, rv);
277
4
  rv = (*result)->SetOriginalURI(uri);
278
4
  NS_ENSURE_SUCCESS(rv, rv);
279
4
280
4
  return SubstituteChannel(uri, aLoadInfo, result);
281
4
}
282
283
nsresult
284
SubstitutingProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
285
0
{
286
0
  return NewChannel2(uri, nullptr, result);
287
0
}
288
289
nsresult
290
SubstitutingProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval)
291
0
{
292
0
  // don't override anything.
293
0
  *_retval = false;
294
0
  return NS_OK;
295
0
}
296
297
//----------------------------------------------------------------------------
298
// nsISubstitutingProtocolHandler
299
//----------------------------------------------------------------------------
300
301
nsresult
302
SubstitutingProtocolHandler::SetSubstitution(const nsACString& root, nsIURI *baseURI)
303
0
{
304
0
  // Add-ons use this API but they should not be able to make anything
305
0
  // content-accessible.
306
0
  return SetSubstitutionWithFlags(root, baseURI, 0);
307
0
}
308
309
nsresult
310
SubstitutingProtocolHandler::SetSubstitutionWithFlags(const nsACString& origRoot, nsIURI *baseURI, uint32_t flags)
311
30
{
312
30
  nsAutoCString root;
313
30
  ToLowerCase(origRoot, root);
314
30
315
30
  if (!baseURI) {
316
0
    mSubstitutions.Remove(root);
317
0
    NotifyObservers(root, baseURI);
318
0
    return SendSubstitution(root, baseURI, flags);
319
0
  }
320
30
321
30
  // If baseURI isn't a same-scheme URI, we can set the substitution immediately.
322
30
  nsAutoCString scheme;
323
30
  nsresult rv = baseURI->GetScheme(scheme);
324
30
  NS_ENSURE_SUCCESS(rv, rv);
325
30
  if (!scheme.Equals(mScheme)) {
326
15
    if (mEnforceFileOrJar && !scheme.EqualsLiteral("file") && !scheme.EqualsLiteral("jar")
327
15
        && !scheme.EqualsLiteral("app")) {
328
0
      NS_WARNING("Refusing to create substituting URI to non-file:// target");
329
0
      return NS_ERROR_INVALID_ARG;
330
0
    }
331
15
332
15
    SubstitutionEntry& entry = mSubstitutions.GetOrInsert(root);
333
15
    entry.baseURI = baseURI;
334
15
    entry.flags = flags;
335
15
    NotifyObservers(root, baseURI);
336
15
    return SendSubstitution(root, baseURI, flags);
337
15
  }
338
15
339
15
  // baseURI is a same-type substituting URI, let's resolve it first.
340
15
  nsAutoCString newBase;
341
15
  rv = ResolveURI(baseURI, newBase);
342
15
  NS_ENSURE_SUCCESS(rv, rv);
343
15
344
15
  nsCOMPtr<nsIURI> newBaseURI;
345
15
  rv = mIOService->NewURI(newBase, nullptr, nullptr, getter_AddRefs(newBaseURI));
346
15
  NS_ENSURE_SUCCESS(rv, rv);
347
15
348
15
  SubstitutionEntry& entry = mSubstitutions.GetOrInsert(root);
349
15
  entry.baseURI = newBaseURI;
350
15
  entry.flags = flags;
351
15
  NotifyObservers(root, baseURI);
352
15
  return SendSubstitution(root, newBaseURI, flags);
353
15
}
354
355
nsresult
356
SubstitutingProtocolHandler::GetSubstitution(const nsACString& origRoot, nsIURI **result)
357
0
{
358
0
  NS_ENSURE_ARG_POINTER(result);
359
0
360
0
  nsAutoCString root;
361
0
  ToLowerCase(origRoot, root);
362
0
363
0
  SubstitutionEntry entry;
364
0
  if (mSubstitutions.Get(root, &entry)) {
365
0
    nsCOMPtr<nsIURI> baseURI = entry.baseURI;
366
0
    baseURI.forget(result);
367
0
    return NS_OK;
368
0
  }
369
0
370
0
  uint32_t flags;
371
0
  return GetSubstitutionInternal(root, result, &flags);
372
0
}
373
374
nsresult
375
SubstitutingProtocolHandler::GetSubstitutionFlags(const nsACString& root, uint32_t* flags)
376
0
{
377
#ifdef DEBUG
378
  nsAutoCString lcRoot;
379
  ToLowerCase(root, lcRoot);
380
  MOZ_ASSERT(root.Equals(lcRoot), "GetSubstitutionFlags should never receive mixed-case root name");
381
#endif
382
383
0
  *flags = 0;
384
0
  SubstitutionEntry entry;
385
0
  if (mSubstitutions.Get(root, &entry)) {
386
0
    *flags = entry.flags;
387
0
    return NS_OK;
388
0
  }
389
0
390
0
  nsCOMPtr<nsIURI> baseURI;
391
0
  return GetSubstitutionInternal(root, getter_AddRefs(baseURI), flags);
392
0
}
393
394
nsresult
395
SubstitutingProtocolHandler::HasSubstitution(const nsACString& origRoot, bool *result)
396
0
{
397
0
  NS_ENSURE_ARG_POINTER(result);
398
0
399
0
  nsAutoCString root;
400
0
  ToLowerCase(origRoot, root);
401
0
402
0
  *result = HasSubstitution(root);
403
0
  return NS_OK;
404
0
}
405
406
nsresult
407
SubstitutingProtocolHandler::ResolveURI(nsIURI *uri, nsACString &result)
408
27
{
409
27
  nsresult rv;
410
27
411
27
  nsAutoCString host;
412
27
  nsAutoCString path;
413
27
  nsAutoCString pathname;
414
27
415
27
  nsCOMPtr<nsIURL> url = do_QueryInterface(uri);
416
27
  if (!url) {
417
0
    return NS_ERROR_MALFORMED_URI;
418
0
  }
419
27
420
27
  rv = uri->GetAsciiHost(host);
421
27
  if (NS_FAILED(rv)) return rv;
422
27
423
27
  rv = uri->GetPathQueryRef(path);
424
27
  if (NS_FAILED(rv)) return rv;
425
27
426
27
  rv = url->GetFilePath(pathname);
427
27
  if (NS_FAILED(rv)) return rv;
428
27
429
27
  if (ResolveSpecialCases(host, path, pathname, result)) {
430
27
    return NS_OK;
431
27
  }
432
0
433
0
  nsCOMPtr<nsIURI> baseURI;
434
0
  rv = GetSubstitution(host, getter_AddRefs(baseURI));
435
0
  if (NS_FAILED(rv)) return rv;
436
0
437
0
  // Unescape the path so we can perform some checks on it.
438
0
  NS_UnescapeURL(pathname);
439
0
  if (pathname.FindChar('\\') != -1) {
440
0
    return NS_ERROR_MALFORMED_URI;
441
0
  }
442
0
443
0
  // Some code relies on an empty path resolving to a file rather than a
444
0
  // directory.
445
0
  NS_ASSERTION(path.CharAt(0) == '/', "Path must begin with '/'");
446
0
  if (path.Length() == 1) {
447
0
    rv = baseURI->GetSpec(result);
448
0
  } else {
449
0
    // Make sure we always resolve the path as file-relative to our target URI.
450
0
    // When the baseURI is a nsIFileURL, and the directory it points to doesn't
451
0
    // exist, it doesn't end with a /. In that case, a file-relative resolution
452
0
    // is going to pick something in the parent directory, so we resolve using
453
0
    // an absolute path derived from the full path in that case.
454
0
    nsCOMPtr<nsIFileURL> baseDir = do_QueryInterface(baseURI);
455
0
    if (baseDir) {
456
0
      nsAutoCString basePath;
457
0
      rv = baseURI->GetFilePath(basePath);
458
0
      if (NS_SUCCEEDED(rv) && !StringEndsWith(basePath, NS_LITERAL_CSTRING("/"))) {
459
0
        // Cf. the assertion above, path already starts with a /, so prefixing
460
0
        // with a string that doesn't end with one will leave us wit the right
461
0
        // amount of /.
462
0
        path.Insert(basePath, 0);
463
0
      } else {
464
0
        // Allow to fall through below.
465
0
        baseDir = nullptr;
466
0
      }
467
0
    }
468
0
    if (!baseDir) {
469
0
      path.Insert('.', 0);
470
0
    }
471
0
    rv = baseURI->Resolve(path, result);
472
0
  }
473
0
474
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
475
0
    return rv;
476
0
  }
477
0
478
0
  if (MOZ_LOG_TEST(gResLog, LogLevel::Debug)) {
479
0
    nsAutoCString spec;
480
0
    uri->GetAsciiSpec(spec);
481
0
    MOZ_LOG(gResLog, LogLevel::Debug, ("%s\n -> %s\n", spec.get(), PromiseFlatCString(result).get()));
482
0
  }
483
0
  return rv;
484
0
}
485
486
nsresult
487
SubstitutingProtocolHandler::AddObserver(nsISubstitutionObserver* aObserver)
488
0
{
489
0
  NS_ENSURE_ARG(aObserver);
490
0
  if (mObservers.Contains(aObserver)) {
491
0
    return NS_ERROR_DUPLICATE_HANDLE;
492
0
  }
493
0
494
0
  mObservers.AppendElement(aObserver);
495
0
  return NS_OK;
496
0
}
497
498
nsresult
499
SubstitutingProtocolHandler::RemoveObserver(nsISubstitutionObserver* aObserver)
500
0
{
501
0
  NS_ENSURE_ARG(aObserver);
502
0
  if (!mObservers.Contains(aObserver)) {
503
0
    return NS_ERROR_INVALID_ARG;
504
0
  }
505
0
506
0
  mObservers.RemoveElement(aObserver);
507
0
  return NS_OK;
508
0
}
509
510
void
511
SubstitutingProtocolHandler::NotifyObservers(const nsACString& aRoot,
512
                                             nsIURI* aBaseURI)
513
30
{
514
30
  for (size_t i = 0; i < mObservers.Length(); ++i) {
515
0
    mObservers[i]->OnSetSubstitution(aRoot, aBaseURI);
516
0
  }
517
30
}
518
519
} // namespace net
520
} // namespace mozilla