dom/system/gonk/TimeZoneSettingObserver.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial