Bug 939318 - Introduce the "network.notify.changed" prefs. r=mcmanus
☠☠ backed out by f9bfe115fee5 ☠ ☠
authorDaniel Stenberg <daniel@haxx.se>
Sun, 24 Aug 2014 23:22:00 -0400
changeset 223282 cc131cf9ef81b6980ab5fe8c091f8758d2b22ae2
parent 223281 02925699b9bbcc29718cdd03c54354e9a83e1659
child 223283 21dcd9e1b6078839497f1f8b0bcc20ae5e3684b1
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcmanus
bugs939318
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 939318 - Introduce the "network.notify.changed" prefs. r=mcmanus Makes it possible to disable 'changed' events. Disable the events in test_ping_aboutnetworking.js and in test_proxy-failover_passing.js
modules/libpref/init/all.js
netwerk/base/src/nsIOService.cpp
netwerk/base/src/nsIOService.h
netwerk/system/win32/nsNotifyAddrListener.cpp
netwerk/system/win32/nsNotifyAddrListener.h
netwerk/test/unit/test_ping_aboutnetworking.js
netwerk/test/unit/test_proxy-failover_passing.js
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -981,16 +981,20 @@ pref("security.fileuri.strict_origin_pol
 // prevents necko connecting to ports 1-5 unless the protocol
 // overrides.
 
 // Allow necko to do A/B testing. Will generally only happen if
 // telemetry is also enabled as otherwise there is no way to report
 // the results
 pref("network.allow-experiments", true);
 
+// Allow the network changed event to get sent when a network topology or
+// setup change is noticed while running.
+pref("network.notify.changed", true);
+
 // Transmit UDP busy-work to the LAN when anticipating low latency
 // network reads and on wifi to mitigate 802.11 Power Save Polling delays
 pref("network.tickle-wifi.enabled", false);
 pref("network.tickle-wifi.duration", 400);
 pref("network.tickle-wifi.delay", 16);
 
 // Turn off interprocess security checks. Needed to run xpcshell tests.
 pref("network.disable.ipc.security", false);
--- a/netwerk/base/src/nsIOService.cpp
+++ b/netwerk/base/src/nsIOService.cpp
@@ -58,16 +58,17 @@ using mozilla::net::IsNeckoChild;
 #define AUTODIAL_PREF              "network.autodial-helper.enabled"
 #define MANAGE_OFFLINE_STATUS_PREF "network.manage-offline-status"
 
 // Nb: these have been misnomers since bug 715770 removed the buffer cache.
 // "network.segment.count" and "network.segment.size" would be better names,
 // but the old names are still used to preserve backward compatibility.
 #define NECKO_BUFFER_CACHE_COUNT_PREF "network.buffer.cache.count"
 #define NECKO_BUFFER_CACHE_SIZE_PREF  "network.buffer.cache.size"
+#define NETWORK_NOTIFY_CHANGED_PREF   "network.notify.changed"
 
 #define MAX_RECURSION_COUNT 50
 
 nsIOService* gIOService = nullptr;
 static bool gHasWarnedUploadChannel2;
 
 // A general port blacklist.  Connections to these ports will not be allowed
 // unless the protocol overrides.
@@ -156,16 +157,17 @@ nsIOService::nsIOService()
     , 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;
@@ -196,16 +198,17 @@ nsIOService::Init()
     nsCOMPtr<nsIPrefBranch> prefBranch;
     GetPrefBranch(getter_AddRefs(prefBranch));
     if (prefBranch) {
         prefBranch->AddObserver(PORT_PREF_PREFIX, this, true);
         prefBranch->AddObserver(AUTODIAL_PREF, this, true);
         prefBranch->AddObserver(MANAGE_OFFLINE_STATUS_PREF, this, true);
         prefBranch->AddObserver(NECKO_BUFFER_CACHE_COUNT_PREF, this, true);
         prefBranch->AddObserver(NECKO_BUFFER_CACHE_SIZE_PREF, this, true);
+        prefBranch->AddObserver(NETWORK_NOTIFY_CHANGED_PREF, this, true);
         PrefsChanged(prefBranch);
     }
     
     // Register for profile change notifications
     nsCOMPtr<nsIObserverService> observerService =
         mozilla::services::GetObserverService();
     if (observerService) {
         observerService->AddObserver(this, kProfileChangeNetTeardownTopic, true);
@@ -861,16 +864,24 @@ nsIOService::PrefsChanged(nsIPrefBranch 
              * the upper limit here is arbitrary. having a 1mb segment size
              * is pretty crazy.  if you remove this, consider adding some
              * integer rollover test.
              */
             if (size > 0 && size < 1024*1024)
                 gDefaultSegmentSize = size;
         NS_WARN_IF_FALSE( (!(size & (size - 1))) , "network segment size is not a power of 2!");
     }
+
+    if (!pref || strcmp(pref, NETWORK_NOTIFY_CHANGED_PREF) == 0) {
+        bool allow;
+        nsresult rv = prefs->GetBoolPref(NETWORK_NOTIFY_CHANGED_PREF, &allow);
+        if (NS_SUCCEEDED(rv)) {
+            mNetworkNotifyChanged = allow;
+        }
+    }
 }
 
 void
 nsIOService::ParsePortList(nsIPrefBranch *prefBranch, const char *pref, bool remove)
 {
     nsXPIDLCString portList;
 
     // Get a pref string and chop it up into a list of ports.
@@ -1052,17 +1063,17 @@ nsIOService::Observe(nsISupports *subjec
 #endif
     } else if (!strcmp(topic, NS_WIDGET_WAKE_OBSERVER_TOPIC)) {
         // coming back alive from sleep
         nsCOMPtr<nsIObserverService> observerService =
             mozilla::services::GetObserverService();
 
         NS_ASSERTION(observerService, "The observer service should not be null");
 
-        if (observerService) {
+        if (observerService && mNetworkNotifyChanged) {
             (void)observerService->
                 NotifyObservers(nullptr,
                                 NS_NETWORK_LINK_TOPIC,
                                 MOZ_UTF16(NS_NETWORK_LINK_DATA_CHANGED));
         }
     }
 
     return NS_OK;
--- a/netwerk/base/src/nsIOService.h
+++ b/netwerk/base/src/nsIOService.h
@@ -139,16 +139,17 @@ private:
     nsWeakPtr                            mWeakHandler[NS_N(gScheme)];
 
     // 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;
--- a/netwerk/system/win32/nsNotifyAddrListener.cpp
+++ b/netwerk/system/win32/nsNotifyAddrListener.cpp
@@ -22,27 +22,30 @@
 #include "nsThreadUtils.h"
 #include "nsIObserverService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsNotifyAddrListener.h"
 #include "nsString.h"
 #include "nsAutoPtr.h"
 #include "mozilla/Services.h"
 #include "nsCRT.h"
+#include "nsIPrefService.h"
 
 #include <iptypes.h>
 #include <iphlpapi.h>
 
 static HMODULE sNetshell;
 static decltype(NcFreeNetconProperties)* sNcFreeNetconProperties;
 
 static HMODULE sIphlpapi;
 static decltype(NotifyIpInterfaceChange)* sNotifyIpInterfaceChange;
 static decltype(CancelMibChangeNotify2)* sCancelMibChangeNotify2;
 
+static const char kAllowChangedEvents[] = "network.notify.changed";
+
 static void InitIphlpapi(void)
 {
     if (!sIphlpapi) {
         sIphlpapi = LoadLibraryW(L"Iphlpapi.dll");
         if (sIphlpapi) {
             sNotifyIpInterfaceChange = (decltype(NotifyIpInterfaceChange)*)
                 GetProcAddress(sIphlpapi, "NotifyIpInterfaceChange");
             sCancelMibChangeNotify2 = (decltype(CancelMibChangeNotify2)*)
@@ -85,16 +88,17 @@ NS_IMPL_ISUPPORTS(nsNotifyAddrListener,
                   nsIRunnable,
                   nsIObserver)
 
 nsNotifyAddrListener::nsNotifyAddrListener()
     : mLinkUp(true)  // assume true by default
     , mStatusKnown(false)
     , mCheckAttempted(false)
     , mShutdownEvent(nullptr)
+    , mAllowChangedEvent(true)
 {
     InitIphlpapi();
 }
 
 nsNotifyAddrListener::~nsNotifyAddrListener()
 {
     NS_ASSERTION(!mThread, "nsNotifyAddrListener thread shutdown failed");
     FreeDynamicLibraries();
@@ -183,39 +187,63 @@ nsNotifyAddrListener::Run()
         if (ret == NO_ERROR) {
             ret = WaitForSingleObject(mShutdownEvent, INFINITE);
         }
         sCancelMibChangeNotify2(interfacechange);
     }
     return NS_OK;
 }
 
+void
+nsNotifyAddrListener::updateFromPref(nsIPrefBranch *prefs)
+{
+    MOZ_ASSERT(NS_IsMainThread());
+    bool allow=true;
+    nsresult rv = prefs->GetBoolPref(kAllowChangedEvents, &allow);
+    if (NS_SUCCEEDED(rv)) {
+        mAllowChangedEvent = allow;
+    }
+}
+
 NS_IMETHODIMP
 nsNotifyAddrListener::Observe(nsISupports *subject,
                               const char *topic,
                               const char16_t *data)
 {
-    if (!strcmp("xpcom-shutdown-threads", topic))
+    if (!strcmp("xpcom-shutdown-threads", topic)) {
         Shutdown();
+    } else if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, topic)) {
+        nsCOMPtr<nsIPrefBranch> prefs = do_QueryInterface(subject);
+        if (prefs) {
+            updateFromPref(prefs);
+        }
+    }
 
     return NS_OK;
 }
 
 nsresult
 nsNotifyAddrListener::Init(void)
 {
     nsCOMPtr<nsIObserverService> observerService =
         mozilla::services::GetObserverService();
     if (!observerService)
         return NS_ERROR_FAILURE;
 
     nsresult rv = observerService->AddObserver(this, "xpcom-shutdown-threads",
                                                false);
     NS_ENSURE_SUCCESS(rv, rv);
 
+    // monitor preference change
+    nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+    if (prefs) {
+        prefs->AddObserver(kAllowChangedEvents, this, true);
+        updateFromPref(prefs);
+    }
+
     mShutdownEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
     NS_ENSURE_TRUE(mShutdownEvent, NS_ERROR_OUT_OF_MEMORY);
 
     rv = NS_NewThread(getter_AddRefs(mThread), this);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return NS_OK;
 }
@@ -489,18 +517,20 @@ nsNotifyAddrListener::CheckLinkStatus(vo
     } else {
         ret = CheckAdaptersAddresses();
         if (ret != ERROR_SUCCESS) {
             mLinkUp = true;
         }
 
         if (mLinkUp && (prevCsum != mIPInterfaceChecksum)) {
             // Network is online. Topology has changed. Always send CHANGED
-            // before UP.
-            SendEvent(NS_NETWORK_LINK_DATA_CHANGED);
+            // before UP - if allowed to.
+            if (mAllowChangedEvent) {
+                SendEvent(NS_NETWORK_LINK_DATA_CHANGED);
+            }
         }
         if (prevLinkUp != mLinkUp) {
             // UP/DOWN status changed, send appropriate UP/DOWN event
             SendEvent(mLinkUp ?
                       NS_NETWORK_LINK_DATA_UP : NS_NETWORK_LINK_DATA_DOWN);
         }
     }
 }
--- a/netwerk/system/win32/nsNotifyAddrListener.h
+++ b/netwerk/system/win32/nsNotifyAddrListener.h
@@ -9,16 +9,19 @@
 #include <windows.h>
 #include <winsock2.h>
 #include <iptypes.h>
 #include "nsINetworkLinkService.h"
 #include "nsIRunnable.h"
 #include "nsIObserver.h"
 #include "nsThreadUtils.h"
 #include "nsCOMPtr.h"
+#include "nsIPrefBranch.h"
+
+class nsIPrefBranch;
 
 class nsNotifyAddrListener : public nsINetworkLinkService,
                              public nsIRunnable,
                              public nsIObserver
 {
     virtual ~nsNotifyAddrListener();
 
 public:
@@ -57,14 +60,20 @@ protected:
     bool  CheckICSGateway(PIP_ADAPTER_ADDRESSES aAdapter);
     bool  CheckICSStatus(PWCHAR aAdapterName);
 
     nsCOMPtr<nsIThread> mThread;
 
     HANDLE        mShutdownEvent;
 
 private:
+    // read the pref value and set mAllowChangedEvent
+    void updateFromPref(nsIPrefBranch *prefs);
+
     // This is a checksum of various meta data for all network interfaces
     // considered UP at last check.
     ULONG mIPInterfaceChecksum;
+
+    // Network changed events are enabled
+    bool mAllowChangedEvent;
 };
 
 #endif /* NSNOTIFYADDRLISTENER_H_ */
--- a/netwerk/test/unit/test_ping_aboutnetworking.js
+++ b/netwerk/test/unit/test_ping_aboutnetworking.js
@@ -37,16 +37,22 @@ function test_sockets(serverSocket) {
     do_check_eq(data.sockets[index].port, serverSocket.port);
     do_check_eq(data.sockets[index].tcp, 1);
 
     do_test_finished();
   });
 }
 
 function run_test() {
+  var ps = Cc["@mozilla.org/preferences-service;1"]
+    .getService(Ci.nsIPrefBranch);
+  // disable network changed events to avoid the the risk of having the dns
+  // cache getting flushed behind our back
+  ps.setBoolPref("network.notify.changed", false);
+
   let serverSocket = Components.classes["@mozilla.org/network/server-socket;1"]
     .createInstance(Ci.nsIServerSocket);
   serverSocket.init(-1, true, -1);
 
   do_test_pending();
   gDashboard.requestConnection("localhost", serverSocket.port,
                                "tcp", 15, function(connInfo) {
     if (connInfo.status == "NS_NET_STATUS_CONNECTED_TO") {
--- a/netwerk/test/unit/test_proxy-failover_passing.js
+++ b/netwerk/test/unit/test_proxy-failover_passing.js
@@ -31,14 +31,17 @@ function run_test()
   var prefserv = Cc["@mozilla.org/preferences-service;1"].
                  getService(Ci.nsIPrefService);
   var prefs = prefserv.getBranch("network.proxy.");
   prefs.setIntPref("type", 2);
   prefs.setCharPref("autoconfig_url", "data:text/plain," +
     "function FindProxyForURL(url, host) {return 'PROXY a_non_existent_domain_x7x6c572v:80; PROXY localhost:" +
     httpServer.identity.primaryPort + "';}"
   );
+  // disable network changed events to avoid the the risk of having the 
+  // proxyservice reset in the middle
+  prefserv.setBoolPref("network.notify.changed", false);
 
   var chan = make_channel("http://localhost:" +
                           httpServer.identity.primaryPort + "/content");
   chan.asyncOpen(new ChannelListener(finish_test, null), null);
   do_test_pending();
 }