--- a/netwerk/base/public/nsIIOService.idl
+++ b/netwerk/base/public/nsIIOService.idl
@@ -84,16 +84,34 @@ interface nsIIOService : nsISupports
* available -- that's hard to detect without causing the dialer to
* come up).
*
* Changing this fires observer notifications ... see below.
*/
attribute boolean offline;
/**
+ * Set whether network appears to be offline for network connections from
+ * a given appID.
+ *
+ * Calling this function may fire the "network:app-offline-status-changed"
+ * notification, which is also sent to child processes containing this appId.
+ * 'state' must one of nsIAppOfflineInfo::{ONLINE|OFFLINE|WIFI_ONLY}.
+ */
+ void setAppOffline(in uint32_t appId, in long state);
+
+ /**
+ * Returns true if given appId is currently not allowed to make network
+ * connections. It will return true if the app is in the wifi-only state
+ * and we are currently on a 3G connection.
+ */
+ boolean isAppOffline(in uint32_t appId);
+
+
+ /**
* Checks if a port number is banned. This involves consulting a list of
* unsafe ports, corresponding to network services that may be easily
* exploitable. If the given port is considered unsafe, then the protocol
* handler (corresponding to aScheme) will be asked whether it wishes to
* override the IO service's decision to block the port. This gives the
* protocol handler ultimate control over its own security policy while
* ensuring reasonable, default protection.
*
@@ -112,16 +130,28 @@ interface nsIIOService : nsISupports
* @param aSpec the URL string to parse
* @return URL scheme
*
* @throws NS_ERROR_MALFORMED_URI if URL string is not of the right form.
*/
ACString extractScheme(in AUTF8String urlString);
};
+[scriptable, uuid(4ac296a0-ca1b-44f4-8787-117a88cb70fb)]
+interface nsIAppOfflineInfo : nsISupports
+{
+ readonly attribute unsigned long appId;
+
+ const long ONLINE = 1;
+ const long OFFLINE = 2;
+ const long WIFI_ONLY = 3;
+
+ readonly attribute long mode;
+};
+
%{C++
/**
* We send notifications through nsIObserverService with topic
* NS_IOSERVICE_GOING_OFFLINE_TOPIC and data NS_IOSERVICE_OFFLINE
* when 'offline' has changed from false to true, and we are about
* to shut down network services such as DNS. When those
* services have been shut down, we send a notification with
* topic NS_IOSERVICE_OFFLINE_STATUS_TOPIC and data
@@ -131,9 +161,15 @@ interface nsIIOService : nsISupports
* network services have been restarted, we send a notification
* with topic NS_IOSERVICE_OFFLINE_STATUS_TOPIC and data
* NS_IOSERVICE_ONLINE.
*/
#define NS_IOSERVICE_GOING_OFFLINE_TOPIC "network:offline-about-to-go-offline"
#define NS_IOSERVICE_OFFLINE_STATUS_TOPIC "network:offline-status-changed"
#define NS_IOSERVICE_OFFLINE "offline"
#define NS_IOSERVICE_ONLINE "online"
+
+/**
+ * When network:app-offline-status-changed is fired,
+ * the 'Subject' argument is a nsIOfflineAppInfo.
+ */
+#define NS_IOSERVICE_APP_OFFLINE_STATUS_TOPIC "network:app-offline-status-changed"
%}
new file mode 100644
--- /dev/null
+++ b/netwerk/base/src/OfflineObserver.cpp
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "OfflineObserver.h"
+#include "nsNetUtil.h"
+#include "nsIOService.h"
+#include "mozilla/net/NeckoCommon.h"
+#include "nsIObserverService.h"
+#include "nsThreadUtils.h"
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS(OfflineObserver, nsIObserver)
+
+void
+OfflineObserver::RegisterOfflineObserver()
+{
+ if (NS_IsMainThread()) {
+ RegisterOfflineObserverMainThread();
+ } else {
+ nsRefPtr<nsIRunnable> event =
+ NS_NewRunnableMethod(this, &OfflineObserver::RegisterOfflineObserverMainThread);
+ NS_DispatchToMainThread(event);
+ }
+}
+
+void
+OfflineObserver::RemoveOfflineObserver()
+{
+ if (NS_IsMainThread()) {
+ RemoveOfflineObserverMainThread();
+ } else {
+ nsRefPtr<nsIRunnable> event =
+ NS_NewRunnableMethod(this, &OfflineObserver::RemoveOfflineObserverMainThread);
+ NS_DispatchToMainThread(event);
+ }
+}
+
+void
+OfflineObserver::RegisterOfflineObserverMainThread()
+{
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ if (!observerService) {
+ return;
+ }
+ nsresult rv = observerService->AddObserver(this,
+ NS_IOSERVICE_APP_OFFLINE_STATUS_TOPIC, false);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to register observer");
+ }
+}
+
+void
+OfflineObserver::RemoveOfflineObserverMainThread()
+{
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ if (observerService) {
+ observerService->RemoveObserver(this, NS_IOSERVICE_APP_OFFLINE_STATUS_TOPIC);
+ }
+}
+
+OfflineObserver::OfflineObserver(DisconnectableParent * parent)
+{
+ mParent = parent;
+ RegisterOfflineObserver();
+}
+
+void
+OfflineObserver::RemoveObserver()
+{
+ RemoveOfflineObserver();
+ mParent = nullptr;
+}
+
+NS_IMETHODIMP
+OfflineObserver::Observe(nsISupports *aSubject,
+ const char *aTopic,
+ const char16_t *aData)
+{
+ if (mParent &&
+ !strcmp(aTopic, NS_IOSERVICE_APP_OFFLINE_STATUS_TOPIC)) {
+ mParent->OfflineNotification(aSubject);
+ }
+ return NS_OK;
+}
+
+uint32_t
+DisconnectableParent::GetAppId()
+{
+ return NECKO_UNKNOWN_APP_ID;
+}
+
+nsresult
+DisconnectableParent::OfflineNotification(nsISupports *aSubject)
+{
+ nsCOMPtr<nsIAppOfflineInfo> info(do_QueryInterface(aSubject));
+ if (!info) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ uint32_t targetAppId = NECKO_UNKNOWN_APP_ID;
+ info->GetAppId(&targetAppId);
+
+ // Obtain App ID
+ uint32_t appId = GetAppId();
+ if (appId != targetAppId) {
+ return NS_OK;
+ }
+
+ // If the app is offline, close the socket
+ if (NS_IsAppOffline(appId)) {
+ OfflineDisconnect();
+ }
+
+ return NS_OK;
+}
+
+} // net namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/netwerk/base/src/OfflineObserver.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsOfflineObserver_h__
+#define nsOfflineObserver_h__
+
+#include "nsIObserver.h"
+
+namespace mozilla {
+namespace net {
+
+/**
+ * Parents should extend this class and have a nsRefPtr<OfflineObserver> member.
+ * The constructor should initialize the member to new OfflineObserver(this)
+ * and the destructor should call RemoveObserver on the member.
+ *
+ * GetAppId and OfflineDisconnect are called from the default implementation
+ * of OfflineNotification. These should be overridden by classes that don't
+ * provide an implementation of OfflineNotification.
+ */
+class DisconnectableParent
+{
+public:
+ // This is called on the main thread, by the OfflineObserver.
+ // aSubject is of type nsAppOfflineInfo and contains appId and offline mode.
+ virtual nsresult OfflineNotification(nsISupports *aSubject);
+
+ // GetAppId returns the appId for the app associated with the parent
+ virtual uint32_t GetAppId();
+
+ // OfflineDisconnect cancels all existing connections in the parent when
+ // the app becomes offline.
+ virtual void OfflineDisconnect() { }
+};
+
+/**
+ * This class observes the "network:app-offline-status-changed" topic and calls
+ * OfflineNotification on the DisconnectableParent with the subject.
+ */
+class OfflineObserver
+ : public nsIObserver
+{
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+public:
+ // A nsRefPtr to this object should be kept by the disconnectable parent.
+
+ OfflineObserver(DisconnectableParent * parent);
+ // This method needs to be called in the destructor of the parent
+ // It removes the observer from the nsObserverService list, and it clears
+ // the pointer it holds to the disconnectable parent.
+ void RemoveObserver();
+private:
+
+ // These methods are called to register and unregister the observer.
+ // If they are called on the main thread they register the observer right
+ // away, otherwise they dispatch and event to the main thread
+ void RegisterOfflineObserver();
+ void RemoveOfflineObserver();
+ void RegisterOfflineObserverMainThread();
+ void RemoveOfflineObserverMainThread();
+private:
+ virtual ~OfflineObserver() { }
+ DisconnectableParent * mParent;
+};
+
+} // net namespace
+} // mozilla namespace
+
+#endif // nsOfflineObserver_h__
--- a/netwerk/base/src/moz.build
+++ b/netwerk/base/src/moz.build
@@ -12,16 +12,17 @@ EXPORTS += [
'nsURLParsers.h',
]
EXPORTS.mozilla.net += [
'ChannelDiverterChild.h',
'ChannelDiverterParent.h',
'Dashboard.h',
'DashboardTypes.h',
+ 'OfflineObserver.h',
]
UNIFIED_SOURCES += [
'ArrayBufferInputStream.cpp',
'BackgroundFileSaver.cpp',
'ChannelDiverterChild.cpp',
'ChannelDiverterParent.cpp',
'Dashboard.cpp',
@@ -80,16 +81,17 @@ UNIFIED_SOURCES += [
'TLSServerSocket.cpp',
]
# These files cannot be built in unified mode because they force NSPR logging.
SOURCES += [
'nsAsyncRedirectVerifyHelper.cpp',
'nsSocketTransport2.cpp',
'nsSocketTransportService2.cpp',
+ 'OfflineObserver.cpp',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
SOURCES += [
'nsAutodialWin.cpp',
'nsNativeConnectionHelper.cpp',
'nsURLHelperWin.cpp',
]
--- a/netwerk/base/src/nsIOService.cpp
+++ b/netwerk/base/src/nsIOService.cpp
@@ -23,27 +23,32 @@
#include "nsNetCID.h"
#include "nsCRT.h"
#include "nsSimpleNestedURI.h"
#include "nsNetUtil.h"
#include "nsTArray.h"
#include "nsIConsoleService.h"
#include "nsIUploadChannel2.h"
#include "nsXULAppAPI.h"
+#include "nsIScriptSecurityManager.h"
#include "nsIProtocolProxyCallback.h"
#include "nsICancelable.h"
#include "nsINetworkLinkService.h"
#include "nsPISocketTransportService.h"
#include "nsAsyncRedirectVerifyHelper.h"
#include "nsURLHelper.h"
#include "nsPIDNSService.h"
#include "nsIProtocolProxyService2.h"
#include "MainThreadUtils.h"
#include "nsIWidget.h"
+#ifdef MOZ_WIDGET_GONK
+#include "nsINetworkManager.h"
+#endif
+
#if defined(XP_WIN)
#include "nsNativeConnectionHelper.h"
#endif
using namespace mozilla;
#define PORT_PREF_PREFIX "network.security.ports."
#define PORT_PREF(x) PORT_PREF_PREFIX x
@@ -128,34 +133,38 @@ int16_t gBadPortList[] = {
4045, // lockd
6000, // x11
0, // This MUST be zero so that we can populating the array
};
static const char kProfileChangeNetTeardownTopic[] = "profile-change-net-teardown";
static const char kProfileChangeNetRestoreTopic[] = "profile-change-net-restore";
static const char kProfileDoChange[] = "profile-do-change";
+static const char kNetworkActiveChanged[] = "network-active-changed";
// Necko buffer defaults
uint32_t nsIOService::gDefaultSegmentSize = 4096;
uint32_t nsIOService::gDefaultSegmentCount = 24;
+NS_IMPL_ISUPPORTS(nsAppOfflineInfo, nsIAppOfflineInfo)
+
////////////////////////////////////////////////////////////////////////////////
nsIOService::nsIOService()
: mOffline(true)
, mOfflineForProfileChange(false)
, mManageOfflineStatus(false)
, mSettingOffline(false)
, mSetOfflineValue(false)
, mShutdown(false)
, mNetworkLinkServiceInitialized(false)
, mChannelEventSinks(NS_CHANNEL_EVENT_SINK_CATEGORY)
, mAutoDialEnabled(false)
, mNetworkNotifyChanged(true)
+ , mPreviousWifiState(-1)
{
}
nsresult
nsIOService::Init()
{
nsresult rv;
@@ -199,16 +208,17 @@ nsIOService::Init()
mozilla::services::GetObserverService();
if (observerService) {
observerService->AddObserver(this, kProfileChangeNetTeardownTopic, true);
observerService->AddObserver(this, kProfileChangeNetRestoreTopic, true);
observerService->AddObserver(this, kProfileDoChange, true);
observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, true);
observerService->AddObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC, true);
+ observerService->AddObserver(this, kNetworkActiveChanged, true);
}
else
NS_WARNING("failed to get observer service");
gIOService = this;
InitializeNetworkLinkService();
@@ -909,16 +919,71 @@ nsIOService::ParsePortList(nsIPrefBranch
void
nsIOService::GetPrefBranch(nsIPrefBranch **result)
{
*result = nullptr;
CallGetService(NS_PREFSERVICE_CONTRACTID, result);
}
+// This returns true if wifi-only apps should have connectivity.
+static bool
+IsWifiActive()
+{
+ // We don't need to do this check inside the child process
+ if (XRE_GetProcessType() != GeckoProcessType_Default) {
+ return false;
+ }
+#ifdef MOZ_WIDGET_GONK
+ // On B2G we query the network manager for the active interface
+ nsCOMPtr<nsINetworkManager> networkManager =
+ do_GetService("@mozilla.org/network/manager;1");
+ if (!networkManager) {
+ return false;
+ }
+ nsCOMPtr<nsINetworkInterface> active;
+ networkManager->GetActive(getter_AddRefs(active));
+ if (!active) {
+ return false;
+ }
+ int32_t type;
+ if (NS_FAILED(active->GetType(&type))) {
+ return false;
+ }
+ switch (type) {
+ case nsINetworkInterface::NETWORK_TYPE_WIFI:
+ case nsINetworkInterface::NETWORK_TYPE_WIFI_P2P:
+ return true;
+ default:
+ return false;
+ }
+#else
+ // On anything else than B2G we return true so than wifi-only
+ // apps don't think they are offline.
+ return true;
+#endif
+}
+
+struct EnumeratorParams {
+ nsIOService *service;
+ int32_t status;
+};
+
+PLDHashOperator
+nsIOService::EnumerateWifiAppsChangingState(const unsigned int &aKey,
+ int32_t aValue,
+ void *aUserArg)
+{
+ EnumeratorParams *params = reinterpret_cast<EnumeratorParams*>(aUserArg);
+ if (aValue == nsIAppOfflineInfo::WIFI_ONLY) {
+ params->service->NotifyAppOfflineStatus(aKey, params->status);
+ }
+ return PL_DHASH_NEXT;
+}
+
// nsIObserver interface
NS_IMETHODIMP
nsIOService::Observe(nsISupports *subject,
const char *topic,
const char16_t *data)
{
if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(subject);
@@ -971,16 +1036,45 @@ nsIOService::Observe(nsISupports *subjec
NS_ASSERTION(observerService, "The observer service should not be null");
if (observerService && mNetworkNotifyChanged) {
(void)observerService->
NotifyObservers(nullptr,
NS_NETWORK_LINK_TOPIC,
MOZ_UTF16(NS_NETWORK_LINK_DATA_CHANGED));
}
+ } else if (!strcmp(topic, kNetworkActiveChanged)) {
+#ifdef MOZ_WIDGET_GONK
+ if (IsNeckoChild()) {
+ return NS_OK;
+ }
+ nsCOMPtr<nsINetworkInterface> interface = do_QueryInterface(subject);
+ if (!interface) {
+ return NS_ERROR_FAILURE;
+ }
+ int32_t state;
+ if (NS_FAILED(interface->GetState(&state))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ bool wifiActive = IsWifiActive();
+ int32_t newWifiState = wifiActive ?
+ nsINetworkInterface::NETWORK_TYPE_WIFI :
+ nsINetworkInterface::NETWORK_TYPE_MOBILE;
+ if (mPreviousWifiState != newWifiState) {
+ // Notify wifi-only apps of their new status
+ int32_t status = wifiActive ?
+ nsIAppOfflineInfo::ONLINE : nsIAppOfflineInfo::OFFLINE;
+
+ EnumeratorParams params = {this, status};
+ mAppsOfflineStatus.EnumerateRead(EnumerateWifiAppsChangingState, ¶ms);
+ }
+
+ mPreviousWifiState = newWifiState;
+#endif
}
return NS_OK;
}
// nsINetUtil interface
NS_IMETHODIMP
nsIOService::ParseContentType(const nsACString &aTypeHeader,
@@ -1283,8 +1377,148 @@ nsIOService::SpeculativeConnect(nsIURI *
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsICancelable> cancelable;
nsRefPtr<IOServiceProxyCallback> callback =
new IOServiceProxyCallback(aCallbacks, this);
return pps->AsyncResolve(aURI, 0, callback, getter_AddRefs(cancelable));
}
+
+void
+nsIOService::NotifyAppOfflineStatus(uint32_t appId, int32_t state)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread(),
+ "Should be called on the main thread");
+
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ MOZ_ASSERT(observerService, "The observer service should not be null");
+
+ if (observerService) {
+ nsRefPtr<nsAppOfflineInfo> info = new nsAppOfflineInfo(appId, state);
+ observerService->NotifyObservers(
+ info,
+ NS_IOSERVICE_APP_OFFLINE_STATUS_TOPIC,
+ MOZ_UTF16("all data in nsIAppOfflineInfo subject argument"));
+ }
+}
+
+namespace {
+
+class SetAppOfflineMainThread : public nsRunnable
+{
+public:
+ SetAppOfflineMainThread(uint32_t aAppId, int32_t aState)
+ : mAppId(aAppId)
+ , mState(aState)
+ {
+ }
+
+ NS_IMETHOD Run()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ gIOService->SetAppOfflineInternal(mAppId, mState);
+ return NS_OK;
+ }
+private:
+ uint32_t mAppId;
+ int32_t mState;
+};
+
+}
+
+NS_IMETHODIMP
+nsIOService::SetAppOffline(uint32_t aAppId, int32_t aState)
+{
+ NS_ENSURE_TRUE(XRE_GetProcessType() == GeckoProcessType_Default,
+ NS_ERROR_FAILURE);
+ NS_ENSURE_TRUE(aAppId != nsIScriptSecurityManager::NO_APP_ID,
+ NS_ERROR_INVALID_ARG);
+ NS_ENSURE_TRUE(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
+ NS_ERROR_INVALID_ARG);
+
+ if (!NS_IsMainThread()) {
+ NS_DispatchToMainThread(new SetAppOfflineMainThread(aAppId, aState));
+ return NS_OK;
+ }
+
+ SetAppOfflineInternal(aAppId, aState);
+
+ return NS_OK;
+}
+
+void
+nsIOService::SetAppOfflineInternal(uint32_t aAppId, int32_t aState)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ NS_ENSURE_TRUE_VOID(NS_IsMainThread());
+
+ int32_t state;
+ if (mAppsOfflineStatus.Get(aAppId, &state) && state == aState) {
+ // The app is already in this state. Nothing needs to be done.
+ return;
+ }
+
+ bool wifiActive = IsWifiActive();
+ bool offline = (state == nsIAppOfflineInfo::OFFLINE) ||
+ (state == nsIAppOfflineInfo::WIFI_ONLY && !wifiActive);
+
+ switch (aState) {
+ case nsIAppOfflineInfo::OFFLINE:
+ mAppsOfflineStatus.Put(aAppId, nsIAppOfflineInfo::OFFLINE);
+ if (!offline) {
+ NotifyAppOfflineStatus(aAppId, nsIAppOfflineInfo::OFFLINE);
+ }
+ break;
+ case nsIAppOfflineInfo::WIFI_ONLY:
+ mAppsOfflineStatus.Put(aAppId, nsIAppOfflineInfo::WIFI_ONLY);
+ if (offline && wifiActive) {
+ NotifyAppOfflineStatus(aAppId, nsIAppOfflineInfo::ONLINE);
+ } else if (!offline && !wifiActive) {
+ NotifyAppOfflineStatus(aAppId, nsIAppOfflineInfo::OFFLINE);
+ }
+ break;
+ case nsIAppOfflineInfo::ONLINE:
+ mAppsOfflineStatus.Remove(aAppId);
+ if (offline) {
+ NotifyAppOfflineStatus(aAppId, nsIAppOfflineInfo::ONLINE);
+ }
+ break;
+ default:
+ break;
+ }
+
+}
+
+NS_IMETHODIMP
+nsIOService::IsAppOffline(uint32_t aAppId, bool* aResult)
+{
+ NS_ENSURE_ARG(aResult);
+ *aResult = mOffline;
+
+ if (mOffline) {
+ // If the entire browser is offline, return that status
+ return NS_OK;
+ }
+
+ if (aAppId == NECKO_NO_APP_ID ||
+ aAppId == NECKO_UNKNOWN_APP_ID) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ int32_t state;
+ if (mAppsOfflineStatus.Get(aAppId, &state)) {
+ switch (state) {
+ case nsIAppOfflineInfo::OFFLINE:
+ *aResult = true;
+ break;
+ case nsIAppOfflineInfo::WIFI_ONLY:
+ *aResult = !IsWifiActive();
+ break;
+ default:
+ // The app is online by default
+ break;
+ }
+ }
+
+ return NS_OK;
+}
--- a/netwerk/base/src/nsIOService.h
+++ b/netwerk/base/src/nsIOService.h
@@ -12,16 +12,17 @@
#include "nsCOMPtr.h"
#include "nsWeakPtr.h"
#include "nsIObserver.h"
#include "nsWeakReference.h"
#include "nsINetUtil.h"
#include "nsIChannelEventSink.h"
#include "nsCategoryCache.h"
#include "nsISpeculativeConnect.h"
+#include "nsDataHashtable.h"
#include "mozilla/Attributes.h"
#define NS_N(x) (sizeof(x)/sizeof(*x))
// We don't want to expose this observer topic.
// Intended internal use only for remoting offline/inline events.
// See Bug 552829
#define NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC "ipc:network:set-offline"
@@ -32,16 +33,22 @@ static const char gScheme[][sizeof("reso
class nsAsyncRedirectVerifyHelper;
class nsINetworkLinkService;
class nsIPrefBranch;
class nsIProtocolProxyService2;
class nsIProxyInfo;
class nsPIDNSService;
class nsPISocketTransportService;
+namespace mozilla {
+namespace net {
+ class NeckoChild;
+} // namespace net
+} // namespace mozilla
+
class nsIOService MOZ_FINAL : public nsIIOService2
, public nsIObserver
, public nsINetUtil
, public nsISpeculativeConnect
, public nsSupportsWeakReference
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
@@ -69,16 +76,19 @@ public:
bool IsOffline() { return mOffline; }
bool IsLinkUp();
bool IsComingOnline() const {
return mOffline && mSettingOffline && !mSetOfflineValue;
}
+ // Should only be called from NeckoChild. Use SetAppOffline instead.
+ void SetAppOfflineInternal(uint32_t appId, int32_t status);
+
private:
// These shouldn't be called directly:
// - construct using GetInstance
// - destroy using Release
nsIOService();
~nsIOService();
nsresult OnNetworkLinkEvent(const char *data);
@@ -97,16 +107,21 @@ private:
nsresult InitializeSocketTransportService();
nsresult InitializeNetworkLinkService();
// consolidated helper function
void LookupProxyInfo(nsIURI *aURI, nsIURI *aProxyURI, uint32_t aProxyFlags,
nsCString *aScheme, nsIProxyInfo **outPI);
+ // notify content processes of offline status
+ // 'status' must be a nsIAppOfflineInfo mode constant.
+ void NotifyAppOfflineStatus(uint32_t appId, int32_t status);
+ static PLDHashOperator EnumerateWifiAppsChangingState(const unsigned int &, int32_t, void*);
+
private:
bool mOffline;
bool mOfflineForProfileChange;
bool mManageOfflineStatus;
// Used to handle SetOffline() reentrancy. See the comment in
// SetOffline() for more details.
bool mSettingOffline;
@@ -125,20 +140,57 @@ private:
// cached categories
nsCategoryCache<nsIChannelEventSink> mChannelEventSinks;
nsTArray<int32_t> mRestrictedPortList;
bool mAutoDialEnabled;
bool mNetworkNotifyChanged;
+ int32_t mPreviousWifiState;
+ // Hashtable of (appId, nsIAppOffineInfo::mode) pairs
+ // that is used especially in IsAppOffline
+ nsDataHashtable<nsUint32HashKey, int32_t> mAppsOfflineStatus;
public:
// Used for all default buffer sizes that necko allocates.
static uint32_t gDefaultSegmentSize;
static uint32_t gDefaultSegmentCount;
};
/**
+ * This class is passed as the subject to a NotifyObservers call for the
+ * "network:app-offline-status-changed" topic.
+ * Observers will use the appId and mode to get the offline status of an app.
+ */
+class nsAppOfflineInfo : public nsIAppOfflineInfo
+{
+ NS_DECL_THREADSAFE_ISUPPORTS
+public:
+ nsAppOfflineInfo(uint32_t aAppId, int32_t aMode)
+ : mAppId(aAppId), mMode(aMode)
+ {
+ }
+
+ NS_IMETHODIMP GetMode(int32_t *aMode)
+ {
+ *aMode = mMode;
+ return NS_OK;
+ }
+
+ NS_IMETHODIMP GetAppId(uint32_t *aAppId)
+ {
+ *aAppId = mAppId;
+ return NS_OK;
+ }
+
+private:
+ virtual ~nsAppOfflineInfo() {}
+
+ uint32_t mAppId;
+ int32_t mMode;
+};
+
+/**
* Reference to the IO service singleton. May be null.
*/
extern nsIOService* gIOService;
#endif // nsIOService_h__
--- a/netwerk/ipc/NeckoChild.cpp
+++ b/netwerk/ipc/NeckoChild.cpp
@@ -21,16 +21,17 @@
#include "mozilla/dom/network/TCPSocketChild.h"
#include "mozilla/dom/network/TCPServerSocketChild.h"
#include "mozilla/dom/network/UDPSocketChild.h"
#ifdef NECKO_PROTOCOL_rtsp
#include "mozilla/net/RtspControllerChild.h"
#include "mozilla/net/RtspChannelChild.h"
#endif
#include "SerializedLoadContext.h"
+#include "nsIOService.h"
using mozilla::dom::TCPSocketChild;
using mozilla::dom::TCPServerSocketChild;
using mozilla::dom::UDPSocketChild;
namespace mozilla {
namespace net {
@@ -312,10 +313,22 @@ NeckoChild::RecvAsyncAuthPromptForNested
MOZ_CRASH();
return false;
}
dom::TabChild* tabChild = iter->second;
tabChild->SendAsyncAuthPrompt(aUri, aRealm, aCallbackId);
return true;
}
+bool
+NeckoChild::RecvAppOfflineStatus(const uint32_t& aId, const bool& aOffline)
+{
+ // Instantiate the service to make sure gIOService is initialized
+ nsCOMPtr<nsIIOService> ioService = do_GetIOService();
+ if (gIOService) {
+ gIOService->SetAppOfflineInternal(aId, aOffline ?
+ nsIAppOfflineInfo::OFFLINE : nsIAppOfflineInfo::ONLINE);
+ }
+ return true;
+}
+
}} // mozilla::net
--- a/netwerk/ipc/NeckoChild.h
+++ b/netwerk/ipc/NeckoChild.h
@@ -70,16 +70,17 @@ protected:
virtual PChannelDiverterChild*
AllocPChannelDiverterChild(const ChannelDiverterArgs& channel) MOZ_OVERRIDE;
virtual bool
DeallocPChannelDiverterChild(PChannelDiverterChild* actor) MOZ_OVERRIDE;
virtual bool RecvAsyncAuthPromptForNestedFrame(const uint64_t& aNestedFrameId,
const nsCString& aUri,
const nsString& aRealm,
const uint64_t& aCallbackId) MOZ_OVERRIDE;
+ virtual bool RecvAppOfflineStatus(const uint32_t& aId, const bool& aOffline) MOZ_OVERRIDE;
};
/**
* Reference to the PNecko Child protocol.
* Null if this is not a content process.
*/
extern PNeckoChild *gNeckoChild;
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -32,16 +32,18 @@
#include "nsHTMLDNSPrefetch.h"
#include "nsIAppsService.h"
#include "nsEscape.h"
#include "RemoteOpenFileParent.h"
#include "SerializedLoadContext.h"
#include "nsAuthInformationHolder.h"
#include "nsIAuthPromptCallback.h"
#include "nsPrincipal.h"
+#include "nsIOService.h"
+#include "mozilla/net/OfflineObserver.h"
using mozilla::dom::ContentParent;
using mozilla::dom::TabParent;
using mozilla::net::PTCPSocketParent;
using mozilla::dom::TCPSocketParent;
using mozilla::net::PTCPServerSocketParent;
using mozilla::dom::TCPServerSocketParent;
using mozilla::net::PUDPSocketParent;
@@ -69,20 +71,25 @@ NeckoParent::NeckoParent()
appsService->GetWebAppsBasePath(webPath);
}
// corePath may be empty: we don't use it for all build types
MOZ_ASSERT(!webPath.IsEmpty());
LossyCopyUTF16toASCII(corePath, mCoreAppsBasePath);
LossyCopyUTF16toASCII(webPath, mWebAppsBasePath);
}
+
+ mObserver = new OfflineObserver(this);
}
NeckoParent::~NeckoParent()
{
+ if (mObserver) {
+ mObserver->RemoveObserver();
+ }
}
static PBOverrideStatus
PBOverrideStatusFromLoadContext(const SerializedLoadContext& aSerialized)
{
if (!aSerialized.IsNotNull() && aSerialized.IsPrivateBitValid()) {
return aSerialized.mUsePrivateBrowsing ?
kPBOverride_Private :
@@ -791,9 +798,48 @@ NeckoParent::RecvOnAuthCancelled(const u
if (!callback) {
return true;
}
CallbackMap().erase(aCallbackId);
callback->OnAuthCancelled(nullptr, aUserCancel);
return true;
}
+nsresult
+NeckoParent::OfflineNotification(nsISupports *aSubject)
+{
+ nsCOMPtr<nsIAppOfflineInfo> info(do_QueryInterface(aSubject));
+ if (!info) {
+ return NS_OK;
+ }
+
+ uint32_t targetAppId = NECKO_UNKNOWN_APP_ID;
+ info->GetAppId(&targetAppId);
+
+ for (uint32_t i = 0; i < Manager()->ManagedPBrowserParent().Length(); ++i) {
+ nsRefPtr<TabParent> tabParent =
+ static_cast<TabParent*>(Manager()->ManagedPBrowserParent()[i]);
+ uint32_t appId = tabParent->OwnOrContainingAppId();
+
+ if (appId == targetAppId) {
+ if (gIOService) {
+ bool offline = false;
+ nsresult rv = gIOService->IsAppOffline(appId, &offline);
+ if (NS_FAILED(rv)) {
+ printf_stderr("Unexpected - NeckoParent: "
+ "appId not found by isAppOffline(): %u\n", appId);
+ break;
+ }
+ if (!SendAppOfflineStatus(appId, offline)) {
+ printf_stderr("NeckoParent: "
+ "SendAppOfflineStatus failed for appId: %u\n", appId);
+ }
+ // Once we found the targetAppId, we don't need to continue
+ break;
+ }
+ }
+
+ }
+
+ return NS_OK;
+}
+
}} // mozilla::net
--- a/netwerk/ipc/NeckoParent.h
+++ b/netwerk/ipc/NeckoParent.h
@@ -2,33 +2,35 @@
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/net/PNeckoParent.h"
#include "mozilla/net/NeckoCommon.h"
+#include "mozilla/net/OfflineObserver.h"
#ifndef mozilla_net_NeckoParent_h
#define mozilla_net_NeckoParent_h
namespace mozilla {
namespace net {
// Used to override channel Private Browsing status if needed.
enum PBOverrideStatus {
kPBOverride_Unset = 0,
kPBOverride_Private,
kPBOverride_NotPrivate
};
// Header file contents
-class NeckoParent :
- public PNeckoParent
+class NeckoParent
+ : public PNeckoParent
+ , public DisconnectableParent
{
public:
NeckoParent();
virtual ~NeckoParent();
MOZ_WARN_UNUSED_RESULT
static const char *
GetValidatedAppInfo(const SerializedLoadContext& aSerialized,
@@ -46,17 +48,17 @@ public:
MOZ_WARN_UNUSED_RESULT
static const char*
CreateChannelLoadContext(const PBrowserOrId& aBrowser,
PContentParent* aContent,
const SerializedLoadContext& aSerialized,
nsCOMPtr<nsILoadContext> &aResult);
virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
-
+ virtual nsresult OfflineNotification(nsISupports *) MOZ_OVERRIDE;
virtual void
CloneManagees(ProtocolBase* aSource,
mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE;
virtual PCookieServiceParent* AllocPCookieServiceParent() MOZ_OVERRIDE;
virtual bool
RecvPCookieServiceConstructor(PCookieServiceParent* aActor) MOZ_OVERRIDE
{
return PNeckoParent::RecvPCookieServiceConstructor(aActor);
@@ -196,14 +198,15 @@ protected:
const nsString& aPassword,
const nsString& aDomain) MOZ_OVERRIDE;
virtual bool RecvOnAuthCancelled(const uint64_t& aCallbackId,
const bool& aUserCancel) MOZ_OVERRIDE;
private:
nsCString mCoreAppsBasePath;
nsCString mWebAppsBasePath;
+ nsRefPtr<OfflineObserver> mObserver;
};
} // namespace net
} // namespace mozilla
#endif // mozilla_net_NeckoParent_h
--- a/netwerk/ipc/PNecko.ipdl
+++ b/netwerk/ipc/PNecko.ipdl
@@ -95,16 +95,18 @@ parent:
child:
/*
* Bring up the http auth prompt for a nested remote mozbrowser.
* NestedFrameId is the id corresponding to the PBrowser. It is the same id
* that was passed to the PBrowserOrId param in to the PHttpChannel constructor
*/
AsyncAuthPromptForNestedFrame(uint64_t nestedFrameId, nsCString uri,
nsString realm, uint64_t callbackId);
+ // Notifies child that a given app is now offline (or online)
+ AppOfflineStatus(uint32_t appId, bool offline);
both:
// Actually we need PTCPSocket() for parent. But ipdl disallows us having different
// signatures on parent and child. So when constructing the parent side object, we just
// leave host/port unused.
PTCPSocket(nsString host, uint16_t port);
};
--- a/netwerk/protocol/ftp/FTPChannelParent.cpp
+++ b/netwerk/protocol/ftp/FTPChannelParent.cpp
@@ -13,16 +13,17 @@
#include "nsIHttpChannelInternal.h"
#include "nsIForcePendingChannel.h"
#include "mozilla/ipc/InputStreamUtils.h"
#include "mozilla/ipc/URIUtils.h"
#include "mozilla/unused.h"
#include "SerializedLoadContext.h"
#include "nsIContentPolicy.h"
#include "mozilla/ipc/BackgroundUtils.h"
+#include "nsIOService.h"
using namespace mozilla::ipc;
#undef LOG
#define LOG(args) PR_LOG(gFTPLog, PR_LOG_DEBUG, args)
namespace mozilla {
namespace net {
@@ -34,21 +35,26 @@ FTPChannelParent::FTPChannelParent(nsILo
, mStatus(NS_OK)
, mDivertingFromChild(false)
, mDivertedOnStartRequest(false)
, mSuspendedForDiversion(false)
{
nsIProtocolHandler* handler;
CallGetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "ftp", &handler);
NS_ASSERTION(handler, "no ftp handler");
+
+ mObserver = new OfflineObserver(this);
}
FTPChannelParent::~FTPChannelParent()
{
gFtpHandler->Release();
+ if (mObserver) {
+ mObserver->RemoveObserver();
+ }
}
void
FTPChannelParent::ActorDestroy(ActorDestroyReason why)
{
// We may still have refcount>0 if the channel hasn't called OnStopRequest
// yet, but we must not send any more msgs to child.
mIPCClosed = true;
@@ -110,16 +116,27 @@ FTPChannelParent::DoAsyncOpen(const URIP
#ifdef DEBUG
nsCString uriSpec;
uri->GetSpec(uriSpec);
LOG(("FTPChannelParent DoAsyncOpen [this=%p uri=%s]\n",
this, uriSpec.get()));
#endif
+ bool app_offline = false;
+ uint32_t appId = GetAppId();
+ if (appId != NECKO_UNKNOWN_APP_ID &&
+ appId != NECKO_NO_APP_ID) {
+ gIOService->IsAppOffline(appId, &app_offline);
+ LOG(("FTP app id %u is offline %d\n", appId, app_offline));
+ }
+
+ if (app_offline)
+ return SendFailedAsyncOpen(NS_ERROR_OFFLINE);
+
nsresult rv;
nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv));
if (NS_FAILED(rv))
return SendFailedAsyncOpen(rv);
nsCOMPtr<nsIPrincipal> requestingPrincipal =
mozilla::ipc::PrincipalInfoToPrincipal(aRequestingPrincipalInfo, &rv);
if (NS_FAILED(rv)) {
@@ -191,16 +208,17 @@ FTPChannelParent::ConnectChannel(const u
return true;
}
bool
FTPChannelParent::RecvCancel(const nsresult& status)
{
if (mChannel)
mChannel->Cancel(status);
+
return true;
}
bool
FTPChannelParent::RecvSuspend()
{
if (mChannel)
mChannel->Suspend();
@@ -637,16 +655,35 @@ FTPChannelParent::NotifyDiversionFailed(
mDivertToListener = nullptr;
mChannel = nullptr;
if (!mIPCClosed) {
unused << SendDeleteSelf();
}
}
+void
+FTPChannelParent::OfflineDisconnect()
+{
+ if (mChannel) {
+ mChannel->Cancel(NS_ERROR_OFFLINE);
+ }
+ mStatus = NS_ERROR_OFFLINE;
+}
+
+uint32_t
+FTPChannelParent::GetAppId()
+{
+ uint32_t appId = NECKO_UNKNOWN_APP_ID;
+ if (mLoadContext) {
+ mLoadContext->GetAppId(&appId);
+ }
+ return appId;
+}
+
//-----------------------------------------------------------------------------
// FTPChannelParent::nsIChannelEventSink
//-----------------------------------------------------------------------------
NS_IMETHODIMP
FTPChannelParent::AsyncOnChannelRedirect(
nsIChannel *oldChannel,
nsIChannel *newChannel,
--- a/netwerk/protocol/ftp/FTPChannelParent.h
+++ b/netwerk/protocol/ftp/FTPChannelParent.h
@@ -8,28 +8,30 @@
#ifndef mozilla_net_FTPChannelParent_h
#define mozilla_net_FTPChannelParent_h
#include "ADivertableParentChannel.h"
#include "mozilla/net/PFTPChannelParent.h"
#include "mozilla/net/NeckoParent.h"
#include "nsIParentChannel.h"
#include "nsIInterfaceRequestor.h"
+#include "OfflineObserver.h"
class nsFtpChannel;
class nsILoadContext;
namespace mozilla {
namespace net {
class FTPChannelParent : public PFTPChannelParent
, public nsIParentChannel
, public nsIInterfaceRequestor
, public ADivertableParentChannel
, public nsIChannelEventSink
+ , public DisconnectableParent
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIPARENTCHANNEL
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSICHANNELEVENTSINK
@@ -77,16 +79,19 @@ protected:
virtual bool RecvDivertOnDataAvailable(const nsCString& data,
const uint64_t& offset,
const uint32_t& count) MOZ_OVERRIDE;
virtual bool RecvDivertOnStopRequest(const nsresult& statusCode) MOZ_OVERRIDE;
virtual bool RecvDivertComplete() MOZ_OVERRIDE;
virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
+ void OfflineDisconnect() MOZ_OVERRIDE;
+ uint32_t GetAppId() MOZ_OVERRIDE;
+
// if configured to use HTTP proxy for FTP, this can an an HTTP channel.
nsCOMPtr<nsIChannel> mChannel;
bool mIPCClosed;
nsCOMPtr<nsILoadContext> mLoadContext;
PBOverrideStatus mPBOverride;
@@ -101,14 +106,15 @@ protected:
// received from the child channel.
bool mDivertingFromChild;
// Set if OnStart|StopRequest was called during a diversion from the child.
bool mDivertedOnStartRequest;
// Set if we successfully suspended the nsHttpChannel for diversion. Unset
// when we call ResumeForDiversion.
bool mSuspendedForDiversion;
+ nsRefPtr<OfflineObserver> mObserver;
};
} // namespace net
} // namespace mozilla
#endif // mozilla_net_FTPChannelParent_h
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -24,16 +24,19 @@
#include "nsIApplicationCacheService.h"
#include "mozilla/ipc/InputStreamUtils.h"
#include "mozilla/ipc/URIUtils.h"
#include "SerializedLoadContext.h"
#include "nsIAuthInformation.h"
#include "nsIAuthPromptCallback.h"
#include "nsIContentPolicy.h"
#include "mozilla/ipc/BackgroundUtils.h"
+#include "nsIOService.h"
+#include "nsICachingChannel.h"
+
using namespace mozilla::dom;
using namespace mozilla::ipc;
namespace mozilla {
namespace net {
HttpChannelParent::HttpChannelParent(const PBrowserOrId& iframeEmbedding,
@@ -61,20 +64,25 @@ HttpChannelParent::HttpChannelParent(con
MOZ_ASSERT(gHttpHandler);
mHttpHandler = gHttpHandler;
if (iframeEmbedding.type() == PBrowserOrId::TPBrowserParent) {
mTabParent = static_cast<dom::TabParent*>(iframeEmbedding.get_PBrowserParent());
} else {
mNestedFrameId = iframeEmbedding.get_uint64_t();
}
+
+ mObserver = new OfflineObserver(this);
}
HttpChannelParent::~HttpChannelParent()
{
+ if (mObserver) {
+ mObserver->RemoveObserver();
+ }
}
void
HttpChannelParent::ActorDestroy(ActorDestroyReason why)
{
// We may still have refcount>0 if nsHttpChannel hasn't called OnStopRequest
// yet, but child process has crashed. We must not try to send any more msgs
// to child, or IPDL will kill chrome process, too.
@@ -159,17 +167,17 @@ HttpChannelParent::GetInterface(const ns
//-----------------------------------------------------------------------------
bool
HttpChannelParent::DoAsyncOpen( const URIParams& aURI,
const OptionalURIParams& aOriginalURI,
const OptionalURIParams& aDocURI,
const OptionalURIParams& aReferrerURI,
const OptionalURIParams& aAPIRedirectToURI,
- const uint32_t& loadFlags,
+ const uint32_t& aLoadFlags,
const RequestHeaderTuples& requestHeaders,
const nsCString& requestMethod,
const OptionalInputStreamParams& uploadStream,
const bool& uploadStreamHasHeaders,
const uint16_t& priority,
const uint8_t& redirectionLimit,
const bool& allowPipelining,
const bool& allowSTS,
@@ -208,16 +216,28 @@ HttpChannelParent::DoAsyncOpen( const U
return SendFailedAsyncOpen(rv);
nsCOMPtr<nsIPrincipal> requestingPrincipal =
mozilla::ipc::PrincipalInfoToPrincipal(aRequestingPrincipalInfo, &rv);
if (NS_FAILED(rv)) {
return SendFailedAsyncOpen(rv);
}
+ bool appOffline = false;
+ uint32_t appId = GetAppId();
+ if (appId != NECKO_UNKNOWN_APP_ID &&
+ appId != NECKO_NO_APP_ID) {
+ gIOService->IsAppOffline(appId, &appOffline);
+ }
+
+ uint32_t loadFlags = aLoadFlags;
+ if (appOffline) {
+ loadFlags |= nsICachingChannel::LOAD_ONLY_FROM_CACHE;
+ }
+
nsCOMPtr<nsIChannel> channel;
rv = NS_NewChannel(getter_AddRefs(channel),
uri,
requestingPrincipal,
aSecurityFlags,
aContentPolicyType,
nullptr, // aChannelPolicy
nullptr, // loadGroup
@@ -304,20 +324,18 @@ HttpChannelParent::DoAsyncOpen( const U
if (NS_SUCCEEDED(rv)) {
appCacheChan->SetApplicationCache(appCache);
setChooseApplicationCache = false;
}
}
if (setChooseApplicationCache) {
bool inBrowser = false;
- uint32_t appId = NECKO_NO_APP_ID;
if (mLoadContext) {
mLoadContext->GetIsInBrowserElement(&inBrowser);
- mLoadContext->GetAppId(&appId);
}
bool chooseAppCache = false;
nsCOMPtr<nsIScriptSecurityManager> secMan =
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
if (secMan) {
nsCOMPtr<nsIPrincipal> principal;
secMan->GetAppCodebasePrincipal(uri, appId, inBrowser, getter_AddRefs(principal));
@@ -352,16 +370,28 @@ HttpChannelParent::ConnectChannel(const
if (mPBOverride != kPBOverride_Unset) {
// redirected-to channel may not support PB
nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryObject(mChannel);
if (pbChannel) {
pbChannel->SetPrivate(mPBOverride == kPBOverride_Private ? true : false);
}
}
+ bool appOffline = false;
+ uint32_t appId = GetAppId();
+ if (appId != NECKO_UNKNOWN_APP_ID &&
+ appId != NECKO_NO_APP_ID) {
+ gIOService->IsAppOffline(appId, &appOffline);
+ }
+
+ if (appOffline) {
+ mChannel->Cancel(NS_ERROR_OFFLINE);
+ mStatus = NS_ERROR_OFFLINE;
+ }
+
return true;
}
bool
HttpChannelParent::RecvSetPriority(const uint16_t& priority)
{
if (mChannel) {
mChannel->SetPriority(priority);
@@ -1031,16 +1061,35 @@ HttpChannelParent::NotifyDiversionFailed
mParentListener = nullptr;
mChannel = nullptr;
if (!mIPCClosed) {
unused << SendDeleteSelf();
}
}
+void
+HttpChannelParent::OfflineDisconnect()
+{
+ if (mChannel) {
+ mChannel->Cancel(NS_ERROR_OFFLINE);
+ }
+ mStatus = NS_ERROR_OFFLINE;
+}
+
+uint32_t
+HttpChannelParent::GetAppId()
+{
+ uint32_t appId = NECKO_UNKNOWN_APP_ID;
+ if (mLoadContext) {
+ mLoadContext->GetAppId(&appId);
+ }
+ return appId;
+}
+
NS_IMETHODIMP
HttpChannelParent::GetAuthPrompt(uint32_t aPromptReason, const nsIID& iid,
void** aResult)
{
nsCOMPtr<nsIAuthPrompt2> prompt =
new NeckoParent::NestedFrameAuthPrompt(Manager(), mNestedFrameId);
prompt.forget(aResult);
return NS_OK;
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -8,16 +8,18 @@
#ifndef mozilla_net_HttpChannelParent_h
#define mozilla_net_HttpChannelParent_h
#include "ADivertableParentChannel.h"
#include "nsHttp.h"
#include "mozilla/net/PHttpChannelParent.h"
#include "mozilla/net/NeckoCommon.h"
#include "mozilla/net/NeckoParent.h"
+#include "OfflineObserver.h"
+#include "nsIObserver.h"
#include "nsIParentRedirectingChannel.h"
#include "nsIProgressEventSink.h"
#include "nsHttpChannel.h"
#include "nsIAuthPromptProvider.h"
class nsICacheEntry;
class nsIAssociatedContentSecurity;
@@ -33,16 +35,17 @@ class HttpChannelParentListener;
class PBrowserOrId;
class HttpChannelParent : public PHttpChannelParent
, public nsIParentRedirectingChannel
, public nsIProgressEventSink
, public nsIInterfaceRequestor
, public ADivertableParentChannel
, public nsIAuthPromptProvider
+ , public DisconnectableParent
{
virtual ~HttpChannelParent();
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIPARENTCHANNEL
@@ -124,16 +127,19 @@ protected:
nsresult ResumeForDiversion();
// Asynchronously calls NotifyDiversionFailed.
void FailDiversion(nsresult aErrorCode, bool aSkipResume = true);
friend class HttpChannelParentListener;
nsRefPtr<mozilla::dom::TabParent> mTabParent;
+ void OfflineDisconnect() MOZ_OVERRIDE;
+ uint32_t GetAppId() MOZ_OVERRIDE;
+
private:
nsRefPtr<nsHttpChannel> mChannel;
nsCOMPtr<nsICacheEntry> mCacheEntry;
nsCOMPtr<nsIAssociatedContentSecurity> mAssociatedContentSecurity;
bool mIPCClosed; // PHttpChannel actor has been Closed()
nsCOMPtr<nsIChannel> mRedirectChannel;
nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
@@ -145,16 +151,18 @@ private:
nsresult mStoredStatus;
uint64_t mStoredProgress;
uint64_t mStoredProgressMax;
bool mSentRedirect1Begin : 1;
bool mSentRedirect1BeginFailed : 1;
bool mReceivedRedirect2Verify : 1;
+ nsRefPtr<OfflineObserver> mObserver;
+
PBOverrideStatus mPBOverride;
nsCOMPtr<nsILoadContext> mLoadContext;
nsRefPtr<nsHttpHandler> mHttpHandler;
nsRefPtr<HttpChannelParentListener> mParentListener;
// Set to the canceled status value if the main channel was canceled.
nsresult mStatus;
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -2579,18 +2579,26 @@ nsHttpChannel::OpenCacheEntry(bool using
// This is a fallback channel, open fallback URI instead
rv = NS_NewURI(getter_AddRefs(openURI), mFallbackKey);
NS_ENSURE_SUCCESS(rv, rv);
}
else {
openURI = mURI;
}
+ uint32_t appId = info->AppId();
+ bool appOffline = false;
+
+ if (appId != NECKO_NO_APP_ID) {
+ gIOService->IsAppOffline(appId, &appOffline);
+ LOG(("nsHttpChannel::OpenCacheEntry appId: %u, offline: %d\n", appId, appOffline));
+ }
+
uint32_t cacheEntryOpenFlags;
- bool offline = gIOService->IsOffline();
+ bool offline = gIOService->IsOffline() || appOffline;
if (offline || (mLoadFlags & INHIBIT_CACHING)) {
if (BYPASS_LOCAL_CACHE(mLoadFlags) && !offline) {
goto bypassCacheEntryOpen;
}
cacheEntryOpenFlags = nsICacheStorage::OPEN_READONLY;
mCacheEntryIsReadOnly = true;
}
else if (BYPASS_LOCAL_CACHE(mLoadFlags) && !mApplicationCache) {
--- a/netwerk/protocol/websocket/WebSocketChannelParent.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannelParent.cpp
@@ -5,16 +5,18 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "WebSocketLog.h"
#include "WebSocketChannelParent.h"
#include "nsIAuthPromptProvider.h"
#include "mozilla/ipc/InputStreamUtils.h"
#include "mozilla/ipc/URIUtils.h"
#include "SerializedLoadContext.h"
+#include "nsIOService.h"
+#include "mozilla/net/NeckoCommon.h"
using namespace mozilla::ipc;
namespace mozilla {
namespace net {
NS_IMPL_ISUPPORTS(WebSocketChannelParent,
nsIWebSocketListener,
@@ -28,18 +30,25 @@ WebSocketChannelParent::WebSocketChannel
, mIPCOpen(true)
{
// Websocket channels can't have a private browsing override
MOZ_ASSERT_IF(!aLoadContext, aOverrideStatus == kPBOverride_Unset);
#if defined(PR_LOGGING)
if (!webSocketLog)
webSocketLog = PR_NewLogModule("nsWebSocket");
#endif
+ mObserver = new OfflineObserver(this);
}
+WebSocketChannelParent::~WebSocketChannelParent()
+{
+ if (mObserver) {
+ mObserver->RemoveObserver();
+ }
+}
//-----------------------------------------------------------------------------
// WebSocketChannelParent::PWebSocketChannelParent
//-----------------------------------------------------------------------------
bool
WebSocketChannelParent::RecvDeleteSelf()
{
LOG(("WebSocketChannelParent::RecvDeleteSelf() %p\n", this));
@@ -58,16 +67,27 @@ WebSocketChannelParent::RecvAsyncOpen(co
const uint32_t& aPingTimeout,
const bool& aClientSetPingTimeout)
{
LOG(("WebSocketChannelParent::RecvAsyncOpen() %p\n", this));
nsresult rv;
nsCOMPtr<nsIURI> uri;
+
+ bool appOffline = false;
+ uint32_t appId = GetAppId();
+ if (appId != NECKO_UNKNOWN_APP_ID &&
+ appId != NECKO_NO_APP_ID) {
+ gIOService->IsAppOffline(appId, &appOffline);
+ if (appOffline) {
+ goto fail;
+ }
+ }
+
if (aSecure) {
mChannel =
do_CreateInstance("@mozilla.org/network/protocol;1?name=wss", &rv);
} else {
mChannel =
do_CreateInstance("@mozilla.org/network/protocol;1?name=ws", &rv);
}
if (NS_FAILED(rv))
@@ -112,16 +132,17 @@ fail:
bool
WebSocketChannelParent::RecvClose(const uint16_t& code, const nsCString& reason)
{
LOG(("WebSocketChannelParent::RecvClose() %p\n", this));
if (mChannel) {
nsresult rv = mChannel->Close(code, reason);
NS_ENSURE_SUCCESS(rv, true);
}
+
return true;
}
bool
WebSocketChannelParent::RecvSendMsg(const nsCString& aMsg)
{
LOG(("WebSocketChannelParent::RecvSendMsg() %p\n", this));
if (mChannel) {
@@ -253,11 +274,29 @@ WebSocketChannelParent::GetInterface(con
NS_ADDREF(mLoadContext);
*result = static_cast<nsILoadContext*>(mLoadContext);
return NS_OK;
}
return QueryInterface(iid, result);
}
+void
+WebSocketChannelParent::OfflineDisconnect()
+{
+ if (mChannel) {
+ mChannel->Close(nsIWebSocketChannel::CLOSE_GOING_AWAY,
+ nsCString("App is offline"));
+ }
+}
+
+uint32_t
+WebSocketChannelParent::GetAppId()
+{
+ uint32_t appId = NECKO_UNKNOWN_APP_ID;
+ if (mLoadContext) {
+ mLoadContext->GetAppId(&appId);
+ }
+ return appId;
+}
} // namespace net
} // namespace mozilla
--- a/netwerk/protocol/websocket/WebSocketChannelParent.h
+++ b/netwerk/protocol/websocket/WebSocketChannelParent.h
@@ -10,28 +10,29 @@
#include "mozilla/net/PWebSocketParent.h"
#include "mozilla/net/NeckoParent.h"
#include "nsIInterfaceRequestor.h"
#include "nsIWebSocketListener.h"
#include "nsIWebSocketChannel.h"
#include "nsILoadContext.h"
#include "nsCOMPtr.h"
#include "nsString.h"
+#include "OfflineObserver.h"
class nsIAuthPromptProvider;
namespace mozilla {
namespace net {
class WebSocketChannelParent : public PWebSocketParent,
public nsIWebSocketListener,
+ public DisconnectableParent,
public nsIInterfaceRequestor
{
- ~WebSocketChannelParent() {}
-
+ ~WebSocketChannelParent();
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIWEBSOCKETLISTENER
NS_DECL_NSIINTERFACEREQUESTOR
WebSocketChannelParent(nsIAuthPromptProvider* aAuthProvider,
nsILoadContext* aLoadContext,
PBOverrideStatus aOverrideStatus);
@@ -49,16 +50,20 @@ class WebSocketChannelParent : public PW
bool RecvSendMsg(const nsCString& aMsg) MOZ_OVERRIDE;
bool RecvSendBinaryMsg(const nsCString& aMsg) MOZ_OVERRIDE;
bool RecvSendBinaryStream(const InputStreamParams& aStream,
const uint32_t& aLength) MOZ_OVERRIDE;
bool RecvDeleteSelf() MOZ_OVERRIDE;
void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
+ void OfflineDisconnect() MOZ_OVERRIDE;
+ uint32_t GetAppId() MOZ_OVERRIDE;
+ nsRefPtr<OfflineObserver> mObserver;
+
nsCOMPtr<nsIAuthPromptProvider> mAuthProvider;
nsCOMPtr<nsIWebSocketChannel> mChannel;
nsCOMPtr<nsILoadContext> mLoadContext;
bool mIPCOpen;
};
} // namespace net