Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "ThirdPartyUtil.h"
6 #include "mozilla/Preferences.h"
7 #include "nsNetUtil.h"
8 #include "nsIServiceManager.h"
9 #include "nsIHttpChannelInternal.h"
10 #include "nsIDOMWindow.h"
11 #include "nsICookiePermission.h"
12 #include "nsIDOMDocument.h"
13 #include "nsIDocument.h"
14 #include "nsILoadContext.h"
15 #include "nsIPrincipal.h"
16 #include "nsIScriptObjectPrincipal.h"
17 #include "nsThreadUtils.h"
18 #include "nsPrintfCString.h"
19 #include "nsIConsoleService.h"
20 #include "nsContentUtils.h"
22 NS_IMPL_ISUPPORTS(ThirdPartyUtil, mozIThirdPartyUtil)
24 nsresult
25 ThirdPartyUtil::Init()
26 {
27 NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_AVAILABLE);
29 nsresult rv;
30 mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv);
31 mCookiePermissions = do_GetService(NS_COOKIEPERMISSION_CONTRACTID);
32 return rv;
33 }
35 // Determine if aFirstDomain is a different base domain to aSecondURI; or, if
36 // the concept of base domain does not apply, determine if the two hosts are not
37 // string-identical.
38 nsresult
39 ThirdPartyUtil::IsThirdPartyInternal(const nsCString& aFirstDomain,
40 nsIURI* aSecondURI,
41 bool* aResult)
42 {
43 NS_ASSERTION(aSecondURI, "null URI!");
45 // Get the base domain for aSecondURI.
46 nsCString secondDomain;
47 nsresult rv = GetBaseDomain(aSecondURI, secondDomain);
48 if (NS_FAILED(rv))
49 return rv;
51 // Check strict equality.
52 *aResult = aFirstDomain != secondDomain;
53 return NS_OK;
54 }
56 // Return true if aURI's scheme is white listed, in which case
57 // getFirstPartyURI() will not require that the firstPartyURI contains a host.
58 bool ThirdPartyUtil::SchemeIsWhiteListed(nsIURI *aURI)
59 {
60 if (!aURI)
61 return false;
63 nsAutoCString scheme;
64 nsresult rv = aURI->GetScheme(scheme);
65 NS_ENSURE_SUCCESS(rv, false);
67 return (scheme.Equals("about") || scheme.Equals("moz-safe-about")
68 || scheme.Equals("chrome"));
69 }
71 // Get the URI associated with a window.
72 already_AddRefed<nsIURI>
73 ThirdPartyUtil::GetURIFromWindow(nsIDOMWindow* aWin)
74 {
75 nsCOMPtr<nsIScriptObjectPrincipal> scriptObjPrin = do_QueryInterface(aWin);
76 NS_ENSURE_TRUE(scriptObjPrin, nullptr);
78 nsIPrincipal* prin = scriptObjPrin->GetPrincipal();
79 NS_ENSURE_TRUE(prin, nullptr);
81 nsCOMPtr<nsIURI> result;
82 prin->GetURI(getter_AddRefs(result));
83 return result.forget();
84 }
87 nsresult
88 ThirdPartyUtil::GetOriginatingURI(nsIChannel *aChannel, nsIURI **aURI)
89 {
90 /* to find the originating URI, we use the loadgroup of the channel to obtain
91 * the window owning the load, and from there, we find the top same-type
92 * window and its URI. there are several possible cases:
93 *
94 * 1) no channel.
95 *
96 * 2) a channel with the "force allow third party cookies" option set.
97 * since we may not have a window, we return the channel URI in this case.
98 *
99 * 3) a channel, but no window. this can occur when the consumer kicking
100 * off the load doesn't provide one to the channel, and should be limited
101 * to loads of certain types of resources.
102 *
103 * 4) a window equal to the top window of same type, with the channel its
104 * document channel. this covers the case of a freshly kicked-off load
105 * (e.g. the user typing something in the location bar, or clicking on a
106 * bookmark), where the window's URI hasn't yet been set, and will be
107 * bogus. we return the channel URI in this case.
108 *
109 * 5) Anything else. this covers most cases for an ordinary page load from
110 * the location bar, and will catch nested frames within a page, image
111 * loads, etc. we return the URI of the root window's document's principal
112 * in this case.
113 */
115 *aURI = nullptr;
117 // case 1)
118 if (!aChannel)
119 return NS_ERROR_NULL_POINTER;
121 // case 2)
122 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal = do_QueryInterface(aChannel);
123 if (httpChannelInternal)
124 {
125 bool doForce = false;
126 if (NS_SUCCEEDED(httpChannelInternal->GetForceAllowThirdPartyCookie(&doForce)) && doForce)
127 {
128 // return the channel's URI (we may not have a window)
129 aChannel->GetURI(aURI);
130 if (!*aURI)
131 return NS_ERROR_NULL_POINTER;
133 return NS_OK;
134 }
136 // TODO: Why don't we just use this here:
137 // httpChannelInternal->GetDocumentURI(aURI);
138 }
140 // find the associated window and its top window
141 nsCOMPtr<nsILoadContext> ctx;
142 NS_QueryNotificationCallbacks(aChannel, ctx);
143 nsCOMPtr<nsIDOMWindow> topWin, ourWin;
144 if (ctx) {
145 ctx->GetTopWindow(getter_AddRefs(topWin));
146 ctx->GetAssociatedWindow(getter_AddRefs(ourWin));
147 }
149 // case 3)
150 if (!topWin)
151 return NS_ERROR_INVALID_ARG;
153 // case 4)
154 if (ourWin == topWin) {
155 // Check whether this is the document channel for this window (representing
156 // a load of a new page). This is a bit of a nasty hack, but we will
157 // hopefully flag these channels better later.
158 nsLoadFlags flags;
159 aChannel->GetLoadFlags(&flags);
161 if (flags & nsIChannel::LOAD_DOCUMENT_URI) {
162 // get the channel URI - the window's will be bogus
163 aChannel->GetURI(aURI);
164 if (!*aURI)
165 return NS_ERROR_NULL_POINTER;
167 return NS_OK;
168 }
169 }
171 // case 5) - get the originating URI from the top window's principal
172 nsCOMPtr<nsIScriptObjectPrincipal> scriptObjPrin = do_QueryInterface(topWin);
173 NS_ENSURE_TRUE(scriptObjPrin, NS_ERROR_UNEXPECTED);
175 nsIPrincipal* prin = scriptObjPrin->GetPrincipal();
176 NS_ENSURE_TRUE(prin, NS_ERROR_UNEXPECTED);
178 prin->GetURI(aURI);
180 if (!*aURI)
181 return NS_ERROR_NULL_POINTER;
183 // all done!
184 return NS_OK;
185 }
188 // Determine if aFirstURI is third party with respect to aSecondURI. See docs
189 // for mozIThirdPartyUtil.
190 NS_IMETHODIMP
191 ThirdPartyUtil::IsThirdPartyURI(nsIURI* aFirstURI,
192 nsIURI* aSecondURI,
193 bool* aResult)
194 {
195 NS_ENSURE_ARG(aFirstURI);
196 NS_ENSURE_ARG(aSecondURI);
197 NS_ASSERTION(aResult, "null outparam pointer");
199 nsCString firstHost;
200 nsresult rv = GetBaseDomain(aFirstURI, firstHost);
201 if (NS_FAILED(rv))
202 return rv;
204 return IsThirdPartyInternal(firstHost, aSecondURI, aResult);
205 }
207 // Determine if any URI of the window hierarchy of aWindow is foreign with
208 // respect to aSecondURI. See docs for mozIThirdPartyUtil.
209 NS_IMETHODIMP
210 ThirdPartyUtil::IsThirdPartyWindow(nsIDOMWindow* aWindow,
211 nsIURI* aURI,
212 bool* aResult)
213 {
214 NS_ENSURE_ARG(aWindow);
215 NS_ASSERTION(aResult, "null outparam pointer");
217 bool result;
219 // Get the URI of the window, and its base domain.
220 nsCOMPtr<nsIURI> currentURI = GetURIFromWindow(aWindow);
221 NS_ENSURE_TRUE(currentURI, NS_ERROR_INVALID_ARG);
223 nsCString bottomDomain;
224 nsresult rv = GetBaseDomain(currentURI, bottomDomain);
225 if (NS_FAILED(rv))
226 return rv;
228 if (aURI) {
229 // Determine whether aURI is foreign with respect to currentURI.
230 rv = IsThirdPartyInternal(bottomDomain, aURI, &result);
231 if (NS_FAILED(rv))
232 return rv;
234 if (result) {
235 *aResult = true;
236 return NS_OK;
237 }
238 }
240 nsCOMPtr<nsIDOMWindow> current = aWindow, parent;
241 nsCOMPtr<nsIURI> parentURI;
242 do {
243 // We use GetScriptableParent rather than GetParent because we consider
244 // <iframe mozbrowser/mozapp> to be a top-level frame.
245 rv = current->GetScriptableParent(getter_AddRefs(parent));
246 NS_ENSURE_SUCCESS(rv, rv);
248 if (SameCOMIdentity(parent, current)) {
249 // We're at the topmost content window. We already know the answer.
250 *aResult = false;
251 return NS_OK;
252 }
254 parentURI = GetURIFromWindow(parent);
255 NS_ENSURE_TRUE(parentURI, NS_ERROR_INVALID_ARG);
257 rv = IsThirdPartyInternal(bottomDomain, parentURI, &result);
258 if (NS_FAILED(rv))
259 return rv;
261 if (result) {
262 *aResult = true;
263 return NS_OK;
264 }
266 current = parent;
267 currentURI = parentURI;
268 } while (1);
270 NS_NOTREACHED("should've returned");
271 return NS_ERROR_UNEXPECTED;
272 }
274 // Determine if the URI associated with aChannel or any URI of the window
275 // hierarchy associated with the channel is foreign with respect to aSecondURI.
276 // See docs for mozIThirdPartyUtil.
277 NS_IMETHODIMP
278 ThirdPartyUtil::IsThirdPartyChannel(nsIChannel* aChannel,
279 nsIURI* aURI,
280 bool* aResult)
281 {
282 NS_ENSURE_ARG(aChannel);
283 NS_ASSERTION(aResult, "null outparam pointer");
285 nsresult rv;
286 bool doForce = false;
287 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
288 do_QueryInterface(aChannel);
289 if (httpChannelInternal) {
290 rv = httpChannelInternal->GetForceAllowThirdPartyCookie(&doForce);
291 NS_ENSURE_SUCCESS(rv, rv);
293 // If aURI was not supplied, and we're forcing, then we're by definition
294 // not foreign. If aURI was supplied, we still want to check whether it's
295 // foreign with respect to the channel URI. (The forcing only applies to
296 // whatever window hierarchy exists above the channel.)
297 if (doForce && !aURI) {
298 *aResult = false;
299 return NS_OK;
300 }
301 }
303 // Obtain the URI from the channel, and its base domain.
304 nsCOMPtr<nsIURI> channelURI;
305 aChannel->GetURI(getter_AddRefs(channelURI));
306 NS_ENSURE_TRUE(channelURI, NS_ERROR_INVALID_ARG);
308 nsCString channelDomain;
309 rv = GetBaseDomain(channelURI, channelDomain);
310 if (NS_FAILED(rv))
311 return rv;
313 if (aURI) {
314 // Determine whether aURI is foreign with respect to channelURI.
315 bool result;
316 rv = IsThirdPartyInternal(channelDomain, aURI, &result);
317 if (NS_FAILED(rv))
318 return rv;
320 // If it's foreign, or we're forcing, we're done.
321 if (result || doForce) {
322 *aResult = result;
323 return NS_OK;
324 }
325 }
327 // Find the associated window and its parent window.
328 nsCOMPtr<nsILoadContext> ctx;
329 NS_QueryNotificationCallbacks(aChannel, ctx);
330 if (!ctx) return NS_ERROR_INVALID_ARG;
332 // If there is no window, the consumer kicking off the load didn't provide one
333 // to the channel. This is limited to loads of certain types of resources. If
334 // those loads require cookies, the forceAllowThirdPartyCookie property should
335 // be set on the channel.
336 nsCOMPtr<nsIDOMWindow> ourWin, parentWin;
337 ctx->GetAssociatedWindow(getter_AddRefs(ourWin));
338 if (!ourWin) return NS_ERROR_INVALID_ARG;
340 // We use GetScriptableParent rather than GetParent because we consider
341 // <iframe mozbrowser/mozapp> to be a top-level frame.
342 ourWin->GetScriptableParent(getter_AddRefs(parentWin));
343 NS_ENSURE_TRUE(parentWin, NS_ERROR_INVALID_ARG);
345 // Check whether this is the document channel for this window (representing a
346 // load of a new page). In that situation we want to avoid comparing
347 // channelURI to ourWin, since what's in ourWin right now will be replaced as
348 // the channel loads. This covers the case of a freshly kicked-off load
349 // (e.g. the user typing something in the location bar, or clicking on a
350 // bookmark), where the window's URI hasn't yet been set, and will be bogus.
351 // It also covers situations where a subframe is navigated to someting that
352 // is same-origin with all its ancestors. This is a bit of a nasty hack, but
353 // we will hopefully flag these channels better later.
354 nsLoadFlags flags;
355 rv = aChannel->GetLoadFlags(&flags);
356 NS_ENSURE_SUCCESS(rv, rv);
358 if (flags & nsIChannel::LOAD_DOCUMENT_URI) {
359 if (SameCOMIdentity(ourWin, parentWin)) {
360 // We only need to compare aURI to the channel URI -- the window's will be
361 // bogus. We already know the answer.
362 *aResult = false;
363 return NS_OK;
364 }
366 // Make sure to still compare to ourWin's ancestors
367 ourWin = parentWin;
368 }
370 // Check the window hierarchy. This covers most cases for an ordinary page
371 // load from the location bar.
372 return IsThirdPartyWindow(ourWin, channelURI, aResult);
373 }
375 // Get the base domain for aHostURI; e.g. for "www.bbc.co.uk", this would be
376 // "bbc.co.uk". Only properly-formed URI's are tolerated, though a trailing
377 // dot may be present. If aHostURI is an IP address, an alias such as
378 // 'localhost', an eTLD such as 'co.uk', or the empty string, aBaseDomain will
379 // be the exact host. The result of this function should only be used in exact
380 // string comparisons, since substring comparisons will not be valid for the
381 // special cases elided above.
382 NS_IMETHODIMP
383 ThirdPartyUtil::GetBaseDomain(nsIURI* aHostURI,
384 nsACString& aBaseDomain)
385 {
386 // Get the base domain. this will fail if the host contains a leading dot,
387 // more than one trailing dot, or is otherwise malformed.
388 nsresult rv = mTLDService->GetBaseDomain(aHostURI, 0, aBaseDomain);
389 if (rv == NS_ERROR_HOST_IS_IP_ADDRESS ||
390 rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
391 // aHostURI is either an IP address, an alias such as 'localhost', an eTLD
392 // such as 'co.uk', or the empty string. Uses the normalized host in such
393 // cases.
394 rv = aHostURI->GetAsciiHost(aBaseDomain);
395 }
396 NS_ENSURE_SUCCESS(rv, rv);
398 // aHostURI (and thus aBaseDomain) may be the string '.'. If so, fail.
399 if (aBaseDomain.Length() == 1 && aBaseDomain.Last() == '.')
400 return NS_ERROR_INVALID_ARG;
402 // Reject any URIs without a host that aren't file:// URIs. This makes it the
403 // only way we can get a base domain consisting of the empty string, which
404 // means we can safely perform foreign tests on such URIs where "not foreign"
405 // means "the involved URIs are all file://".
406 if (aBaseDomain.IsEmpty()) {
407 bool isFileURI = false;
408 aHostURI->SchemeIs("file", &isFileURI);
409 NS_ENSURE_TRUE(isFileURI, NS_ERROR_INVALID_ARG);
410 }
412 return NS_OK;
413 }
415 // Returns true if First Party Isolation is currently active for the given nsIChannel.
416 // Depends on Preference setting and possibly the state of Private Browsing mode.
417 bool ThirdPartyUtil::IsFirstPartyIsolationActive(nsIChannel *aChannel, nsIDocument *aDoc)
418 {
419 int32_t isolationState = mozilla::Preferences::GetInt("privacy.thirdparty.isolate");
420 if (isolationState == 1) {
421 if (!aChannel && aDoc) {
422 // No channel passed directly. Can we get a channel from aDoc?
423 aChannel = aDoc->GetChannel();
424 }
425 return aChannel && NS_UsePrivateBrowsing(aChannel);
426 } else { // (isolationState == 0) || (isolationState == 2)
427 return (isolationState == 2);
428 }
429 }
431 // Produces a URI that uniquely identifies the first party to which
432 // image cache and dom storage objects should be isolated. If isolation
433 // is deactivated, then aOutput will return null.
434 // Not scriptable due to the use of an nsIDocument parameter.
435 NS_IMETHODIMP
436 ThirdPartyUtil::GetFirstPartyIsolationURI(nsIChannel *aChannel, nsIDocument *aDoc, nsIURI **aOutput)
437 {
438 bool isolationActive = IsFirstPartyIsolationActive(aChannel, aDoc);
439 if (isolationActive) {
440 return GetFirstPartyURI(aChannel, aDoc, aOutput);
441 } else {
442 // We return a null pointer when isolation is off.
443 *aOutput = nullptr;
444 return NS_OK;
445 }
446 }
448 // Not scriptable due to the use of an nsIDocument parameter.
449 NS_IMETHODIMP
450 ThirdPartyUtil::GetFirstPartyURI(nsIChannel *aChannel,
451 nsIDocument *aDoc,
452 nsIURI **aOutput)
453 {
454 return GetFirstPartyURIInternal(aChannel, aDoc, true, aOutput);
455 }
457 nsresult
458 ThirdPartyUtil::GetFirstPartyURIInternal(nsIChannel *aChannel,
459 nsIDocument *aDoc,
460 bool aLogErrors,
461 nsIURI **aOutput)
462 {
463 nsresult rv = NS_ERROR_NULL_POINTER;
464 nsCOMPtr<nsIURI> srcURI;
466 if (!aOutput)
467 return rv;
469 *aOutput = nullptr;
471 if (!aChannel && aDoc) {
472 aChannel = aDoc->GetChannel();
473 }
475 // If aChannel is specified or available, use the official route
476 // for sure
477 if (aChannel) {
478 rv = GetOriginatingURI(aChannel, aOutput);
479 aChannel->GetURI(getter_AddRefs(srcURI));
480 if (NS_SUCCEEDED(rv) && *aOutput) {
481 // At this point, about: and chrome: URLs have been mapped to file: or
482 // jar: URLs. Try to recover the original URL.
483 nsAutoCString scheme;
484 nsresult rv2 = (*aOutput)->GetScheme(scheme);
485 NS_ENSURE_SUCCESS(rv2, rv2);
486 if (scheme.Equals("file") || scheme.Equals("jar")) {
487 nsCOMPtr<nsIURI> originalURI;
488 rv2 = aChannel->GetOriginalURI(getter_AddRefs(originalURI));
489 if (NS_SUCCEEDED(rv2) && originalURI) {
490 NS_RELEASE(*aOutput);
491 NS_ADDREF(*aOutput = originalURI);
492 }
493 }
494 }
495 }
497 // If the channel was missing, closed or broken, try the
498 // window hierarchy directly.
499 //
500 // This might fail to work for first-party loads themselves, but
501 // we don't need this codepath for that case.
502 if (NS_FAILED(rv) && aDoc) {
503 nsCOMPtr<nsIDOMWindow> top;
504 nsCOMPtr<nsIDOMDocument> topDDoc;
505 nsIURI *docURI = nullptr;
506 srcURI = aDoc->GetDocumentURI();
508 if (aDoc->GetWindow()) {
509 aDoc->GetWindow()->GetTop(getter_AddRefs(top));
510 top->GetDocument(getter_AddRefs(topDDoc));
512 nsCOMPtr<nsIDocument> topDoc(do_QueryInterface(topDDoc));
513 docURI = topDoc->GetOriginalURI();
514 if (docURI) {
515 // Give us a mutable URI and also addref
516 rv = NS_EnsureSafeToReturn(docURI, aOutput);
517 }
518 } else {
519 // XXX: Chrome callers (such as NoScript) can end up here
520 // through getImageData/canvas usage with no document state
521 // (no Window and a document URI of about:blank). Propogate
522 // rv fail (by doing nothing), and hope caller recovers.
523 }
525 if (*aOutput)
526 rv = NS_OK;
527 }
529 if (*aOutput && !SchemeIsWhiteListed(*aOutput)) {
530 // If URI scheme is not whitelisted and the URI lacks a hostname, force a
531 // failure.
532 nsAutoCString host;
533 rv = (*aOutput)->GetHost(host);
534 if (NS_SUCCEEDED(rv) && (host.Length() == 0)) {
535 rv = NS_ERROR_FAILURE;
536 }
537 }
539 // Log failure to error console.
540 if (aLogErrors && NS_FAILED(rv)) {
541 nsCOMPtr<nsIConsoleService> console
542 (do_GetService(NS_CONSOLESERVICE_CONTRACTID));
543 if (console) {
544 nsCString spec;
545 nsCString srcSpec("unknown");
547 if (srcURI)
548 srcURI->GetSpec(srcSpec);
550 if (*aOutput)
551 (*aOutput)->GetSpec(spec);
552 if (spec.Length() > 0) {
553 nsPrintfCString msg("getFirstPartyURI failed for %s: no host in first party URI %s",
554 srcSpec.get(), spec.get()); // TODO: L10N
555 console->LogStringMessage(NS_ConvertUTF8toUTF16(msg).get());
556 } else {
557 nsPrintfCString msg("getFirstPartyURI failed for %s: 0x%x", srcSpec.get(), rv);
558 console->LogStringMessage(NS_ConvertUTF8toUTF16(msg).get());
559 }
560 }
562 if (*aOutput) {
563 // discard return object.
564 (*aOutput)->Release();
565 *aOutput = nullptr;
566 }
567 }
569 // TODO: We could provide a route through the loadgroup + notification
570 // callbacks too, but either channel or document was always available
571 // in the cases where this function was originally needed (the image cache).
572 // The notification callbacks also appear to suffers from the same limitation
573 // as the document path. See nsICookiePermissions.GetOriginatingURI() for
574 // details.
576 return rv;
577 }
579 NS_IMETHODIMP
580 ThirdPartyUtil::GetFirstPartyURIFromChannel(nsIChannel *aChannel,
581 bool aLogErrors,
582 nsIURI **aOutput)
583 {
584 return GetFirstPartyURIInternal(aChannel, nullptr, aLogErrors, aOutput);
585 }
587 NS_IMETHODIMP
588 ThirdPartyUtil::GetFirstPartyHostForIsolation(nsIURI *aFirstPartyURI,
589 nsACString& aHost)
590 {
591 if (!aFirstPartyURI)
592 return NS_ERROR_INVALID_ARG;
594 if (!SchemeIsWhiteListed(aFirstPartyURI)) {
595 nsresult rv = aFirstPartyURI->GetHost(aHost);
596 return (aHost.Length() > 0) ? NS_OK : rv;
597 }
599 // This URI lacks a host, so construct and return a pseudo-host.
600 aHost = "--NoFirstPartyHost-";
602 // Append the scheme. To ensure that the pseudo-hosts are consistent
603 // when the hacky "moz-safe-about" scheme is used, map it back to "about".
604 nsAutoCString scheme;
605 nsresult rv = aFirstPartyURI->GetScheme(scheme);
606 NS_ENSURE_SUCCESS(rv, rv);
607 if (scheme.Equals("moz-safe-about"))
608 aHost.Append("about");
609 else
610 aHost.Append(scheme);
612 // Append the URL's file name (e.g., -browser.xul) or its path (e.g.,
613 // -home for about:home)
614 nsAutoCString s;
615 nsCOMPtr<nsIURL> url = do_QueryInterface(aFirstPartyURI);
616 if (url)
617 url->GetFileName(s);
618 else
619 aFirstPartyURI->GetPath(s);
621 if (s.Length() > 0) {
622 aHost.Append("-");
623 aHost.Append(s);
624 }
626 aHost.Append("--");
627 return NS_OK;
628 }