netwerk/base/src/nsIOService.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 /* vim:set ts=4 sw=4 cindent et: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "mozilla/DebugOnly.h"
michael@0 8
michael@0 9 #include "nsIOService.h"
michael@0 10 #include "nsIProtocolHandler.h"
michael@0 11 #include "nsIFileProtocolHandler.h"
michael@0 12 #include "nscore.h"
michael@0 13 #include "nsIURI.h"
michael@0 14 #include "prprf.h"
michael@0 15 #include "nsIErrorService.h"
michael@0 16 #include "netCore.h"
michael@0 17 #include "nsIObserverService.h"
michael@0 18 #include "nsIPrefService.h"
michael@0 19 #include "nsXPCOM.h"
michael@0 20 #include "nsIProxiedProtocolHandler.h"
michael@0 21 #include "nsIProxyInfo.h"
michael@0 22 #include "nsEscape.h"
michael@0 23 #include "nsNetCID.h"
michael@0 24 #include "nsCRT.h"
michael@0 25 #include "nsSimpleNestedURI.h"
michael@0 26 #include "nsNetUtil.h"
michael@0 27 #include "nsTArray.h"
michael@0 28 #include "nsIConsoleService.h"
michael@0 29 #include "nsIUploadChannel2.h"
michael@0 30 #include "nsXULAppAPI.h"
michael@0 31 #include "nsIProtocolProxyCallback.h"
michael@0 32 #include "nsICancelable.h"
michael@0 33 #include "nsINetworkLinkService.h"
michael@0 34 #include "nsPISocketTransportService.h"
michael@0 35 #include "nsAsyncRedirectVerifyHelper.h"
michael@0 36 #include "nsURLHelper.h"
michael@0 37 #include "nsPIDNSService.h"
michael@0 38 #include "nsIProtocolProxyService2.h"
michael@0 39 #include "MainThreadUtils.h"
michael@0 40
michael@0 41 #if defined(XP_WIN)
michael@0 42 #include "nsNativeConnectionHelper.h"
michael@0 43 #endif
michael@0 44
michael@0 45 using namespace mozilla;
michael@0 46
michael@0 47 #define PORT_PREF_PREFIX "network.security.ports."
michael@0 48 #define PORT_PREF(x) PORT_PREF_PREFIX x
michael@0 49 #define AUTODIAL_PREF "network.autodial-helper.enabled"
michael@0 50 #define MANAGE_OFFLINE_STATUS_PREF "network.manage-offline-status"
michael@0 51
michael@0 52 // Nb: these have been misnomers since bug 715770 removed the buffer cache.
michael@0 53 // "network.segment.count" and "network.segment.size" would be better names,
michael@0 54 // but the old names are still used to preserve backward compatibility.
michael@0 55 #define NECKO_BUFFER_CACHE_COUNT_PREF "network.buffer.cache.count"
michael@0 56 #define NECKO_BUFFER_CACHE_SIZE_PREF "network.buffer.cache.size"
michael@0 57
michael@0 58 #define MAX_RECURSION_COUNT 50
michael@0 59
michael@0 60 nsIOService* gIOService = nullptr;
michael@0 61 static bool gHasWarnedUploadChannel2;
michael@0 62
michael@0 63 // A general port blacklist. Connections to these ports will not be allowed unless
michael@0 64 // the protocol overrides.
michael@0 65 //
michael@0 66 // TODO: I am sure that there are more ports to be added.
michael@0 67 // This cut is based on the classic mozilla codebase
michael@0 68
michael@0 69 int16_t gBadPortList[] = {
michael@0 70 1, // tcpmux
michael@0 71 7, // echo
michael@0 72 9, // discard
michael@0 73 11, // systat
michael@0 74 13, // daytime
michael@0 75 15, // netstat
michael@0 76 17, // qotd
michael@0 77 19, // chargen
michael@0 78 20, // ftp-data
michael@0 79 21, // ftp-cntl
michael@0 80 22, // ssh
michael@0 81 23, // telnet
michael@0 82 25, // smtp
michael@0 83 37, // time
michael@0 84 42, // name
michael@0 85 43, // nicname
michael@0 86 53, // domain
michael@0 87 77, // priv-rjs
michael@0 88 79, // finger
michael@0 89 87, // ttylink
michael@0 90 95, // supdup
michael@0 91 101, // hostriame
michael@0 92 102, // iso-tsap
michael@0 93 103, // gppitnp
michael@0 94 104, // acr-nema
michael@0 95 109, // pop2
michael@0 96 110, // pop3
michael@0 97 111, // sunrpc
michael@0 98 113, // auth
michael@0 99 115, // sftp
michael@0 100 117, // uucp-path
michael@0 101 119, // nntp
michael@0 102 123, // NTP
michael@0 103 135, // loc-srv / epmap
michael@0 104 139, // netbios
michael@0 105 143, // imap2
michael@0 106 179, // BGP
michael@0 107 389, // ldap
michael@0 108 465, // smtp+ssl
michael@0 109 512, // print / exec
michael@0 110 513, // login
michael@0 111 514, // shell
michael@0 112 515, // printer
michael@0 113 526, // tempo
michael@0 114 530, // courier
michael@0 115 531, // Chat
michael@0 116 532, // netnews
michael@0 117 540, // uucp
michael@0 118 556, // remotefs
michael@0 119 563, // nntp+ssl
michael@0 120 587, //
michael@0 121 601, //
michael@0 122 636, // ldap+ssl
michael@0 123 993, // imap+ssl
michael@0 124 995, // pop3+ssl
michael@0 125 2049, // nfs
michael@0 126 4045, // lockd
michael@0 127 6000, // x11
michael@0 128 0, // This MUST be zero so that we can populating the array
michael@0 129 };
michael@0 130
michael@0 131 static const char kProfileChangeNetTeardownTopic[] = "profile-change-net-teardown";
michael@0 132 static const char kProfileChangeNetRestoreTopic[] = "profile-change-net-restore";
michael@0 133 static const char kProfileDoChange[] = "profile-do-change";
michael@0 134
michael@0 135 // Necko buffer defaults
michael@0 136 uint32_t nsIOService::gDefaultSegmentSize = 4096;
michael@0 137 uint32_t nsIOService::gDefaultSegmentCount = 24;
michael@0 138
michael@0 139 ////////////////////////////////////////////////////////////////////////////////
michael@0 140
michael@0 141 nsIOService::nsIOService()
michael@0 142 : mOffline(true)
michael@0 143 , mOfflineForProfileChange(false)
michael@0 144 , mManageOfflineStatus(false)
michael@0 145 , mSettingOffline(false)
michael@0 146 , mSetOfflineValue(false)
michael@0 147 , mShutdown(false)
michael@0 148 , mNetworkLinkServiceInitialized(false)
michael@0 149 , mChannelEventSinks(NS_CHANNEL_EVENT_SINK_CATEGORY)
michael@0 150 , mAutoDialEnabled(false)
michael@0 151 {
michael@0 152 }
michael@0 153
michael@0 154 nsresult
michael@0 155 nsIOService::Init()
michael@0 156 {
michael@0 157 nsresult rv;
michael@0 158
michael@0 159 // We need to get references to the DNS service so that we can shut it
michael@0 160 // down later. If we wait until the nsIOService is being shut down,
michael@0 161 // GetService will fail at that point.
michael@0 162
michael@0 163 mDNSService = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
michael@0 164 if (NS_FAILED(rv)) {
michael@0 165 NS_WARNING("failed to get DNS service");
michael@0 166 return rv;
michael@0 167 }
michael@0 168
michael@0 169 // XXX hack until xpidl supports error info directly (bug 13423)
michael@0 170 nsCOMPtr<nsIErrorService> errorService = do_GetService(NS_ERRORSERVICE_CONTRACTID);
michael@0 171 if (errorService) {
michael@0 172 errorService->RegisterErrorStringBundle(NS_ERROR_MODULE_NETWORK, NECKO_MSGS_URL);
michael@0 173 }
michael@0 174 else
michael@0 175 NS_WARNING("failed to get error service");
michael@0 176
michael@0 177 // setup our bad port list stuff
michael@0 178 for(int i=0; gBadPortList[i]; i++)
michael@0 179 mRestrictedPortList.AppendElement(gBadPortList[i]);
michael@0 180
michael@0 181 // Further modifications to the port list come from prefs
michael@0 182 nsCOMPtr<nsIPrefBranch> prefBranch;
michael@0 183 GetPrefBranch(getter_AddRefs(prefBranch));
michael@0 184 if (prefBranch) {
michael@0 185 prefBranch->AddObserver(PORT_PREF_PREFIX, this, true);
michael@0 186 prefBranch->AddObserver(AUTODIAL_PREF, this, true);
michael@0 187 prefBranch->AddObserver(MANAGE_OFFLINE_STATUS_PREF, this, true);
michael@0 188 prefBranch->AddObserver(NECKO_BUFFER_CACHE_COUNT_PREF, this, true);
michael@0 189 prefBranch->AddObserver(NECKO_BUFFER_CACHE_SIZE_PREF, this, true);
michael@0 190 PrefsChanged(prefBranch);
michael@0 191 }
michael@0 192
michael@0 193 // Register for profile change notifications
michael@0 194 nsCOMPtr<nsIObserverService> observerService =
michael@0 195 mozilla::services::GetObserverService();
michael@0 196 if (observerService) {
michael@0 197 observerService->AddObserver(this, kProfileChangeNetTeardownTopic, true);
michael@0 198 observerService->AddObserver(this, kProfileChangeNetRestoreTopic, true);
michael@0 199 observerService->AddObserver(this, kProfileDoChange, true);
michael@0 200 observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
michael@0 201 observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, true);
michael@0 202 }
michael@0 203 else
michael@0 204 NS_WARNING("failed to get observer service");
michael@0 205
michael@0 206 gIOService = this;
michael@0 207
michael@0 208 InitializeNetworkLinkService();
michael@0 209
michael@0 210 return NS_OK;
michael@0 211 }
michael@0 212
michael@0 213
michael@0 214 nsIOService::~nsIOService()
michael@0 215 {
michael@0 216 gIOService = nullptr;
michael@0 217 }
michael@0 218
michael@0 219 nsresult
michael@0 220 nsIOService::InitializeSocketTransportService()
michael@0 221 {
michael@0 222 nsresult rv = NS_OK;
michael@0 223
michael@0 224 if (!mSocketTransportService) {
michael@0 225 mSocketTransportService = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
michael@0 226 if (NS_FAILED(rv)) {
michael@0 227 NS_WARNING("failed to get socket transport service");
michael@0 228 }
michael@0 229 }
michael@0 230
michael@0 231 if (mSocketTransportService) {
michael@0 232 rv = mSocketTransportService->Init();
michael@0 233 NS_ASSERTION(NS_SUCCEEDED(rv), "socket transport service init failed");
michael@0 234 mSocketTransportService->SetAutodialEnabled(mAutoDialEnabled);
michael@0 235 mSocketTransportService->SetOffline(false);
michael@0 236 }
michael@0 237
michael@0 238 return rv;
michael@0 239 }
michael@0 240
michael@0 241 nsresult
michael@0 242 nsIOService::InitializeNetworkLinkService()
michael@0 243 {
michael@0 244 nsresult rv = NS_OK;
michael@0 245
michael@0 246 if (mNetworkLinkServiceInitialized)
michael@0 247 return rv;
michael@0 248
michael@0 249 if (!NS_IsMainThread()) {
michael@0 250 NS_WARNING("Network link service should be created on main thread");
michael@0 251 return NS_ERROR_FAILURE;
michael@0 252 }
michael@0 253
michael@0 254 // go into managed mode if we can, and chrome process
michael@0 255 if (XRE_GetProcessType() == GeckoProcessType_Default)
michael@0 256 {
michael@0 257 mNetworkLinkService = do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID, &rv);
michael@0 258 }
michael@0 259
michael@0 260 if (mNetworkLinkService) {
michael@0 261 mNetworkLinkServiceInitialized = true;
michael@0 262 }
michael@0 263 else {
michael@0 264 // We can't really determine if the machine has a usable network connection,
michael@0 265 // so let's cross our fingers!
michael@0 266 mManageOfflineStatus = false;
michael@0 267 }
michael@0 268
michael@0 269
michael@0 270 if (mManageOfflineStatus)
michael@0 271 TrackNetworkLinkStatusForOffline();
michael@0 272 else
michael@0 273 SetOffline(false);
michael@0 274
michael@0 275 return rv;
michael@0 276 }
michael@0 277
michael@0 278 nsIOService*
michael@0 279 nsIOService::GetInstance() {
michael@0 280 if (!gIOService) {
michael@0 281 gIOService = new nsIOService();
michael@0 282 if (!gIOService)
michael@0 283 return nullptr;
michael@0 284 NS_ADDREF(gIOService);
michael@0 285
michael@0 286 nsresult rv = gIOService->Init();
michael@0 287 if (NS_FAILED(rv)) {
michael@0 288 NS_RELEASE(gIOService);
michael@0 289 return nullptr;
michael@0 290 }
michael@0 291 return gIOService;
michael@0 292 }
michael@0 293 NS_ADDREF(gIOService);
michael@0 294 return gIOService;
michael@0 295 }
michael@0 296
michael@0 297 NS_IMPL_ISUPPORTS(nsIOService,
michael@0 298 nsIIOService,
michael@0 299 nsIIOService2,
michael@0 300 nsINetUtil,
michael@0 301 nsISpeculativeConnect,
michael@0 302 nsIObserver,
michael@0 303 nsISupportsWeakReference)
michael@0 304
michael@0 305 ////////////////////////////////////////////////////////////////////////////////
michael@0 306
michael@0 307 nsresult
michael@0 308 nsIOService::AsyncOnChannelRedirect(nsIChannel* oldChan, nsIChannel* newChan,
michael@0 309 uint32_t flags,
michael@0 310 nsAsyncRedirectVerifyHelper *helper)
michael@0 311 {
michael@0 312 nsCOMPtr<nsIChannelEventSink> sink =
michael@0 313 do_GetService(NS_GLOBAL_CHANNELEVENTSINK_CONTRACTID);
michael@0 314 if (sink) {
michael@0 315 nsresult rv = helper->DelegateOnChannelRedirect(sink, oldChan,
michael@0 316 newChan, flags);
michael@0 317 if (NS_FAILED(rv))
michael@0 318 return rv;
michael@0 319 }
michael@0 320
michael@0 321 // Finally, our category
michael@0 322 nsCOMArray<nsIChannelEventSink> entries;
michael@0 323 mChannelEventSinks.GetEntries(entries);
michael@0 324 int32_t len = entries.Count();
michael@0 325 for (int32_t i = 0; i < len; ++i) {
michael@0 326 nsresult rv = helper->DelegateOnChannelRedirect(entries[i], oldChan,
michael@0 327 newChan, flags);
michael@0 328 if (NS_FAILED(rv))
michael@0 329 return rv;
michael@0 330 }
michael@0 331 return NS_OK;
michael@0 332 }
michael@0 333
michael@0 334 nsresult
michael@0 335 nsIOService::CacheProtocolHandler(const char *scheme, nsIProtocolHandler *handler)
michael@0 336 {
michael@0 337 for (unsigned int i=0; i<NS_N(gScheme); i++)
michael@0 338 {
michael@0 339 if (!nsCRT::strcasecmp(scheme, gScheme[i]))
michael@0 340 {
michael@0 341 nsresult rv;
michael@0 342 NS_ASSERTION(!mWeakHandler[i], "Protocol handler already cached");
michael@0 343 // Make sure the handler supports weak references.
michael@0 344 nsCOMPtr<nsISupportsWeakReference> factoryPtr = do_QueryInterface(handler, &rv);
michael@0 345 if (!factoryPtr)
michael@0 346 {
michael@0 347 // Don't cache handlers that don't support weak reference as
michael@0 348 // there is real danger of a circular reference.
michael@0 349 #ifdef DEBUG_dp
michael@0 350 printf("DEBUG: %s protcol handler doesn't support weak ref. Not cached.\n", scheme);
michael@0 351 #endif /* DEBUG_dp */
michael@0 352 return NS_ERROR_FAILURE;
michael@0 353 }
michael@0 354 mWeakHandler[i] = do_GetWeakReference(handler);
michael@0 355 return NS_OK;
michael@0 356 }
michael@0 357 }
michael@0 358 return NS_ERROR_FAILURE;
michael@0 359 }
michael@0 360
michael@0 361 nsresult
michael@0 362 nsIOService::GetCachedProtocolHandler(const char *scheme, nsIProtocolHandler **result, uint32_t start, uint32_t end)
michael@0 363 {
michael@0 364 uint32_t len = end - start - 1;
michael@0 365 for (unsigned int i=0; i<NS_N(gScheme); i++)
michael@0 366 {
michael@0 367 if (!mWeakHandler[i])
michael@0 368 continue;
michael@0 369
michael@0 370 // handle unterminated strings
michael@0 371 // start is inclusive, end is exclusive, len = end - start - 1
michael@0 372 if (end ? (!nsCRT::strncasecmp(scheme + start, gScheme[i], len)
michael@0 373 && gScheme[i][len] == '\0')
michael@0 374 : (!nsCRT::strcasecmp(scheme, gScheme[i])))
michael@0 375 {
michael@0 376 return CallQueryReferent(mWeakHandler[i].get(), result);
michael@0 377 }
michael@0 378 }
michael@0 379 return NS_ERROR_FAILURE;
michael@0 380 }
michael@0 381
michael@0 382 NS_IMETHODIMP
michael@0 383 nsIOService::GetProtocolHandler(const char* scheme, nsIProtocolHandler* *result)
michael@0 384 {
michael@0 385 nsresult rv;
michael@0 386
michael@0 387 NS_ENSURE_ARG_POINTER(scheme);
michael@0 388 // XXX we may want to speed this up by introducing our own protocol
michael@0 389 // scheme -> protocol handler mapping, avoiding the string manipulation
michael@0 390 // and service manager stuff
michael@0 391
michael@0 392 rv = GetCachedProtocolHandler(scheme, result);
michael@0 393 if (NS_SUCCEEDED(rv))
michael@0 394 return rv;
michael@0 395
michael@0 396 bool externalProtocol = false;
michael@0 397 nsCOMPtr<nsIPrefBranch> prefBranch;
michael@0 398 GetPrefBranch(getter_AddRefs(prefBranch));
michael@0 399 if (prefBranch) {
michael@0 400 nsAutoCString externalProtocolPref("network.protocol-handler.external.");
michael@0 401 externalProtocolPref += scheme;
michael@0 402 rv = prefBranch->GetBoolPref(externalProtocolPref.get(), &externalProtocol);
michael@0 403 if (NS_FAILED(rv)) {
michael@0 404 externalProtocol = false;
michael@0 405 }
michael@0 406 }
michael@0 407
michael@0 408 if (!externalProtocol) {
michael@0 409 nsAutoCString contractID(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX);
michael@0 410 contractID += scheme;
michael@0 411 ToLowerCase(contractID);
michael@0 412
michael@0 413 rv = CallGetService(contractID.get(), result);
michael@0 414 if (NS_SUCCEEDED(rv)) {
michael@0 415 CacheProtocolHandler(scheme, *result);
michael@0 416 return rv;
michael@0 417 }
michael@0 418
michael@0 419 #ifdef MOZ_X11
michael@0 420 // check to see whether GVFS can handle this URI scheme. if it can
michael@0 421 // create a nsIURI for the "scheme:", then we assume it has support for
michael@0 422 // the requested protocol. otherwise, we failover to using the default
michael@0 423 // protocol handler.
michael@0 424
michael@0 425 rv = CallGetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX"moz-gio",
michael@0 426 result);
michael@0 427 if (NS_SUCCEEDED(rv)) {
michael@0 428 nsAutoCString spec(scheme);
michael@0 429 spec.Append(':');
michael@0 430
michael@0 431 nsIURI *uri;
michael@0 432 rv = (*result)->NewURI(spec, nullptr, nullptr, &uri);
michael@0 433 if (NS_SUCCEEDED(rv)) {
michael@0 434 NS_RELEASE(uri);
michael@0 435 return rv;
michael@0 436 }
michael@0 437
michael@0 438 NS_RELEASE(*result);
michael@0 439 }
michael@0 440
michael@0 441 // check to see whether GnomeVFS can handle this URI scheme. if it can
michael@0 442 // create a nsIURI for the "scheme:", then we assume it has support for
michael@0 443 // the requested protocol. otherwise, we failover to using the default
michael@0 444 // protocol handler.
michael@0 445
michael@0 446 // XXX should this be generalized into something that searches a
michael@0 447 // category? (see bug 234714)
michael@0 448
michael@0 449 rv = CallGetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX"moz-gnomevfs",
michael@0 450 result);
michael@0 451 if (NS_SUCCEEDED(rv)) {
michael@0 452 nsAutoCString spec(scheme);
michael@0 453 spec.Append(':');
michael@0 454
michael@0 455 nsIURI *uri;
michael@0 456 rv = (*result)->NewURI(spec, nullptr, nullptr, &uri);
michael@0 457 if (NS_SUCCEEDED(rv)) {
michael@0 458 NS_RELEASE(uri);
michael@0 459 return rv;
michael@0 460 }
michael@0 461
michael@0 462 NS_RELEASE(*result);
michael@0 463 }
michael@0 464 #endif
michael@0 465 }
michael@0 466
michael@0 467 // Okay we don't have a protocol handler to handle this url type, so use
michael@0 468 // the default protocol handler. This will cause urls to get dispatched
michael@0 469 // out to the OS ('cause we can't do anything with them) when we try to
michael@0 470 // read from a channel created by the default protocol handler.
michael@0 471
michael@0 472 rv = CallGetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX"default",
michael@0 473 result);
michael@0 474 if (NS_FAILED(rv))
michael@0 475 return NS_ERROR_UNKNOWN_PROTOCOL;
michael@0 476
michael@0 477 return rv;
michael@0 478 }
michael@0 479
michael@0 480 NS_IMETHODIMP
michael@0 481 nsIOService::ExtractScheme(const nsACString &inURI, nsACString &scheme)
michael@0 482 {
michael@0 483 return net_ExtractURLScheme(inURI, nullptr, nullptr, &scheme);
michael@0 484 }
michael@0 485
michael@0 486 NS_IMETHODIMP
michael@0 487 nsIOService::GetProtocolFlags(const char* scheme, uint32_t *flags)
michael@0 488 {
michael@0 489 nsCOMPtr<nsIProtocolHandler> handler;
michael@0 490 nsresult rv = GetProtocolHandler(scheme, getter_AddRefs(handler));
michael@0 491 if (NS_FAILED(rv)) return rv;
michael@0 492
michael@0 493 rv = handler->GetProtocolFlags(flags);
michael@0 494 return rv;
michael@0 495 }
michael@0 496
michael@0 497 class AutoIncrement
michael@0 498 {
michael@0 499 public:
michael@0 500 AutoIncrement(uint32_t *var) : mVar(var)
michael@0 501 {
michael@0 502 ++*var;
michael@0 503 }
michael@0 504 ~AutoIncrement()
michael@0 505 {
michael@0 506 --*mVar;
michael@0 507 }
michael@0 508 private:
michael@0 509 uint32_t *mVar;
michael@0 510 };
michael@0 511
michael@0 512 nsresult
michael@0 513 nsIOService::NewURI(const nsACString &aSpec, const char *aCharset, nsIURI *aBaseURI, nsIURI **result)
michael@0 514 {
michael@0 515 NS_ASSERTION(NS_IsMainThread(), "wrong thread");
michael@0 516
michael@0 517 static uint32_t recursionCount = 0;
michael@0 518 if (recursionCount >= MAX_RECURSION_COUNT)
michael@0 519 return NS_ERROR_MALFORMED_URI;
michael@0 520 AutoIncrement inc(&recursionCount);
michael@0 521
michael@0 522 nsAutoCString scheme;
michael@0 523 nsresult rv = ExtractScheme(aSpec, scheme);
michael@0 524 if (NS_FAILED(rv)) {
michael@0 525 // then aSpec is relative
michael@0 526 if (!aBaseURI)
michael@0 527 return NS_ERROR_MALFORMED_URI;
michael@0 528
michael@0 529 rv = aBaseURI->GetScheme(scheme);
michael@0 530 if (NS_FAILED(rv)) return rv;
michael@0 531 }
michael@0 532
michael@0 533 // now get the handler for this scheme
michael@0 534 nsCOMPtr<nsIProtocolHandler> handler;
michael@0 535 rv = GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
michael@0 536 if (NS_FAILED(rv)) return rv;
michael@0 537
michael@0 538 return handler->NewURI(aSpec, aCharset, aBaseURI, result);
michael@0 539 }
michael@0 540
michael@0 541
michael@0 542 NS_IMETHODIMP
michael@0 543 nsIOService::NewFileURI(nsIFile *file, nsIURI **result)
michael@0 544 {
michael@0 545 nsresult rv;
michael@0 546 NS_ENSURE_ARG_POINTER(file);
michael@0 547
michael@0 548 nsCOMPtr<nsIProtocolHandler> handler;
michael@0 549
michael@0 550 rv = GetProtocolHandler("file", getter_AddRefs(handler));
michael@0 551 if (NS_FAILED(rv)) return rv;
michael@0 552
michael@0 553 nsCOMPtr<nsIFileProtocolHandler> fileHandler( do_QueryInterface(handler, &rv) );
michael@0 554 if (NS_FAILED(rv)) return rv;
michael@0 555
michael@0 556 return fileHandler->NewFileURI(file, result);
michael@0 557 }
michael@0 558
michael@0 559 NS_IMETHODIMP
michael@0 560 nsIOService::NewChannelFromURI(nsIURI *aURI, nsIChannel **result)
michael@0 561 {
michael@0 562 return NewChannelFromURIWithProxyFlags(aURI, nullptr, 0, result);
michael@0 563 }
michael@0 564
michael@0 565 NS_IMETHODIMP
michael@0 566 nsIOService::NewChannelFromURIWithProxyFlags(nsIURI *aURI,
michael@0 567 nsIURI *aProxyURI,
michael@0 568 uint32_t aProxyFlags,
michael@0 569 nsIChannel **result)
michael@0 570 {
michael@0 571 nsresult rv;
michael@0 572 NS_ENSURE_ARG_POINTER(aURI);
michael@0 573
michael@0 574 nsAutoCString scheme;
michael@0 575 rv = aURI->GetScheme(scheme);
michael@0 576 if (NS_FAILED(rv))
michael@0 577 return rv;
michael@0 578
michael@0 579 nsCOMPtr<nsIProtocolHandler> handler;
michael@0 580 rv = GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
michael@0 581 if (NS_FAILED(rv))
michael@0 582 return rv;
michael@0 583
michael@0 584 uint32_t protoFlags;
michael@0 585 rv = handler->GetProtocolFlags(&protoFlags);
michael@0 586 if (NS_FAILED(rv))
michael@0 587 return rv;
michael@0 588
michael@0 589 nsCOMPtr<nsIProxiedProtocolHandler> pph = do_QueryInterface(handler);
michael@0 590 if (pph)
michael@0 591 rv = pph->NewProxiedChannel(aURI, nullptr, aProxyFlags, aProxyURI, result);
michael@0 592 else
michael@0 593 rv = handler->NewChannel(aURI, result);
michael@0 594 NS_ENSURE_SUCCESS(rv, rv);
michael@0 595
michael@0 596 // Some extensions override the http protocol handler and provide their own
michael@0 597 // implementation. The channels returned from that implementation doesn't
michael@0 598 // seem to always implement the nsIUploadChannel2 interface, presumably
michael@0 599 // because it's a new interface.
michael@0 600 // Eventually we should remove this and simply require that http channels
michael@0 601 // implement the new interface.
michael@0 602 // See bug 529041
michael@0 603 if (!gHasWarnedUploadChannel2 && scheme.EqualsLiteral("http")) {
michael@0 604 nsCOMPtr<nsIUploadChannel2> uploadChannel2 = do_QueryInterface(*result);
michael@0 605 if (!uploadChannel2) {
michael@0 606 nsCOMPtr<nsIConsoleService> consoleService =
michael@0 607 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
michael@0 608 if (consoleService) {
michael@0 609 consoleService->LogStringMessage(NS_LITERAL_STRING(
michael@0 610 "Http channel implementation doesn't support nsIUploadChannel2. An extension has supplied a non-functional http protocol handler. This will break behavior and in future releases not work at all."
michael@0 611 ).get());
michael@0 612 }
michael@0 613 gHasWarnedUploadChannel2 = true;
michael@0 614 }
michael@0 615 }
michael@0 616
michael@0 617 return NS_OK;
michael@0 618 }
michael@0 619
michael@0 620 NS_IMETHODIMP
michael@0 621 nsIOService::NewChannel(const nsACString &aSpec, const char *aCharset, nsIURI *aBaseURI, nsIChannel **result)
michael@0 622 {
michael@0 623 nsresult rv;
michael@0 624 nsCOMPtr<nsIURI> uri;
michael@0 625 rv = NewURI(aSpec, aCharset, aBaseURI, getter_AddRefs(uri));
michael@0 626 if (NS_FAILED(rv)) return rv;
michael@0 627
michael@0 628 return NewChannelFromURI(uri, result);
michael@0 629 }
michael@0 630
michael@0 631 bool
michael@0 632 nsIOService::IsLinkUp()
michael@0 633 {
michael@0 634 InitializeNetworkLinkService();
michael@0 635
michael@0 636 if (!mNetworkLinkService) {
michael@0 637 // We cannot decide, assume the link is up
michael@0 638 return true;
michael@0 639 }
michael@0 640
michael@0 641 bool isLinkUp;
michael@0 642 nsresult rv;
michael@0 643 rv = mNetworkLinkService->GetIsLinkUp(&isLinkUp);
michael@0 644 if (NS_FAILED(rv)) {
michael@0 645 return true;
michael@0 646 }
michael@0 647
michael@0 648 return isLinkUp;
michael@0 649 }
michael@0 650
michael@0 651 NS_IMETHODIMP
michael@0 652 nsIOService::GetOffline(bool *offline)
michael@0 653 {
michael@0 654 *offline = mOffline;
michael@0 655 return NS_OK;
michael@0 656 }
michael@0 657
michael@0 658 NS_IMETHODIMP
michael@0 659 nsIOService::SetOffline(bool offline)
michael@0 660 {
michael@0 661 // When someone wants to go online (!offline) after we got XPCOM shutdown
michael@0 662 // throw ERROR_NOT_AVAILABLE to prevent return to online state.
michael@0 663 if ((mShutdown || mOfflineForProfileChange) && !offline)
michael@0 664 return NS_ERROR_NOT_AVAILABLE;
michael@0 665
michael@0 666 // SetOffline() may re-enter while it's shutting down services.
michael@0 667 // If that happens, save the most recent value and it will be
michael@0 668 // processed when the first SetOffline() call is done bringing
michael@0 669 // down the service.
michael@0 670 mSetOfflineValue = offline;
michael@0 671 if (mSettingOffline) {
michael@0 672 return NS_OK;
michael@0 673 }
michael@0 674
michael@0 675 mSettingOffline = true;
michael@0 676
michael@0 677 nsCOMPtr<nsIObserverService> observerService =
michael@0 678 mozilla::services::GetObserverService();
michael@0 679
michael@0 680 NS_ASSERTION(observerService, "The observer service should not be null");
michael@0 681
michael@0 682 if (XRE_GetProcessType() == GeckoProcessType_Default) {
michael@0 683 if (observerService) {
michael@0 684 (void)observerService->NotifyObservers(nullptr,
michael@0 685 NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC, offline ?
michael@0 686 MOZ_UTF16("true") :
michael@0 687 MOZ_UTF16("false"));
michael@0 688 }
michael@0 689 }
michael@0 690
michael@0 691 nsIIOService *subject = static_cast<nsIIOService *>(this);
michael@0 692 while (mSetOfflineValue != mOffline) {
michael@0 693 offline = mSetOfflineValue;
michael@0 694
michael@0 695 if (offline && !mOffline) {
michael@0 696 NS_NAMED_LITERAL_STRING(offlineString, NS_IOSERVICE_OFFLINE);
michael@0 697 mOffline = true; // indicate we're trying to shutdown
michael@0 698
michael@0 699 // don't care if notifications fail
michael@0 700 if (observerService)
michael@0 701 observerService->NotifyObservers(subject,
michael@0 702 NS_IOSERVICE_GOING_OFFLINE_TOPIC,
michael@0 703 offlineString.get());
michael@0 704
michael@0 705 if (mDNSService)
michael@0 706 mDNSService->SetOffline(true);
michael@0 707
michael@0 708 if (mSocketTransportService)
michael@0 709 mSocketTransportService->SetOffline(true);
michael@0 710
michael@0 711 if (observerService)
michael@0 712 observerService->NotifyObservers(subject,
michael@0 713 NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
michael@0 714 offlineString.get());
michael@0 715 }
michael@0 716 else if (!offline && mOffline) {
michael@0 717 // go online
michael@0 718 if (mDNSService) {
michael@0 719 mDNSService->SetOffline(false);
michael@0 720 DebugOnly<nsresult> rv = mDNSService->Init();
michael@0 721 NS_ASSERTION(NS_SUCCEEDED(rv), "DNS service init failed");
michael@0 722 }
michael@0 723 InitializeSocketTransportService();
michael@0 724 mOffline = false; // indicate success only AFTER we've
michael@0 725 // brought up the services
michael@0 726
michael@0 727 // trigger a PAC reload when we come back online
michael@0 728 if (mProxyService)
michael@0 729 mProxyService->ReloadPAC();
michael@0 730
michael@0 731 // don't care if notification fails
michael@0 732 if (observerService)
michael@0 733 observerService->NotifyObservers(subject,
michael@0 734 NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
michael@0 735 NS_LITERAL_STRING(NS_IOSERVICE_ONLINE).get());
michael@0 736 }
michael@0 737 }
michael@0 738
michael@0 739 // Don't notify here, as the above notifications (if used) suffice.
michael@0 740 if ((mShutdown || mOfflineForProfileChange) && mOffline) {
michael@0 741 // be sure to try and shutdown both (even if the first fails)...
michael@0 742 // shutdown dns service first, because it has callbacks for socket transport
michael@0 743 if (mDNSService) {
michael@0 744 DebugOnly<nsresult> rv = mDNSService->Shutdown();
michael@0 745 NS_ASSERTION(NS_SUCCEEDED(rv), "DNS service shutdown failed");
michael@0 746 }
michael@0 747 if (mSocketTransportService) {
michael@0 748 DebugOnly<nsresult> rv = mSocketTransportService->Shutdown();
michael@0 749 NS_ASSERTION(NS_SUCCEEDED(rv), "socket transport service shutdown failed");
michael@0 750 }
michael@0 751 }
michael@0 752
michael@0 753 mSettingOffline = false;
michael@0 754
michael@0 755 return NS_OK;
michael@0 756 }
michael@0 757
michael@0 758
michael@0 759 NS_IMETHODIMP
michael@0 760 nsIOService::AllowPort(int32_t inPort, const char *scheme, bool *_retval)
michael@0 761 {
michael@0 762 int16_t port = inPort;
michael@0 763 if (port == -1) {
michael@0 764 *_retval = true;
michael@0 765 return NS_OK;
michael@0 766 }
michael@0 767
michael@0 768 if (port == 0) {
michael@0 769 *_retval = false;
michael@0 770 return NS_OK;
michael@0 771 }
michael@0 772
michael@0 773 // first check to see if the port is in our blacklist:
michael@0 774 int32_t badPortListCnt = mRestrictedPortList.Length();
michael@0 775 for (int i=0; i<badPortListCnt; i++)
michael@0 776 {
michael@0 777 if (port == mRestrictedPortList[i])
michael@0 778 {
michael@0 779 *_retval = false;
michael@0 780
michael@0 781 // check to see if the protocol wants to override
michael@0 782 if (!scheme)
michael@0 783 return NS_OK;
michael@0 784
michael@0 785 nsCOMPtr<nsIProtocolHandler> handler;
michael@0 786 nsresult rv = GetProtocolHandler(scheme, getter_AddRefs(handler));
michael@0 787 if (NS_FAILED(rv)) return rv;
michael@0 788
michael@0 789 // let the protocol handler decide
michael@0 790 return handler->AllowPort(port, scheme, _retval);
michael@0 791 }
michael@0 792 }
michael@0 793
michael@0 794 *_retval = true;
michael@0 795 return NS_OK;
michael@0 796 }
michael@0 797
michael@0 798 ////////////////////////////////////////////////////////////////////////////////
michael@0 799
michael@0 800 void
michael@0 801 nsIOService::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
michael@0 802 {
michael@0 803 if (!prefs) return;
michael@0 804
michael@0 805 // Look for extra ports to block
michael@0 806 if (!pref || strcmp(pref, PORT_PREF("banned")) == 0)
michael@0 807 ParsePortList(prefs, PORT_PREF("banned"), false);
michael@0 808
michael@0 809 // ...as well as previous blocks to remove.
michael@0 810 if (!pref || strcmp(pref, PORT_PREF("banned.override")) == 0)
michael@0 811 ParsePortList(prefs, PORT_PREF("banned.override"), true);
michael@0 812
michael@0 813 if (!pref || strcmp(pref, AUTODIAL_PREF) == 0) {
michael@0 814 bool enableAutodial = false;
michael@0 815 nsresult rv = prefs->GetBoolPref(AUTODIAL_PREF, &enableAutodial);
michael@0 816 // If pref not found, default to disabled.
michael@0 817 mAutoDialEnabled = enableAutodial;
michael@0 818 if (NS_SUCCEEDED(rv)) {
michael@0 819 if (mSocketTransportService)
michael@0 820 mSocketTransportService->SetAutodialEnabled(enableAutodial);
michael@0 821 }
michael@0 822 }
michael@0 823
michael@0 824 if (!pref || strcmp(pref, MANAGE_OFFLINE_STATUS_PREF) == 0) {
michael@0 825 bool manage;
michael@0 826 if (mNetworkLinkServiceInitialized &&
michael@0 827 NS_SUCCEEDED(prefs->GetBoolPref(MANAGE_OFFLINE_STATUS_PREF,
michael@0 828 &manage)))
michael@0 829 SetManageOfflineStatus(manage);
michael@0 830 }
michael@0 831
michael@0 832 if (!pref || strcmp(pref, NECKO_BUFFER_CACHE_COUNT_PREF) == 0) {
michael@0 833 int32_t count;
michael@0 834 if (NS_SUCCEEDED(prefs->GetIntPref(NECKO_BUFFER_CACHE_COUNT_PREF,
michael@0 835 &count)))
michael@0 836 /* check for bogus values and default if we find such a value */
michael@0 837 if (count > 0)
michael@0 838 gDefaultSegmentCount = count;
michael@0 839 }
michael@0 840
michael@0 841 if (!pref || strcmp(pref, NECKO_BUFFER_CACHE_SIZE_PREF) == 0) {
michael@0 842 int32_t size;
michael@0 843 if (NS_SUCCEEDED(prefs->GetIntPref(NECKO_BUFFER_CACHE_SIZE_PREF,
michael@0 844 &size)))
michael@0 845 /* check for bogus values and default if we find such a value
michael@0 846 * the upper limit here is arbitrary. having a 1mb segment size
michael@0 847 * is pretty crazy. if you remove this, consider adding some
michael@0 848 * integer rollover test.
michael@0 849 */
michael@0 850 if (size > 0 && size < 1024*1024)
michael@0 851 gDefaultSegmentSize = size;
michael@0 852 NS_WARN_IF_FALSE( (!(size & (size - 1))) , "network segment size is not a power of 2!");
michael@0 853 }
michael@0 854 }
michael@0 855
michael@0 856 void
michael@0 857 nsIOService::ParsePortList(nsIPrefBranch *prefBranch, const char *pref, bool remove)
michael@0 858 {
michael@0 859 nsXPIDLCString portList;
michael@0 860
michael@0 861 // Get a pref string and chop it up into a list of ports.
michael@0 862 prefBranch->GetCharPref(pref, getter_Copies(portList));
michael@0 863 if (portList) {
michael@0 864 nsTArray<nsCString> portListArray;
michael@0 865 ParseString(portList, ',', portListArray);
michael@0 866 uint32_t index;
michael@0 867 for (index=0; index < portListArray.Length(); index++) {
michael@0 868 portListArray[index].StripWhitespace();
michael@0 869 int32_t portBegin, portEnd;
michael@0 870
michael@0 871 if (PR_sscanf(portListArray[index].get(), "%d-%d", &portBegin, &portEnd) == 2) {
michael@0 872 if ((portBegin < 65536) && (portEnd < 65536)) {
michael@0 873 int32_t curPort;
michael@0 874 if (remove) {
michael@0 875 for (curPort=portBegin; curPort <= portEnd; curPort++)
michael@0 876 mRestrictedPortList.RemoveElement(curPort);
michael@0 877 } else {
michael@0 878 for (curPort=portBegin; curPort <= portEnd; curPort++)
michael@0 879 mRestrictedPortList.AppendElement(curPort);
michael@0 880 }
michael@0 881 }
michael@0 882 } else {
michael@0 883 nsresult aErrorCode;
michael@0 884 int32_t port = portListArray[index].ToInteger(&aErrorCode);
michael@0 885 if (NS_SUCCEEDED(aErrorCode) && port < 65536) {
michael@0 886 if (remove)
michael@0 887 mRestrictedPortList.RemoveElement(port);
michael@0 888 else
michael@0 889 mRestrictedPortList.AppendElement(port);
michael@0 890 }
michael@0 891 }
michael@0 892
michael@0 893 }
michael@0 894 }
michael@0 895 }
michael@0 896
michael@0 897 void
michael@0 898 nsIOService::GetPrefBranch(nsIPrefBranch **result)
michael@0 899 {
michael@0 900 *result = nullptr;
michael@0 901 CallGetService(NS_PREFSERVICE_CONTRACTID, result);
michael@0 902 }
michael@0 903
michael@0 904 // nsIObserver interface
michael@0 905 NS_IMETHODIMP
michael@0 906 nsIOService::Observe(nsISupports *subject,
michael@0 907 const char *topic,
michael@0 908 const char16_t *data)
michael@0 909 {
michael@0 910 if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
michael@0 911 nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(subject);
michael@0 912 if (prefBranch)
michael@0 913 PrefsChanged(prefBranch, NS_ConvertUTF16toUTF8(data).get());
michael@0 914 }
michael@0 915 else if (!strcmp(topic, kProfileChangeNetTeardownTopic)) {
michael@0 916 if (!mOffline) {
michael@0 917 mOfflineForProfileChange = true;
michael@0 918 SetOffline(true);
michael@0 919 }
michael@0 920 }
michael@0 921 else if (!strcmp(topic, kProfileChangeNetRestoreTopic)) {
michael@0 922 if (mOfflineForProfileChange) {
michael@0 923 mOfflineForProfileChange = false;
michael@0 924 if (!mManageOfflineStatus ||
michael@0 925 NS_FAILED(TrackNetworkLinkStatusForOffline())) {
michael@0 926 SetOffline(false);
michael@0 927 }
michael@0 928 }
michael@0 929 }
michael@0 930 else if (!strcmp(topic, kProfileDoChange)) {
michael@0 931 if (data && NS_LITERAL_STRING("startup").Equals(data)) {
michael@0 932 // Lazy initialization of network link service (see bug 620472)
michael@0 933 InitializeNetworkLinkService();
michael@0 934 // Set up the initilization flag regardless the actuall result.
michael@0 935 // If we fail here, we will fail always on.
michael@0 936 mNetworkLinkServiceInitialized = true;
michael@0 937 // And now reflect the preference setting
michael@0 938 nsCOMPtr<nsIPrefBranch> prefBranch;
michael@0 939 GetPrefBranch(getter_AddRefs(prefBranch));
michael@0 940 PrefsChanged(prefBranch, MANAGE_OFFLINE_STATUS_PREF);
michael@0 941 }
michael@0 942 }
michael@0 943 else if (!strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
michael@0 944 // Remember we passed XPCOM shutdown notification to prevent any
michael@0 945 // changes of the offline status from now. We must not allow going
michael@0 946 // online after this point.
michael@0 947 mShutdown = true;
michael@0 948
michael@0 949 SetOffline(true);
michael@0 950
michael@0 951 // Break circular reference.
michael@0 952 mProxyService = nullptr;
michael@0 953 }
michael@0 954 else if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) {
michael@0 955 if (!mOfflineForProfileChange && mManageOfflineStatus) {
michael@0 956 TrackNetworkLinkStatusForOffline();
michael@0 957 }
michael@0 958 }
michael@0 959
michael@0 960 return NS_OK;
michael@0 961 }
michael@0 962
michael@0 963 // nsINetUtil interface
michael@0 964 NS_IMETHODIMP
michael@0 965 nsIOService::ParseContentType(const nsACString &aTypeHeader,
michael@0 966 nsACString &aCharset,
michael@0 967 bool *aHadCharset,
michael@0 968 nsACString &aContentType)
michael@0 969 {
michael@0 970 net_ParseContentType(aTypeHeader, aContentType, aCharset, aHadCharset);
michael@0 971 return NS_OK;
michael@0 972 }
michael@0 973
michael@0 974 NS_IMETHODIMP
michael@0 975 nsIOService::ProtocolHasFlags(nsIURI *uri,
michael@0 976 uint32_t flags,
michael@0 977 bool *result)
michael@0 978 {
michael@0 979 NS_ENSURE_ARG(uri);
michael@0 980
michael@0 981 *result = false;
michael@0 982 nsAutoCString scheme;
michael@0 983 nsresult rv = uri->GetScheme(scheme);
michael@0 984 NS_ENSURE_SUCCESS(rv, rv);
michael@0 985
michael@0 986 uint32_t protocolFlags;
michael@0 987 rv = GetProtocolFlags(scheme.get(), &protocolFlags);
michael@0 988
michael@0 989 if (NS_SUCCEEDED(rv)) {
michael@0 990 *result = (protocolFlags & flags) == flags;
michael@0 991 }
michael@0 992
michael@0 993 return rv;
michael@0 994 }
michael@0 995
michael@0 996 NS_IMETHODIMP
michael@0 997 nsIOService::URIChainHasFlags(nsIURI *uri,
michael@0 998 uint32_t flags,
michael@0 999 bool *result)
michael@0 1000 {
michael@0 1001 nsresult rv = ProtocolHasFlags(uri, flags, result);
michael@0 1002 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1003
michael@0 1004 if (*result) {
michael@0 1005 return rv;
michael@0 1006 }
michael@0 1007
michael@0 1008 // Dig deeper into the chain. Note that this is not a do/while loop to
michael@0 1009 // avoid the extra addref/release on |uri| in the common (non-nested) case.
michael@0 1010 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(uri);
michael@0 1011 while (nestedURI) {
michael@0 1012 nsCOMPtr<nsIURI> innerURI;
michael@0 1013 rv = nestedURI->GetInnerURI(getter_AddRefs(innerURI));
michael@0 1014 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1015
michael@0 1016 rv = ProtocolHasFlags(innerURI, flags, result);
michael@0 1017
michael@0 1018 if (*result) {
michael@0 1019 return rv;
michael@0 1020 }
michael@0 1021
michael@0 1022 nestedURI = do_QueryInterface(innerURI);
michael@0 1023 }
michael@0 1024
michael@0 1025 return rv;
michael@0 1026 }
michael@0 1027
michael@0 1028 NS_IMETHODIMP
michael@0 1029 nsIOService::ToImmutableURI(nsIURI* uri, nsIURI** result)
michael@0 1030 {
michael@0 1031 if (!uri) {
michael@0 1032 *result = nullptr;
michael@0 1033 return NS_OK;
michael@0 1034 }
michael@0 1035
michael@0 1036 nsresult rv = NS_EnsureSafeToReturn(uri, result);
michael@0 1037 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1038
michael@0 1039 NS_TryToSetImmutable(*result);
michael@0 1040 return NS_OK;
michael@0 1041 }
michael@0 1042
michael@0 1043 NS_IMETHODIMP
michael@0 1044 nsIOService::NewSimpleNestedURI(nsIURI* aURI, nsIURI** aResult)
michael@0 1045 {
michael@0 1046 NS_ENSURE_ARG(aURI);
michael@0 1047
michael@0 1048 nsCOMPtr<nsIURI> safeURI;
michael@0 1049 nsresult rv = NS_EnsureSafeToReturn(aURI, getter_AddRefs(safeURI));
michael@0 1050 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1051
michael@0 1052 NS_IF_ADDREF(*aResult = new nsSimpleNestedURI(safeURI));
michael@0 1053 return *aResult ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
michael@0 1054 }
michael@0 1055
michael@0 1056 NS_IMETHODIMP
michael@0 1057 nsIOService::SetManageOfflineStatus(bool aManage) {
michael@0 1058 nsresult rv = NS_OK;
michael@0 1059
michael@0 1060 // SetManageOfflineStatus must throw when we fail to go from non-managed
michael@0 1061 // to managed. Usually because there is no link monitoring service
michael@0 1062 // available. Failure to do this switch is detected by a failure of
michael@0 1063 // TrackNetworkLinkStatusForOffline(). When there is no network link
michael@0 1064 // available during call to InitializeNetworkLinkService(), application is
michael@0 1065 // put to offline mode. And when we change mMangeOfflineStatus to false
michael@0 1066 // on the next line we get stuck on being offline even though the link
michael@0 1067 // becomes later available.
michael@0 1068 bool wasManaged = mManageOfflineStatus;
michael@0 1069 mManageOfflineStatus = aManage;
michael@0 1070
michael@0 1071 InitializeNetworkLinkService();
michael@0 1072
michael@0 1073 if (mManageOfflineStatus && !wasManaged) {
michael@0 1074 rv = TrackNetworkLinkStatusForOffline();
michael@0 1075 if (NS_FAILED(rv))
michael@0 1076 mManageOfflineStatus = false;
michael@0 1077 }
michael@0 1078 return rv;
michael@0 1079 }
michael@0 1080
michael@0 1081 NS_IMETHODIMP
michael@0 1082 nsIOService::GetManageOfflineStatus(bool* aManage) {
michael@0 1083 *aManage = mManageOfflineStatus;
michael@0 1084 return NS_OK;
michael@0 1085 }
michael@0 1086
michael@0 1087 nsresult
michael@0 1088 nsIOService::TrackNetworkLinkStatusForOffline()
michael@0 1089 {
michael@0 1090 NS_ASSERTION(mManageOfflineStatus,
michael@0 1091 "Don't call this unless we're managing the offline status");
michael@0 1092 if (!mNetworkLinkService)
michael@0 1093 return NS_ERROR_FAILURE;
michael@0 1094
michael@0 1095 if (mShutdown)
michael@0 1096 return NS_ERROR_NOT_AVAILABLE;
michael@0 1097
michael@0 1098 // check to make sure this won't collide with Autodial
michael@0 1099 if (mSocketTransportService) {
michael@0 1100 bool autodialEnabled = false;
michael@0 1101 mSocketTransportService->GetAutodialEnabled(&autodialEnabled);
michael@0 1102 // If autodialing-on-link-down is enabled, check if the OS auto dial
michael@0 1103 // option is set to always autodial. If so, then we are
michael@0 1104 // always up for the purposes of offline management.
michael@0 1105 if (autodialEnabled) {
michael@0 1106 #if defined(XP_WIN)
michael@0 1107 // On Windows, we should first check with the OS
michael@0 1108 // to see if autodial is enabled. If it is
michael@0 1109 // enabled then we are allowed to manage the
michael@0 1110 // offline state.
michael@0 1111 if(nsNativeConnectionHelper::IsAutodialEnabled())
michael@0 1112 return SetOffline(false);
michael@0 1113 #else
michael@0 1114 return SetOffline(false);
michael@0 1115 #endif
michael@0 1116 }
michael@0 1117 }
michael@0 1118
michael@0 1119 bool isUp;
michael@0 1120 nsresult rv = mNetworkLinkService->GetIsLinkUp(&isUp);
michael@0 1121 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1122 return SetOffline(!isUp);
michael@0 1123 }
michael@0 1124
michael@0 1125 NS_IMETHODIMP
michael@0 1126 nsIOService::EscapeString(const nsACString& aString,
michael@0 1127 uint32_t aEscapeType,
michael@0 1128 nsACString& aResult)
michael@0 1129 {
michael@0 1130 NS_ENSURE_ARG_MAX(aEscapeType, 4);
michael@0 1131
michael@0 1132 nsAutoCString stringCopy(aString);
michael@0 1133 nsCString result;
michael@0 1134
michael@0 1135 if (!NS_Escape(stringCopy, result, (nsEscapeMask) aEscapeType))
michael@0 1136 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1137
michael@0 1138 aResult.Assign(result);
michael@0 1139
michael@0 1140 return NS_OK;
michael@0 1141 }
michael@0 1142
michael@0 1143 NS_IMETHODIMP
michael@0 1144 nsIOService::EscapeURL(const nsACString &aStr,
michael@0 1145 uint32_t aFlags, nsACString &aResult)
michael@0 1146 {
michael@0 1147 aResult.Truncate();
michael@0 1148 NS_EscapeURL(aStr.BeginReading(), aStr.Length(),
michael@0 1149 aFlags | esc_AlwaysCopy, aResult);
michael@0 1150 return NS_OK;
michael@0 1151 }
michael@0 1152
michael@0 1153 NS_IMETHODIMP
michael@0 1154 nsIOService::UnescapeString(const nsACString &aStr,
michael@0 1155 uint32_t aFlags, nsACString &aResult)
michael@0 1156 {
michael@0 1157 aResult.Truncate();
michael@0 1158 NS_UnescapeURL(aStr.BeginReading(), aStr.Length(),
michael@0 1159 aFlags | esc_AlwaysCopy, aResult);
michael@0 1160 return NS_OK;
michael@0 1161 }
michael@0 1162
michael@0 1163 NS_IMETHODIMP
michael@0 1164 nsIOService::ExtractCharsetFromContentType(const nsACString &aTypeHeader,
michael@0 1165 nsACString &aCharset,
michael@0 1166 int32_t *aCharsetStart,
michael@0 1167 int32_t *aCharsetEnd,
michael@0 1168 bool *aHadCharset)
michael@0 1169 {
michael@0 1170 nsAutoCString ignored;
michael@0 1171 net_ParseContentType(aTypeHeader, ignored, aCharset, aHadCharset,
michael@0 1172 aCharsetStart, aCharsetEnd);
michael@0 1173 if (*aHadCharset && *aCharsetStart == *aCharsetEnd) {
michael@0 1174 *aHadCharset = false;
michael@0 1175 }
michael@0 1176 return NS_OK;
michael@0 1177 }
michael@0 1178
michael@0 1179 // nsISpeculativeConnect
michael@0 1180 class IOServiceProxyCallback MOZ_FINAL : public nsIProtocolProxyCallback
michael@0 1181 {
michael@0 1182 public:
michael@0 1183 NS_DECL_ISUPPORTS
michael@0 1184 NS_DECL_NSIPROTOCOLPROXYCALLBACK
michael@0 1185
michael@0 1186 IOServiceProxyCallback(nsIInterfaceRequestor *aCallbacks,
michael@0 1187 nsIOService *aIOService)
michael@0 1188 : mCallbacks(aCallbacks)
michael@0 1189 , mIOService(aIOService)
michael@0 1190 { }
michael@0 1191
michael@0 1192 private:
michael@0 1193 nsRefPtr<nsIInterfaceRequestor> mCallbacks;
michael@0 1194 nsRefPtr<nsIOService> mIOService;
michael@0 1195 };
michael@0 1196
michael@0 1197 NS_IMPL_ISUPPORTS(IOServiceProxyCallback, nsIProtocolProxyCallback)
michael@0 1198
michael@0 1199 NS_IMETHODIMP
michael@0 1200 IOServiceProxyCallback::OnProxyAvailable(nsICancelable *request, nsIChannel *channel,
michael@0 1201 nsIProxyInfo *pi, nsresult status)
michael@0 1202 {
michael@0 1203 // Checking proxy status for speculative connect
michael@0 1204 nsAutoCString type;
michael@0 1205 if (NS_SUCCEEDED(status) && pi &&
michael@0 1206 NS_SUCCEEDED(pi->GetType(type)) &&
michael@0 1207 !type.EqualsLiteral("direct")) {
michael@0 1208 // proxies dont do speculative connect
michael@0 1209 return NS_OK;
michael@0 1210 }
michael@0 1211
michael@0 1212 nsCOMPtr<nsIURI> uri;
michael@0 1213 nsresult rv = channel->GetURI(getter_AddRefs(uri));
michael@0 1214 if (NS_FAILED(rv))
michael@0 1215 return NS_OK;
michael@0 1216
michael@0 1217 nsAutoCString scheme;
michael@0 1218 rv = uri->GetScheme(scheme);
michael@0 1219 if (NS_FAILED(rv))
michael@0 1220 return NS_OK;
michael@0 1221
michael@0 1222 nsCOMPtr<nsIProtocolHandler> handler;
michael@0 1223 rv = mIOService->GetProtocolHandler(scheme.get(),
michael@0 1224 getter_AddRefs(handler));
michael@0 1225 if (NS_FAILED(rv))
michael@0 1226 return NS_OK;
michael@0 1227
michael@0 1228 nsCOMPtr<nsISpeculativeConnect> speculativeHandler =
michael@0 1229 do_QueryInterface(handler);
michael@0 1230 if (!speculativeHandler)
michael@0 1231 return NS_OK;
michael@0 1232
michael@0 1233 speculativeHandler->SpeculativeConnect(uri,
michael@0 1234 mCallbacks);
michael@0 1235 return NS_OK;
michael@0 1236 }
michael@0 1237
michael@0 1238 NS_IMETHODIMP
michael@0 1239 nsIOService::SpeculativeConnect(nsIURI *aURI,
michael@0 1240 nsIInterfaceRequestor *aCallbacks)
michael@0 1241 {
michael@0 1242 // Check for proxy information. If there is a proxy configured then a
michael@0 1243 // speculative connect should not be performed because the potential
michael@0 1244 // reward is slim with tcp peers closely located to the browser.
michael@0 1245 nsresult rv;
michael@0 1246 nsCOMPtr<nsIProtocolProxyService> pps =
michael@0 1247 do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
michael@0 1248 if (NS_FAILED(rv))
michael@0 1249 return rv;
michael@0 1250
michael@0 1251 nsCOMPtr<nsIChannel> channel;
michael@0 1252 rv = NewChannelFromURI(aURI, getter_AddRefs(channel));
michael@0 1253 if (NS_FAILED(rv))
michael@0 1254 return rv;
michael@0 1255
michael@0 1256 nsCOMPtr<nsICancelable> cancelable;
michael@0 1257 nsRefPtr<IOServiceProxyCallback> callback =
michael@0 1258 new IOServiceProxyCallback(aCallbacks, this);
michael@0 1259 return pps->AsyncResolve(channel, 0, callback, getter_AddRefs(cancelable));
michael@0 1260 }

mercurial