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

     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

mercurial