summaryrefslogtreecommitdiffstats
path: root/dom/flyweb/FlyWebService.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /dom/flyweb/FlyWebService.cpp
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/flyweb/FlyWebService.cpp')
-rw-r--r--dom/flyweb/FlyWebService.cpp1310
1 files changed, 1310 insertions, 0 deletions
diff --git a/dom/flyweb/FlyWebService.cpp b/dom/flyweb/FlyWebService.cpp
new file mode 100644
index 000000000..5f3b0d66f
--- /dev/null
+++ b/dom/flyweb/FlyWebService.cpp
@@ -0,0 +1,1310 @@
+/* -*- 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 "mozilla/dom/FlyWebService.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/FlyWebPublishedServerIPC.h"
+#include "mozilla/AddonPathService.h"
+#include "nsISocketTransportService.h"
+#include "mdns/libmdns/nsDNSServiceInfo.h"
+#include "nsIUUIDGenerator.h"
+#include "nsStandardURL.h"
+#include "mozilla/Services.h"
+#include "nsISupportsPrimitives.h"
+#include "mozilla/dom/FlyWebDiscoveryManagerBinding.h"
+#include "prnetdb.h"
+#include "DNS.h"
+#include "nsContentPermissionHelper.h"
+#include "nsSocketTransportService2.h"
+#include "nsSocketTransport2.h"
+#include "nsHashPropertyBag.h"
+#include "nsNetUtil.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIProperty.h"
+#include "nsICertOverrideService.h"
+
+namespace mozilla {
+namespace dom {
+
+struct FlyWebPublishOptions;
+
+static LazyLogModule gFlyWebServiceLog("FlyWebService");
+#undef LOG_I
+#define LOG_I(...) MOZ_LOG(mozilla::dom::gFlyWebServiceLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
+
+#undef LOG_E
+#define LOG_E(...) MOZ_LOG(mozilla::dom::gFlyWebServiceLog, mozilla::LogLevel::Error, (__VA_ARGS__))
+
+#undef LOG_TEST_I
+#define LOG_TEST_I(...) MOZ_LOG_TEST(mozilla::dom::gFlyWebServiceLog, mozilla::LogLevel::Debug)
+
+class FlyWebPublishServerPermissionCheck final
+ : public nsIContentPermissionRequest
+ , public nsIRunnable
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ FlyWebPublishServerPermissionCheck(const nsCString& aServiceName, uint64_t aWindowID,
+ FlyWebPublishedServer* aServer)
+ : mServiceName(aServiceName)
+ , mWindowID(aWindowID)
+ , mServer(aServer)
+ {}
+
+ uint64_t WindowID() const
+ {
+ return mWindowID;
+ }
+
+ NS_IMETHOD Run() override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsGlobalWindow* globalWindow = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
+ if (!globalWindow) {
+ return Cancel();
+ }
+ mWindow = globalWindow->AsInner();
+ if (NS_WARN_IF(!mWindow)) {
+ return Cancel();
+ }
+
+ nsCOMPtr<nsIDocument> doc = mWindow->GetDoc();
+ if (NS_WARN_IF(!doc)) {
+ return Cancel();
+ }
+
+ mPrincipal = doc->NodePrincipal();
+ MOZ_ASSERT(mPrincipal);
+
+ mRequester = new nsContentPermissionRequester(mWindow);
+ return nsContentPermissionUtils::AskPermission(this, mWindow);
+ }
+
+ NS_IMETHOD Cancel() override
+ {
+ Resolve(false);
+ return NS_OK;
+ }
+
+ NS_IMETHOD Allow(JS::HandleValue aChoices) override
+ {
+ MOZ_ASSERT(aChoices.isUndefined());
+ Resolve(true);
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetTypes(nsIArray** aTypes) override
+ {
+ nsTArray<nsString> emptyOptions;
+ return nsContentPermissionUtils::CreatePermissionArray(NS_LITERAL_CSTRING("flyweb-publish-server"),
+ NS_LITERAL_CSTRING("unused"), emptyOptions, aTypes);
+ }
+
+ NS_IMETHOD GetRequester(nsIContentPermissionRequester** aRequester) override
+ {
+ NS_ENSURE_ARG_POINTER(aRequester);
+ nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
+ requester.forget(aRequester);
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetPrincipal(nsIPrincipal** aRequestingPrincipal) override
+ {
+ NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal);
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetWindow(mozIDOMWindow** aRequestingWindow) override
+ {
+ NS_IF_ADDREF(*aRequestingWindow = mWindow);
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetElement(nsIDOMElement** aRequestingElement) override
+ {
+ *aRequestingElement = nullptr;
+ return NS_OK;
+ }
+
+private:
+ void Resolve(bool aResolve)
+ {
+ mServer->PermissionGranted(aResolve);
+ }
+
+ virtual ~FlyWebPublishServerPermissionCheck() = default;
+
+ nsCString mServiceName;
+ uint64_t mWindowID;
+ RefPtr<FlyWebPublishedServer> mServer;
+ nsCOMPtr<nsPIDOMWindowInner> mWindow;
+ nsCOMPtr<nsIPrincipal> mPrincipal;
+ nsCOMPtr<nsIContentPermissionRequester> mRequester;
+};
+
+NS_IMPL_ISUPPORTS(FlyWebPublishServerPermissionCheck,
+ nsIContentPermissionRequest,
+ nsIRunnable)
+
+class FlyWebMDNSService final
+ : public nsIDNSServiceDiscoveryListener
+ , public nsIDNSServiceResolveListener
+ , public nsIDNSRegistrationListener
+ , public nsITimerCallback
+{
+ friend class FlyWebService;
+
+private:
+ enum DiscoveryState {
+ DISCOVERY_IDLE,
+ DISCOVERY_STARTING,
+ DISCOVERY_RUNNING,
+ DISCOVERY_STOPPING
+ };
+
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDNSSERVICEDISCOVERYLISTENER
+ NS_DECL_NSIDNSSERVICERESOLVELISTENER
+ NS_DECL_NSIDNSREGISTRATIONLISTENER
+ NS_DECL_NSITIMERCALLBACK
+
+ explicit FlyWebMDNSService(FlyWebService* aService,
+ const nsACString& aServiceType);
+
+private:
+ virtual ~FlyWebMDNSService() = default;
+
+ nsresult Init();
+ nsresult StartDiscovery();
+ nsresult StopDiscovery();
+
+ void ListDiscoveredServices(nsTArray<FlyWebDiscoveredService>& aServices);
+ bool HasService(const nsAString& aServiceId);
+ nsresult PairWithService(const nsAString& aServiceId,
+ UniquePtr<FlyWebService::PairedInfo>& aInfo);
+
+ nsresult StartDiscoveryOf(FlyWebPublishedServerImpl* aServer);
+
+ void EnsureDiscoveryStarted();
+ void EnsureDiscoveryStopped();
+
+ // Cycle-breaking link to manager.
+ FlyWebService* mService;
+ nsCString mServiceType;
+
+ // Indicates the desired state of the system. If mDiscoveryActive is true,
+ // it indicates that backend discovery "should be happening", and discovery
+ // events should be forwarded to listeners.
+ // If false, the backend discovery "should be idle", and any discovery events
+ // that show up should not be forwarded to listeners.
+ bool mDiscoveryActive;
+
+ uint32_t mNumConsecutiveStartDiscoveryFailures;
+
+ // Represents the internal discovery state as it relates to nsDNSServiceDiscovery.
+ // When mDiscoveryActive is true, this state will periodically loop from
+ // (IDLE => STARTING => RUNNING => STOPPING => IDLE).
+ DiscoveryState mDiscoveryState;
+
+ nsCOMPtr<nsITimer> mDiscoveryStartTimer;
+ nsCOMPtr<nsITimer> mDiscoveryStopTimer;
+ nsCOMPtr<nsIDNSServiceDiscovery> mDNSServiceDiscovery;
+ nsCOMPtr<nsICancelable> mCancelDiscovery;
+ nsTHashtable<nsStringHashKey> mNewServiceSet;
+
+ struct DiscoveredInfo
+ {
+ explicit DiscoveredInfo(nsIDNSServiceInfo* aDNSServiceInfo);
+ FlyWebDiscoveredService mService;
+ nsCOMPtr<nsIDNSServiceInfo> mDNSServiceInfo;
+ };
+ nsClassHashtable<nsStringHashKey, DiscoveredInfo> mServiceMap;
+};
+
+void
+LogDNSInfo(nsIDNSServiceInfo* aServiceInfo, const char* aFunc)
+{
+ if (!LOG_TEST_I()) {
+ return;
+ }
+
+ nsCString tmp;
+ aServiceInfo->GetServiceName(tmp);
+ LOG_I("%s: serviceName=%s", aFunc, tmp.get());
+
+ aServiceInfo->GetHost(tmp);
+ LOG_I("%s: host=%s", aFunc, tmp.get());
+
+ aServiceInfo->GetAddress(tmp);
+ LOG_I("%s: address=%s", aFunc, tmp.get());
+
+ uint16_t port = -2;
+ aServiceInfo->GetPort(&port);
+ LOG_I("%s: port=%d", aFunc, (int)port);
+
+ nsCOMPtr<nsIPropertyBag2> attributes;
+ aServiceInfo->GetAttributes(getter_AddRefs(attributes));
+ if (!attributes) {
+ LOG_I("%s: no attributes", aFunc);
+ } else {
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ attributes->GetEnumerator(getter_AddRefs(enumerator));
+ MOZ_ASSERT(enumerator);
+
+ LOG_I("%s: attributes start", aFunc);
+
+ bool hasMoreElements;
+ while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreElements)) &&
+ hasMoreElements) {
+ nsCOMPtr<nsISupports> element;
+ MOZ_ALWAYS_SUCCEEDS(enumerator->GetNext(getter_AddRefs(element)));
+ nsCOMPtr<nsIProperty> property = do_QueryInterface(element);
+ MOZ_ASSERT(property);
+
+ nsAutoString name;
+ nsCOMPtr<nsIVariant> value;
+ MOZ_ALWAYS_SUCCEEDS(property->GetName(name));
+ MOZ_ALWAYS_SUCCEEDS(property->GetValue(getter_AddRefs(value)));
+
+ nsAutoCString str;
+ nsresult rv = value->GetAsACString(str);
+ if (NS_SUCCEEDED(rv)) {
+ LOG_I("%s: attribute name=%s value=%s", aFunc,
+ NS_ConvertUTF16toUTF8(name).get(), str.get());
+ } else {
+ uint16_t type;
+ MOZ_ALWAYS_SUCCEEDS(value->GetDataType(&type));
+ LOG_I("%s: attribute *unstringifiable* name=%s type=%d", aFunc,
+ NS_ConvertUTF16toUTF8(name).get(), (int)type);
+ }
+ }
+
+ LOG_I("%s: attributes end", aFunc);
+ }
+}
+
+NS_IMPL_ISUPPORTS(FlyWebMDNSService,
+ nsIDNSServiceDiscoveryListener,
+ nsIDNSServiceResolveListener,
+ nsIDNSRegistrationListener,
+ nsITimerCallback)
+
+FlyWebMDNSService::FlyWebMDNSService(
+ FlyWebService* aService,
+ const nsACString& aServiceType)
+ : mService(aService)
+ , mServiceType(aServiceType)
+ , mDiscoveryActive(false)
+ , mNumConsecutiveStartDiscoveryFailures(0)
+ , mDiscoveryState(DISCOVERY_IDLE)
+{}
+
+nsresult
+FlyWebMDNSService::OnDiscoveryStarted(const nsACString& aServiceType)
+{
+ MOZ_ASSERT(mDiscoveryState == DISCOVERY_STARTING);
+ mDiscoveryState = DISCOVERY_RUNNING;
+ // Reset consecutive start discovery failures.
+ mNumConsecutiveStartDiscoveryFailures = 0;
+ LOG_I("===========================================");
+ LOG_I("MDNSService::OnDiscoveryStarted(%s)", PromiseFlatCString(aServiceType).get());
+ LOG_I("===========================================");
+
+ // Clear the new service array.
+ mNewServiceSet.Clear();
+
+ // If service discovery is inactive, then stop network discovery immediately.
+ if (!mDiscoveryActive) {
+ // Set the stop timer to fire immediately.
+ Unused << NS_WARN_IF(NS_FAILED(mDiscoveryStopTimer->InitWithCallback(this, 0, nsITimer::TYPE_ONE_SHOT)));
+ return NS_OK;
+ }
+
+ // Otherwise, set the stop timer to fire in 5 seconds.
+ Unused << NS_WARN_IF(NS_FAILED(mDiscoveryStopTimer->InitWithCallback(this, 5 * 1000, nsITimer::TYPE_ONE_SHOT)));
+
+ return NS_OK;
+}
+
+nsresult
+FlyWebMDNSService::OnDiscoveryStopped(const nsACString& aServiceType)
+{
+ LOG_I("///////////////////////////////////////////");
+ LOG_I("MDNSService::OnDiscoveryStopped(%s)", PromiseFlatCString(aServiceType).get());
+ LOG_I("///////////////////////////////////////////");
+ MOZ_ASSERT(mDiscoveryState == DISCOVERY_STOPPING);
+ mDiscoveryState = DISCOVERY_IDLE;
+
+ // If service discovery is inactive, then discard all results and do not proceed.
+ if (!mDiscoveryActive) {
+ mServiceMap.Clear();
+ mNewServiceSet.Clear();
+ return NS_OK;
+ }
+
+ // Process the service map, add to the pair map.
+ for (auto iter = mServiceMap.Iter(); !iter.Done(); iter.Next()) {
+ DiscoveredInfo* service = iter.UserData();
+
+ if (!mNewServiceSet.Contains(service->mService.mServiceId)) {
+ iter.Remove();
+ }
+ }
+
+ // Notify FlyWebService of changed service list.
+ mService->NotifyDiscoveredServicesChanged();
+
+ // Start discovery again immediately.
+ Unused << NS_WARN_IF(NS_FAILED(mDiscoveryStartTimer->InitWithCallback(this, 0, nsITimer::TYPE_ONE_SHOT)));
+
+ return NS_OK;
+}
+
+nsresult
+FlyWebMDNSService::OnServiceFound(nsIDNSServiceInfo* aServiceInfo)
+{
+ LogDNSInfo(aServiceInfo, "FlyWebMDNSService::OnServiceFound");
+
+ // If discovery is not active, don't do anything with the result.
+ // If there is no discovery underway, ignore this.
+ if (!mDiscoveryActive || mDiscoveryState != DISCOVERY_RUNNING) {
+ return NS_OK;
+ }
+
+ // Discovery is underway - resolve the service.
+ nsresult rv = mDNSServiceDiscovery->ResolveService(aServiceInfo, this);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+nsresult
+FlyWebMDNSService::OnServiceLost(nsIDNSServiceInfo* aServiceInfo)
+{
+ LogDNSInfo(aServiceInfo, "FlyWebMDNSService::OnServiceLost");
+
+ return NS_OK;
+}
+
+nsresult
+FlyWebMDNSService::OnStartDiscoveryFailed(const nsACString& aServiceType, int32_t aErrorCode)
+{
+ LOG_E("MDNSService::OnStartDiscoveryFailed(%s): %d", PromiseFlatCString(aServiceType).get(), (int) aErrorCode);
+
+ MOZ_ASSERT(mDiscoveryState == DISCOVERY_STARTING);
+ mDiscoveryState = DISCOVERY_IDLE;
+ mNumConsecutiveStartDiscoveryFailures++;
+
+ // If discovery is active, and the number of consecutive failures is < 3, try starting again.
+ if (mDiscoveryActive && mNumConsecutiveStartDiscoveryFailures < 3) {
+ Unused << NS_WARN_IF(NS_FAILED(mDiscoveryStartTimer->InitWithCallback(this, 0, nsITimer::TYPE_ONE_SHOT)));
+ }
+
+ return NS_OK;
+}
+
+nsresult
+FlyWebMDNSService::OnStopDiscoveryFailed(const nsACString& aServiceType, int32_t aErrorCode)
+{
+ LOG_E("MDNSService::OnStopDiscoveryFailed(%s)", PromiseFlatCString(aServiceType).get());
+ MOZ_ASSERT(mDiscoveryState == DISCOVERY_STOPPING);
+ mDiscoveryState = DISCOVERY_IDLE;
+
+ // If discovery is active, start discovery again immediately.
+ if (mDiscoveryActive) {
+ Unused << NS_WARN_IF(NS_FAILED(mDiscoveryStartTimer->InitWithCallback(this, 0, nsITimer::TYPE_ONE_SHOT)));
+ }
+
+ return NS_OK;
+}
+
+static bool
+IsAcceptableServiceAddress(const nsCString& addr)
+{
+ PRNetAddr prNetAddr;
+ PRStatus status = PR_StringToNetAddr(addr.get(), &prNetAddr);
+ if (status == PR_FAILURE) {
+ return false;
+ }
+ // Only allow ipv4 addreses for now.
+ return prNetAddr.raw.family == PR_AF_INET;
+}
+
+nsresult
+FlyWebMDNSService::OnServiceResolved(nsIDNSServiceInfo* aServiceInfo)
+{
+ LogDNSInfo(aServiceInfo, "FlyWebMDNSService::OnServiceResolved");
+
+ // If discovery is not active, don't do anything with the result.
+ // If there is no discovery underway, ignore this resolve.
+ if (!mDiscoveryActive || mDiscoveryState != DISCOVERY_RUNNING) {
+ return NS_OK;
+ }
+
+ nsresult rv;
+
+ nsCString address;
+ rv = aServiceInfo->GetAddress(address);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!IsAcceptableServiceAddress(address)) {
+ return NS_OK;
+ }
+
+ // Create a new serviceInfo and stuff it in the new service array.
+ UniquePtr<DiscoveredInfo> svc(new DiscoveredInfo(aServiceInfo));
+ mNewServiceSet.PutEntry(svc->mService.mServiceId);
+
+ DiscoveredInfo* existingSvc =
+ mServiceMap.Get(svc->mService.mServiceId);
+ if (existingSvc) {
+ // Update the underlying DNS service info, but leave the old object in place.
+ existingSvc->mDNSServiceInfo = aServiceInfo;
+ } else {
+ DiscoveredInfo* info = svc.release();
+ mServiceMap.Put(info->mService.mServiceId, info);
+ }
+
+ // Notify FlyWebService of changed service list.
+ mService->NotifyDiscoveredServicesChanged();
+
+ return NS_OK;
+}
+
+FlyWebMDNSService::DiscoveredInfo::DiscoveredInfo(nsIDNSServiceInfo* aDNSServiceInfo)
+ : mDNSServiceInfo(aDNSServiceInfo)
+{
+ nsCString tmp;
+ DebugOnly<nsresult> drv = aDNSServiceInfo->GetServiceName(tmp);
+ MOZ_ASSERT(NS_SUCCEEDED(drv));
+ CopyUTF8toUTF16(tmp, mService.mDisplayName);
+
+ mService.mTransport = NS_LITERAL_STRING("mdns");
+
+ drv = aDNSServiceInfo->GetServiceType(tmp);
+ MOZ_ASSERT(NS_SUCCEEDED(drv));
+ CopyUTF8toUTF16(tmp, mService.mServiceType);
+
+ nsCOMPtr<nsIPropertyBag2> attrs;
+ drv = aDNSServiceInfo->GetAttributes(getter_AddRefs(attrs));
+ MOZ_ASSERT(NS_SUCCEEDED(drv));
+ if (attrs) {
+ attrs->GetPropertyAsAString(NS_LITERAL_STRING("cert"), mService.mCert);
+ attrs->GetPropertyAsAString(NS_LITERAL_STRING("path"), mService.mPath);
+ }
+
+ // Construct a service id from the name, host, address, and port.
+ nsCString cHost;
+ drv = aDNSServiceInfo->GetHost(cHost);
+ MOZ_ASSERT(NS_SUCCEEDED(drv));
+
+ nsCString cAddress;
+ drv = aDNSServiceInfo->GetAddress(cAddress);
+ MOZ_ASSERT(NS_SUCCEEDED(drv));
+
+ uint16_t port;
+ drv = aDNSServiceInfo->GetPort(&port);
+ MOZ_ASSERT(NS_SUCCEEDED(drv));
+ nsAutoString portStr;
+ portStr.AppendInt(port, 10);
+
+ mService.mServiceId =
+ NS_ConvertUTF8toUTF16(cAddress) +
+ NS_LITERAL_STRING(":") +
+ portStr +
+ NS_LITERAL_STRING("|") +
+ mService.mServiceType +
+ NS_LITERAL_STRING("|") +
+ NS_ConvertUTF8toUTF16(cHost) +
+ NS_LITERAL_STRING("|") +
+ mService.mDisplayName;
+}
+
+
+nsresult
+FlyWebMDNSService::OnResolveFailed(nsIDNSServiceInfo* aServiceInfo, int32_t aErrorCode)
+{
+ LogDNSInfo(aServiceInfo, "FlyWebMDNSService::OnResolveFailed");
+
+ return NS_OK;
+}
+
+nsresult
+FlyWebMDNSService::OnServiceRegistered(nsIDNSServiceInfo* aServiceInfo)
+{
+ LogDNSInfo(aServiceInfo, "FlyWebMDNSService::OnServiceRegistered");
+
+ nsCString cName;
+ if (NS_WARN_IF(NS_FAILED(aServiceInfo->GetServiceName(cName)))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsString name = NS_ConvertUTF8toUTF16(cName);
+ RefPtr<FlyWebPublishedServer> existingServer =
+ FlyWebService::GetOrCreate()->FindPublishedServerByName(name);
+ if (!existingServer) {
+ return NS_ERROR_FAILURE;
+ }
+
+ existingServer->PublishedServerStarted(NS_OK);
+
+ return NS_OK;
+}
+
+nsresult
+FlyWebMDNSService::OnServiceUnregistered(nsIDNSServiceInfo* aServiceInfo)
+{
+ LogDNSInfo(aServiceInfo, "FlyWebMDNSService::OnServiceUnregistered");
+
+ nsCString cName;
+ if (NS_WARN_IF(NS_FAILED(aServiceInfo->GetServiceName(cName)))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsString name = NS_ConvertUTF8toUTF16(cName);
+ RefPtr<FlyWebPublishedServer> existingServer =
+ FlyWebService::GetOrCreate()->FindPublishedServerByName(name);
+ if (!existingServer) {
+ return NS_ERROR_FAILURE;
+ }
+
+ LOG_I("OnServiceRegistered(MDNS): De-advertised server with name %s.", cName.get());
+
+ return NS_OK;
+}
+
+nsresult
+FlyWebMDNSService::OnRegistrationFailed(nsIDNSServiceInfo* aServiceInfo, int32_t errorCode)
+{
+ LogDNSInfo(aServiceInfo, "FlyWebMDNSService::OnRegistrationFailed");
+
+ nsCString cName;
+ if (NS_WARN_IF(NS_FAILED(aServiceInfo->GetServiceName(cName)))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsString name = NS_ConvertUTF8toUTF16(cName);
+ RefPtr<FlyWebPublishedServer> existingServer =
+ FlyWebService::GetOrCreate()->FindPublishedServerByName(name);
+ if (!existingServer) {
+ return NS_ERROR_FAILURE;
+ }
+
+ LOG_I("OnServiceRegistered(MDNS): Registration of server with name %s failed.", cName.get());
+
+ // Remove the nsICancelable from the published server.
+ existingServer->PublishedServerStarted(NS_ERROR_FAILURE);
+ return NS_OK;
+}
+
+nsresult
+FlyWebMDNSService::OnUnregistrationFailed(nsIDNSServiceInfo* aServiceInfo, int32_t errorCode)
+{
+ LogDNSInfo(aServiceInfo, "FlyWebMDNSService::OnUnregistrationFailed");
+
+ nsCString cName;
+ if (NS_WARN_IF(NS_FAILED(aServiceInfo->GetServiceName(cName)))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsString name = NS_ConvertUTF8toUTF16(cName);
+ RefPtr<FlyWebPublishedServer> existingServer =
+ FlyWebService::GetOrCreate()->FindPublishedServerByName(name);
+ if (!existingServer) {
+ return NS_ERROR_FAILURE;
+ }
+
+ LOG_I("OnServiceRegistered(MDNS): Un-Advertisement of server with name %s failed.", cName.get());
+ return NS_OK;
+}
+
+nsresult
+FlyWebMDNSService::Notify(nsITimer* timer)
+{
+ if (timer == mDiscoveryStopTimer.get()) {
+ LOG_I("MDNSService::Notify() got discovery stop timeout");
+ // Internet discovery stop timer has fired.
+ nsresult rv = StopDiscovery();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ return NS_OK;
+ }
+
+ if (timer == mDiscoveryStartTimer.get()) {
+ LOG_I("MDNSService::Notify() got discovery start timeout");
+ // Internet discovery start timer has fired.
+ nsresult rv = StartDiscovery();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ return NS_OK;
+ }
+
+ LOG_E("MDNSService::Notify got unknown timeout.");
+ return NS_OK;
+}
+
+nsresult
+FlyWebMDNSService::Init()
+{
+ MOZ_ASSERT(mDiscoveryState == DISCOVERY_IDLE);
+
+ mDiscoveryStartTimer = do_CreateInstance("@mozilla.org/timer;1");
+ if (!mDiscoveryStartTimer) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mDiscoveryStopTimer = do_CreateInstance("@mozilla.org/timer;1");
+ if (!mDiscoveryStopTimer) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv;
+ mDNSServiceDiscovery = do_GetService(DNSSERVICEDISCOVERY_CONTRACT_ID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+FlyWebMDNSService::StartDiscovery()
+{
+ nsresult rv;
+
+ // Always cancel the timer.
+ rv = mDiscoveryStartTimer->Cancel();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ LOG_E("FlyWeb failed to cancel DNS service discovery start timer.");
+ }
+
+ // If discovery is not idle, don't start it.
+ if (mDiscoveryState != DISCOVERY_IDLE) {
+ return NS_OK;
+ }
+
+ LOG_I("FlyWeb starting dicovery.");
+ mDiscoveryState = DISCOVERY_STARTING;
+
+ // start the discovery.
+ rv = mDNSServiceDiscovery->StartDiscovery(mServiceType, this,
+ getter_AddRefs(mCancelDiscovery));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ LOG_E("FlyWeb failed to start DNS service discovery.");
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+FlyWebMDNSService::StopDiscovery()
+{
+ nsresult rv;
+
+ // Always cancel the timer.
+ rv = mDiscoveryStopTimer->Cancel();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ LOG_E("FlyWeb failed to cancel DNS service discovery stop timer.");
+ }
+
+ // If discovery is not running, do nothing.
+ if (mDiscoveryState != DISCOVERY_RUNNING) {
+ return NS_OK;
+ }
+
+ LOG_I("FlyWeb stopping dicovery.");
+
+ // Mark service discovery as stopping.
+ mDiscoveryState = DISCOVERY_STOPPING;
+
+ if (mCancelDiscovery) {
+ LOG_I("MDNSService::StopDiscovery() - mCancelDiscovery exists!");
+ nsCOMPtr<nsICancelable> cancelDiscovery = mCancelDiscovery.forget();
+ rv = cancelDiscovery->Cancel(NS_ERROR_ABORT);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ LOG_E("FlyWeb failed to cancel DNS stop service discovery.");
+ }
+ } else {
+ LOG_I("MDNSService::StopDiscovery() - mCancelDiscovery does not exist!");
+ mDiscoveryState = DISCOVERY_IDLE;
+ }
+
+ return NS_OK;
+}
+
+void
+FlyWebMDNSService::ListDiscoveredServices(nsTArray<FlyWebDiscoveredService>& aServices)
+{
+ for (auto iter = mServiceMap.Iter(); !iter.Done(); iter.Next()) {
+ aServices.AppendElement(iter.UserData()->mService);
+ }
+}
+
+bool
+FlyWebMDNSService::HasService(const nsAString& aServiceId)
+{
+ return mServiceMap.Contains(aServiceId);
+}
+
+nsresult
+FlyWebMDNSService::PairWithService(const nsAString& aServiceId,
+ UniquePtr<FlyWebService::PairedInfo>& aInfo)
+{
+ MOZ_ASSERT(HasService(aServiceId));
+
+ nsresult rv;
+ nsCOMPtr<nsIUUIDGenerator> uuidgen =
+ do_GetService("@mozilla.org/uuid-generator;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsID id;
+ rv = uuidgen->GenerateUUIDInPlace(&id);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aInfo.reset(new FlyWebService::PairedInfo());
+
+ char uuidChars[NSID_LENGTH];
+ id.ToProvidedString(uuidChars);
+ CopyUTF8toUTF16(Substring(uuidChars + 1, uuidChars + NSID_LENGTH - 2),
+ aInfo->mService.mHostname);
+
+ DiscoveredInfo* discInfo = mServiceMap.Get(aServiceId);
+
+ nsAutoString url;
+ if (discInfo->mService.mCert.IsEmpty()) {
+ url.AssignLiteral("http://");
+ } else {
+ url.AssignLiteral("https://");
+ }
+ url.Append(aInfo->mService.mHostname + NS_LITERAL_STRING("/"));
+ nsCOMPtr<nsIURI> uiURL;
+ NS_NewURI(getter_AddRefs(uiURL), url);
+ MOZ_ASSERT(uiURL);
+ if (!discInfo->mService.mPath.IsEmpty()) {
+ nsCOMPtr<nsIURI> tmp = uiURL.forget();
+ NS_NewURI(getter_AddRefs(uiURL), discInfo->mService.mPath, nullptr, tmp);
+ }
+ if (uiURL) {
+ nsAutoCString spec;
+ uiURL->GetSpec(spec);
+ CopyUTF8toUTF16(spec, aInfo->mService.mUiUrl);
+ }
+
+ aInfo->mService.mDiscoveredService = discInfo->mService;
+ aInfo->mDNSServiceInfo = discInfo->mDNSServiceInfo;
+
+ return NS_OK;
+}
+
+nsresult
+FlyWebMDNSService::StartDiscoveryOf(FlyWebPublishedServerImpl* aServer)
+{
+
+ RefPtr<FlyWebPublishedServer> existingServer =
+ FlyWebService::GetOrCreate()->FindPublishedServerByName(aServer->Name());
+ MOZ_ASSERT(existingServer);
+
+ // Advertise the service via mdns.
+ RefPtr<net::nsDNSServiceInfo> serviceInfo(new net::nsDNSServiceInfo());
+
+ serviceInfo->SetPort(aServer->Port());
+ serviceInfo->SetServiceType(mServiceType);
+
+ nsCString certKey;
+ aServer->GetCertKey(certKey);
+ nsString uiURL;
+ aServer->GetUiUrl(uiURL);
+
+ if (!uiURL.IsEmpty() || !certKey.IsEmpty()) {
+ RefPtr<nsHashPropertyBag> attrs = new nsHashPropertyBag();
+ if (!uiURL.IsEmpty()) {
+ attrs->SetPropertyAsAString(NS_LITERAL_STRING("path"), uiURL);
+ }
+ if (!certKey.IsEmpty()) {
+ attrs->SetPropertyAsACString(NS_LITERAL_STRING("cert"), certKey);
+ }
+ serviceInfo->SetAttributes(attrs);
+ }
+
+ nsCString cstrName = NS_ConvertUTF16toUTF8(aServer->Name());
+ LOG_I("MDNSService::StartDiscoveryOf() advertising service %s", cstrName.get());
+ serviceInfo->SetServiceName(cstrName);
+
+ LogDNSInfo(serviceInfo, "FlyWebMDNSService::StartDiscoveryOf");
+
+ // Advertise the service.
+ nsCOMPtr<nsICancelable> cancelRegister;
+ nsresult rv = mDNSServiceDiscovery->
+ RegisterService(serviceInfo, this, getter_AddRefs(cancelRegister));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // All done.
+ aServer->SetCancelRegister(cancelRegister);
+
+ return NS_OK;
+}
+
+void
+FlyWebMDNSService::EnsureDiscoveryStarted()
+{
+ mDiscoveryActive = true;
+ // If state is idle, start discovery immediately.
+ if (mDiscoveryState == DISCOVERY_IDLE) {
+ StartDiscovery();
+ }
+}
+
+void
+FlyWebMDNSService::EnsureDiscoveryStopped()
+{
+ // All we need to do is set the flag to false.
+ // If current state is IDLE, it's already the correct state.
+ // Otherwise, the handlers for the internal state
+ // transitions will check this flag and drive the state
+ // towards IDLE.
+ mDiscoveryActive = false;
+}
+
+static StaticRefPtr<FlyWebService> gFlyWebService;
+
+NS_IMPL_ISUPPORTS(FlyWebService, nsIObserver)
+
+FlyWebService::FlyWebService()
+ : mMonitor("FlyWebService::mMonitor")
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->AddObserver(this, "inner-window-destroyed", false);
+ }
+}
+
+FlyWebService::~FlyWebService()
+{
+}
+
+FlyWebService*
+FlyWebService::GetExisting()
+{
+ return gFlyWebService;
+}
+
+FlyWebService*
+FlyWebService::GetOrCreate()
+{
+ if (!gFlyWebService) {
+ gFlyWebService = new FlyWebService();
+ ClearOnShutdown(&gFlyWebService);
+ ErrorResult rv = gFlyWebService->Init();
+ if (rv.Failed()) {
+ gFlyWebService = nullptr;
+ return nullptr;
+ }
+ }
+ return gFlyWebService;
+}
+
+ErrorResult
+FlyWebService::Init()
+{
+ // Most functions of FlyWebService should not be started in the child.
+ // Instead FlyWebService in the child is mainly responsible for tracking
+ // publishedServer lifetimes. Other functions are handled by the
+ // FlyWebService running in the parent.
+ if (XRE_GetProcessType() == GeckoProcessType_Content) {
+ return ErrorResult(NS_OK);
+ }
+
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!mMDNSHttpService) {
+ mMDNSHttpService = new FlyWebMDNSService(this, NS_LITERAL_CSTRING("_http._tcp."));
+ ErrorResult rv;
+
+ rv = mMDNSHttpService->Init();
+ if (rv.Failed()) {
+ LOG_E("FlyWebService failed to initialize MDNS _http._tcp.");
+ mMDNSHttpService = nullptr;
+ rv.SuppressException();
+ }
+ }
+
+ if (!mMDNSFlywebService) {
+ mMDNSFlywebService = new FlyWebMDNSService(this, NS_LITERAL_CSTRING("_flyweb._tcp."));
+ ErrorResult rv;
+
+ rv = mMDNSFlywebService->Init();
+ if (rv.Failed()) {
+ LOG_E("FlyWebService failed to initialize MDNS _flyweb._tcp.");
+ mMDNSFlywebService = nullptr;
+ rv.SuppressException();
+ }
+ }
+
+ return ErrorResult(NS_OK);
+}
+
+static already_AddRefed<FlyWebPublishPromise>
+MakeRejectionPromise(const char* name)
+{
+ MozPromiseHolder<FlyWebPublishPromise> holder;
+ RefPtr<FlyWebPublishPromise> promise = holder.Ensure(name);
+ holder.Reject(NS_ERROR_FAILURE, name);
+ return promise.forget();
+}
+
+static bool
+CheckForFlyWebAddon(const nsACString& uriString)
+{
+ // Before proceeding, ensure that the FlyWeb system addon exists.
+ nsresult rv;
+ nsCOMPtr<nsIURI> uri;
+ rv = NS_NewURI(getter_AddRefs(uri), uriString);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ JSAddonId *addonId = MapURIToAddonID(uri);
+ if (!addonId) {
+ return false;
+ }
+
+ JSFlatString* flat = JS_ASSERT_STRING_IS_FLAT(JS::StringOfAddonId(addonId));
+ nsAutoString addonIdString;
+ AssignJSFlatString(addonIdString, flat);
+ if (!addonIdString.EqualsLiteral("flyweb@mozilla.org")) {
+ nsCString addonIdCString = NS_ConvertUTF16toUTF8(addonIdString);
+ return false;
+ }
+
+ return true;
+}
+
+already_AddRefed<FlyWebPublishPromise>
+FlyWebService::PublishServer(const nsAString& aName,
+ const FlyWebPublishOptions& aOptions,
+ nsPIDOMWindowInner* aWindow)
+{
+ // Scan uiUrl for illegal characters
+
+ RefPtr<FlyWebPublishedServer> existingServer =
+ FlyWebService::GetOrCreate()->FindPublishedServerByName(aName);
+ if (existingServer) {
+ LOG_I("PublishServer: Trying to publish server with already-existing name %s.",
+ NS_ConvertUTF16toUTF8(aName).get());
+ return MakeRejectionPromise(__func__);
+ }
+
+ RefPtr<FlyWebPublishedServer> server;
+ if (XRE_GetProcessType() == GeckoProcessType_Content) {
+ server = new FlyWebPublishedServerChild(aWindow, aName, aOptions);
+ } else {
+ server = new FlyWebPublishedServerImpl(aWindow, aName, aOptions);
+
+ // Before proceeding, ensure that the FlyWeb system addon exists.
+ if (!CheckForFlyWebAddon(NS_LITERAL_CSTRING("chrome://flyweb/skin/icon-64.png")) &&
+ !CheckForFlyWebAddon(NS_LITERAL_CSTRING("chrome://flyweb/content/icon-64.png")))
+ {
+ LOG_E("PublishServer: Failed to find FlyWeb system addon.");
+ return MakeRejectionPromise(__func__);
+ }
+ }
+
+ if (aWindow) {
+ nsresult rv;
+
+ MOZ_ASSERT(NS_IsMainThread());
+ rv = NS_DispatchToCurrentThread(
+ MakeAndAddRef<FlyWebPublishServerPermissionCheck>(
+ NS_ConvertUTF16toUTF8(aName), aWindow->WindowID(), server));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ LOG_E("PublishServer: Failed to dispatch permission check runnable for %s",
+ NS_ConvertUTF16toUTF8(aName).get());
+ return MakeRejectionPromise(__func__);
+ }
+ } else {
+ // If aWindow is null, we're definitely in the e10s parent process.
+ // In this case, we know that permission has already been granted
+ // by the user because of content-process prompt.
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+ server->PermissionGranted(true);
+ }
+
+ mServers.AppendElement(server);
+
+ return server->GetPublishPromise();
+}
+
+already_AddRefed<FlyWebPublishedServer>
+FlyWebService::FindPublishedServerByName(
+ const nsAString& aName)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ for (FlyWebPublishedServer* publishedServer : mServers) {
+ if (publishedServer->Name().Equals(aName)) {
+ RefPtr<FlyWebPublishedServer> server = publishedServer;
+ return server.forget();
+ }
+ }
+ return nullptr;
+}
+
+void
+FlyWebService::RegisterDiscoveryManager(FlyWebDiscoveryManager* aDiscoveryManager)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ mDiscoveryManagerTable.PutEntry(aDiscoveryManager);
+ if (mMDNSHttpService) {
+ mMDNSHttpService->EnsureDiscoveryStarted();
+ }
+ if (mMDNSFlywebService) {
+ mMDNSFlywebService->EnsureDiscoveryStarted();
+ }
+}
+
+void
+FlyWebService::UnregisterDiscoveryManager(FlyWebDiscoveryManager* aDiscoveryManager)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ mDiscoveryManagerTable.RemoveEntry(aDiscoveryManager);
+ if (mDiscoveryManagerTable.IsEmpty()) {
+ if (mMDNSHttpService) {
+ mMDNSHttpService->EnsureDiscoveryStopped();
+ }
+ if (mMDNSFlywebService) {
+ mMDNSFlywebService->EnsureDiscoveryStopped();
+ }
+ }
+}
+
+NS_IMETHODIMP
+FlyWebService::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (strcmp(aTopic, "inner-window-destroyed")) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
+ NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
+
+ uint64_t innerID;
+ nsresult rv = wrapper->GetData(&innerID);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (FlyWebPublishedServer* server : mServers) {
+ if (server->OwnerWindowID() == innerID) {
+ server->Close();
+ }
+ }
+
+ return NS_OK;
+}
+
+void
+FlyWebService::UnregisterServer(FlyWebPublishedServer* aServer)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ DebugOnly<bool> removed = mServers.RemoveElement(aServer);
+ MOZ_ASSERT(removed);
+}
+
+bool
+FlyWebService::HasConnectionOrServer(uint64_t aWindowID)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ for (FlyWebPublishedServer* server : mServers) {
+ nsPIDOMWindowInner* win = server->GetOwner();
+ if (win && win->WindowID() == aWindowID) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void
+FlyWebService::NotifyDiscoveredServicesChanged()
+{
+ // Process the service map, add to the pair map.
+ for (auto iter = mDiscoveryManagerTable.Iter(); !iter.Done(); iter.Next()) {
+ iter.Get()->GetKey()->NotifyDiscoveredServicesChanged();
+ }
+}
+
+void
+FlyWebService::ListDiscoveredServices(nsTArray<FlyWebDiscoveredService>& aServices)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mMDNSHttpService) {
+ mMDNSHttpService->ListDiscoveredServices(aServices);
+ }
+ if (mMDNSFlywebService) {
+ mMDNSFlywebService->ListDiscoveredServices(aServices);
+ }
+}
+
+void
+FlyWebService::PairWithService(const nsAString& aServiceId,
+ FlyWebPairingCallback& aCallback)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ // See if we have already paired with this service. If so, re-use the
+ // FlyWebPairedService for that.
+ {
+ ReentrantMonitorAutoEnter pairedMapLock(mMonitor);
+ for (auto iter = mPairedServiceTable.Iter(); !iter.Done(); iter.Next()) {
+ PairedInfo* pairInfo = iter.UserData();
+ if (pairInfo->mService.mDiscoveredService.mServiceId.Equals(aServiceId)) {
+ ErrorResult er;
+ ReentrantMonitorAutoExit pairedMapRelease(mMonitor);
+ aCallback.PairingSucceeded(pairInfo->mService, er);
+ ENSURE_SUCCESS_VOID(er);
+ return;
+ }
+ }
+ }
+
+ UniquePtr<PairedInfo> pairInfo;
+
+ nsresult rv = NS_OK;
+ bool notFound = false;
+ if (mMDNSHttpService && mMDNSHttpService->HasService(aServiceId)) {
+ rv = mMDNSHttpService->PairWithService(aServiceId, pairInfo);
+ } else if (mMDNSFlywebService && mMDNSFlywebService->HasService(aServiceId)) {
+ rv = mMDNSFlywebService->PairWithService(aServiceId, pairInfo);
+ } else {
+ notFound = true;
+ }
+
+ if (NS_FAILED(rv)) {
+ ErrorResult result;
+ result.Throw(rv);
+ const nsAString& reason = NS_LITERAL_STRING("Error pairing.");
+ aCallback.PairingFailed(reason, result);
+ ENSURE_SUCCESS_VOID(result);
+ return;
+ }
+
+ if (!pairInfo) {
+ ErrorResult res;
+ const nsAString& reason = notFound ?
+ NS_LITERAL_STRING("No such service.") :
+ NS_LITERAL_STRING("Error pairing.");
+ aCallback.PairingFailed(reason, res);
+ ENSURE_SUCCESS_VOID(res);
+ return;
+ }
+
+ // Add fingerprint to certificate override database.
+ if (!pairInfo->mService.mDiscoveredService.mCert.IsEmpty()) {
+ nsCOMPtr<nsICertOverrideService> override =
+ do_GetService("@mozilla.org/security/certoverride;1");
+ if (!override ||
+ NS_FAILED(override->RememberTemporaryValidityOverrideUsingFingerprint(
+ NS_ConvertUTF16toUTF8(pairInfo->mService.mHostname),
+ -1,
+ NS_ConvertUTF16toUTF8(pairInfo->mService.mDiscoveredService.mCert),
+ nsICertOverrideService::ERROR_UNTRUSTED |
+ nsICertOverrideService::ERROR_MISMATCH))) {
+ ErrorResult res;
+ aCallback.PairingFailed(NS_LITERAL_STRING("Error adding certificate override."), res);
+ ENSURE_SUCCESS_VOID(res);
+ return;
+ }
+ }
+
+ // Grab a weak reference to the PairedInfo so that we can
+ // use it even after ownership has been transferred to mPairedServiceTable
+ PairedInfo* pairInfoWeak = pairInfo.release();
+
+ {
+ ReentrantMonitorAutoEnter pairedMapLock(mMonitor);
+ mPairedServiceTable.Put(
+ NS_ConvertUTF16toUTF8(pairInfoWeak->mService.mHostname), pairInfoWeak);
+ }
+
+ ErrorResult er;
+ aCallback.PairingSucceeded(pairInfoWeak->mService, er);
+ ENSURE_SUCCESS_VOID(er);
+}
+
+nsresult
+FlyWebService::CreateTransportForHost(const char **types,
+ uint32_t typeCount,
+ const nsACString &host,
+ int32_t port,
+ const nsACString &hostRoute,
+ int32_t portRoute,
+ nsIProxyInfo *proxyInfo,
+ nsISocketTransport **result)
+{
+ // This might be called on background threads
+
+ *result = nullptr;
+
+ nsCString ipAddrString;
+ uint16_t discPort;
+
+ {
+ ReentrantMonitorAutoEnter pairedMapLock(mMonitor);
+
+ PairedInfo* info = mPairedServiceTable.Get(host);
+
+ if (!info) {
+ return NS_OK;
+ }
+
+ // Get the ip address of the underlying service.
+ info->mDNSServiceInfo->GetAddress(ipAddrString);
+ info->mDNSServiceInfo->GetPort(&discPort);
+ }
+
+ // Parse it into an NetAddr.
+ PRNetAddr prNetAddr;
+ PRStatus status = PR_StringToNetAddr(ipAddrString.get(), &prNetAddr);
+ NS_ENSURE_FALSE(status == PR_FAILURE, NS_ERROR_FAILURE);
+
+ // Convert PRNetAddr to NetAddr.
+ mozilla::net::NetAddr netAddr;
+ PRNetAddrToNetAddr(&prNetAddr, &netAddr);
+ netAddr.inet.port = htons(discPort);
+
+ RefPtr<mozilla::net::nsSocketTransport> trans = new mozilla::net::nsSocketTransport();
+ nsresult rv = trans->InitPreResolved(
+ types, typeCount, host, port, hostRoute, portRoute, proxyInfo, &netAddr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ trans.forget(result);
+ return NS_OK;
+}
+
+void
+FlyWebService::StartDiscoveryOf(FlyWebPublishedServerImpl* aServer)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ nsresult rv = mMDNSFlywebService ?
+ mMDNSFlywebService->StartDiscoveryOf(aServer) :
+ NS_ERROR_FAILURE;
+
+ if (NS_FAILED(rv)) {
+ aServer->PublishedServerStarted(rv);
+ }
+}
+
+} // namespace dom
+} // namespace mozilla