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 "DiskSpaceWatcher.h"
6 #include "nsIObserverService.h"
7 #include "nsXULAppAPI.h"
8 #include "mozilla/Hal.h"
9 #include "mozilla/ModuleUtils.h"
10 #include "mozilla/Preferences.h"
11 #include "mozilla/ClearOnShutdown.h"
12 #include "mozilla/Services.h"
13 
14 #define NS_DISKSPACEWATCHER_CID \
15   { 0xab218518, 0xf197, 0x4fb4, { 0x8b, 0x0f, 0x8b, 0xb3, 0x4d, 0xf2, 0x4b, 0xf4 } }
16 
17 using namespace mozilla;
18 
19 StaticRefPtr<DiskSpaceWatcher> gDiskSpaceWatcher;
20 
21 NS_IMPL_ISUPPORTS(DiskSpaceWatcher, nsIDiskSpaceWatcher, nsIObserver)
22 
23 uint64_t DiskSpaceWatcher::sFreeSpace = 0;
24 bool DiskSpaceWatcher::sIsDiskFull = false;
25 
DiskSpaceWatcher()26 DiskSpaceWatcher::DiskSpaceWatcher()
27 {
28   MOZ_ASSERT(NS_IsMainThread());
29   MOZ_ASSERT(!gDiskSpaceWatcher);
30 }
31 
~DiskSpaceWatcher()32 DiskSpaceWatcher::~DiskSpaceWatcher()
33 {
34   MOZ_ASSERT(!gDiskSpaceWatcher);
35 }
36 
37 already_AddRefed<DiskSpaceWatcher>
FactoryCreate()38 DiskSpaceWatcher::FactoryCreate()
39 {
40   if (!XRE_IsParentProcess()) {
41     return nullptr;
42   }
43 
44   MOZ_ASSERT(NS_IsMainThread());
45 
46   if (!Preferences::GetBool("disk_space_watcher.enabled", false)) {
47     return nullptr;
48   }
49 
50   if (!gDiskSpaceWatcher) {
51     gDiskSpaceWatcher = new DiskSpaceWatcher();
52     ClearOnShutdown(&gDiskSpaceWatcher);
53   }
54 
55   RefPtr<DiskSpaceWatcher> service = gDiskSpaceWatcher.get();
56   return service.forget();
57 }
58 
59 NS_IMETHODIMP
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)60 DiskSpaceWatcher::Observe(nsISupports* aSubject, const char* aTopic,
61                           const char16_t* aData)
62 {
63   MOZ_ASSERT(NS_IsMainThread());
64 
65   if (!strcmp(aTopic, "profile-after-change")) {
66     nsCOMPtr<nsIObserverService> observerService =
67       mozilla::services::GetObserverService();
68     observerService->AddObserver(this, "profile-before-change", false);
69     mozilla::hal::StartDiskSpaceWatcher();
70     return NS_OK;
71   }
72 
73   if (!strcmp(aTopic, "profile-before-change")) {
74     nsCOMPtr<nsIObserverService> observerService =
75       mozilla::services::GetObserverService();
76     observerService->RemoveObserver(this, "profile-before-change");
77     mozilla::hal::StopDiskSpaceWatcher();
78     return NS_OK;
79   }
80 
81   MOZ_ASSERT(false, "DiskSpaceWatcher got unexpected topic!");
82   return NS_ERROR_UNEXPECTED;
83 }
84 
GetIsDiskFull(bool * aIsDiskFull)85 NS_IMETHODIMP DiskSpaceWatcher::GetIsDiskFull(bool* aIsDiskFull)
86 {
87   *aIsDiskFull = sIsDiskFull;
88   return NS_OK;
89 }
90 
91 // GetFreeSpace is a macro on windows, and that messes up with the c++
92 // compiler.
93 #ifdef XP_WIN
94 #undef GetFreeSpace
95 #endif
GetFreeSpace(uint64_t * aFreeSpace)96 NS_IMETHODIMP DiskSpaceWatcher::GetFreeSpace(uint64_t* aFreeSpace)
97 {
98   *aFreeSpace = sFreeSpace;
99   return NS_OK;
100 }
101 
102 // static
UpdateState(bool aIsDiskFull,uint64_t aFreeSpace)103 void DiskSpaceWatcher::UpdateState(bool aIsDiskFull, uint64_t aFreeSpace)
104 {
105   MOZ_ASSERT(NS_IsMainThread());
106   if (!gDiskSpaceWatcher) {
107     return;
108   }
109 
110   nsCOMPtr<nsIObserverService> observerService =
111     mozilla::services::GetObserverService();
112 
113   sIsDiskFull = aIsDiskFull;
114   sFreeSpace = aFreeSpace;
115 
116   if (!observerService) {
117     return;
118   }
119 
120   const char16_t stateFull[] = { 'f', 'u', 'l', 'l', 0 };
121   const char16_t stateFree[] = { 'f', 'r', 'e', 'e', 0 };
122 
123   nsCOMPtr<nsISupports> subject;
124   CallQueryInterface(gDiskSpaceWatcher.get(), getter_AddRefs(subject));
125   MOZ_ASSERT(subject);
126   observerService->NotifyObservers(subject,
127                                    DISKSPACEWATCHER_OBSERVER_TOPIC,
128                                    sIsDiskFull ? stateFull : stateFree);
129   return;
130 }
131 
132 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(DiskSpaceWatcher,
133                                          DiskSpaceWatcher::FactoryCreate)
134 
135 NS_DEFINE_NAMED_CID(NS_DISKSPACEWATCHER_CID);
136 
137 static const mozilla::Module::CIDEntry kDiskSpaceWatcherCIDs[] = {
138   { &kNS_DISKSPACEWATCHER_CID, false, nullptr, DiskSpaceWatcherConstructor },
139   { nullptr }
140 };
141 
142 static const mozilla::Module::ContractIDEntry kDiskSpaceWatcherContracts[] = {
143   { "@mozilla.org/toolkit/disk-space-watcher;1", &kNS_DISKSPACEWATCHER_CID },
144   { nullptr }
145 };
146 
147 static const mozilla::Module::CategoryEntry kDiskSpaceWatcherCategories[] = {
148 #ifdef MOZ_WIDGET_GONK
149   { "profile-after-change", "Disk Space Watcher Service", DISKSPACEWATCHER_CONTRACTID },
150 #endif
151   { nullptr }
152 };
153 
154 static const mozilla::Module kDiskSpaceWatcherModule = {
155   mozilla::Module::kVersion,
156   kDiskSpaceWatcherCIDs,
157   kDiskSpaceWatcherContracts,
158   kDiskSpaceWatcherCategories
159 };
160 
161 NSMODULE_DEFN(DiskSpaceWatcherModule) = &kDiskSpaceWatcherModule;
162