/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 |