Bug 1323172 - Expose NetworkInformation interface to workers, r=bkelly
☠☠ backed out by 3b5b3408af8f ☠ ☠
authorAndrea Marchesini <amarchesini@mozilla.com>
Thu, 15 Dec 2016 14:36:47 +0100
changeset 371007 b7638db235fd4c28abaca3641a0cfc3a445ffc73
parent 371006 85a362b51c44e661968d572d44b48318a59c53a9
child 371008 4616e2bb5fc438155bc5c98c166a11ad05b5c9f8
push id6996
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 20:48:21 +0000
treeherdermozilla-beta@d89512dab048 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbkelly
bugs1323172
milestone53.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 1323172 - Expose NetworkInformation interface to workers, r=bkelly
dom/base/Navigator.cpp
dom/bindings/Errors.msg
dom/network/Connection.cpp
dom/network/Connection.h
dom/network/ConnectionMainThread.cpp
dom/network/ConnectionMainThread.h
dom/network/ConnectionWorker.cpp
dom/network/ConnectionWorker.h
dom/network/moz.build
dom/network/tests/mochitest.ini
dom/network/tests/test_network_basics_worker.html
dom/network/tests/worker_network_basics.js
dom/webidl/Navigator.webidl
dom/webidl/NetworkInformation.webidl
dom/webidl/WorkerNavigator.webidl
dom/workers/WorkerNavigator.cpp
dom/workers/WorkerNavigator.h
dom/workers/WorkerPrefs.h
dom/workers/test/navigator_worker.js
dom/workers/test/test_worker_interfaces.js
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1722,17 +1722,17 @@ Navigator::GetProperties(nsINetworkPrope
 network::Connection*
 Navigator::GetConnection(ErrorResult& aRv)
 {
   if (!mConnection) {
     if (!mWindow) {
       aRv.Throw(NS_ERROR_UNEXPECTED);
       return nullptr;
     }
-    mConnection = new network::Connection(mWindow);
+    mConnection = network::Connection::CreateForWindow(mWindow);
   }
 
   return mConnection;
 }
 
 #ifdef MOZ_TIME_MANAGER
 time::TimeManager*
 Navigator::GetMozTime(ErrorResult& aRv)
--- a/dom/bindings/Errors.msg
+++ b/dom/bindings/Errors.msg
@@ -99,8 +99,9 @@ MSG_DEF(MSG_INVALID_DURATION_ERROR, 1, J
 MSG_DEF(MSG_INVALID_EASING_ERROR, 1, JSEXN_TYPEERR, "Invalid easing '{0}'.")
 MSG_DEF(MSG_INVALID_SPACING_MODE_ERROR, 1, JSEXN_TYPEERR, "Invalid spacing '{0}'.")
 MSG_DEF(MSG_USELESS_SETTIMEOUT, 1, JSEXN_TYPEERR, "Useless {0} call (missing quotes around argument?)")
 MSG_DEF(MSG_TOKENLIST_NO_SUPPORTED_TOKENS, 2, JSEXN_TYPEERR, "{0} attribute of <{1}> does not define any supported tokens")
 MSG_DEF(MSG_CACHE_STREAM_CLOSED, 0, JSEXN_TYPEERR, "Response body is a cache file stream that has already been closed.")
 MSG_DEF(MSG_TIME_VALUE_OUT_OF_RANGE, 1, JSEXN_TYPEERR, "{0} is outside the supported range for time values.")
 MSG_DEF(MSG_ONLY_IF_CACHED_WITHOUT_SAME_ORIGIN, 1, JSEXN_TYPEERR, "Request mode '{0}' was used, but request cache mode 'only-if-cached' can only be used with request mode 'same-origin'.")
 MSG_DEF(MSG_THRESHOLD_RANGE_ERROR, 0, JSEXN_RANGEERR, "Threshold values must all be in the range [0, 1].")
+MSG_DEF(MSG_WORKER_THREAD_SHUTTING_DOWN, 0, JSEXN_TYPEERR, "The Worker thread is shutting down.")
--- a/dom/network/Connection.cpp
+++ b/dom/network/Connection.cpp
@@ -1,96 +1,136 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 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 <limits>
-#include "mozilla/Hal.h"
-#include "mozilla/dom/network/Connection.h"
+#include "Connection.h"
+#include "ConnectionMainThread.h"
+#include "ConnectionWorker.h"
 #include "nsIDOMClassInfo.h"
-#include "mozilla/Preferences.h"
 #include "Constants.h"
+#include "WorkerPrivate.h"
 
 /**
  * We have to use macros here because our leak analysis tool things we are
  * leaking strings when we have |static const nsString|. Sad :(
  */
 #define CHANGE_EVENT_NAME NS_LITERAL_STRING("typechange")
 
 namespace mozilla {
 namespace dom {
+
+using namespace workers;
+
 namespace network {
 
 NS_IMPL_QUERY_INTERFACE_INHERITED(Connection, DOMEventTargetHelper,
                                   nsINetworkProperties)
 
 // Don't use |Connection| alone, since that confuses nsTraceRefcnt since
 // we're not the only class with that name.
 NS_IMPL_ADDREF_INHERITED(dom::network::Connection, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(dom::network::Connection, DOMEventTargetHelper)
 
 Connection::Connection(nsPIDOMWindowInner* aWindow)
   : DOMEventTargetHelper(aWindow)
   , mType(static_cast<ConnectionType>(kDefaultType))
   , mIsWifi(kDefaultIsWifi)
   , mDHCPGateway(kDefaultDHCPGateway)
+  , mBeenShutDown(false)
 {
-  hal::RegisterNetworkObserver(this);
+}
 
-  hal::NetworkInformation networkInfo;
-  hal::GetCurrentNetworkInformation(&networkInfo);
-
-  UpdateFromNetworkInfo(networkInfo);
+Connection::~Connection()
+{
+  NS_ASSERT_OWNINGTHREAD(Connection);
+  MOZ_ASSERT(mBeenShutDown);
 }
 
 void
 Connection::Shutdown()
 {
-  hal::UnregisterNetworkObserver(this);
+  NS_ASSERT_OWNINGTHREAD(Connection);
+
+  if (mBeenShutDown) {
+    return;
+  }
+
+  mBeenShutDown = true;
+  ShutdownInternal();
 }
 
 NS_IMETHODIMP
-Connection::GetIsWifi(bool *aIsWifi)
+Connection::GetIsWifi(bool* aIsWifi)
 {
+  NS_ENSURE_ARG_POINTER(aIsWifi);
+  NS_ASSERT_OWNINGTHREAD(Connection);
+
   *aIsWifi = mIsWifi;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-Connection::GetDhcpGateway(uint32_t *aGW)
+Connection::GetDhcpGateway(uint32_t* aGW)
 {
+  NS_ENSURE_ARG_POINTER(aGW);
+  NS_ASSERT_OWNINGTHREAD(Connection);
+
   *aGW = mDHCPGateway;
   return NS_OK;
 }
 
-void
-Connection::UpdateFromNetworkInfo(const hal::NetworkInformation& aNetworkInfo)
-{
-  mType = static_cast<ConnectionType>(aNetworkInfo.type());
-  mIsWifi = aNetworkInfo.isWifi();
-  mDHCPGateway = aNetworkInfo.dhcpGateway();
-}
-
-void
-Connection::Notify(const hal::NetworkInformation& aNetworkInfo)
-{
-  ConnectionType previousType = mType;
-
-  UpdateFromNetworkInfo(aNetworkInfo);
-
-  if (previousType == mType) {
-    return;
-  }
-
-  DispatchTrustedEvent(CHANGE_EVENT_NAME);
-}
-
 JSObject*
 Connection::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return NetworkInformationBinding::Wrap(aCx, this, aGivenProto);
 }
 
+void
+Connection::Update(ConnectionType aType, bool aIsWifi, bool aDHCPGateway,
+                   bool aNotify)
+{
+  NS_ASSERT_OWNINGTHREAD(Connection);
+
+  ConnectionType previousType = mType;
+
+  mType = aType;
+  mIsWifi = aIsWifi;
+  mDHCPGateway = aDHCPGateway;
+
+  if (aNotify && previousType != aType) {
+    DispatchTrustedEvent(CHANGE_EVENT_NAME);
+  }
+}
+
+/* static */ bool
+Connection::IsEnabled(JSContext* aCx, JSObject* aObj)
+{
+  if (NS_IsMainThread()) {
+    return Preferences::GetBool("dom.netinfo.enabled");
+  }
+
+  WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
+  MOZ_ASSERT(workerPrivate);
+  return workerPrivate->NetworkInformationEnabled();
+}
+
+/* static */ Connection*
+Connection::CreateForWindow(nsPIDOMWindowInner* aWindow)
+{
+  MOZ_ASSERT(aWindow);
+  return new ConnectionMainThread(aWindow);
+}
+
+/* static */ already_AddRefed<Connection>
+Connection::CreateForWorker(workers::WorkerPrivate* aWorkerPrivate,
+                            ErrorResult& aRv)
+{
+  MOZ_ASSERT(aWorkerPrivate);
+  aWorkerPrivate->AssertIsOnWorkerThread();
+  return ConnectionWorker::Create(aWorkerPrivate, aRv);
+}
+
 } // namespace network
 } // namespace dom
 } // namespace mozilla
--- a/dom/network/Connection.h
+++ b/dom/network/Connection.h
@@ -2,79 +2,89 @@
 /* vim: set ts=8 sts=2 et sw=2 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 mozilla_dom_network_Connection_h
 #define mozilla_dom_network_Connection_h
 
-#include "Types.h"
 #include "mozilla/DOMEventTargetHelper.h"
-#include "mozilla/Observer.h"
 #include "mozilla/dom/NetworkInformationBinding.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsINetworkProperties.h"
 
 namespace mozilla {
 
+namespace workers {
+class WorkerPrivate;
+} // namespace workers
+
 namespace hal {
 class NetworkInformation;
 } // namespace hal
 
 namespace dom {
 namespace network {
 
-class Connection final : public DOMEventTargetHelper
-                       , public NetworkObserver
-                       , public nsINetworkProperties
+class Connection : public DOMEventTargetHelper
+                 , public nsINetworkProperties
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSINETWORKPROPERTIES
+  NS_DECL_OWNINGTHREAD
 
   NS_REALLY_FORWARD_NSIDOMEVENTTARGET(DOMEventTargetHelper)
 
-  explicit Connection(nsPIDOMWindowInner* aWindow);
+  static bool IsEnabled(JSContext* aCx, JSObject* aObj);
+
+  static Connection*
+  CreateForWindow(nsPIDOMWindowInner* aWindow);
+
+  static already_AddRefed<Connection>
+  CreateForWorker(workers::WorkerPrivate* aWorkerPrivate,
+                  ErrorResult& aRv);
 
   void Shutdown();
 
-  // For IObserver
-  void Notify(const hal::NetworkInformation& aNetworkInfo) override;
-
   // WebIDL
 
-  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
 
   ConnectionType Type() const { return mType; }
 
   IMPL_EVENT_HANDLER(typechange)
 
-private:
-  ~Connection() {}
+protected:
+  Connection(nsPIDOMWindowInner* aWindow);
+  virtual ~Connection();
 
-  /**
-   * Update the connection information stored in the object using a
-   * NetworkInformation object.
-   */
-  void UpdateFromNetworkInfo(const hal::NetworkInformation& aNetworkInfo);
+  void Update(ConnectionType aType, bool aIsWifi, bool aDHCPGateway,
+              bool aNotify);
 
+  virtual void ShutdownInternal() = 0;
+
+private:
   /**
    * The type of current connection.
    */
   ConnectionType mType;
 
   /**
    * If the connection is WIFI
    */
   bool mIsWifi;
 
   /**
    * DHCP Gateway information for IPV4, in network byte order. 0 if unassigned.
    */
   uint32_t mDHCPGateway;
+
+  bool mBeenShutDown;
 };
 
 } // namespace network
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_network_Connection_h
new file mode 100644
--- /dev/null
+++ b/dom/network/ConnectionMainThread.cpp
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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 <limits>
+#include "mozilla/Hal.h"
+#include "ConnectionMainThread.h"
+
+namespace mozilla {
+namespace dom {
+namespace network {
+
+ConnectionMainThread::ConnectionMainThread(nsPIDOMWindowInner* aWindow)
+  : Connection(aWindow)
+{
+  hal::RegisterNetworkObserver(this);
+
+  hal::NetworkInformation networkInfo;
+  hal::GetCurrentNetworkInformation(&networkInfo);
+
+  UpdateFromNetworkInfo(networkInfo, false);
+}
+
+ConnectionMainThread::~ConnectionMainThread()
+{
+  Shutdown();
+}
+
+void
+ConnectionMainThread::ShutdownInternal()
+{
+  hal::UnregisterNetworkObserver(this);
+}
+
+void
+ConnectionMainThread::UpdateFromNetworkInfo(const hal::NetworkInformation& aNetworkInfo,
+                                            bool aNotify)
+{
+  Update(static_cast<ConnectionType>(aNetworkInfo.type()),
+         aNetworkInfo.isWifi(), aNetworkInfo.dhcpGateway(), aNotify);
+}
+
+void
+ConnectionMainThread::Notify(const hal::NetworkInformation& aNetworkInfo)
+{
+  UpdateFromNetworkInfo(aNetworkInfo, true);
+}
+
+} // namespace network
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/network/ConnectionMainThread.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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 mozilla_dom_network_ConnectionMainThread_h
+#define mozilla_dom_network_ConnectionMainThread_h
+
+#include "Connection.h"
+#include "mozilla/Observer.h"
+#include "Types.h"
+
+namespace mozilla {
+namespace dom {
+namespace network {
+
+class ConnectionMainThread final : public Connection
+                                 , public NetworkObserver
+{
+public:
+  explicit ConnectionMainThread(nsPIDOMWindowInner* aWindow);
+
+  // For IObserver
+  void Notify(const hal::NetworkInformation& aNetworkInfo) override;
+
+private:
+  ~ConnectionMainThread();
+
+  virtual void ShutdownInternal() override;
+
+  /**
+   * Update the connection information stored in the object using a
+   * NetworkInformation object.
+   */
+  void UpdateFromNetworkInfo(const hal::NetworkInformation& aNetworkInfo,
+                             bool aNotify);
+};
+
+} // namespace network
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_network_ConnectionMainThread_h
new file mode 100644
--- /dev/null
+++ b/dom/network/ConnectionWorker.cpp
@@ -0,0 +1,249 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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 <limits>
+#include "mozilla/Hal.h"
+#include "ConnectionWorker.h"
+#include "WorkerRunnable.h"
+
+namespace mozilla {
+namespace dom {
+namespace network {
+
+class ConnectionProxy final : public NetworkObserver
+                            , public WorkerHolder
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ConnectionProxy)
+
+  static already_AddRefed<ConnectionProxy>
+  Create(WorkerPrivate* aWorkerPrivate, ConnectionWorker* aConnection)
+  {
+    RefPtr<ConnectionProxy> proxy =
+      new ConnectionProxy(aWorkerPrivate, aConnection);
+    if (!proxy->HoldWorker(aWorkerPrivate, Closing)) {
+      proxy->mConnection = nullptr;
+      return nullptr;
+    }
+
+    return proxy.forget();
+  }
+
+  // For IObserver - main-thread only.
+  void Notify(const hal::NetworkInformation& aNetworkInfo) override;
+
+  // Worker notification
+  virtual bool Notify(Status aStatus) override
+  {
+    Shutdown();
+    return true;
+  }
+
+  void Shutdown();
+
+  void Update(ConnectionType aType, bool aIsWifi, bool aDHCPGateway)
+  {
+    MOZ_ASSERT(mConnection);
+    mWorkerPrivate->AssertIsOnWorkerThread();
+    mConnection->Update(aType, aIsWifi, aDHCPGateway, true);
+  }
+
+private:
+  ConnectionProxy(WorkerPrivate* aWorkerPrivate, ConnectionWorker* aConnection)
+    : mConnection(aConnection)
+    , mWorkerPrivate(aWorkerPrivate)
+  {
+    MOZ_ASSERT(mWorkerPrivate);
+    MOZ_ASSERT(mConnection);
+    mWorkerPrivate->AssertIsOnWorkerThread();
+  }
+
+  ~ConnectionProxy() = default;
+
+  // Raw pointer because the ConnectionWorker keeps alive the proxy.
+  // This is touched only on the worker-thread and it's nullified when the
+  // shutdown procedure starts.
+  ConnectionWorker* mConnection;
+
+  WorkerPrivate* mWorkerPrivate;
+};
+
+namespace {
+
+// This class initializes the hal observer on the main-thread.
+class InitializeRunnable : public WorkerMainThreadRunnable
+{
+private:
+  // raw pointer because this is a sync runnable.
+  ConnectionProxy* mProxy;
+  hal::NetworkInformation& mNetworkInfo;
+
+public:
+  InitializeRunnable(WorkerPrivate* aWorkerPrivate,
+                     ConnectionProxy* aProxy,
+                     hal::NetworkInformation& aNetworkInfo)
+    : WorkerMainThreadRunnable(aWorkerPrivate,
+                               NS_LITERAL_CSTRING("ConnectionWorker :: Initialize"))
+    , mProxy(aProxy)
+    , mNetworkInfo(aNetworkInfo)
+  {
+    MOZ_ASSERT(aProxy);
+    aWorkerPrivate->AssertIsOnWorkerThread();
+  }
+
+  bool
+  MainThreadRun()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    hal::RegisterNetworkObserver(mProxy);
+    hal::GetCurrentNetworkInformation(&mNetworkInfo);
+    return true;
+  }
+};
+
+// This class turns down the hal observer on the main-thread.
+class ShutdownRunnable : public WorkerMainThreadRunnable
+{
+private:
+  // raw pointer because this is a sync runnable.
+  ConnectionProxy* mProxy;
+
+public:
+  ShutdownRunnable(WorkerPrivate* aWorkerPrivate, ConnectionProxy* aProxy)
+    : WorkerMainThreadRunnable(aWorkerPrivate,
+                               NS_LITERAL_CSTRING("ConnectionWorker :: Shutdown"))
+    , mProxy(aProxy)
+  {
+    MOZ_ASSERT(aProxy);
+    aWorkerPrivate->AssertIsOnWorkerThread();
+  }
+
+  bool
+  MainThreadRun()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    hal::UnregisterNetworkObserver(mProxy);
+    return true;
+  }
+};
+
+class NotifyRunnable : public WorkerRunnable
+{
+private:
+  RefPtr<ConnectionProxy> mProxy;
+
+  const ConnectionType mConnectionType;
+  const bool mIsWifi;
+  const bool mDHCPGateway;
+
+public:
+  NotifyRunnable(WorkerPrivate* aWorkerPrivate,
+                 ConnectionProxy* aProxy, ConnectionType aType,
+                 bool aIsWifi, bool aDHCPGateway)
+    : WorkerRunnable(aWorkerPrivate)
+    , mProxy(aProxy)
+    , mConnectionType(aType)
+    , mIsWifi(aIsWifi)
+    , mDHCPGateway(aDHCPGateway)
+  {
+    MOZ_ASSERT(aProxy);
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+  {
+    aWorkerPrivate->AssertIsOnWorkerThread();
+    mProxy->Update(mConnectionType, mIsWifi, mDHCPGateway);
+    return true;
+  }
+};
+
+} // anonymous namespace
+
+/* static */ already_AddRefed<ConnectionWorker>
+ConnectionWorker::Create(WorkerPrivate* aWorkerPrivate, ErrorResult& aRv)
+{
+  RefPtr<ConnectionWorker> c = new ConnectionWorker(aWorkerPrivate);
+  c->mProxy = ConnectionProxy::Create(aWorkerPrivate, c);
+  if (!c->mProxy) {
+    aRv.ThrowTypeError<MSG_WORKER_THREAD_SHUTTING_DOWN>();
+    return nullptr;
+  }
+
+  hal::NetworkInformation networkInfo;
+  RefPtr<InitializeRunnable> runnable =
+    new InitializeRunnable(aWorkerPrivate, c->mProxy, networkInfo);
+
+  runnable->Dispatch(aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  c->Update(static_cast<ConnectionType>(networkInfo.type()),
+            networkInfo.isWifi(), networkInfo.dhcpGateway(), false);
+  return c.forget();
+}
+
+ConnectionWorker::ConnectionWorker(WorkerPrivate* aWorkerPrivate)
+  : Connection(nullptr)
+  , mWorkerPrivate(aWorkerPrivate)
+{
+  MOZ_ASSERT(aWorkerPrivate);
+  aWorkerPrivate->AssertIsOnWorkerThread();
+}
+
+ConnectionWorker::~ConnectionWorker()
+{
+  Shutdown();
+}
+
+void
+ConnectionWorker::ShutdownInternal()
+{
+  mWorkerPrivate->AssertIsOnWorkerThread();
+  mProxy->Shutdown();
+}
+
+void
+ConnectionProxy::Notify(const hal::NetworkInformation& aNetworkInfo)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  RefPtr<NotifyRunnable> runnable =
+    new NotifyRunnable(mWorkerPrivate, this,
+                       static_cast<ConnectionType>(aNetworkInfo.type()),
+                       aNetworkInfo.isWifi(), aNetworkInfo.dhcpGateway());
+  runnable->Dispatch();
+}
+
+void
+ConnectionProxy::Shutdown()
+{
+  mWorkerPrivate->AssertIsOnWorkerThread();
+
+  // Already shut down.
+  if (!mConnection) {
+    return;
+  }
+
+  mConnection = nullptr;
+
+  RefPtr<ShutdownRunnable> runnable =
+    new ShutdownRunnable(mWorkerPrivate, this);
+
+  ErrorResult rv;
+  runnable->Dispatch(rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    rv.SuppressException();
+  }
+
+  ReleaseWorker();
+}
+
+} // namespace network
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/network/ConnectionWorker.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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 mozilla_dom_network_ConnectionWorker_h
+#define mozilla_dom_network_ConnectionWorker_h
+
+#include "Connection.h"
+#include "mozilla/Observer.h"
+#include "Types.h"
+
+namespace mozilla {
+namespace dom {
+namespace network {
+
+class ConnectionProxy;
+
+class ConnectionWorker final : public Connection
+{
+  friend class ConnectionProxy;
+
+public:
+  static already_AddRefed<ConnectionWorker>
+  Create(workers::WorkerPrivate* aWorkerPrivate, ErrorResult& aRv);
+
+private:
+  explicit ConnectionWorker(workers::WorkerPrivate* aWorkerPrivate);
+  ~ConnectionWorker();
+
+  virtual void ShutdownInternal() override;
+
+  workers::WorkerPrivate* mWorkerPrivate;
+  RefPtr<ConnectionProxy> mProxy;
+};
+
+} // namespace network
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_network_ConnectionWorker_h
--- a/dom/network/moz.build
+++ b/dom/network/moz.build
@@ -24,16 +24,18 @@ EXPORTS.mozilla.dom.network += [
     'TCPSocketParent.h',
     'Types.h',
     'UDPSocketChild.h',
     'UDPSocketParent.h',
 ]
 
 UNIFIED_SOURCES += [
     'Connection.cpp',
+    'ConnectionMainThread.cpp',
+    'ConnectionWorker.cpp',
     'TCPServerSocket.cpp',
     'TCPServerSocketChild.cpp',
     'TCPServerSocketParent.cpp',
     'TCPSocket.cpp',
     'TCPSocketChild.cpp',
     'TCPSocketParent.cpp',
     'UDPSocket.cpp',
     'UDPSocketChild.cpp',
@@ -53,14 +55,18 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk
     ]
 
 IPDL_SOURCES += [
     'PTCPServerSocket.ipdl',
     'PTCPSocket.ipdl',
     'PUDPSocket.ipdl',
 ]
 
+LOCAL_INCLUDES += [
+    '../workers',
+]
+
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wno-error=shadow']
--- a/dom/network/tests/mochitest.ini
+++ b/dom/network/tests/mochitest.ini
@@ -1,8 +1,11 @@
 [DEFAULT]
 support-files =
   add_task.js
+  worker_network_basics.js
 
 [test_network_basics.html]
 skip-if = toolkit == 'android'
+[test_network_basics_worker.html]
+skip-if = toolkit == 'android'
 [test_tcpsocket_default_permissions.html]
 [test_tcpsocket_enabled_no_perm.html]
new file mode 100644
--- /dev/null
+++ b/dom/network/tests/test_network_basics_worker.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Network in workers API</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Network Information API **/
+function test() {
+  let w = new Worker('worker_network_basics.js');
+  w.onmessage = function(e) {
+    if (e.data.type == 'status') {
+      ok(e.data.status, e.data.msg);
+    } else if (e.data.type == 'finish') {
+      SimpleTest.finish();
+    } else {
+      ok(false, "Unknown message type");
+    }
+  }
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({'set': [["dom.netinfo.enabled", true]]}, test);
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/network/tests/worker_network_basics.js
@@ -0,0 +1,24 @@
+function ok(a, msg) {
+  postMessage({type: 'status', status: !!a, msg: msg});
+}
+
+function is(a, b, msg) {
+  ok(a === b, msg);
+}
+
+function finish() {
+  postMessage({type: 'finish'});
+}
+
+ok('connection' in navigator, "navigator.connection should exist");
+
+ok(navigator.connection, "navigator.connection returns an object");
+
+ok(navigator.connection instanceof EventTarget,
+   "navigator.connection is a EventTarget object");
+
+ok('type' in navigator.connection,
+   "type should be a Connection attribute");
+is(navigator.connection.type, "none",
+   "By default connection.type equals to none");
+finish();
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -8,16 +8,17 @@
  * http://www.w3.org/TR/tracking-dnt/
  * http://www.w3.org/TR/geolocation-API/#geolocation_interface
  * http://www.w3.org/TR/battery-status/#navigatorbattery-interface
  * http://www.w3.org/TR/vibration/#vibration-interface
  * http://www.w3.org/2012/sysapps/runtime/#extension-to-the-navigator-interface-1
  * https://dvcs.w3.org/hg/gamepad/raw-file/default/gamepad.html#navigator-interface-extension
  * http://www.w3.org/TR/beacon/#sec-beacon-method
  * https://html.spec.whatwg.org/#navigatorconcurrenthardware
+ * http://wicg.github.io/netinfo/#extensions-to-the-navigator-interface
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-navigator-object
 [HeaderFile="Navigator.h"]
--- a/dom/webidl/NetworkInformation.webidl
+++ b/dom/webidl/NetworkInformation.webidl
@@ -13,13 +13,14 @@ enum ConnectionType {
     "bluetooth",
     "ethernet",
     "wifi",
     "other",
     "none",
     "unknown"
 };
 
-[Pref="dom.netinfo.enabled"]
+[Func="mozilla::dom::network::Connection::IsEnabled",
+ Exposed=(Window,Worker)]
 interface NetworkInformation : EventTarget {
     readonly    attribute ConnectionType type;
                 attribute EventHandler   ontypechange;
 };
--- a/dom/webidl/WorkerNavigator.webidl
+++ b/dom/webidl/WorkerNavigator.webidl
@@ -7,8 +7,15 @@
 interface WorkerNavigator {
 };
 
 WorkerNavigator implements NavigatorID;
 WorkerNavigator implements NavigatorLanguage;
 WorkerNavigator implements NavigatorOnLine;
 WorkerNavigator implements NavigatorConcurrentHardware;
 WorkerNavigator implements NavigatorStorage;
+
+// http://wicg.github.io/netinfo/#extensions-to-the-navigator-interface
+[Exposed=(Worker)]
+partial interface WorkerNavigator {
+    [Func="mozilla::dom::network::Connection::IsEnabled", Throws]
+    readonly attribute NetworkInformation connection;
+};
--- a/dom/workers/WorkerNavigator.cpp
+++ b/dom/workers/WorkerNavigator.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseWorkerProxy.h"
 #include "mozilla/dom/StorageManager.h"
 #include "mozilla/dom/WorkerNavigator.h"
 #include "mozilla/dom/WorkerNavigatorBinding.h"
+#include "mozilla/dom/network/Connection.h"
 
 #include "nsProxyRelease.h"
 #include "RuntimeService.h"
 
 #include "nsIDocument.h"
 
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
@@ -22,20 +23,33 @@
 
 #include "mozilla/dom/Navigator.h"
 
 namespace mozilla {
 namespace dom {
 
 using namespace mozilla::dom::workers;
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WorkerNavigator, mStorageManager);
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WorkerNavigator, mStorageManager,
+                                      mConnection);
+
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WorkerNavigator, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WorkerNavigator, Release)
 
+WorkerNavigator::WorkerNavigator(const NavigatorProperties& aProperties,
+                                 bool aOnline)
+  : mProperties(aProperties)
+  , mOnline(aOnline)
+{
+}
+
+WorkerNavigator::~WorkerNavigator()
+{
+}
+
 /* static */ already_AddRefed<WorkerNavigator>
 WorkerNavigator::Create(bool aOnLine)
 {
   RuntimeService* rts = RuntimeService::GetService();
   MOZ_ASSERT(rts);
 
   const RuntimeService::NavigatorProperties& properties =
     rts->GetNavigatorProperties();
@@ -178,10 +192,24 @@ WorkerNavigator::Storage()
     MOZ_ASSERT(global);
 
     mStorageManager = new StorageManager(global);
   }
 
   return mStorageManager;
 }
 
+network::Connection*
+WorkerNavigator::GetConnection(ErrorResult& aRv)
+{
+  if (!mConnection) {
+    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(workerPrivate);
+
+    mConnection = network::Connection::CreateForWorker(workerPrivate, aRv);
+  }
+
+  return mConnection;
+}
+
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/workers/WorkerNavigator.h
+++ b/dom/workers/WorkerNavigator.h
@@ -14,36 +14,31 @@
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/StorageManager.h"
 
 namespace mozilla {
 namespace dom {
 class Promise;
 class StorageManager;
 
+namespace network {
+class Connection;
+} // namespace network
+
 class WorkerNavigator final : public nsWrapperCache
 {
   typedef struct workers::RuntimeService::NavigatorProperties NavigatorProperties;
 
   NavigatorProperties mProperties;
   RefPtr<StorageManager> mStorageManager;
+  RefPtr<network::Connection> mConnection;
   bool mOnline;
 
-  WorkerNavigator(const NavigatorProperties& aProperties,
-                  bool aOnline)
-    : mProperties(aProperties)
-    , mOnline(aOnline)
-  {
-    MOZ_COUNT_CTOR(WorkerNavigator);
-  }
-
-  ~WorkerNavigator()
-  {
-    MOZ_COUNT_DTOR(WorkerNavigator);
-  }
+  WorkerNavigator(const NavigatorProperties& aProperties, bool aOnline);
+  ~WorkerNavigator();
 
 public:
 
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WorkerNavigator)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WorkerNavigator)
 
   static already_AddRefed<WorkerNavigator>
   Create(bool aOnLine);
@@ -105,14 +100,16 @@ public:
     mOnline = aOnline;
   }
 
   void SetLanguages(const nsTArray<nsString>& aLanguages);
 
   uint64_t HardwareConcurrency() const;
 
   StorageManager* Storage();
+
+  network::Connection* GetConnection(ErrorResult& aRv);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_workernavigator_h__
--- a/dom/workers/WorkerPrefs.h
+++ b/dom/workers/WorkerPrefs.h
@@ -34,16 +34,17 @@ WORKER_SIMPLE_PREF("dom.webnotifications
 WORKER_SIMPLE_PREF("dom.serviceWorkers.enabled", ServiceWorkersEnabled, SERVICEWORKERS_ENABLED)
 WORKER_SIMPLE_PREF("dom.serviceWorkers.testing.enabled", ServiceWorkersTestingEnabled, SERVICEWORKERS_TESTING_ENABLED)
 WORKER_SIMPLE_PREF("dom.serviceWorkers.openWindow.enabled", OpenWindowEnabled, OPEN_WINDOW_ENABLED)
 WORKER_SIMPLE_PREF("dom.storageManager.enabled", StorageManagerEnabled, STORAGEMANAGER_ENABLED)
 WORKER_SIMPLE_PREF("dom.push.enabled", PushEnabled, PUSH_ENABLED)
 WORKER_SIMPLE_PREF("dom.requestcontext.enabled", RequestContextEnabled, REQUESTCONTEXT_ENABLED)
 WORKER_SIMPLE_PREF("gfx.offscreencanvas.enabled", OffscreenCanvasEnabled, OFFSCREENCANVAS_ENABLED)
 WORKER_SIMPLE_PREF("dom.webkitBlink.dirPicker.enabled", WebkitBlinkDirectoryPickerEnabled, DOM_WEBKITBLINK_DIRPICKER_WEBKITBLINK)
+WORKER_SIMPLE_PREF("dom.netinfo.enabled", NetworkInformationEnabled, NETWORKINFORMATION_ENABLED)
 WORKER_PREF("dom.workers.latestJSVersion", JSVersionChanged)
 WORKER_PREF("intl.accept_languages", PrefLanguagesChanged)
 WORKER_PREF("general.appname.override", AppNameOverrideChanged)
 WORKER_PREF("general.appversion.override", AppVersionOverrideChanged)
 WORKER_PREF("general.platform.override", PlatformOverrideChanged)
 #ifdef JS_GC_ZEAL
 WORKER_PREF("dom.workers.options.gcZeal", LoadGCZealOptions)
 #endif
--- a/dom/workers/test/navigator_worker.js
+++ b/dom/workers/test/navigator_worker.js
@@ -11,16 +11,17 @@ var supportedProps = [
   "platform",
   "product",
   "userAgent",
   "onLine",
   "language",
   "languages",
   "hardwareConcurrency",
   { name: "storage", nightly: true },
+  { name: "connection", nightly: true },
 ];
 
 self.onmessage = function(event) {
   if (!event || !event.data) {
     return;
   }
 
   startTest(event.data);
--- a/dom/workers/test/test_worker_interfaces.js
+++ b/dom/workers/test/test_worker_interfaces.js
@@ -146,16 +146,18 @@ var interfaceNamesInGlobalScope =
     "ImageData",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MessageChannel",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MessageEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MessagePort",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "NetworkInformation", nightly: true},
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "Notification",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     { name: "OffscreenCanvas", disabled: true },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Performance",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PerformanceEntry",
 // IMPORTANT: Do not change this list without review from a DOM peer!