michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "NetworkWorker.h" michael@0: #include "NetworkUtils.h" michael@0: #include michael@0: #include "mozilla/ModuleUtils.h" michael@0: #include "mozilla/ClearOnShutdown.h" michael@0: #include "nsXULAppAPI.h" michael@0: #include "nsCxPusher.h" michael@0: michael@0: #define NS_NETWORKWORKER_CID \ michael@0: { 0x6df093e1, 0x8127, 0x4fa7, {0x90, 0x13, 0xa3, 0xaa, 0xa7, 0x79, 0xbb, 0xdd} } michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: using namespace mozilla::ipc; michael@0: michael@0: namespace mozilla { michael@0: michael@0: nsCOMPtr gWorkerThread; michael@0: michael@0: // The singleton network worker, to be used on the main thread. michael@0: StaticRefPtr gNetworkWorker; michael@0: michael@0: // The singleton networkutils class, that can be used on any thread. michael@0: static nsAutoPtr gNetworkUtils; michael@0: michael@0: // Runnable used dispatch command result on the main thread. michael@0: class NetworkResultDispatcher : public nsRunnable michael@0: { michael@0: public: michael@0: NetworkResultDispatcher(const NetworkResultOptions& aResult) michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: michael@0: #define COPY_FIELD(prop) mResult.prop = aResult.prop; michael@0: COPY_FIELD(mId) michael@0: COPY_FIELD(mRet) michael@0: COPY_FIELD(mBroadcast) michael@0: COPY_FIELD(mTopic) michael@0: COPY_FIELD(mReason) michael@0: COPY_FIELD(mResultCode) michael@0: COPY_FIELD(mResultReason) michael@0: COPY_FIELD(mError) michael@0: COPY_FIELD(mRxBytes) michael@0: COPY_FIELD(mTxBytes) michael@0: COPY_FIELD(mDate) michael@0: COPY_FIELD(mEnable) michael@0: COPY_FIELD(mResult) michael@0: COPY_FIELD(mSuccess) michael@0: COPY_FIELD(mCurExternalIfname) michael@0: COPY_FIELD(mCurInternalIfname) michael@0: #undef COPY_FIELD michael@0: } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (gNetworkWorker) { michael@0: gNetworkWorker->DispatchNetworkResult(mResult); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: private: michael@0: NetworkResultOptions mResult; michael@0: }; michael@0: michael@0: // Runnable used dispatch netd command on the worker thread. michael@0: class NetworkCommandDispatcher : public nsRunnable michael@0: { michael@0: public: michael@0: NetworkCommandDispatcher(const NetworkParams& aParams) michael@0: : mParams(aParams) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: michael@0: if (gNetworkUtils) { michael@0: gNetworkUtils->ExecuteCommand(mParams); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: private: michael@0: NetworkParams mParams; michael@0: }; michael@0: michael@0: // Runnable used dispatch netd result on the worker thread. michael@0: class NetdEventRunnable : public nsRunnable michael@0: { michael@0: public: michael@0: NetdEventRunnable(NetdCommand* aCommand) michael@0: : mCommand(aCommand) michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: michael@0: if (gNetworkUtils) { michael@0: gNetworkUtils->onNetdMessage(mCommand); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsAutoPtr mCommand; michael@0: }; michael@0: michael@0: class NetdMessageConsumer : public NetdConsumer michael@0: { michael@0: public: michael@0: NetdMessageConsumer() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: } michael@0: michael@0: void MessageReceived(NetdCommand* aCommand) michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: michael@0: nsCOMPtr runnable = new NetdEventRunnable(aCommand); michael@0: if (gWorkerThread) { michael@0: gWorkerThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL); michael@0: } michael@0: } michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(NetworkWorker, nsINetworkWorker) michael@0: michael@0: NetworkWorker::NetworkWorker() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(!gNetworkWorker); michael@0: } michael@0: michael@0: NetworkWorker::~NetworkWorker() michael@0: { michael@0: MOZ_ASSERT(!gNetworkWorker); michael@0: MOZ_ASSERT(!mListener); michael@0: } michael@0: michael@0: already_AddRefed michael@0: NetworkWorker::FactoryCreate() michael@0: { michael@0: if (XRE_GetProcessType() != GeckoProcessType_Default) { michael@0: return nullptr; michael@0: } michael@0: michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!gNetworkWorker) { michael@0: gNetworkWorker = new NetworkWorker(); michael@0: ClearOnShutdown(&gNetworkWorker); michael@0: michael@0: gNetworkUtils = new NetworkUtils(NetworkWorker::NotifyResult); michael@0: ClearOnShutdown(&gNetworkUtils); michael@0: } michael@0: michael@0: nsRefPtr worker = gNetworkWorker.get(); michael@0: return worker.forget(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: NetworkWorker::Start(nsINetworkEventListener* aListener) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(aListener); michael@0: michael@0: if (mListener) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult rv; michael@0: michael@0: rv = NS_NewNamedThread("NetworkWorker", getter_AddRefs(gWorkerThread)); michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("Can't create network control thread"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: StartNetd(new NetdMessageConsumer()); michael@0: mListener = aListener; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: NetworkWorker::Shutdown() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!mListener) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: StopNetd(); michael@0: michael@0: gWorkerThread->Shutdown(); michael@0: gWorkerThread = nullptr; michael@0: michael@0: mListener = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Receive command from main thread (NetworkService.js). michael@0: NS_IMETHODIMP michael@0: NetworkWorker::PostMessage(JS::Handle aOptions, JSContext* aCx) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: NetworkCommandOptions options; michael@0: if (!options.Init(aCx, aOptions)) { michael@0: NS_WARNING("Bad dictionary passed to NetworkWorker::SendCommand"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // Dispatch the command to the control thread. michael@0: NetworkParams NetworkParams(options); michael@0: nsCOMPtr runnable = new NetworkCommandDispatcher(NetworkParams); michael@0: if (gWorkerThread) { michael@0: gWorkerThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: NetworkWorker::DispatchNetworkResult(const NetworkResultOptions& aOptions) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: mozilla::AutoSafeJSContext cx; michael@0: JS::RootedValue val(cx); michael@0: michael@0: if (!aOptions.ToObject(cx, &val)) { michael@0: return; michael@0: } michael@0: michael@0: // Call the listener with a JS value. michael@0: if (mListener) { michael@0: mListener->OnEvent(val); michael@0: } michael@0: } michael@0: michael@0: // Callback function from network worker thread to update result on main thread. michael@0: void michael@0: NetworkWorker::NotifyResult(NetworkResultOptions& aResult) michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: michael@0: nsCOMPtr runnable = new NetworkResultDispatcher(aResult); michael@0: NS_DispatchToMainThread(runnable); michael@0: } michael@0: michael@0: NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(NetworkWorker, michael@0: NetworkWorker::FactoryCreate) michael@0: michael@0: NS_DEFINE_NAMED_CID(NS_NETWORKWORKER_CID); michael@0: michael@0: static const mozilla::Module::CIDEntry kNetworkWorkerCIDs[] = { michael@0: { &kNS_NETWORKWORKER_CID, false, nullptr, NetworkWorkerConstructor }, michael@0: { nullptr } michael@0: }; michael@0: michael@0: static const mozilla::Module::ContractIDEntry kNetworkWorkerContracts[] = { michael@0: { "@mozilla.org/network/worker;1", &kNS_NETWORKWORKER_CID }, michael@0: { nullptr } michael@0: }; michael@0: michael@0: static const mozilla::Module kNetworkWorkerModule = { michael@0: mozilla::Module::kVersion, michael@0: kNetworkWorkerCIDs, michael@0: kNetworkWorkerContracts, michael@0: nullptr michael@0: }; michael@0: michael@0: } // namespace mozilla michael@0: michael@0: NSMODULE_DEFN(NetworkWorkerModule) = &kNetworkWorkerModule;