Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "base/message_loop.h"
6 #include "jsapi.h"
7 #include "mozilla/Attributes.h"
8 #include "mozilla/ClearOnShutdown.h"
9 #include "mozilla/Hal.h"
10 #include "mozilla/Services.h"
11 #include "mozilla/StaticPtr.h"
12 #include "nsCOMPtr.h"
13 #include "nsDebug.h"
14 #include "nsIObserver.h"
15 #include "nsIObserverService.h"
16 #include "nsISettingsService.h"
17 #include "nsJSUtils.h"
18 #include "nsServiceManagerUtils.h"
19 #include "nsString.h"
20 #include "TimeZoneSettingObserver.h"
21 #include "xpcpublic.h"
22 #include "nsContentUtils.h"
23 #include "nsCxPusher.h"
24 #include "nsPrintfCString.h"
26 #undef LOG
27 #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Time Zone Setting" , ## args)
28 #define ERR(args...) __android_log_print(ANDROID_LOG_ERROR, "Time Zone Setting" , ## args)
30 #define TIME_TIMEZONE "time.timezone"
31 #define MOZSETTINGS_CHANGED "mozsettings-changed"
33 using namespace mozilla;
35 namespace {
37 class TimeZoneSettingObserver : public nsIObserver
38 {
39 public:
40 NS_DECL_ISUPPORTS
41 NS_DECL_NSIOBSERVER
43 TimeZoneSettingObserver();
44 virtual ~TimeZoneSettingObserver();
45 static nsresult SetTimeZone(const JS::Value &aValue, JSContext *aContext);
46 };
48 class TimeZoneSettingCb MOZ_FINAL : public nsISettingsServiceCallback
49 {
50 public:
51 NS_DECL_ISUPPORTS
53 TimeZoneSettingCb() {}
55 NS_IMETHOD Handle(const nsAString &aName, JS::Handle<JS::Value> aResult) {
57 JSContext *cx = nsContentUtils::GetCurrentJSContext();
58 NS_ENSURE_TRUE(cx, NS_OK);
60 // If we don't have time.timezone value in the settings, we need
61 // to initialize the settings based on the current system timezone
62 // to make settings consistent with system. This usually happens
63 // at the very first boot. After that, settings must have a value.
64 if (aResult.isNull()) {
65 // Get the current system time zone offset. Note that we need to
66 // convert the value to a UTC representation in the format of
67 // "UTC{+,-}hh:mm", so that the Gaia end can know how to interpret.
68 // E.g., -480 is "UTC+08:00"; 630 is "UTC-10:30".
69 int32_t timeZoneOffset = hal::GetTimezoneOffset();
70 nsPrintfCString curTimeZone("UTC%+03d:%02d",
71 -timeZoneOffset / 60,
72 abs(timeZoneOffset) % 60);
74 // Convert it to a JS string.
75 NS_ConvertUTF8toUTF16 utf16Str(curTimeZone);
77 JS::Rooted<JSString*> jsStr(cx, JS_NewUCStringCopyN(cx,
78 utf16Str.get(),
79 utf16Str.Length()));
80 if (!jsStr) {
81 return NS_ERROR_OUT_OF_MEMORY;
82 }
84 // Set the settings based on the current system timezone.
85 nsCOMPtr<nsISettingsServiceLock> lock;
86 nsCOMPtr<nsISettingsService> settingsService =
87 do_GetService("@mozilla.org/settingsService;1");
88 if (!settingsService) {
89 ERR("Failed to get settingsLock service!");
90 return NS_OK;
91 }
92 settingsService->CreateLock(nullptr, getter_AddRefs(lock));
93 JS::Rooted<JS::Value> value(cx, JS::StringValue(jsStr));
94 lock->Set(TIME_TIMEZONE, value, nullptr, nullptr);
95 return NS_OK;
96 }
98 // Set the system timezone based on the current settings.
99 if (aResult.isString()) {
100 return TimeZoneSettingObserver::SetTimeZone(aResult, cx);
101 }
103 return NS_OK;
104 }
106 NS_IMETHOD HandleError(const nsAString &aName) {
107 ERR("TimeZoneSettingCb::HandleError: %s\n", NS_LossyConvertUTF16toASCII(aName).get());
108 return NS_OK;
109 }
110 };
112 NS_IMPL_ISUPPORTS(TimeZoneSettingCb, nsISettingsServiceCallback)
114 TimeZoneSettingObserver::TimeZoneSettingObserver()
115 {
116 // Setup an observer to watch changes to the setting.
117 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
118 if (!observerService) {
119 ERR("GetObserverService failed");
120 return;
121 }
122 nsresult rv;
123 rv = observerService->AddObserver(this, MOZSETTINGS_CHANGED, false);
124 if (NS_FAILED(rv)) {
125 ERR("AddObserver failed");
126 return;
127 }
129 // Read the 'time.timezone' setting in order to start with a known
130 // value at boot time. The handle() will be called after reading.
131 nsCOMPtr<nsISettingsServiceLock> lock;
132 nsCOMPtr<nsISettingsService> settingsService =
133 do_GetService("@mozilla.org/settingsService;1");
134 if (!settingsService) {
135 ERR("Failed to get settingsLock service!");
136 return;
137 }
138 settingsService->CreateLock(nullptr, getter_AddRefs(lock));
139 nsCOMPtr<nsISettingsServiceCallback> callback = new TimeZoneSettingCb();
140 lock->Get(TIME_TIMEZONE, callback);
141 }
143 nsresult TimeZoneSettingObserver::SetTimeZone(const JS::Value &aValue, JSContext *aContext)
144 {
145 // Convert the JS value to a nsCString type.
146 // The value should be a JS string like "America/Chicago" or "UTC-05:00".
147 nsDependentJSString valueStr;
148 if (!valueStr.init(aContext, aValue.toString())) {
149 ERR("Failed to convert JS value to nsCString");
150 return NS_ERROR_FAILURE;
151 }
152 NS_ConvertUTF16toUTF8 newTimezone(valueStr);
154 // Hal expects opposite sign from general notations,
155 // so we need to flip it.
156 if (newTimezone.Find(NS_LITERAL_CSTRING("UTC+")) == 0) {
157 if (!newTimezone.SetCharAt('-', 3)) {
158 return NS_ERROR_FAILURE;
159 }
160 } else if (newTimezone.Find(NS_LITERAL_CSTRING("UTC-")) == 0) {
161 if (!newTimezone.SetCharAt('+', 3)) {
162 return NS_ERROR_FAILURE;
163 }
164 }
166 // Set the timezone only when the system timezone is not identical.
167 nsCString curTimezone = hal::GetTimezone();
168 if (!curTimezone.Equals(newTimezone)) {
169 hal::SetTimezone(newTimezone);
170 }
172 return NS_OK;
173 }
175 TimeZoneSettingObserver::~TimeZoneSettingObserver()
176 {
177 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
178 if (observerService) {
179 observerService->RemoveObserver(this, MOZSETTINGS_CHANGED);
180 }
181 }
183 NS_IMPL_ISUPPORTS(TimeZoneSettingObserver, nsIObserver)
185 NS_IMETHODIMP
186 TimeZoneSettingObserver::Observe(nsISupports *aSubject,
187 const char *aTopic,
188 const char16_t *aData)
189 {
190 if (strcmp(aTopic, MOZSETTINGS_CHANGED) != 0) {
191 return NS_OK;
192 }
194 // Note that this function gets called for any and all settings changes,
195 // so we need to carefully check if we have the one we're interested in.
196 //
197 // The string that we're interested in will be a JSON string that looks like:
198 // {"key":"time.timezone","value":"America/Chicago"} or
199 // {"key":"time.timezone","value":"UTC-05:00"}
201 AutoSafeJSContext cx;
203 // Parse the JSON value.
204 nsDependentString dataStr(aData);
205 JS::Rooted<JS::Value> val(cx);
206 if (!JS_ParseJSON(cx, dataStr.get(), dataStr.Length(), &val) ||
207 !val.isObject()) {
208 return NS_OK;
209 }
211 // Get the key, which should be the JS string "time.timezone".
212 JS::Rooted<JSObject*> obj(cx, &val.toObject());
213 JS::Rooted<JS::Value> key(cx);
214 if (!JS_GetProperty(cx, obj, "key", &key) ||
215 !key.isString()) {
216 return NS_OK;
217 }
218 bool match;
219 if (!JS_StringEqualsAscii(cx, key.toString(), TIME_TIMEZONE, &match) ||
220 !match) {
221 return NS_OK;
222 }
224 // Get the value, which should be a JS string like "America/Chicago".
225 JS::Rooted<JS::Value> value(cx);
226 if (!JS_GetProperty(cx, obj, "value", &value) ||
227 !value.isString()) {
228 return NS_OK;
229 }
231 // Set the system timezone.
232 return SetTimeZone(value, cx);
233 }
235 } // anonymous namespace
237 static mozilla::StaticRefPtr<TimeZoneSettingObserver> sTimeZoneSettingObserver;
238 namespace mozilla {
239 namespace system {
240 void
241 InitializeTimeZoneSettingObserver()
242 {
243 sTimeZoneSettingObserver = new TimeZoneSettingObserver();
244 ClearOnShutdown(&sTimeZoneSettingObserver);
245 }
247 } // namespace system
248 } // namespace mozilla