michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "TimeChangeObserver.h" michael@0: #include "mozilla/Hal.h" michael@0: #include "mozilla/Observer.h" michael@0: #include "mozilla/HalTypes.h" michael@0: #include "nsWeakPtr.h" michael@0: #include "nsTObserverArray.h" michael@0: #include "mozilla/ClearOnShutdown.h" michael@0: #include "mozilla/Services.h" michael@0: #include "mozilla/StaticPtr.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsIDocument.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::hal; michael@0: using namespace mozilla::services; michael@0: michael@0: class nsSystemTimeChangeObserver : public SystemClockChangeObserver, michael@0: public SystemTimezoneChangeObserver michael@0: { michael@0: typedef nsTObserverArray ListenerArray; michael@0: public: michael@0: static nsSystemTimeChangeObserver* GetInstance(); michael@0: virtual ~nsSystemTimeChangeObserver(); michael@0: michael@0: // Implementing hal::SystemClockChangeObserver::Notify() michael@0: void Notify(const int64_t& aClockDeltaMS); michael@0: michael@0: // Implementing hal::SystemTimezoneChangeObserver::Notify() michael@0: void Notify( michael@0: const mozilla::hal::SystemTimezoneChangeInformation& aSystemTimezoneChangeInfo); michael@0: michael@0: nsresult AddWindowListenerImpl(nsPIDOMWindow* aWindow); michael@0: nsresult RemoveWindowListenerImpl(nsPIDOMWindow* aWindow); michael@0: michael@0: private: michael@0: nsSystemTimeChangeObserver() { }; michael@0: ListenerArray mWindowListeners; michael@0: void FireMozTimeChangeEvent(); michael@0: }; michael@0: michael@0: StaticAutoPtr sObserver; michael@0: michael@0: nsSystemTimeChangeObserver* nsSystemTimeChangeObserver::GetInstance() michael@0: { michael@0: if (!sObserver) { michael@0: sObserver = new nsSystemTimeChangeObserver(); michael@0: ClearOnShutdown(&sObserver); michael@0: } michael@0: return sObserver; michael@0: } michael@0: michael@0: nsSystemTimeChangeObserver::~nsSystemTimeChangeObserver() michael@0: { michael@0: UnregisterSystemClockChangeObserver(this); michael@0: UnregisterSystemTimezoneChangeObserver(this); michael@0: } michael@0: michael@0: void michael@0: nsSystemTimeChangeObserver::FireMozTimeChangeEvent() michael@0: { michael@0: ListenerArray::ForwardIterator iter(mWindowListeners); michael@0: while (iter.HasMore()) { michael@0: nsWeakPtr weakWindow = iter.GetNext(); michael@0: nsCOMPtr innerWindow = do_QueryReferent(weakWindow); michael@0: nsCOMPtr outerWindow; michael@0: nsCOMPtr document; michael@0: if (!innerWindow || michael@0: !(document = innerWindow->GetExtantDoc()) || michael@0: !(outerWindow = innerWindow->GetOuterWindow())) { michael@0: mWindowListeners.RemoveElement(weakWindow); michael@0: continue; michael@0: } michael@0: michael@0: nsContentUtils::DispatchTrustedEvent(document, outerWindow, michael@0: NS_LITERAL_STRING("moztimechange"), /* bubbles = */ true, michael@0: /* canceable = */ false); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsSystemTimeChangeObserver::Notify(const int64_t& aClockDeltaMS) michael@0: { michael@0: // Notify observers that the system clock has been adjusted. michael@0: nsCOMPtr observerService = GetObserverService(); michael@0: if (observerService) { michael@0: nsString dataStr; michael@0: dataStr.AppendFloat(static_cast(aClockDeltaMS)); michael@0: observerService->NotifyObservers( michael@0: nullptr, "system-clock-change", dataStr.get()); michael@0: } michael@0: michael@0: FireMozTimeChangeEvent(); michael@0: } michael@0: michael@0: void michael@0: nsSystemTimeChangeObserver::Notify( michael@0: const SystemTimezoneChangeInformation& aSystemTimezoneChangeInfo) michael@0: { michael@0: FireMozTimeChangeEvent(); michael@0: } michael@0: michael@0: nsresult michael@0: mozilla::time::AddWindowListener(nsPIDOMWindow* aWindow) michael@0: { michael@0: return nsSystemTimeChangeObserver::GetInstance()->AddWindowListenerImpl(aWindow); michael@0: } michael@0: michael@0: nsresult michael@0: nsSystemTimeChangeObserver::AddWindowListenerImpl(nsPIDOMWindow* aWindow) michael@0: { michael@0: if (!aWindow) { michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: } michael@0: michael@0: if (aWindow->IsOuterWindow()) { michael@0: aWindow = aWindow->GetCurrentInnerWindow(); michael@0: if (!aWindow) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: michael@0: nsWeakPtr windowWeakRef = do_GetWeakReference(aWindow); michael@0: NS_ASSERTION(windowWeakRef, "nsIDOMWindow implementations shuld support weak ref"); michael@0: michael@0: if (mWindowListeners.IndexOf(windowWeakRef) != michael@0: ListenerArray::array_type::NoIndex) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (mWindowListeners.IsEmpty()) { michael@0: RegisterSystemClockChangeObserver(sObserver); michael@0: RegisterSystemTimezoneChangeObserver(sObserver); michael@0: } michael@0: michael@0: mWindowListeners.AppendElement(windowWeakRef); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: mozilla::time::RemoveWindowListener(nsPIDOMWindow* aWindow) michael@0: { michael@0: if (!sObserver) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: return nsSystemTimeChangeObserver::GetInstance()->RemoveWindowListenerImpl(aWindow); michael@0: } michael@0: michael@0: nsresult michael@0: nsSystemTimeChangeObserver::RemoveWindowListenerImpl(nsPIDOMWindow* aWindow) michael@0: { michael@0: if (!aWindow) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (aWindow->IsOuterWindow()) { michael@0: aWindow = aWindow->GetCurrentInnerWindow(); michael@0: if (!aWindow) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: michael@0: nsWeakPtr windowWeakRef = do_GetWeakReference(aWindow); michael@0: mWindowListeners.RemoveElement(windowWeakRef); michael@0: michael@0: if (mWindowListeners.IsEmpty()) { michael@0: UnregisterSystemClockChangeObserver(sObserver); michael@0: UnregisterSystemTimezoneChangeObserver(sObserver); michael@0: } michael@0: michael@0: return NS_OK; michael@0: }