forked from mirrors/gecko-dev
		
	Differential Revision: https://phabricator.services.mozilla.com/D63899 --HG-- extra : moz-landing-system : lando
		
			
				
	
	
		
			205 lines
		
	
	
	
		
			6.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			205 lines
		
	
	
	
		
			6.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | 
						|
/* vim:set ts=2 sts=2 sw=2 et cin: */
 | 
						|
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
#include "ToastNotification.h"
 | 
						|
 | 
						|
#include "mozilla/WindowsVersion.h"
 | 
						|
#include "nsComponentManagerUtils.h"
 | 
						|
#include "nsCOMPtr.h"
 | 
						|
#include "nsIObserverService.h"
 | 
						|
#include "nsString.h"
 | 
						|
#include "nsThreadUtils.h"
 | 
						|
#include "ToastNotificationHandler.h"
 | 
						|
#include "WinTaskbar.h"
 | 
						|
 | 
						|
namespace mozilla {
 | 
						|
namespace widget {
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS(ToastNotification, nsIAlertsService, nsIObserver,
 | 
						|
                  nsISupportsWeakReference)
 | 
						|
 | 
						|
ToastNotification::ToastNotification() = default;
 | 
						|
 | 
						|
ToastNotification::~ToastNotification() = default;
 | 
						|
 | 
						|
nsresult ToastNotification::Init() {
 | 
						|
  if (!IsWin8OrLater()) {
 | 
						|
    return NS_ERROR_NOT_IMPLEMENTED;
 | 
						|
  }
 | 
						|
 | 
						|
  nsAutoString uid;
 | 
						|
  if (NS_WARN_IF(!WinTaskbar::GetAppUserModelID(uid))) {
 | 
						|
    // Windows Toast Notification requires AppId
 | 
						|
    return NS_ERROR_NOT_IMPLEMENTED;
 | 
						|
  }
 | 
						|
 | 
						|
  nsresult rv =
 | 
						|
      NS_NewNamedThread("ToastBgThread", getter_AddRefs(mBackgroundThread));
 | 
						|
  if (NS_WARN_IF(NS_FAILED(rv))) {
 | 
						|
    return rv;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsIObserverService> obsServ =
 | 
						|
      do_GetService("@mozilla.org/observer-service;1");
 | 
						|
  if (obsServ) {
 | 
						|
    obsServ->AddObserver(this, "quit-application", true);
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
nsresult ToastNotification::BackgroundDispatch(nsIRunnable* runnable) {
 | 
						|
  return mBackgroundThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
ToastNotification::Observe(nsISupports* aSubject, const char* aTopic,
 | 
						|
                           const char16_t* aData) {
 | 
						|
  // Got quit-application
 | 
						|
  // The handlers destructors will do the right thing (de-register with
 | 
						|
  // Windows).
 | 
						|
  for (auto iter = mActiveHandlers.Iter(); !iter.Done(); iter.Next()) {
 | 
						|
    RefPtr<ToastNotificationHandler> handler = iter.UserData();
 | 
						|
    iter.Remove();
 | 
						|
 | 
						|
    // Break the cycle between the handler and the MSCOM notification so the
 | 
						|
    // handler's destructor will be called.
 | 
						|
    handler->UnregisterHandler();
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
ToastNotification::ShowAlertNotification(
 | 
						|
    const nsAString& aImageUrl, const nsAString& aAlertTitle,
 | 
						|
    const nsAString& aAlertText, bool aAlertTextClickable,
 | 
						|
    const nsAString& aAlertCookie, nsIObserver* aAlertListener,
 | 
						|
    const nsAString& aAlertName, const nsAString& aBidi, const nsAString& aLang,
 | 
						|
    const nsAString& aData, nsIPrincipal* aPrincipal, bool aInPrivateBrowsing,
 | 
						|
    bool aRequireInteraction) {
 | 
						|
  nsCOMPtr<nsIAlertNotification> alert =
 | 
						|
      do_CreateInstance(ALERT_NOTIFICATION_CONTRACTID);
 | 
						|
  if (NS_WARN_IF(!alert)) {
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
  nsresult rv =
 | 
						|
      alert->Init(aAlertName, aImageUrl, aAlertTitle, aAlertText,
 | 
						|
                  aAlertTextClickable, aAlertCookie, aBidi, aLang, aData,
 | 
						|
                  aPrincipal, aInPrivateBrowsing, aRequireInteraction);
 | 
						|
  if (NS_WARN_IF(NS_FAILED(rv))) {
 | 
						|
    return rv;
 | 
						|
  }
 | 
						|
  return ShowAlert(alert, aAlertListener);
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
ToastNotification::ShowPersistentNotification(const nsAString& aPersistentData,
 | 
						|
                                              nsIAlertNotification* aAlert,
 | 
						|
                                              nsIObserver* aAlertListener) {
 | 
						|
  return ShowAlert(aAlert, aAlertListener);
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
ToastNotification::ShowAlert(nsIAlertNotification* aAlert,
 | 
						|
                             nsIObserver* aAlertListener) {
 | 
						|
  if (NS_WARN_IF(!aAlert)) {
 | 
						|
    return NS_ERROR_INVALID_ARG;
 | 
						|
  }
 | 
						|
 | 
						|
  nsAutoString cookie;
 | 
						|
  nsresult rv = aAlert->GetCookie(cookie);
 | 
						|
  if (NS_WARN_IF(NS_FAILED(rv))) {
 | 
						|
    return rv;
 | 
						|
  }
 | 
						|
 | 
						|
  nsAutoString name;
 | 
						|
  rv = aAlert->GetName(name);
 | 
						|
  if (NS_WARN_IF(NS_FAILED(rv))) {
 | 
						|
    return rv;
 | 
						|
  }
 | 
						|
 | 
						|
  nsAutoString title;
 | 
						|
  rv = aAlert->GetTitle(title);
 | 
						|
  if (NS_WARN_IF(NS_FAILED(rv))) {
 | 
						|
    return rv;
 | 
						|
  }
 | 
						|
 | 
						|
  nsAutoString text;
 | 
						|
  rv = aAlert->GetText(text);
 | 
						|
  if (NS_WARN_IF(NS_FAILED(rv))) {
 | 
						|
    return rv;
 | 
						|
  }
 | 
						|
 | 
						|
  bool textClickable;
 | 
						|
  rv = aAlert->GetTextClickable(&textClickable);
 | 
						|
  if (NS_WARN_IF(NS_FAILED(rv))) {
 | 
						|
    return rv;
 | 
						|
  }
 | 
						|
 | 
						|
  nsAutoString hostPort;
 | 
						|
  rv = aAlert->GetSource(hostPort);
 | 
						|
  if (NS_WARN_IF(NS_FAILED(rv))) {
 | 
						|
    return rv;
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<ToastNotificationHandler> oldHandler = mActiveHandlers.Get(name);
 | 
						|
 | 
						|
  RefPtr<ToastNotificationHandler> handler = new ToastNotificationHandler(
 | 
						|
      this, aAlertListener, name, cookie, title, text, hostPort, textClickable);
 | 
						|
  mActiveHandlers.Put(name, RefPtr{handler});
 | 
						|
 | 
						|
  rv = handler->InitAlertAsync(aAlert);
 | 
						|
  if (NS_WARN_IF(NS_FAILED(rv))) {
 | 
						|
    mActiveHandlers.Remove(name);
 | 
						|
    handler->UnregisterHandler();
 | 
						|
    return rv;
 | 
						|
  }
 | 
						|
 | 
						|
  // If there was a previous handler with the same name then unregister it.
 | 
						|
  if (oldHandler) {
 | 
						|
    oldHandler->UnregisterHandler();
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
ToastNotification::CloseAlert(const nsAString& aAlertName,
 | 
						|
                              nsIPrincipal* aPrincipal) {
 | 
						|
  RefPtr<ToastNotificationHandler> handler;
 | 
						|
  if (NS_WARN_IF(!mActiveHandlers.Get(aAlertName, getter_AddRefs(handler)))) {
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
  mActiveHandlers.Remove(aAlertName);
 | 
						|
  handler->UnregisterHandler();
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
bool ToastNotification::IsActiveHandler(const nsAString& aAlertName,
 | 
						|
                                        ToastNotificationHandler* aHandler) {
 | 
						|
  RefPtr<ToastNotificationHandler> handler;
 | 
						|
  if (NS_WARN_IF(!mActiveHandlers.Get(aAlertName, getter_AddRefs(handler)))) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  return handler == aHandler;
 | 
						|
}
 | 
						|
 | 
						|
void ToastNotification::RemoveHandler(const nsAString& aAlertName,
 | 
						|
                                      ToastNotificationHandler* aHandler) {
 | 
						|
  // The alert may have been replaced; only remove it from the active
 | 
						|
  // handlers map if it's the same.
 | 
						|
  if (IsActiveHandler(aAlertName, aHandler)) {
 | 
						|
    // Terrible things happen if the destructor of a handler is called inside
 | 
						|
    // the hashtable .Remove() method. Wait until we have returned from there.
 | 
						|
    RefPtr<ToastNotificationHandler> kungFuDeathGrip(aHandler);
 | 
						|
    mActiveHandlers.Remove(aAlertName);
 | 
						|
    aHandler->UnregisterHandler();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace widget
 | 
						|
}  // namespace mozilla
 |