Bug 786419 - Provide way to "set network offline" per app r=jduell

This commit is contained in:
Valentin Gosu 2014-08-23 06:05:56 +03:00
parent 9623afb943
commit 077e9036e0
38 changed files with 1122 additions and 24 deletions

View File

@ -94,6 +94,10 @@ nsHTMLDNSPrefetch::Shutdown()
bool
nsHTMLDNSPrefetch::IsAllowed (nsIDocument *aDocument)
{
if (NS_IsAppOffline(aDocument->NodePrincipal())) {
return false;
}
// There is no need to do prefetch on non UI scenarios such as XMLHttpRequest.
return aDocument->IsDNSPrefetchAllowed() && aDocument->GetWindow();
}

View File

@ -593,6 +593,10 @@ Navigator::CookieEnabled()
bool
Navigator::OnLine()
{
if (mWindow && mWindow->GetDoc()) {
return !NS_IsAppOffline(mWindow->GetDoc()->NodePrincipal());
}
return !NS_IsOffline();
}

View File

@ -10745,7 +10745,7 @@ nsGlobalWindow::FireOfflineStatusEvent()
if (!IsCurrentInnerWindow())
return;
nsAutoString name;
if (NS_IsOffline()) {
if (NS_IsOffline() || NS_IsAppOffline(GetPrincipal())) {
name.AssignLiteral("offline");
} else {
name.AssignLiteral("online");
@ -11301,7 +11301,8 @@ nsresult
nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData)
{
if (!nsCRT::strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC)) {
if (!nsCRT::strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC) ||
!nsCRT::strcmp(aTopic, NS_IOSERVICE_APP_OFFLINE_STATUS_TOPIC)) {
if (IsFrozen()) {
// if an even number of notifications arrive while we're frozen,
// we don't need to fire.

View File

@ -521,6 +521,11 @@ child:
*/
UIResolutionChanged();
/**
* Tell the child of an app's offline status
*/
AppOfflineStatus(uint32_t id, bool offline);
/*
* FIXME: write protocol!

View File

@ -80,6 +80,7 @@
#include "UnitTransforms.h"
#include "ClientLayerManager.h"
#include "LayersLogging.h"
#include "nsIOService.h"
#include "nsColorPickerProxy.h"
@ -2445,6 +2446,18 @@ TabChild::RecvAsyncMessage(const nsString& aMessage,
return true;
}
bool
TabChild::RecvAppOfflineStatus(const uint32_t& aId, const bool& aOffline)
{
// Instantiate the service to make sure gIOService is initialized
nsCOMPtr<nsIIOService> ioService = mozilla::services::GetIOService();
if (gIOService && ioService) {
gIOService->SetAppOfflineInternal(aId, aOffline ?
nsIAppOfflineInfo::OFFLINE : nsIAppOfflineInfo::ONLINE);
}
return true;
}
class UnloadScriptEvent : public nsRunnable
{
public:

View File

@ -368,6 +368,8 @@ public:
const InfallibleTArray<CpowEntry>& aCpows,
const IPC::Principal& aPrincipal) MOZ_OVERRIDE;
virtual bool RecvAppOfflineStatus(const uint32_t& aId, const bool& aOffline) MOZ_OVERRIDE;
virtual PDocumentRendererChild*
AllocPDocumentRendererChild(const nsRect& documentRect, const gfx::Matrix& transform,
const nsString& bgcolor,

View File

@ -237,6 +237,7 @@ TabParent::TabParent(nsIContentParent* aManager, const TabContext& aContext, uin
, mMarkedDestroying(false)
, mIsDestroyed(false)
, mAppPackageFileDescriptorSent(false)
, mSendOfflineStatus(true)
, mChromeFlags(aChromeFlags)
{
MOZ_ASSERT(aManager);
@ -502,6 +503,14 @@ TabParent::LoadURL(nsIURI* aURI)
return;
}
uint32_t appId = OwnOrContainingAppId();
if (mSendOfflineStatus && NS_IsAppOffline(appId)) {
// If the app is offline in the parent process
// pass that state to the child process as well
unused << SendAppOfflineStatus(appId, true);
}
mSendOfflineStatus = false;
unused << SendLoadURL(spec);
// If this app is a packaged app then we can speed startup by sending over

View File

@ -416,6 +416,10 @@ private:
// Whether we have already sent a FileDescriptor for the app package.
bool mAppPackageFileDescriptorSent;
// Whether we need to send the offline status to the TabChild
// This is true, until the first call of LoadURL
bool mSendOfflineStatus;
uint32_t mChromeFlags;
nsCOMPtr<nsILoadContext> mLoadContext;

View File

@ -150,6 +150,15 @@ GlobalPCList.prototype = {
} else if (data == "online") {
this._networkdown = false;
}
} else if (topic == "network:app-offline-status-changed") {
// App just went offline. The subject also contains the appId,
// but navigator.onLine checks that for us
if (!this._networkdown && !this._win.navigator.onLine) {
for (let winId in this._list) {
cleanupWinId(this._list, winId);
}
}
this._networkdown = !this._win.navigator.onLine;
} else if (topic == "gmp-plugin-crash") {
// a plugin crashed; if it's associated with any of our PCs, fire an
// event to the DOM window
@ -331,7 +340,7 @@ RTCPeerConnection.prototype = {
}
this._mustValidateRTCConfiguration(rtcConfig,
"RTCPeerConnection constructor passed invalid RTCConfiguration");
if (_globalPCList._networkdown) {
if (_globalPCList._networkdown || !this._win.navigator.onLine) {
throw new this._win.DOMError("",
"Can't create RTCPeerConnections when the network is down");
}

View File

@ -50,10 +50,55 @@ NS_INTERFACE_MAP_END
TCPSocketParentBase::TCPSocketParentBase()
: mIPCOpen(false)
{
mObserver = new mozilla::net::OfflineObserver(this);
}
TCPSocketParentBase::~TCPSocketParentBase()
{
if (mObserver) {
mObserver->RemoveObserver();
}
}
uint32_t
TCPSocketParent::GetAppId()
{
uint32_t appId = nsIScriptSecurityManager::UNKNOWN_APP_ID;
const PContentParent *content = Manager()->Manager();
const InfallibleTArray<PBrowserParent*>& browsers = content->ManagedPBrowserParent();
if (browsers.Length() > 0) {
TabParent *tab = static_cast<TabParent*>(browsers[0]);
appId = tab->OwnAppId();
}
return appId;
};
nsresult
TCPSocketParent::OfflineNotification(nsISupports *aSubject)
{
nsCOMPtr<nsIAppOfflineInfo> info(do_QueryInterface(aSubject));
if (!info) {
return NS_OK;
}
uint32_t targetAppId = nsIScriptSecurityManager::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 (mSocket && NS_IsAppOffline(appId)) {
mSocket->Close();
mSocket = nullptr;
mIntermediaryObj = nullptr;
mIntermediary = nullptr;
}
return NS_OK;
}
void
@ -95,12 +140,12 @@ TCPSocketParent::RecvOpen(const nsString& aHost, const uint16_t& aPort, const bo
}
// Obtain App ID
uint32_t appId = nsIScriptSecurityManager::NO_APP_ID;
const PContentParent *content = Manager()->Manager();
const InfallibleTArray<PBrowserParent*>& browsers = content->ManagedPBrowserParent();
if (browsers.Length() > 0) {
TabParent *tab = static_cast<TabParent*>(browsers[0]);
appId = tab->OwnAppId();
uint32_t appId = GetAppId();
if (NS_IsAppOffline(appId)) {
NS_ERROR("Can't open socket because app is offline");
FireInteralError(this, __LINE__);
return true;
}
nsresult rv;

View File

@ -11,6 +11,7 @@
#include "nsCOMPtr.h"
#include "nsIDOMTCPSocket.h"
#include "js/TypeDecls.h"
#include "mozilla/net/OfflineObserver.h"
#define TCPSOCKETPARENT_CID \
{ 0x4e7246c6, 0xa8b3, 0x426d, { 0x9c, 0x17, 0x76, 0xda, 0xb1, 0xe1, 0xe1, 0x4a } }
@ -21,6 +22,7 @@ namespace dom {
class PBrowserParent;
class TCPSocketParentBase : public nsITCPSocketParent
, public mozilla::net::DisconnectableParent
{
public:
NS_DECL_CYCLE_COLLECTION_CLASS(TCPSocketParentBase)
@ -35,6 +37,7 @@ protected:
nsCOMPtr<nsITCPSocketIntermediary> mIntermediary;
nsCOMPtr<nsIDOMTCPSocket> mSocket;
nsRefPtr<mozilla::net::OfflineObserver> mObserver;
bool mIPCOpen;
};
@ -58,6 +61,8 @@ public:
const uint32_t& aTrackingNumber) MOZ_OVERRIDE;
virtual bool RecvRequestDelete() MOZ_OVERRIDE;
virtual nsresult OfflineNotification(nsISupports *) MOZ_OVERRIDE;
virtual uint32_t GetAppId() MOZ_OVERRIDE;
private:
virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;

View File

@ -11,6 +11,10 @@
#include "nsINetAddr.h"
#include "mozilla/unused.h"
#include "mozilla/net/DNS.h"
#include "nsNetUtil.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/TabParent.h"
#include "mozilla/net/PNeckoParent.h"
namespace mozilla {
namespace dom {
@ -58,10 +62,60 @@ ConvertNetAddrToString(mozilla::net::NetAddr &netAddr, nsACString *address, uint
NS_IMPL_ISUPPORTS(UDPSocketParent, nsIUDPSocketListener)
UDPSocketParent::UDPSocketParent(nsIUDPSocketFilter *filter)
: mIPCOpen(true)
, mFilter(filter)
{
mObserver = new mozilla::net::OfflineObserver(this);
}
UDPSocketParent::~UDPSocketParent()
{
if (mObserver) {
mObserver->RemoveObserver();
}
}
uint32_t
UDPSocketParent::GetAppId()
{
uint32_t appId = nsIScriptSecurityManager::UNKNOWN_APP_ID;
const PContentParent *content = Manager()->Manager();
const InfallibleTArray<PBrowserParent*>& browsers = content->ManagedPBrowserParent();
if (browsers.Length() > 0) {
TabParent *tab = static_cast<TabParent*>(browsers[0]);
appId = tab->OwnAppId();
}
return appId;
};
nsresult
UDPSocketParent::OfflineNotification(nsISupports *aSubject)
{
nsCOMPtr<nsIAppOfflineInfo> info(do_QueryInterface(aSubject));
if (!info) {
return NS_OK;
}
uint32_t targetAppId = nsIScriptSecurityManager::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 (mSocket && NS_IsAppOffline(appId)) {
mSocket->Close();
}
return NS_OK;
}
// PUDPSocketParent methods
bool

View File

@ -11,20 +11,20 @@
#include "nsCOMPtr.h"
#include "nsIUDPSocket.h"
#include "nsIUDPSocketFilter.h"
#include "mozilla/net/OfflineObserver.h"
namespace mozilla {
namespace dom {
class UDPSocketParent : public mozilla::net::PUDPSocketParent
, public nsIUDPSocketListener
, public mozilla::net::DisconnectableParent
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIUDPSOCKETLISTENER
explicit UDPSocketParent(nsIUDPSocketFilter* filter) :
mIPCOpen(true),
mFilter(filter) {}
explicit UDPSocketParent(nsIUDPSocketFilter* filter);
bool Init(const nsCString& aHost, const uint16_t aPort);
@ -36,6 +36,8 @@ public:
const mozilla::net::NetAddr& addr);
virtual bool RecvRequestDelete() MOZ_OVERRIDE;
virtual nsresult OfflineNotification(nsISupports *) MOZ_OVERRIDE;
virtual uint32_t GetAppId() MOZ_OVERRIDE;
private:
virtual ~UDPSocketParent();
@ -44,6 +46,7 @@ private:
bool mIPCOpen;
nsCOMPtr<nsIUDPSocket> mSocket;
nsCOMPtr<nsIUDPSocketFilter> mFilter;
nsRefPtr<mozilla::net::OfflineObserver> mObserver;
};
} // namespace dom

View File

@ -2476,6 +2476,28 @@ RuntimeService::Observe(nsISupports* aSubject, const char* aTopic,
SendOfflineStatusChangeEventToAllWorkers(NS_IsOffline());
return NS_OK;
}
if (!strcmp(aTopic, NS_IOSERVICE_APP_OFFLINE_STATUS_TOPIC)) {
nsCOMPtr<nsIAppOfflineInfo> info(do_QueryInterface(aSubject));
if (!info) {
return NS_OK;
}
nsIPrincipal * principal = GetPrincipalForAsmJSCacheOp();
if (!principal) {
return NS_OK;
}
uint32_t appId = nsIScriptSecurityManager::UNKNOWN_APP_ID;
principal->GetAppId(&appId);
uint32_t notificationAppId = nsIScriptSecurityManager::UNKNOWN_APP_ID;
info->GetAppId(&notificationAppId);
if (appId != notificationAppId) {
return NS_OK;
}
SendOfflineStatusChangeEventToAllWorkers(NS_IsAppOffline(appId));
}
NS_NOTREACHED("Unknown observer topic!");
return NS_OK;

View File

@ -3528,7 +3528,7 @@ WorkerPrivate::WorkerPrivate(JSContext* aCx,
else {
AssertIsOnMainThread();
RuntimeService::GetDefaultPreferences(mPreferences);
mOnLine = !NS_IsOffline();
mOnLine = !NS_IsOffline() && !NS_IsAppOffline(aLoadInfo.mPrincipal);
}
}

View File

@ -88,6 +88,24 @@ interface nsIIOService : nsISupports
*/
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
@ -117,6 +135,18 @@ interface nsIIOService : nsISupports
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
@ -136,4 +166,10 @@ interface nsIIOService : nsISupports
#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"
%}

View File

@ -75,6 +75,7 @@
#include "nsIRedirectChannelRegistrar.h"
#include "nsIMIMEHeaderParam.h"
#include "nsILoadContext.h"
#include "nsIScriptSecurityManager.h"
#include "mozilla/Services.h"
#include "nsIPrivateBrowsingChannel.h"
#include "mozIApplicationClearPrivateDataParams.h"
@ -1616,6 +1617,30 @@ NS_IsOffline()
return offline;
}
inline bool
NS_IsAppOffline(uint32_t appId)
{
bool appOffline = false;
nsCOMPtr<nsIIOService> io(
do_GetService("@mozilla.org/network/io-service;1"));
if (io) {
io->IsAppOffline(appId, &appOffline);
}
return appOffline;
}
inline bool
NS_IsAppOffline(nsIPrincipal * principal)
{
if (!principal) {
return NS_IsOffline();
}
uint32_t appId = nsIScriptSecurityManager::UNKNOWN_APP_ID;
principal->GetAppId(&appId);
return NS_IsAppOffline(appId);
}
/**
* Helper functions for implementing nsINestedURI::innermostURI.
*

View File

@ -0,0 +1,118 @@
/* -*- 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;
}
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

View File

@ -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() = 0;
// 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__

View File

@ -16,6 +16,7 @@ EXPORTS.mozilla.net += [
'ChannelDiverterParent.h',
'Dashboard.h',
'DashboardTypes.h',
'OfflineObserver.h',
]
UNIFIED_SOURCES += [
@ -83,6 +84,7 @@ SOURCES += [
'nsAsyncRedirectVerifyHelper.cpp',
'nsSocketTransport2.cpp',
'nsSocketTransportService2.cpp',
'OfflineObserver.cpp',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':

View File

@ -28,6 +28,7 @@
#include "nsIConsoleService.h"
#include "nsIUploadChannel2.h"
#include "nsXULAppAPI.h"
#include "nsIScriptSecurityManager.h"
#include "nsIProtocolProxyCallback.h"
#include "nsICancelable.h"
#include "nsINetworkLinkService.h"
@ -37,12 +38,19 @@
#include "nsPIDNSService.h"
#include "nsIProtocolProxyService2.h"
#include "MainThreadUtils.h"
#include "nsThreadUtils.h"
#include "mozilla/net/NeckoCommon.h"
#ifdef MOZ_WIDGET_GONK
#include "nsINetworkManager.h"
#endif
#if defined(XP_WIN)
#include "nsNativeConnectionHelper.h"
#endif
using namespace mozilla;
using mozilla::net::IsNeckoChild;
#define PORT_PREF_PREFIX "network.security.ports."
#define PORT_PREF(x) PORT_PREF_PREFIX x
@ -131,11 +139,15 @@ int16_t gBadPortList[] = {
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()
@ -148,6 +160,7 @@ nsIOService::nsIOService()
, mNetworkLinkServiceInitialized(false)
, mChannelEventSinks(NS_CHANNEL_EVENT_SINK_CATEGORY)
, mAutoDialEnabled(false)
, mPreviousWifiState(-1)
{
}
@ -199,6 +212,7 @@ nsIOService::Init()
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, kNetworkActiveChanged, true);
}
else
NS_WARNING("failed to get observer service");
@ -901,6 +915,62 @@ nsIOService::GetPrefBranch(nsIPrefBranch **result)
CallGetService(NS_PREFSERVICE_CONTRACTID, result);
}
// This returns true if wifi-only apps should have connectivity.
// Always returns false in the child process (should not depend on this method)
static bool
IsWifiActive()
{
// We don't need to do this check inside the child process
if (IsNeckoChild()) {
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,
@ -956,7 +1026,37 @@ nsIOService::Observe(nsISupports *subject,
TrackNetworkLinkStatusForOffline();
}
}
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, &params);
}
mPreviousWifiState = newWifiState;
#endif
}
return NS_OK;
}
@ -1250,3 +1350,156 @@ nsIOService::SpeculativeConnect(nsIURI *aURI,
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(!IsNeckoChild(),
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;
}
// This method may be called in both the parent and the child process
// In parent it only gets called in from nsIOService::SetAppOffline
// and SetAppOfflineMainThread::Run
// In the child, it may get called from NeckoChild::RecvAppOfflineStatus
// and TabChild::RecvAppOfflineStatus.
// Note that in the child process, apps should never be in a WIFI_ONLY
// because wifi status is not available on the child
void
nsIOService::SetAppOfflineInternal(uint32_t aAppId, int32_t aState)
{
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_TRUE_VOID(NS_IsMainThread());
int32_t state = nsIAppOfflineInfo::ONLINE;
mAppsOfflineStatus.Get(aAppId, &state);
if (state == aState) {
// The app is already in this state. Nothing needs to be done.
return;
}
// wifiActive will always be false in the child process
// but it will be true in the parent process on Desktop Firefox as it does
// not have wifi-detection capabilities
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:
MOZ_RELEASE_ASSERT(!IsNeckoChild());
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:
MOZ_RELEASE_ASSERT(!IsNeckoChild());
*aResult = !IsWifiActive();
break;
default:
// The app is online by default
break;
}
}
return NS_OK;
}

View File

@ -17,6 +17,7 @@
#include "nsIChannelEventSink.h"
#include "nsCategoryCache.h"
#include "nsISpeculativeConnect.h"
#include "nsDataHashtable.h"
#include "mozilla/Attributes.h"
#define NS_N(x) (sizeof(x)/sizeof(*x))
@ -37,6 +38,12 @@ 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
@ -74,6 +81,9 @@ public:
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
@ -102,6 +112,11 @@ private:
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;
@ -129,12 +144,50 @@ private:
nsTArray<int32_t> mRestrictedPortList;
bool mAutoDialEnabled;
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.
*/

View File

@ -26,6 +26,7 @@
#include "mozilla/net/RtspChannelChild.h"
#endif
#include "SerializedLoadContext.h"
#include "nsIOService.h"
using mozilla::dom::TCPSocketChild;
using mozilla::dom::TCPServerSocketChild;
@ -319,5 +320,17 @@ NeckoChild::RecvAsyncAuthPromptForNestedFrame(const uint64_t& aNestedFrameId,
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

View File

@ -77,6 +77,7 @@ protected:
const nsCString& aUri,
const nsString& aRealm,
const uint64_t& aCallbackId) MOZ_OVERRIDE;
virtual bool RecvAppOfflineStatus(const uint32_t& aId, const bool& aOffline) MOZ_OVERRIDE;
};
/**

View File

@ -37,6 +37,8 @@
#include "SerializedLoadContext.h"
#include "nsAuthInformationHolder.h"
#include "nsIAuthPromptCallback.h"
#include "nsIOService.h"
#include "mozilla/net/OfflineObserver.h"
using mozilla::dom::ContentParent;
using mozilla::dom::TabParent;
@ -74,10 +76,15 @@ NeckoParent::NeckoParent()
LossyCopyUTF16toASCII(corePath, mCoreAppsBasePath);
LossyCopyUTF16toASCII(webPath, mWebAppsBasePath);
}
mObserver = new OfflineObserver(this);
}
NeckoParent::~NeckoParent()
{
if (mObserver) {
mObserver->RemoveObserver();
}
}
static PBOverrideStatus
@ -805,4 +812,43 @@ NeckoParent::RecvOnAuthCancelled(const uint64_t& aCallbackId,
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

View File

@ -7,6 +7,7 @@
#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
@ -22,8 +23,9 @@ enum PBOverrideStatus {
};
// Header file contents
class NeckoParent :
public PNeckoParent
class NeckoParent
: public PNeckoParent
, public DisconnectableParent
{
public:
NeckoParent();
@ -51,7 +53,8 @@ public:
nsCOMPtr<nsILoadContext> &aResult);
virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
virtual nsresult OfflineNotification(nsISupports *) MOZ_OVERRIDE;
virtual uint32_t GetAppId() MOZ_OVERRIDE { return NECKO_UNKNOWN_APP_ID; }
virtual void
CloneManagees(ProtocolBase* aSource,
mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE;
@ -206,6 +209,7 @@ protected:
private:
nsCString mCoreAppsBasePath;
nsCString mWebAppsBasePath;
nsRefPtr<OfflineObserver> mObserver;
};
} // namespace net

View File

@ -100,6 +100,8 @@ child:
*/
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

View File

@ -16,6 +16,7 @@
#include "mozilla/ipc/URIUtils.h"
#include "mozilla/unused.h"
#include "SerializedLoadContext.h"
#include "nsIOService.h"
using namespace mozilla::ipc;
@ -37,11 +38,16 @@ FTPChannelParent::FTPChannelParent(nsILoadContext* aLoadContext, PBOverrideStatu
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
@ -108,7 +114,18 @@ FTPChannelParent::DoAsyncOpen(const URIParams& aURI,
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));
}
nsresult rv;
if (app_offline)
return SendFailedAsyncOpen(NS_ERROR_OFFLINE);
nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv));
if (NS_FAILED(rv))
return SendFailedAsyncOpen(rv);
@ -173,6 +190,7 @@ FTPChannelParent::RecvCancel(const nsresult& status)
{
if (mChannel)
mChannel->Cancel(status);
return true;
}
@ -619,6 +637,25 @@ FTPChannelParent::NotifyDiversionFailed(nsresult aErrorCode,
}
}
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
//-----------------------------------------------------------------------------

View File

@ -13,6 +13,7 @@
#include "mozilla/net/NeckoParent.h"
#include "nsIParentChannel.h"
#include "nsIInterfaceRequestor.h"
#include "OfflineObserver.h"
class nsFtpChannel;
class nsILoadContext;
@ -25,6 +26,7 @@ class FTPChannelParent : public PFTPChannelParent
, public nsIInterfaceRequestor
, public ADivertableParentChannel
, public nsIChannelEventSink
, public DisconnectableParent
{
public:
NS_DECL_ISUPPORTS
@ -79,6 +81,9 @@ protected:
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;
@ -103,6 +108,7 @@ protected:
// Set if we successfully suspended the nsHttpChannel for diversion. Unset
// when we call ResumeForDiversion.
bool mSuspendedForDiversion;
nsRefPtr<OfflineObserver> mObserver;
};
} // namespace net

View File

@ -2513,7 +2513,11 @@ nsFtpState::CheckCache()
// Set cache access requested:
nsCacheAccessMode accessReq;
if (NS_IsOffline()) {
uint32_t appId;
bool isInBrowser;
NS_GetAppInfo(mChannel, &appId, &isInBrowser);
if (NS_IsOffline() || NS_IsAppOffline(appId)) {
accessReq = nsICache::ACCESS_READ; // can only read
} else if (mChannel->HasLoadFlag(nsIRequest::LOAD_BYPASS_CACHE)) {
accessReq = nsICache::ACCESS_WRITE; // replace cache entry

View File

@ -27,6 +27,8 @@
#include "SerializedLoadContext.h"
#include "nsIAuthInformation.h"
#include "nsIAuthPromptCallback.h"
#include "nsIOService.h"
#include "nsICachingChannel.h"
using namespace mozilla::dom;
using namespace mozilla::ipc;
@ -64,10 +66,15 @@ HttpChannelParent::HttpChannelParent(const PBrowserOrId& iframeEmbedding,
} else {
mNestedFrameId = iframeEmbedding.get_uint64_t();
}
mObserver = new OfflineObserver(this);
}
HttpChannelParent::~HttpChannelParent()
{
if (mObserver) {
mObserver->RemoveObserver();
}
}
void
@ -160,7 +167,7 @@ HttpChannelParent::DoAsyncOpen( const URIParams& aURI,
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,
@ -200,6 +207,20 @@ HttpChannelParent::DoAsyncOpen( const URIParams& aURI,
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;
loadFlags |= nsIRequest::LOAD_FROM_CACHE;
loadFlags |= nsICachingChannel::LOAD_NO_NETWORK_IO;
}
nsCOMPtr<nsIChannel> channel;
rv = NS_NewChannel(getter_AddRefs(channel), uri, ios, nullptr, nullptr, loadFlags);
if (NS_FAILED(rv))
@ -285,10 +306,8 @@ HttpChannelParent::DoAsyncOpen( const URIParams& aURI,
if (setChooseApplicationCache) {
bool inBrowser = false;
uint32_t appId = NECKO_NO_APP_ID;
if (mLoadContext) {
mLoadContext->GetIsInBrowserElement(&inBrowser);
mLoadContext->GetAppId(&appId);
}
bool chooseAppCache = false;
@ -333,6 +352,22 @@ HttpChannelParent::ConnectChannel(const uint32_t& channelId)
}
}
bool appOffline = false;
uint32_t appId = GetAppId();
if (appId != NECKO_UNKNOWN_APP_ID &&
appId != NECKO_NO_APP_ID) {
gIOService->IsAppOffline(appId, &appOffline);
}
if (appOffline) {
uint32_t loadFlags;
mChannel->GetLoadFlags(&loadFlags);
loadFlags |= nsICachingChannel::LOAD_ONLY_FROM_CACHE;
loadFlags |= nsIRequest::LOAD_FROM_CACHE;
loadFlags |= nsICachingChannel::LOAD_NO_NETWORK_IO;
mChannel->SetLoadFlags(loadFlags);
}
return true;
}
@ -1001,6 +1036,25 @@ HttpChannelParent::NotifyDiversionFailed(nsresult aErrorCode,
}
}
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)

View File

@ -13,6 +13,8 @@
#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"
@ -38,6 +40,7 @@ class HttpChannelParent : public PHttpChannelParent
, public nsIInterfaceRequestor
, public ADivertableParentChannel
, public nsIAuthPromptProvider
, public DisconnectableParent
{
virtual ~HttpChannelParent();
@ -126,6 +129,9 @@ protected:
friend class HttpChannelParentListener;
nsRefPtr<mozilla::dom::TabParent> mTabParent;
void OfflineDisconnect() MOZ_OVERRIDE;
uint32_t GetAppId() MOZ_OVERRIDE;
private:
nsRefPtr<nsHttpChannel> mChannel;
nsCOMPtr<nsICacheEntry> mCacheEntry;
@ -147,6 +153,8 @@ private:
bool mSentRedirect1BeginFailed : 1;
bool mReceivedRedirect2Verify : 1;
nsRefPtr<OfflineObserver> mObserver;
PBOverrideStatus mPBOverride;
nsCOMPtr<nsILoadContext> mLoadContext;

View File

@ -2561,8 +2561,16 @@ nsHttpChannel::OpenCacheEntry(bool usingSSL)
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;

View File

@ -10,6 +10,8 @@
#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;
@ -33,8 +35,15 @@ WebSocketChannelParent::WebSocketChannelParent(nsIAuthPromptProvider* aAuthProvi
if (!webSocketLog)
webSocketLog = PR_NewLogModule("nsWebSocket");
#endif
mObserver = new OfflineObserver(this);
}
WebSocketChannelParent::~WebSocketChannelParent()
{
if (mObserver) {
mObserver->RemoveObserver();
}
}
//-----------------------------------------------------------------------------
// WebSocketChannelParent::PWebSocketChannelParent
//-----------------------------------------------------------------------------
@ -63,6 +72,17 @@ WebSocketChannelParent::RecvAsyncOpen(const URIParams& aURI,
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);
@ -117,6 +137,7 @@ WebSocketChannelParent::RecvClose(const uint16_t& code, const nsCString& reason)
nsresult rv = mChannel->Close(code, reason);
NS_ENSURE_SUCCESS(rv, true);
}
return true;
}
@ -258,6 +279,24 @@ WebSocketChannelParent::GetInterface(const nsIID & iid, void **result)
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

View File

@ -15,6 +15,7 @@
#include "nsILoadContext.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "OfflineObserver.h"
class nsIAuthPromptProvider;
@ -23,10 +24,10 @@ namespace net {
class WebSocketChannelParent : public PWebSocketParent,
public nsIWebSocketListener,
public DisconnectableParent,
public nsIInterfaceRequestor
{
~WebSocketChannelParent() {}
~WebSocketChannelParent();
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIWEBSOCKETLISTENER
@ -54,6 +55,10 @@ class WebSocketChannelParent : public PWebSocketParent,
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;

View File

@ -0,0 +1,55 @@
function inChildProcess() {
return Cc["@mozilla.org/xre/app-info;1"]
.getService(Ci.nsIXULRuntime)
.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
}
function makeChan(url, appId, inBrowser) {
var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
var chan = ios.newChannel(url, null, null).QueryInterface(Ci.nsIHttpChannel);
chan.notificationCallbacks = {
appId: appId,
isInBrowserElement: inBrowser,
QueryInterface: function(iid) {
if (iid.equals(Ci.nsILoadContext))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
},
getInterface: function(iid) { return this.QueryInterface(iid); }
};
return chan;
}
// Simple online load
function run_test() {
do_test_pending();
var chan = makeChan("http://localhost:12345/first", 14, false);
chan.asyncOpen(new ChannelListener(checkResponse, "response0"), null);
}
// Should return cached result
function test1() {
do_test_pending();
var chan = makeChan("http://localhost:12345/first", 14, false);
chan.asyncOpen(new ChannelListener(checkResponse, "response0"), null);
}
// This request should fail
function test2() {
do_test_pending();
var chan = makeChan("http://localhost:12345/second", 14, false);
chan.asyncOpen(new ChannelListener(checkResponse, "", CL_EXPECT_FAILURE), null);
}
// This request should succeed
function test3() {
do_test_pending();
var chan = makeChan("http://localhost:12345/second", 14, false);
chan.asyncOpen(new ChannelListener(checkResponse, "response3"), null);
}
function checkResponse(req, buffer, expected) {
do_check_eq(buffer, expected);
do_test_finished();
}

View File

@ -0,0 +1,74 @@
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
var test_index = 0;
var responses = [
"response0", // This should be the first returned value
"response1", // This response should not be recevied. Load response0 from cache
"response2", // This request should fail
"response3", // This request should succeed
];
function http_handler(metadata, response) {
response.setHeader("Content-Type", "text/plain", false);
response.setHeader("Cache-Control", "no-cache", false);
response.setStatusLine(metadata.httpVersion, 200, "OK");
var body = responses[test_index];
response.bodyOutputStream.write(body, body.length);
}
function set_app_offline(appId, offline) {
let ioservice = Cc['@mozilla.org/network/io-service;1'].
getService(Ci.nsIIOService);
ioservice.setAppOffline(appId, offline);
}
var httpserv;
function setup() {
httpserv = new HttpServer();
httpserv.registerPathHandler("/first", http_handler);
httpserv.registerPathHandler("/second", http_handler);
httpserv.start(12345);
}
function run_test() {
setup();
test0();
}
// Test that app 14 can open channel
function test0() {
test_index = 0;
run_test_in_child("child_app_offline.js", test1);
}
// Set app 14 offline and check that it still gets a cached response
function test1() {
test_index = 1;
set_app_offline(14, Ci.nsIAppOfflineInfo.OFFLINE);
sendCommand('test1();\n', test2);
}
// Check that app 14 can't open a channel to a new location
function test2() {
test_index = 2;
sendCommand('test2();\n', test3);
}
// Set app online and check that it now works
function test3() {
test_index = 3;
set_app_offline(14, Ci.nsIAppOfflineInfo.ONLINE);
sendCommand('test3();\n', ending);
}
function ending(val) {
do_test_finished();
}

View File

@ -2,6 +2,7 @@
head = head_channels_clone.js head_cc.js
tail =
support-files = disabled_test_bug528292_wrap.js
child_app_offline.js
[test_bug248970_cookie_wrap.js]
[test_cacheflags_wrap.js]
@ -34,3 +35,4 @@ skip-if = true
[test_XHR_redirects.js]
[test_redirect_history_wrap.js]
[test_reply_without_content_type_wrap.js]
[test_app_offline_http.js]