Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set sw=2 ts=8 et ft=cpp : */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 : * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "Hal.h"
8 :
9 : #include "HalImpl.h"
10 : #include "HalLog.h"
11 : #include "HalSandbox.h"
12 : #include "nsIDOMWindow.h"
13 : #include "nsIDocument.h"
14 : #include "nsIDocShell.h"
15 : #include "nsITabChild.h"
16 : #include "nsIWebNavigation.h"
17 : #include "nsThreadUtils.h"
18 : #include "nsXULAppAPI.h"
19 : #include "nsPIDOMWindow.h"
20 : #include "nsJSUtils.h"
21 : #include "mozilla/ClearOnShutdown.h"
22 : #include "mozilla/Observer.h"
23 : #include "mozilla/Services.h"
24 : #include "mozilla/StaticPtr.h"
25 : #include "mozilla/dom/ContentChild.h"
26 : #include "mozilla/dom/ContentParent.h"
27 : #include "mozilla/dom/ScreenOrientation.h"
28 : #include "WindowIdentifier.h"
29 :
30 : #ifdef XP_WIN
31 : #include <process.h>
32 : #define getpid _getpid
33 : #endif
34 :
35 : using namespace mozilla::services;
36 : using namespace mozilla::dom;
37 :
38 : #define PROXY_IF_SANDBOXED(_call) \
39 : do { \
40 : if (InSandbox()) { \
41 : if (!hal_sandbox::HalChildDestroyed()) { \
42 : hal_sandbox::_call; \
43 : } \
44 : } else { \
45 : hal_impl::_call; \
46 : } \
47 : } while (0)
48 :
49 : #define RETURN_PROXY_IF_SANDBOXED(_call, defValue)\
50 : do { \
51 : if (InSandbox()) { \
52 : if (hal_sandbox::HalChildDestroyed()) { \
53 : return defValue; \
54 : } \
55 : return hal_sandbox::_call; \
56 : } else { \
57 : return hal_impl::_call; \
58 : } \
59 : } while (0)
60 :
61 : namespace mozilla {
62 : namespace hal {
63 :
64 : mozilla::LogModule *
65 0 : GetHalLog()
66 : {
67 : static mozilla::LazyLogModule sHalLog("hal");
68 0 : return sHalLog;
69 : }
70 :
71 : namespace {
72 :
73 : void
74 0 : AssertMainThread()
75 : {
76 0 : MOZ_ASSERT(NS_IsMainThread());
77 0 : }
78 :
79 : bool
80 0 : InSandbox()
81 : {
82 0 : return GeckoProcessType_Content == XRE_GetProcessType();
83 : }
84 :
85 : void
86 0 : AssertMainProcess()
87 : {
88 0 : MOZ_ASSERT(GeckoProcessType_Default == XRE_GetProcessType());
89 0 : }
90 :
91 : bool
92 0 : WindowIsActive(nsPIDOMWindowInner* aWindow)
93 : {
94 0 : nsIDocument* document = aWindow->GetDoc();
95 0 : NS_ENSURE_TRUE(document, false);
96 :
97 0 : return !document->Hidden();
98 : }
99 :
100 0 : StaticAutoPtr<WindowIdentifier::IDArrayType> gLastIDToVibrate;
101 :
102 0 : void InitLastIDToVibrate()
103 : {
104 0 : gLastIDToVibrate = new WindowIdentifier::IDArrayType();
105 0 : ClearOnShutdown(&gLastIDToVibrate);
106 0 : }
107 :
108 : } // namespace
109 :
110 : void
111 0 : Vibrate(const nsTArray<uint32_t>& pattern, nsPIDOMWindowInner* window)
112 : {
113 0 : Vibrate(pattern, WindowIdentifier(window));
114 0 : }
115 :
116 : void
117 0 : Vibrate(const nsTArray<uint32_t>& pattern, const WindowIdentifier &id)
118 : {
119 0 : AssertMainThread();
120 :
121 : // Only active windows may start vibrations. If |id| hasn't gone
122 : // through the IPC layer -- that is, if our caller is the outside
123 : // world, not hal_proxy -- check whether the window is active. If
124 : // |id| has gone through IPC, don't check the window's visibility;
125 : // only the window corresponding to the bottommost process has its
126 : // visibility state set correctly.
127 0 : if (!id.HasTraveledThroughIPC() && !WindowIsActive(id.GetWindow())) {
128 0 : HAL_LOG("Vibrate: Window is inactive, dropping vibrate.");
129 : return;
130 : }
131 :
132 0 : if (!InSandbox()) {
133 0 : if (!gLastIDToVibrate) {
134 0 : InitLastIDToVibrate();
135 : }
136 0 : *gLastIDToVibrate = id.AsArray();
137 : }
138 :
139 : // Don't forward our ID if we are not in the sandbox, because hal_impl
140 : // doesn't need it, and we don't want it to be tempted to read it. The
141 : // empty identifier will assert if it's used.
142 0 : PROXY_IF_SANDBOXED(Vibrate(pattern, InSandbox() ? id : WindowIdentifier()));
143 : }
144 :
145 : void
146 0 : CancelVibrate(nsPIDOMWindowInner* window)
147 : {
148 0 : CancelVibrate(WindowIdentifier(window));
149 0 : }
150 :
151 : void
152 0 : CancelVibrate(const WindowIdentifier &id)
153 : {
154 0 : AssertMainThread();
155 :
156 : // Although only active windows may start vibrations, a window may
157 : // cancel its own vibration even if it's no longer active.
158 : //
159 : // After a window is marked as inactive, it sends a CancelVibrate
160 : // request. We want this request to cancel a playing vibration
161 : // started by that window, so we certainly don't want to reject the
162 : // cancellation request because the window is now inactive.
163 : //
164 : // But it could be the case that, after this window became inactive,
165 : // some other window came along and started a vibration. We don't
166 : // want this window's cancellation request to cancel that window's
167 : // actively-playing vibration!
168 : //
169 : // To solve this problem, we keep track of the id of the last window
170 : // to start a vibration, and only accepts cancellation requests from
171 : // the same window. All other cancellation requests are ignored.
172 :
173 0 : if (InSandbox() || (gLastIDToVibrate && *gLastIDToVibrate == id.AsArray())) {
174 : // Don't forward our ID if we are not in the sandbox, because hal_impl
175 : // doesn't need it, and we don't want it to be tempted to read it. The
176 : // empty identifier will assert if it's used.
177 0 : PROXY_IF_SANDBOXED(CancelVibrate(InSandbox() ? id : WindowIdentifier()));
178 : }
179 0 : }
180 :
181 : template <class InfoType>
182 : class ObserversManager
183 : {
184 : public:
185 0 : void AddObserver(Observer<InfoType>* aObserver) {
186 0 : if (!mObservers) {
187 0 : mObservers = new mozilla::ObserverList<InfoType>();
188 : }
189 :
190 0 : mObservers->AddObserver(aObserver);
191 :
192 0 : if (mObservers->Length() == 1) {
193 0 : EnableNotifications();
194 : }
195 0 : }
196 :
197 0 : void RemoveObserver(Observer<InfoType>* aObserver) {
198 0 : bool removed = mObservers && mObservers->RemoveObserver(aObserver);
199 0 : if (!removed) {
200 : return;
201 : }
202 :
203 0 : if (mObservers->Length() == 0) {
204 0 : DisableNotifications();
205 :
206 0 : OnNotificationsDisabled();
207 :
208 0 : delete mObservers;
209 0 : mObservers = nullptr;
210 : }
211 : }
212 :
213 0 : void BroadcastInformation(const InfoType& aInfo) {
214 : // It is possible for mObservers to be nullptr here on some platforms,
215 : // because a call to BroadcastInformation gets queued up asynchronously
216 : // while RemoveObserver is running (and before the notifications are
217 : // disabled). The queued call can then get run after mObservers has
218 : // been nulled out. See bug 757025.
219 0 : if (!mObservers) {
220 : return;
221 : }
222 0 : mObservers->Broadcast(aInfo);
223 : }
224 :
225 : protected:
226 : virtual void EnableNotifications() = 0;
227 : virtual void DisableNotifications() = 0;
228 0 : virtual void OnNotificationsDisabled() {}
229 :
230 : private:
231 : mozilla::ObserverList<InfoType>* mObservers;
232 : };
233 :
234 : template <class InfoType>
235 0 : class CachingObserversManager : public ObserversManager<InfoType>
236 : {
237 : public:
238 0 : InfoType GetCurrentInformation() {
239 0 : if (mHasValidCache) {
240 0 : return mInfo;
241 : }
242 :
243 0 : GetCurrentInformationInternal(&mInfo);
244 0 : mHasValidCache = true;
245 0 : return mInfo;
246 : }
247 :
248 0 : void CacheInformation(const InfoType& aInfo) {
249 0 : mHasValidCache = true;
250 0 : mInfo = aInfo;
251 0 : }
252 :
253 0 : void BroadcastCachedInformation() {
254 0 : this->BroadcastInformation(mInfo);
255 0 : }
256 :
257 : protected:
258 : virtual void GetCurrentInformationInternal(InfoType*) = 0;
259 :
260 0 : void OnNotificationsDisabled() override {
261 0 : mHasValidCache = false;
262 0 : }
263 :
264 : private:
265 : InfoType mInfo;
266 : bool mHasValidCache;
267 : };
268 :
269 0 : class BatteryObserversManager : public CachingObserversManager<BatteryInformation>
270 : {
271 : protected:
272 0 : void EnableNotifications() override {
273 0 : PROXY_IF_SANDBOXED(EnableBatteryNotifications());
274 0 : }
275 :
276 0 : void DisableNotifications() override {
277 0 : PROXY_IF_SANDBOXED(DisableBatteryNotifications());
278 0 : }
279 :
280 0 : void GetCurrentInformationInternal(BatteryInformation* aInfo) override {
281 0 : PROXY_IF_SANDBOXED(GetCurrentBatteryInformation(aInfo));
282 0 : }
283 : };
284 :
285 : static BatteryObserversManager&
286 0 : BatteryObservers()
287 : {
288 0 : static BatteryObserversManager sBatteryObservers;
289 0 : AssertMainThread();
290 0 : return sBatteryObservers;
291 : }
292 :
293 0 : class NetworkObserversManager : public CachingObserversManager<NetworkInformation>
294 : {
295 : protected:
296 0 : void EnableNotifications() override {
297 0 : PROXY_IF_SANDBOXED(EnableNetworkNotifications());
298 0 : }
299 :
300 0 : void DisableNotifications() override {
301 0 : PROXY_IF_SANDBOXED(DisableNetworkNotifications());
302 0 : }
303 :
304 0 : void GetCurrentInformationInternal(NetworkInformation* aInfo) override {
305 0 : PROXY_IF_SANDBOXED(GetCurrentNetworkInformation(aInfo));
306 0 : }
307 : };
308 :
309 : static NetworkObserversManager&
310 0 : NetworkObservers()
311 : {
312 0 : static NetworkObserversManager sNetworkObservers;
313 0 : AssertMainThread();
314 0 : return sNetworkObservers;
315 : }
316 :
317 0 : class WakeLockObserversManager : public ObserversManager<WakeLockInformation>
318 : {
319 : protected:
320 0 : void EnableNotifications() override {
321 0 : PROXY_IF_SANDBOXED(EnableWakeLockNotifications());
322 0 : }
323 :
324 0 : void DisableNotifications() override {
325 0 : PROXY_IF_SANDBOXED(DisableWakeLockNotifications());
326 0 : }
327 : };
328 :
329 : static WakeLockObserversManager&
330 0 : WakeLockObservers()
331 : {
332 0 : static WakeLockObserversManager sWakeLockObservers;
333 0 : AssertMainThread();
334 0 : return sWakeLockObservers;
335 : }
336 :
337 0 : class ScreenConfigurationObserversManager : public CachingObserversManager<ScreenConfiguration>
338 : {
339 : protected:
340 0 : void EnableNotifications() override {
341 0 : PROXY_IF_SANDBOXED(EnableScreenConfigurationNotifications());
342 0 : }
343 :
344 0 : void DisableNotifications() override {
345 0 : PROXY_IF_SANDBOXED(DisableScreenConfigurationNotifications());
346 0 : }
347 :
348 0 : void GetCurrentInformationInternal(ScreenConfiguration* aInfo) override {
349 0 : PROXY_IF_SANDBOXED(GetCurrentScreenConfiguration(aInfo));
350 0 : }
351 : };
352 :
353 : static ScreenConfigurationObserversManager&
354 0 : ScreenConfigurationObservers()
355 : {
356 0 : AssertMainThread();
357 0 : static ScreenConfigurationObserversManager sScreenConfigurationObservers;
358 0 : return sScreenConfigurationObservers;
359 : }
360 :
361 : void
362 0 : RegisterBatteryObserver(BatteryObserver* aObserver)
363 : {
364 0 : AssertMainThread();
365 0 : BatteryObservers().AddObserver(aObserver);
366 0 : }
367 :
368 : void
369 0 : UnregisterBatteryObserver(BatteryObserver* aObserver)
370 : {
371 0 : AssertMainThread();
372 0 : BatteryObservers().RemoveObserver(aObserver);
373 0 : }
374 :
375 : void
376 0 : GetCurrentBatteryInformation(BatteryInformation* aInfo)
377 : {
378 0 : AssertMainThread();
379 0 : *aInfo = BatteryObservers().GetCurrentInformation();
380 0 : }
381 :
382 : void
383 0 : NotifyBatteryChange(const BatteryInformation& aInfo)
384 : {
385 0 : AssertMainThread();
386 0 : BatteryObservers().CacheInformation(aInfo);
387 0 : BatteryObservers().BroadcastCachedInformation();
388 0 : }
389 :
390 : void
391 0 : EnableSensorNotifications(SensorType aSensor) {
392 0 : AssertMainThread();
393 0 : PROXY_IF_SANDBOXED(EnableSensorNotifications(aSensor));
394 0 : }
395 :
396 : void
397 0 : DisableSensorNotifications(SensorType aSensor) {
398 0 : AssertMainThread();
399 0 : PROXY_IF_SANDBOXED(DisableSensorNotifications(aSensor));
400 0 : }
401 :
402 : typedef mozilla::ObserverList<SensorData> SensorObserverList;
403 : static SensorObserverList* gSensorObservers = nullptr;
404 :
405 : static SensorObserverList &
406 0 : GetSensorObservers(SensorType sensor_type) {
407 0 : MOZ_ASSERT(sensor_type < NUM_SENSOR_TYPE);
408 :
409 0 : if(!gSensorObservers) {
410 0 : gSensorObservers = new SensorObserverList[NUM_SENSOR_TYPE];
411 : }
412 0 : return gSensorObservers[sensor_type];
413 : }
414 :
415 : void
416 0 : RegisterSensorObserver(SensorType aSensor, ISensorObserver *aObserver) {
417 0 : SensorObserverList &observers = GetSensorObservers(aSensor);
418 :
419 0 : AssertMainThread();
420 :
421 0 : observers.AddObserver(aObserver);
422 0 : if(observers.Length() == 1) {
423 0 : EnableSensorNotifications(aSensor);
424 : }
425 0 : }
426 :
427 : void
428 0 : UnregisterSensorObserver(SensorType aSensor, ISensorObserver *aObserver) {
429 0 : AssertMainThread();
430 :
431 0 : if (!gSensorObservers) {
432 : return;
433 : }
434 :
435 0 : SensorObserverList &observers = GetSensorObservers(aSensor);
436 0 : if (!observers.RemoveObserver(aObserver) || observers.Length() > 0) {
437 : return;
438 : }
439 0 : DisableSensorNotifications(aSensor);
440 :
441 0 : for (int i = 0; i < NUM_SENSOR_TYPE; i++) {
442 0 : if (gSensorObservers[i].Length() > 0) {
443 : return;
444 : }
445 : }
446 :
447 : // We want to destroy gSensorObservers if all observer lists are
448 : // empty, but we have to defer the deallocation via a runnable to
449 : // mainthread (since we may be inside NotifySensorChange()/Broadcast()
450 : // when it calls UnregisterSensorObserver()).
451 0 : SensorObserverList* sensorlists = gSensorObservers;
452 0 : gSensorObservers = nullptr;
453 :
454 : // Unlike DispatchToMainThread, DispatchToCurrentThread doesn't leak a runnable if
455 : // it fails (and we assert we're on MainThread).
456 0 : if (NS_FAILED(NS_DispatchToCurrentThread(NS_NewRunnableFunction("UnregisterSensorObserver",
457 : [sensorlists]() -> void {
458 : delete [] sensorlists;
459 : }))))
460 : {
461 : // Still need to delete sensorlists if the dispatch fails
462 0 : delete [] sensorlists;
463 : }
464 : }
465 :
466 : void
467 0 : NotifySensorChange(const SensorData &aSensorData) {
468 0 : SensorObserverList &observers = GetSensorObservers(aSensorData.sensor());
469 :
470 0 : AssertMainThread();
471 :
472 0 : observers.Broadcast(aSensorData);
473 0 : }
474 :
475 : void
476 0 : RegisterNetworkObserver(NetworkObserver* aObserver)
477 : {
478 0 : AssertMainThread();
479 0 : NetworkObservers().AddObserver(aObserver);
480 0 : }
481 :
482 : void
483 0 : UnregisterNetworkObserver(NetworkObserver* aObserver)
484 : {
485 0 : AssertMainThread();
486 0 : NetworkObservers().RemoveObserver(aObserver);
487 0 : }
488 :
489 : void
490 0 : GetCurrentNetworkInformation(NetworkInformation* aInfo)
491 : {
492 0 : AssertMainThread();
493 0 : *aInfo = NetworkObservers().GetCurrentInformation();
494 0 : }
495 :
496 : void
497 0 : NotifyNetworkChange(const NetworkInformation& aInfo)
498 : {
499 0 : NetworkObservers().CacheInformation(aInfo);
500 0 : NetworkObservers().BroadcastCachedInformation();
501 0 : }
502 :
503 : void
504 0 : RegisterWakeLockObserver(WakeLockObserver* aObserver)
505 : {
506 0 : AssertMainThread();
507 0 : WakeLockObservers().AddObserver(aObserver);
508 0 : }
509 :
510 : void
511 0 : UnregisterWakeLockObserver(WakeLockObserver* aObserver)
512 : {
513 0 : AssertMainThread();
514 0 : WakeLockObservers().RemoveObserver(aObserver);
515 0 : }
516 :
517 : void
518 0 : ModifyWakeLock(const nsAString& aTopic,
519 : WakeLockControl aLockAdjust,
520 : WakeLockControl aHiddenAdjust,
521 : uint64_t aProcessID /* = CONTENT_PROCESS_ID_UNKNOWN */)
522 : {
523 0 : AssertMainThread();
524 :
525 0 : if (aProcessID == CONTENT_PROCESS_ID_UNKNOWN) {
526 0 : aProcessID = InSandbox() ? ContentChild::GetSingleton()->GetID() :
527 : CONTENT_PROCESS_ID_MAIN;
528 : }
529 :
530 0 : PROXY_IF_SANDBOXED(ModifyWakeLock(aTopic, aLockAdjust,
531 : aHiddenAdjust, aProcessID));
532 0 : }
533 :
534 : void
535 0 : GetWakeLockInfo(const nsAString& aTopic, WakeLockInformation* aWakeLockInfo)
536 : {
537 0 : AssertMainThread();
538 0 : PROXY_IF_SANDBOXED(GetWakeLockInfo(aTopic, aWakeLockInfo));
539 0 : }
540 :
541 : void
542 0 : NotifyWakeLockChange(const WakeLockInformation& aInfo)
543 : {
544 0 : AssertMainThread();
545 0 : WakeLockObservers().BroadcastInformation(aInfo);
546 0 : }
547 :
548 : void
549 0 : RegisterScreenConfigurationObserver(ScreenConfigurationObserver* aObserver)
550 : {
551 0 : AssertMainThread();
552 0 : ScreenConfigurationObservers().AddObserver(aObserver);
553 0 : }
554 :
555 : void
556 0 : UnregisterScreenConfigurationObserver(ScreenConfigurationObserver* aObserver)
557 : {
558 0 : AssertMainThread();
559 0 : ScreenConfigurationObservers().RemoveObserver(aObserver);
560 0 : }
561 :
562 : void
563 0 : GetCurrentScreenConfiguration(ScreenConfiguration* aScreenConfiguration)
564 : {
565 0 : AssertMainThread();
566 0 : *aScreenConfiguration = ScreenConfigurationObservers().GetCurrentInformation();
567 0 : }
568 :
569 : void
570 0 : NotifyScreenConfigurationChange(const ScreenConfiguration& aScreenConfiguration)
571 : {
572 0 : ScreenConfigurationObservers().CacheInformation(aScreenConfiguration);
573 0 : ScreenConfigurationObservers().BroadcastCachedInformation();
574 0 : }
575 :
576 : bool
577 0 : LockScreenOrientation(const dom::ScreenOrientationInternal& aOrientation)
578 : {
579 0 : AssertMainThread();
580 0 : RETURN_PROXY_IF_SANDBOXED(LockScreenOrientation(aOrientation), false);
581 : }
582 :
583 : void
584 0 : UnlockScreenOrientation()
585 : {
586 0 : AssertMainThread();
587 0 : PROXY_IF_SANDBOXED(UnlockScreenOrientation());
588 0 : }
589 :
590 : bool
591 0 : SetProcessPrioritySupported()
592 : {
593 0 : RETURN_PROXY_IF_SANDBOXED(SetProcessPrioritySupported(), false);
594 : }
595 :
596 : void
597 0 : SetProcessPriority(int aPid, ProcessPriority aPriority)
598 : {
599 : // n.b. The sandboxed implementation crashes; SetProcessPriority works only
600 : // from the main process.
601 0 : PROXY_IF_SANDBOXED(SetProcessPriority(aPid, aPriority));
602 0 : }
603 :
604 : // From HalTypes.h.
605 : const char*
606 0 : ProcessPriorityToString(ProcessPriority aPriority)
607 : {
608 0 : switch (aPriority) {
609 : case PROCESS_PRIORITY_MASTER:
610 : return "MASTER";
611 : case PROCESS_PRIORITY_PREALLOC:
612 0 : return "PREALLOC";
613 : case PROCESS_PRIORITY_FOREGROUND_HIGH:
614 0 : return "FOREGROUND_HIGH";
615 : case PROCESS_PRIORITY_FOREGROUND:
616 0 : return "FOREGROUND";
617 : case PROCESS_PRIORITY_FOREGROUND_KEYBOARD:
618 0 : return "FOREGROUND_KEYBOARD";
619 : case PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE:
620 0 : return "BACKGROUND_PERCEIVABLE";
621 : case PROCESS_PRIORITY_BACKGROUND:
622 0 : return "BACKGROUND";
623 : case PROCESS_PRIORITY_UNKNOWN:
624 0 : return "UNKNOWN";
625 : default:
626 0 : MOZ_ASSERT(false);
627 : return "???";
628 : }
629 : }
630 :
631 : void
632 0 : StartDiskSpaceWatcher()
633 : {
634 0 : AssertMainProcess();
635 0 : AssertMainThread();
636 0 : PROXY_IF_SANDBOXED(StartDiskSpaceWatcher());
637 0 : }
638 :
639 : void
640 0 : StopDiskSpaceWatcher()
641 : {
642 0 : AssertMainProcess();
643 0 : AssertMainThread();
644 0 : PROXY_IF_SANDBOXED(StopDiskSpaceWatcher());
645 0 : }
646 :
647 : } // namespace hal
648 1 : } // namespace mozilla
|