/* 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 "WifiProxyService.h" #include "nsServiceManagerUtils.h" #include "mozilla/ModuleUtils.h" #include "mozilla/ClearOnShutdown.h" #include "nsXULAppAPI.h" #include "WifiUtils.h" #include "jsapi.h" #include "nsCxPusher.h" #define NS_WIFIPROXYSERVICE_CID \ { 0xc6c9be7e, 0x744f, 0x4222, {0xb2, 0x03, 0xcd, 0x55, 0xdf, 0xc8, 0xbc, 0x12} } using namespace mozilla; using namespace mozilla::dom; namespace mozilla { // The singleton Wifi service, to be used on the main thread. StaticRefPtr gWifiProxyService; // The singleton supplicant class, that can be used on any thread. static nsAutoPtr gWpaSupplicant; // Runnable used dispatch the WaitForEvent result on the main thread. class WifiEventDispatcher : public nsRunnable { public: WifiEventDispatcher(nsAString& aEvent): mEvent(aEvent) { MOZ_ASSERT(!NS_IsMainThread()); } NS_IMETHOD Run() { MOZ_ASSERT(NS_IsMainThread()); gWifiProxyService->DispatchWifiEvent(mEvent); return NS_OK; } private: nsString mEvent; }; // Runnable used to call WaitForEvent on the event thread. class EventRunnable : public nsRunnable { public: EventRunnable() { MOZ_ASSERT(NS_IsMainThread()); } NS_IMETHOD Run() { MOZ_ASSERT(!NS_IsMainThread()); nsAutoString event; gWpaSupplicant->WaitForEvent(event); if (!event.IsEmpty()) { nsCOMPtr runnable = new WifiEventDispatcher(event); NS_DispatchToMainThread(runnable); } return NS_OK; } }; // Runnable used dispatch the Command result on the main thread. class WifiResultDispatcher : public nsRunnable { public: WifiResultDispatcher(WifiResultOptions& aResult) { MOZ_ASSERT(!NS_IsMainThread()); // XXX: is there a better way to copy webidl dictionnaries? // the copy constructor is private. #define COPY_FIELD(prop) mResult.prop = aResult.prop; COPY_FIELD(mId) COPY_FIELD(mStatus) COPY_FIELD(mReply) COPY_FIELD(mRoute) COPY_FIELD(mError) COPY_FIELD(mValue) COPY_FIELD(mIpaddr_str) COPY_FIELD(mGateway_str) COPY_FIELD(mBroadcast_str) COPY_FIELD(mDns1_str) COPY_FIELD(mDns2_str) COPY_FIELD(mMask_str) COPY_FIELD(mServer_str) COPY_FIELD(mVendor_str) COPY_FIELD(mLease) COPY_FIELD(mMask) COPY_FIELD(mIpaddr) COPY_FIELD(mGateway) COPY_FIELD(mDns1) COPY_FIELD(mDns2) COPY_FIELD(mServer) #undef COPY_FIELD } NS_IMETHOD Run() { MOZ_ASSERT(NS_IsMainThread()); gWifiProxyService->DispatchWifiResult(mResult); return NS_OK; } private: WifiResultOptions mResult; }; // Runnable used to call SendCommand on the control thread. class ControlRunnable : public nsRunnable { public: ControlRunnable(CommandOptions aOptions) : mOptions(aOptions) { MOZ_ASSERT(NS_IsMainThread()); } NS_IMETHOD Run() { WifiResultOptions result; if (gWpaSupplicant->ExecuteCommand(mOptions, result)) { nsCOMPtr runnable = new WifiResultDispatcher(result); NS_DispatchToMainThread(runnable); } return NS_OK; } private: CommandOptions mOptions; }; NS_IMPL_ISUPPORTS1(WifiProxyService, nsIWifiProxyService) WifiProxyService::WifiProxyService() { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!gWifiProxyService); } WifiProxyService::~WifiProxyService() { MOZ_ASSERT(!gWifiProxyService); } already_AddRefed WifiProxyService::FactoryCreate() { if (XRE_GetProcessType() != GeckoProcessType_Default) { return nullptr; } MOZ_ASSERT(NS_IsMainThread()); if (!gWifiProxyService) { gWifiProxyService = new WifiProxyService(); ClearOnShutdown(&gWifiProxyService); gWpaSupplicant = new WpaSupplicant(); ClearOnShutdown(&gWpaSupplicant); } nsRefPtr service = gWifiProxyService.get(); return service.forget(); } NS_IMETHODIMP WifiProxyService::Start(nsIWifiEventListener* aListener) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aListener); nsresult rv = NS_NewThread(getter_AddRefs(mEventThread)); if (NS_FAILED(rv)) { NS_WARNING("Can't create wifi event thread"); return NS_ERROR_FAILURE; } rv = NS_NewThread(getter_AddRefs(mControlThread)); if (NS_FAILED(rv)) { NS_WARNING("Can't create wifi control thread"); // Shutdown the event thread. mEventThread->Shutdown(); mEventThread = nullptr; return NS_ERROR_FAILURE; } mListener = aListener; return NS_OK; } NS_IMETHODIMP WifiProxyService::Shutdown() { MOZ_ASSERT(NS_IsMainThread()); mEventThread->Shutdown(); mEventThread = nullptr; mControlThread->Shutdown(); mControlThread = nullptr; return NS_OK; } NS_IMETHODIMP WifiProxyService::SendCommand(const JS::Value& aOptions, JSContext* aCx) { MOZ_ASSERT(NS_IsMainThread()); WifiCommandOptions options; if (!options.Init(aCx, JS::Handle::fromMarkedLocation(&aOptions))) { NS_WARNING("Bad dictionary passed to WifiProxyService::SendCommand"); return NS_ERROR_FAILURE; } // Dispatch the command to the control thread. CommandOptions commandOptions(options); nsCOMPtr runnable = new ControlRunnable(commandOptions); mControlThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL); return NS_OK; } NS_IMETHODIMP WifiProxyService::WaitForEvent() { MOZ_ASSERT(NS_IsMainThread()); nsCOMPtr runnable = new EventRunnable(); mEventThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL); return NS_OK; } void WifiProxyService::DispatchWifiResult(const WifiResultOptions& aOptions) { MOZ_ASSERT(NS_IsMainThread()); mozilla::AutoSafeJSContext cx; JS::RootedValue val(cx); if (!aOptions.ToObject(cx, JS::NullPtr(), &val)) { return; } // Call the listener with a JS value. mListener->OnCommand(val); } void WifiProxyService::DispatchWifiEvent(const nsAString& aEvent) { MOZ_ASSERT(NS_IsMainThread()); // Call the listener. mListener->OnWaitEvent(aEvent); } NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(WifiProxyService, WifiProxyService::FactoryCreate) NS_DEFINE_NAMED_CID(NS_WIFIPROXYSERVICE_CID); static const mozilla::Module::CIDEntry kWifiProxyServiceCIDs[] = { { &kNS_WIFIPROXYSERVICE_CID, false, nullptr, WifiProxyServiceConstructor }, { nullptr } }; static const mozilla::Module::ContractIDEntry kWifiProxyServiceContracts[] = { { "@mozilla.org/wifi/service;1", &kNS_WIFIPROXYSERVICE_CID }, { nullptr } }; static const mozilla::Module kWifiProxyServiceModule = { mozilla::Module::kVersion, kWifiProxyServiceCIDs, kWifiProxyServiceContracts, nullptr }; } // namespace mozilla NSMODULE_DEFN(WifiProxyServiceModule) = &kWifiProxyServiceModule;