From 5882106bb79aacbe381b7e3eaa1720e9c55690d4 Mon Sep 17 00:00:00 2001 From: Valentin Gosu Date: Sat, 2 May 2015 00:14:39 +0300 Subject: [PATCH] Bug 1134596 - Separate nsIOService's network detection from offlineMode r=honzab --- browser/app/profile/firefox.js | 2 +- dom/ipc/ContentChild.cpp | 17 +++- dom/ipc/ContentChild.h | 1 + dom/ipc/ContentParent.cpp | 22 ++++- dom/ipc/ContentParent.h | 1 + dom/ipc/PContent.ipdl | 3 +- netwerk/base/nsIIOService.idl | 18 +++- netwerk/base/nsIOService.cpp | 149 ++++++++++++++++++++++----------- netwerk/base/nsIOService.h | 7 +- netwerk/base/nsNetUtil.h | 7 +- 10 files changed, 171 insertions(+), 56 deletions(-) diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 56619a7f424..d37d0c98cae 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -610,7 +610,7 @@ pref("mousewheel.with_win.action", 1); pref("browser.xul.error_pages.enabled", true); pref("browser.xul.error_pages.expert_bad_cert", false); -// Work Offline is best manually managed by the user. +// If true, network link events will change the value of navigator.onLine pref("network.manage-offline-status", false); // We want to make sure mail URLs are handled externally... diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 62c0350b4ed..046e243eb2d 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -806,12 +806,15 @@ ContentChild::InitXPCOM() NS_WARNING("Couldn't register console listener for child process"); bool isOffline, isLangRTL; + bool isConnected; ClipboardCapabilities clipboardCaps; DomainPolicyClone domainPolicy; - SendGetXPCOMProcessAttributes(&isOffline, &isLangRTL, &mAvailableDictionaries, + SendGetXPCOMProcessAttributes(&isOffline, &isConnected, + &isLangRTL, &mAvailableDictionaries, &clipboardCaps, &domainPolicy); RecvSetOffline(isOffline); + RecvSetConnectivity(isConnected); RecvBidiKeyboardNotify(isLangRTL); // Create the CPOW manager as soon as possible. @@ -1883,6 +1886,18 @@ ContentChild::RecvSetOffline(const bool& offline) return true; } +bool +ContentChild::RecvSetConnectivity(const bool& connectivity) +{ + nsCOMPtr io(do_GetIOService()); + nsCOMPtr ioInternal(do_QueryInterface(io)); + NS_ASSERTION(ioInternal, "IO Service can not be null"); + + ioInternal->SetConnectivity(connectivity); + + return true; +} + void ContentChild::ActorDestroy(ActorDestroyReason why) { diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index 139740418ad..a5b0dae1fff 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -294,6 +294,7 @@ public: virtual bool DeallocPRemoteSpellcheckEngineChild(PRemoteSpellcheckEngineChild*) override; virtual bool RecvSetOffline(const bool& offline) override; + virtual bool RecvSetConnectivity(const bool& connectivity) override; virtual bool RecvSpeakerManagerNotify() override; diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 34fade5903e..2990d34a077 100755 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -405,6 +405,7 @@ bool ContentParent::sNuwaReady = false; #endif #define NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC "ipc:network:set-offline" +#define NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC "ipc:network:set-connectivity" class MemoryReportRequestParent : public PMemoryReportRequestParent { @@ -643,6 +644,7 @@ static const char* sObserverTopics[] = { "xpcom-shutdown", "profile-before-change", NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC, + NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC, "child-memory-reporter-request", "memory-pressure", "child-gc-request", @@ -2935,13 +2937,16 @@ ContentParent::RecvAddNewProcess(const uint32_t& aPid, // Update offline settings. bool isOffline, isLangRTL; + bool isConnected; InfallibleTArray unusedDictionaries; ClipboardCapabilities clipboardCaps; DomainPolicyClone domainPolicy; - RecvGetXPCOMProcessAttributes(&isOffline, &isLangRTL, &unusedDictionaries, + RecvGetXPCOMProcessAttributes(&isOffline, &isConnected, + &isLangRTL, &unusedDictionaries, &clipboardCaps, &domainPolicy); mozilla::unused << content->SendSetOffline(isOffline); + mozilla::unused << content->SendSetConnectivity(isConnected); MOZ_ASSERT(!clipboardCaps.supportsSelectionClipboard() && !clipboardCaps.supportsFindClipboard(), "Unexpected values"); @@ -3032,6 +3037,17 @@ ContentParent::Observe(nsISupports* aSubject, } #ifdef MOZ_NUWA_PROCESS } +#endif + } + else if (!strcmp(aTopic, NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC)) { +#ifdef MOZ_NUWA_PROCESS + if (!(IsNuwaReady() && IsNuwaProcess())) { +#endif + if (!SendSetConnectivity(NS_LITERAL_STRING("true").Equals(aData))) { + return NS_ERROR_NOT_AVAILABLE; + } +#ifdef MOZ_NUWA_PROCESS + } #endif } // listening for alert notifications @@ -3282,6 +3298,7 @@ ContentParent::RecvGetProcessAttributes(ContentParentId* aCpId, bool ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline, + bool* aIsConnected, bool* aIsLangRTL, InfallibleTArray* dictionaries, ClipboardCapabilities* clipboardCaps, @@ -3292,6 +3309,9 @@ ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline, DebugOnly rv = io->GetOffline(aIsOffline); MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting offline?"); + rv = io->GetConnectivity(aIsConnected); + MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting connectivity?"); + nsIBidiKeyboard* bidi = nsContentUtils::GetBidiKeyboard(); *aIsLangRTL = false; diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 1baad990417..5503fd5ed3e 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -546,6 +546,7 @@ private: bool* aIsForApp, bool* aIsForBrowser) override; virtual bool RecvGetXPCOMProcessAttributes(bool* aIsOffline, + bool* aIsConnected, bool* aIsLangRTL, InfallibleTArray* dictionaries, ClipboardCapabilities* clipboardCaps, diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 8029729eae4..3a204fcaf7d 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -525,6 +525,7 @@ child: RegisterChromeItem(ChromeRegistryItem item); async SetOffline(bool offline); + async SetConnectivity(bool connectivity); async NotifyVisited(URIParams uri); @@ -669,7 +670,7 @@ parent: sync GetProcessAttributes() returns (ContentParentId cpId, bool isForApp, bool isForBrowser); sync GetXPCOMProcessAttributes() - returns (bool isOffline, bool isLangRTL, nsString[] dictionaries, + returns (bool isOffline, bool isConnected, bool isLangRTL, nsString[] dictionaries, ClipboardCapabilities clipboardCaps, DomainPolicyClone domainPolicy); diff --git a/netwerk/base/nsIIOService.idl b/netwerk/base/nsIIOService.idl index f4ba1a20a14..4e386b1b3de 100644 --- a/netwerk/base/nsIIOService.idl +++ b/netwerk/base/nsIIOService.idl @@ -22,7 +22,7 @@ interface nsILoadInfo; * as a convenience to the programmer and in some cases to improve performance * by eliminating intermediate data structures and interfaces. */ -[scriptable, uuid(b1c3c61d-2df9-4240-ae16-0355b51a2770)] +[scriptable, uuid(4286de5a-b2ea-446f-8f70-e2a461f42694)] interface nsIIOService : nsISupports { /** @@ -178,6 +178,11 @@ interface nsIIOService : nsISupports */ attribute boolean offline; + /** + * Returns false if there are no interfaces for a network request + */ + readonly attribute boolean connectivity; + /** * Set whether network appears to be offline for network connections from * a given appID. @@ -269,3 +274,14 @@ interface nsIAppOfflineInfo : nsISupports */ #define NS_IOSERVICE_APP_OFFLINE_STATUS_TOPIC "network:app-offline-status-changed" %} + +[builtinclass, uuid(cd66ffef-3bc3-40de-841a-e2dcbea213a2)] +interface nsIIOServiceInternal : nsISupports +{ + /** + * This is an internal method that should only be called from ContentChild + * in order to pass the connectivity state from the chrome process to the + * content process. It throws if called outside the content process. + */ + void SetConnectivity(in boolean connectivity); +}; diff --git a/netwerk/base/nsIOService.cpp b/netwerk/base/nsIOService.cpp index e34c7ba4d6d..860c851ef0a 100644 --- a/netwerk/base/nsIOService.cpp +++ b/netwerk/base/nsIOService.cpp @@ -158,7 +158,8 @@ NS_IMPL_ISUPPORTS(nsAppOfflineInfo, nsIAppOfflineInfo) nsIOService::nsIOService() : mOffline(true) , mOfflineForProfileChange(false) - , mManageOfflineStatus(false) + , mManageLinkStatus(false) + , mConnectivity(true) , mSettingOffline(false) , mSetOfflineValue(false) , mShutdown(false) @@ -284,17 +285,10 @@ nsIOService::InitializeNetworkLinkService() if (mNetworkLinkService) { mNetworkLinkServiceInitialized = true; } - else { - // We can't really determine if the machine has a usable network connection, - // so let's cross our fingers! - mManageOfflineStatus = false; - } - if (mManageOfflineStatus) - OnNetworkLinkEvent(NS_NETWORK_LINK_DATA_UNKNOWN); - else - SetOffline(false); - + // After initializing the networkLinkService, query the connectivity state + OnNetworkLinkEvent(NS_NETWORK_LINK_DATA_UNKNOWN); + return rv; } @@ -323,6 +317,7 @@ NS_IMPL_ISUPPORTS(nsIOService, nsINetUtil, nsISpeculativeConnect, nsIObserver, + nsIIOServiceInternal, nsISupportsWeakReference) //////////////////////////////////////////////////////////////////////////////// @@ -955,7 +950,8 @@ nsIOService::SetOffline(bool offline) mProxyService->ReloadPAC(); // don't care if notification fails - if (observerService) + // Only send the ONLINE notification if there is connectivity + if (observerService && mConnectivity) observerService->NotifyObservers(subject, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, NS_LITERAL_STRING(NS_IOSERVICE_ONLINE).get()); @@ -981,6 +977,72 @@ nsIOService::SetOffline(bool offline) return NS_OK; } +NS_IMETHODIMP +nsIOService::GetConnectivity(bool *aConnectivity) +{ + *aConnectivity = mConnectivity; + return NS_OK; +} + +NS_IMETHODIMP +nsIOService::SetConnectivity(bool aConnectivity) +{ + // This should only be called from ContentChild to pass the connectivity + // value from the chrome process to the content process. + if (XRE_GetProcessType() == GeckoProcessType_Default) { + return NS_ERROR_NOT_AVAILABLE; + } + return SetConnectivityInternal(aConnectivity); +} + + +nsresult +nsIOService::SetConnectivityInternal(bool aConnectivity) +{ + if (mConnectivity == aConnectivity) { + // Nothing to do here. + return NS_OK; + } + mConnectivity = aConnectivity; + + nsCOMPtr observerService = + mozilla::services::GetObserverService(); + if (!observerService) { + return NS_OK; + } + // This notification sends the connectivity to the child processes + if (XRE_GetProcessType() == GeckoProcessType_Default) { + observerService->NotifyObservers(nullptr, + NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC, aConnectivity ? + MOZ_UTF16("true") : + MOZ_UTF16("false")); + } + + if (mOffline) { + // We don't need to send any notifications if we're offline + return NS_OK; + } + + if (aConnectivity) { + // If we were previously offline due to connectivity=false, + // send the ONLINE notification + observerService->NotifyObservers( + static_cast(this), + NS_IOSERVICE_OFFLINE_STATUS_TOPIC, + NS_LITERAL_STRING(NS_IOSERVICE_ONLINE).get()); + } else { + // If we were previously online and lost connectivity + // send the OFFLINE notification + const nsLiteralString offlineString(MOZ_UTF16(NS_IOSERVICE_OFFLINE)); + observerService->NotifyObservers(static_cast(this), + NS_IOSERVICE_GOING_OFFLINE_TOPIC, + offlineString.get()); + observerService->NotifyObservers(static_cast(this), + NS_IOSERVICE_OFFLINE_STATUS_TOPIC, + offlineString.get()); + } + return NS_OK; +} NS_IMETHODIMP nsIOService::AllowPort(int32_t inPort, const char *scheme, bool *_retval) @@ -1209,11 +1271,8 @@ nsIOService::Observe(nsISupports *subject, } else if (!strcmp(topic, kProfileChangeNetRestoreTopic)) { if (mOfflineForProfileChange) { mOfflineForProfileChange = false; - if (!mManageOfflineStatus || - NS_FAILED(OnNetworkLinkEvent(NS_NETWORK_LINK_DATA_UNKNOWN))) { - SetOffline(false); - } - } + SetOffline(false); + } } else if (!strcmp(topic, kProfileDoChange)) { if (data && NS_LITERAL_STRING("startup").Equals(data)) { // Lazy initialization of network link service (see bug 620472) @@ -1221,6 +1280,10 @@ nsIOService::Observe(nsISupports *subject, // Set up the initilization flag regardless the actuall result. // If we fail here, we will fail always on. mNetworkLinkServiceInitialized = true; + + // The browser starts off as offline. We go into online mode after this. + SetOffline(false); + // And now reflect the preference setting nsCOMPtr prefBranch; GetPrefBranch(getter_AddRefs(prefBranch)); @@ -1237,9 +1300,7 @@ nsIOService::Observe(nsISupports *subject, // Break circular reference. mProxyService = nullptr; } else if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) { - if (!mOfflineForProfileChange && mManageOfflineStatus) { - OnNetworkLinkEvent(NS_ConvertUTF16toUTF8(data).get()); - } + OnNetworkLinkEvent(NS_ConvertUTF16toUTF8(data).get()); } else if (!strcmp(topic, NS_WIDGET_WAKE_OBSERVER_TOPIC)) { // coming back alive from sleep nsCOMPtr observerService = @@ -1383,32 +1444,26 @@ nsIOService::NewSimpleNestedURI(nsIURI* aURI, nsIURI** aResult) NS_IMETHODIMP nsIOService::SetManageOfflineStatus(bool aManage) { - nsresult rv = NS_OK; + mManageLinkStatus = aManage; - // SetManageOfflineStatus must throw when we fail to go from non-managed - // to managed. Usually because there is no link monitoring service - // available. Failure to do this switch is detected by a failure of - // OnNetworkLinkEvent(). When there is no network link available during - // call to InitializeNetworkLinkService(), application is put to offline - // mode. And when we change mMangeOfflineStatus to false on the next line - // we get stuck on being offline even though the link becomes later - // available. - bool wasManaged = mManageOfflineStatus; - mManageOfflineStatus = aManage; + // When detection is not activated, the default connectivity state is true. + if (!mManageLinkStatus) { + SetConnectivityInternal(true); + return NS_OK; + } InitializeNetworkLinkService(); - - if (mManageOfflineStatus && !wasManaged) { - rv = OnNetworkLinkEvent(NS_NETWORK_LINK_DATA_UNKNOWN); - if (NS_FAILED(rv)) - mManageOfflineStatus = false; - } - return rv; + // If the NetworkLinkService is already initialized, it does not call + // OnNetworkLinkEvent. This is needed, when mManageLinkStatus goes from + // false to true. + OnNetworkLinkEvent(NS_NETWORK_LINK_DATA_UNKNOWN); + return NS_OK; } NS_IMETHODIMP -nsIOService::GetManageOfflineStatus(bool* aManage) { - *aManage = mManageOfflineStatus; +nsIOService::GetManageOfflineStatus(bool* aManage) +{ + *aManage = mManageLinkStatus; return NS_OK; } @@ -1422,7 +1477,7 @@ nsIOService::OnNetworkLinkEvent(const char *data) if (mShutdown) return NS_ERROR_NOT_AVAILABLE; - if (!mManageOfflineStatus) { + if (!mManageLinkStatus) { return NS_OK; } @@ -1435,21 +1490,19 @@ nsIOService::OnNetworkLinkEvent(const char *data) // dial option is set to always autodial. If so, then we are // always up for the purposes of offline management. if (autodialEnabled) { + bool isUp = true; #if defined(XP_WIN) // On Windows, we should first check with the OS to see if // autodial is enabled. If it is enabled then we are allowed // to manage the offline state. - if (nsNativeConnectionHelper::IsAutodialEnabled()) { - return SetOffline(false); - } -#else - return SetOffline(false); + isUp = nsNativeConnectionHelper::IsAutodialEnabled(); #endif + return SetConnectivityInternal(isUp); } } } - bool isUp; + bool isUp = true; if (!strcmp(data, NS_NETWORK_LINK_DATA_CHANGED)) { // CHANGED means UP/DOWN didn't change return NS_OK; @@ -1465,7 +1518,7 @@ nsIOService::OnNetworkLinkEvent(const char *data) return NS_OK; } - return SetOffline(!isUp); + return SetConnectivityInternal(isUp); } NS_IMETHODIMP diff --git a/netwerk/base/nsIOService.h b/netwerk/base/nsIOService.h index d8631c667bd..5a5507b7c2f 100644 --- a/netwerk/base/nsIOService.h +++ b/netwerk/base/nsIOService.h @@ -26,6 +26,7 @@ // Intended internal use only for remoting offline/inline events. // See Bug 552829 #define NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC "ipc:network:set-offline" +#define NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC "ipc:network:set-connectivity" static const char gScheme[][sizeof("resource")] = {"chrome", "file", "http", "https", "jar", "data", "resource"}; @@ -49,6 +50,7 @@ class nsIOService final : public nsIIOService2 , public nsINetUtil , public nsISpeculativeConnect , public nsSupportsWeakReference + , public nsIIOServiceInternal { public: NS_DECL_THREADSAFE_ISUPPORTS @@ -57,6 +59,7 @@ public: NS_DECL_NSIOBSERVER NS_DECL_NSINETUTIL NS_DECL_NSISPECULATIVECONNECT + NS_DECL_NSIIOSERVICEINTERNAL // Gets the singleton instance of the IO Service, creating it as needed // Returns nullptr on out of memory or failure to initialize. @@ -86,6 +89,7 @@ private: // - destroy using Release nsIOService(); ~nsIOService(); + nsresult SetConnectivityInternal(bool aConnectivity); nsresult OnNetworkLinkEvent(const char *data); @@ -121,7 +125,8 @@ private: private: bool mOffline; bool mOfflineForProfileChange; - bool mManageOfflineStatus; + bool mManageLinkStatus; + bool mConnectivity; // Used to handle SetOffline() reentrancy. See the comment in // SetOffline() for more details. diff --git a/netwerk/base/nsNetUtil.h b/netwerk/base/nsNetUtil.h index 918cd52fcc2..8d4574712c5 100644 --- a/netwerk/base/nsNetUtil.h +++ b/netwerk/base/nsNetUtil.h @@ -1963,10 +1963,13 @@ inline bool NS_IsOffline() { bool offline = true; + bool connectivity = true; nsCOMPtr ios = do_GetIOService(); - if (ios) + if (ios) { ios->GetOffline(&offline); - return offline; + ios->GetConnectivity(&connectivity); + } + return offline || !connectivity; } inline bool