|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 #include "NetworkWorker.h" |
|
6 #include "NetworkUtils.h" |
|
7 #include <nsThreadUtils.h> |
|
8 #include "mozilla/ModuleUtils.h" |
|
9 #include "mozilla/ClearOnShutdown.h" |
|
10 #include "nsXULAppAPI.h" |
|
11 #include "nsCxPusher.h" |
|
12 |
|
13 #define NS_NETWORKWORKER_CID \ |
|
14 { 0x6df093e1, 0x8127, 0x4fa7, {0x90, 0x13, 0xa3, 0xaa, 0xa7, 0x79, 0xbb, 0xdd} } |
|
15 |
|
16 using namespace mozilla; |
|
17 using namespace mozilla::dom; |
|
18 using namespace mozilla::ipc; |
|
19 |
|
20 namespace mozilla { |
|
21 |
|
22 nsCOMPtr<nsIThread> gWorkerThread; |
|
23 |
|
24 // The singleton network worker, to be used on the main thread. |
|
25 StaticRefPtr<NetworkWorker> gNetworkWorker; |
|
26 |
|
27 // The singleton networkutils class, that can be used on any thread. |
|
28 static nsAutoPtr<NetworkUtils> gNetworkUtils; |
|
29 |
|
30 // Runnable used dispatch command result on the main thread. |
|
31 class NetworkResultDispatcher : public nsRunnable |
|
32 { |
|
33 public: |
|
34 NetworkResultDispatcher(const NetworkResultOptions& aResult) |
|
35 { |
|
36 MOZ_ASSERT(!NS_IsMainThread()); |
|
37 |
|
38 #define COPY_FIELD(prop) mResult.prop = aResult.prop; |
|
39 COPY_FIELD(mId) |
|
40 COPY_FIELD(mRet) |
|
41 COPY_FIELD(mBroadcast) |
|
42 COPY_FIELD(mTopic) |
|
43 COPY_FIELD(mReason) |
|
44 COPY_FIELD(mResultCode) |
|
45 COPY_FIELD(mResultReason) |
|
46 COPY_FIELD(mError) |
|
47 COPY_FIELD(mRxBytes) |
|
48 COPY_FIELD(mTxBytes) |
|
49 COPY_FIELD(mDate) |
|
50 COPY_FIELD(mEnable) |
|
51 COPY_FIELD(mResult) |
|
52 COPY_FIELD(mSuccess) |
|
53 COPY_FIELD(mCurExternalIfname) |
|
54 COPY_FIELD(mCurInternalIfname) |
|
55 #undef COPY_FIELD |
|
56 } |
|
57 |
|
58 NS_IMETHOD Run() |
|
59 { |
|
60 MOZ_ASSERT(NS_IsMainThread()); |
|
61 |
|
62 if (gNetworkWorker) { |
|
63 gNetworkWorker->DispatchNetworkResult(mResult); |
|
64 } |
|
65 return NS_OK; |
|
66 } |
|
67 private: |
|
68 NetworkResultOptions mResult; |
|
69 }; |
|
70 |
|
71 // Runnable used dispatch netd command on the worker thread. |
|
72 class NetworkCommandDispatcher : public nsRunnable |
|
73 { |
|
74 public: |
|
75 NetworkCommandDispatcher(const NetworkParams& aParams) |
|
76 : mParams(aParams) |
|
77 { |
|
78 MOZ_ASSERT(NS_IsMainThread()); |
|
79 } |
|
80 |
|
81 NS_IMETHOD Run() |
|
82 { |
|
83 MOZ_ASSERT(!NS_IsMainThread()); |
|
84 |
|
85 if (gNetworkUtils) { |
|
86 gNetworkUtils->ExecuteCommand(mParams); |
|
87 } |
|
88 return NS_OK; |
|
89 } |
|
90 private: |
|
91 NetworkParams mParams; |
|
92 }; |
|
93 |
|
94 // Runnable used dispatch netd result on the worker thread. |
|
95 class NetdEventRunnable : public nsRunnable |
|
96 { |
|
97 public: |
|
98 NetdEventRunnable(NetdCommand* aCommand) |
|
99 : mCommand(aCommand) |
|
100 { |
|
101 MOZ_ASSERT(!NS_IsMainThread()); |
|
102 } |
|
103 |
|
104 NS_IMETHOD Run() |
|
105 { |
|
106 MOZ_ASSERT(!NS_IsMainThread()); |
|
107 |
|
108 if (gNetworkUtils) { |
|
109 gNetworkUtils->onNetdMessage(mCommand); |
|
110 } |
|
111 return NS_OK; |
|
112 } |
|
113 |
|
114 private: |
|
115 nsAutoPtr<NetdCommand> mCommand; |
|
116 }; |
|
117 |
|
118 class NetdMessageConsumer : public NetdConsumer |
|
119 { |
|
120 public: |
|
121 NetdMessageConsumer() |
|
122 { |
|
123 MOZ_ASSERT(NS_IsMainThread()); |
|
124 } |
|
125 |
|
126 void MessageReceived(NetdCommand* aCommand) |
|
127 { |
|
128 MOZ_ASSERT(!NS_IsMainThread()); |
|
129 |
|
130 nsCOMPtr<nsIRunnable> runnable = new NetdEventRunnable(aCommand); |
|
131 if (gWorkerThread) { |
|
132 gWorkerThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL); |
|
133 } |
|
134 } |
|
135 }; |
|
136 |
|
137 NS_IMPL_ISUPPORTS(NetworkWorker, nsINetworkWorker) |
|
138 |
|
139 NetworkWorker::NetworkWorker() |
|
140 { |
|
141 MOZ_ASSERT(NS_IsMainThread()); |
|
142 MOZ_ASSERT(!gNetworkWorker); |
|
143 } |
|
144 |
|
145 NetworkWorker::~NetworkWorker() |
|
146 { |
|
147 MOZ_ASSERT(!gNetworkWorker); |
|
148 MOZ_ASSERT(!mListener); |
|
149 } |
|
150 |
|
151 already_AddRefed<NetworkWorker> |
|
152 NetworkWorker::FactoryCreate() |
|
153 { |
|
154 if (XRE_GetProcessType() != GeckoProcessType_Default) { |
|
155 return nullptr; |
|
156 } |
|
157 |
|
158 MOZ_ASSERT(NS_IsMainThread()); |
|
159 |
|
160 if (!gNetworkWorker) { |
|
161 gNetworkWorker = new NetworkWorker(); |
|
162 ClearOnShutdown(&gNetworkWorker); |
|
163 |
|
164 gNetworkUtils = new NetworkUtils(NetworkWorker::NotifyResult); |
|
165 ClearOnShutdown(&gNetworkUtils); |
|
166 } |
|
167 |
|
168 nsRefPtr<NetworkWorker> worker = gNetworkWorker.get(); |
|
169 return worker.forget(); |
|
170 } |
|
171 |
|
172 NS_IMETHODIMP |
|
173 NetworkWorker::Start(nsINetworkEventListener* aListener) |
|
174 { |
|
175 MOZ_ASSERT(NS_IsMainThread()); |
|
176 MOZ_ASSERT(aListener); |
|
177 |
|
178 if (mListener) { |
|
179 return NS_OK; |
|
180 } |
|
181 |
|
182 nsresult rv; |
|
183 |
|
184 rv = NS_NewNamedThread("NetworkWorker", getter_AddRefs(gWorkerThread)); |
|
185 if (NS_FAILED(rv)) { |
|
186 NS_WARNING("Can't create network control thread"); |
|
187 return NS_ERROR_FAILURE; |
|
188 } |
|
189 |
|
190 StartNetd(new NetdMessageConsumer()); |
|
191 mListener = aListener; |
|
192 |
|
193 return NS_OK; |
|
194 } |
|
195 |
|
196 NS_IMETHODIMP |
|
197 NetworkWorker::Shutdown() |
|
198 { |
|
199 MOZ_ASSERT(NS_IsMainThread()); |
|
200 |
|
201 if (!mListener) { |
|
202 return NS_OK; |
|
203 } |
|
204 |
|
205 StopNetd(); |
|
206 |
|
207 gWorkerThread->Shutdown(); |
|
208 gWorkerThread = nullptr; |
|
209 |
|
210 mListener = nullptr; |
|
211 return NS_OK; |
|
212 } |
|
213 |
|
214 // Receive command from main thread (NetworkService.js). |
|
215 NS_IMETHODIMP |
|
216 NetworkWorker::PostMessage(JS::Handle<JS::Value> aOptions, JSContext* aCx) |
|
217 { |
|
218 MOZ_ASSERT(NS_IsMainThread()); |
|
219 |
|
220 NetworkCommandOptions options; |
|
221 if (!options.Init(aCx, aOptions)) { |
|
222 NS_WARNING("Bad dictionary passed to NetworkWorker::SendCommand"); |
|
223 return NS_ERROR_FAILURE; |
|
224 } |
|
225 |
|
226 // Dispatch the command to the control thread. |
|
227 NetworkParams NetworkParams(options); |
|
228 nsCOMPtr<nsIRunnable> runnable = new NetworkCommandDispatcher(NetworkParams); |
|
229 if (gWorkerThread) { |
|
230 gWorkerThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL); |
|
231 } |
|
232 return NS_OK; |
|
233 } |
|
234 |
|
235 void |
|
236 NetworkWorker::DispatchNetworkResult(const NetworkResultOptions& aOptions) |
|
237 { |
|
238 MOZ_ASSERT(NS_IsMainThread()); |
|
239 |
|
240 mozilla::AutoSafeJSContext cx; |
|
241 JS::RootedValue val(cx); |
|
242 |
|
243 if (!aOptions.ToObject(cx, &val)) { |
|
244 return; |
|
245 } |
|
246 |
|
247 // Call the listener with a JS value. |
|
248 if (mListener) { |
|
249 mListener->OnEvent(val); |
|
250 } |
|
251 } |
|
252 |
|
253 // Callback function from network worker thread to update result on main thread. |
|
254 void |
|
255 NetworkWorker::NotifyResult(NetworkResultOptions& aResult) |
|
256 { |
|
257 MOZ_ASSERT(!NS_IsMainThread()); |
|
258 |
|
259 nsCOMPtr<nsIRunnable> runnable = new NetworkResultDispatcher(aResult); |
|
260 NS_DispatchToMainThread(runnable); |
|
261 } |
|
262 |
|
263 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(NetworkWorker, |
|
264 NetworkWorker::FactoryCreate) |
|
265 |
|
266 NS_DEFINE_NAMED_CID(NS_NETWORKWORKER_CID); |
|
267 |
|
268 static const mozilla::Module::CIDEntry kNetworkWorkerCIDs[] = { |
|
269 { &kNS_NETWORKWORKER_CID, false, nullptr, NetworkWorkerConstructor }, |
|
270 { nullptr } |
|
271 }; |
|
272 |
|
273 static const mozilla::Module::ContractIDEntry kNetworkWorkerContracts[] = { |
|
274 { "@mozilla.org/network/worker;1", &kNS_NETWORKWORKER_CID }, |
|
275 { nullptr } |
|
276 }; |
|
277 |
|
278 static const mozilla::Module kNetworkWorkerModule = { |
|
279 mozilla::Module::kVersion, |
|
280 kNetworkWorkerCIDs, |
|
281 kNetworkWorkerContracts, |
|
282 nullptr |
|
283 }; |
|
284 |
|
285 } // namespace mozilla |
|
286 |
|
287 NSMODULE_DEFN(NetworkWorkerModule) = &kNetworkWorkerModule; |