2013-03-18 06:24:54 -07:00
|
|
|
/* 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 "PCOMContentPermissionRequestChild.h"
|
|
|
|
#include "mozilla/dom/Notification.h"
|
2013-10-11 11:12:13 -07:00
|
|
|
#include "mozilla/dom/AppNotificationServiceOptionsBinding.h"
|
2013-09-10 00:03:37 -07:00
|
|
|
#include "mozilla/dom/OwningNonNull.h"
|
2013-10-02 18:27:53 -07:00
|
|
|
#include "mozilla/dom/Promise.h"
|
2012-07-12 08:45:38 -07:00
|
|
|
#include "mozilla/Preferences.h"
|
2013-03-18 06:24:54 -07:00
|
|
|
#include "TabChild.h"
|
|
|
|
#include "nsContentUtils.h"
|
|
|
|
#include "nsDOMEvent.h"
|
|
|
|
#include "nsIAlertsService.h"
|
2013-10-02 18:27:53 -07:00
|
|
|
#include "nsIAppsService.h"
|
2013-03-18 06:24:54 -07:00
|
|
|
#include "nsIContentPermissionPrompt.h"
|
|
|
|
#include "nsIDocument.h"
|
2013-10-02 18:27:53 -07:00
|
|
|
#include "nsINotificationStorage.h"
|
2013-03-18 06:24:54 -07:00
|
|
|
#include "nsIPermissionManager.h"
|
2013-10-02 18:27:53 -07:00
|
|
|
#include "nsIUUIDGenerator.h"
|
2013-03-18 06:24:54 -07:00
|
|
|
#include "nsServiceManagerUtils.h"
|
|
|
|
#include "nsToolkitCompsCID.h"
|
|
|
|
#include "nsGlobalWindow.h"
|
|
|
|
#include "nsDOMJSUtils.h"
|
2013-09-23 14:30:40 -07:00
|
|
|
#include "nsIScriptSecurityManager.h"
|
2013-10-18 13:57:55 -07:00
|
|
|
#include "mozilla/dom/PermissionMessageUtils.h"
|
2013-09-10 07:49:09 -07:00
|
|
|
#ifdef MOZ_B2G
|
|
|
|
#include "nsIDOMDesktopNotification.h"
|
|
|
|
#endif
|
2013-03-18 06:24:54 -07:00
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
namespace dom {
|
|
|
|
|
2013-10-02 18:27:53 -07:00
|
|
|
class NotificationStorageCallback MOZ_FINAL : public nsINotificationStorageCallback
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
|
|
|
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(NotificationStorageCallback)
|
|
|
|
|
|
|
|
NotificationStorageCallback(const GlobalObject& aGlobal, nsPIDOMWindow* aWindow, Promise* aPromise)
|
|
|
|
: mCount(0),
|
|
|
|
mGlobal(aGlobal.Get()),
|
|
|
|
mWindow(aWindow),
|
|
|
|
mPromise(aPromise)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aWindow);
|
|
|
|
MOZ_ASSERT(aPromise);
|
|
|
|
JSContext* cx = aGlobal.GetContext();
|
|
|
|
JSAutoCompartment ac(cx, mGlobal);
|
|
|
|
mNotifications = JS_NewArrayObject(cx, 0, nullptr);
|
|
|
|
HoldData();
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHOD Handle(const nsAString& aID,
|
|
|
|
const nsAString& aTitle,
|
|
|
|
const nsAString& aDir,
|
|
|
|
const nsAString& aLang,
|
|
|
|
const nsAString& aBody,
|
|
|
|
const nsAString& aTag,
|
|
|
|
const nsAString& aIcon,
|
|
|
|
JSContext* aCx)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(!aID.IsEmpty());
|
|
|
|
|
|
|
|
NotificationOptions options;
|
|
|
|
options.mDir = Notification::StringToDirection(nsString(aDir));
|
|
|
|
options.mLang = aLang;
|
|
|
|
options.mBody = aBody;
|
|
|
|
options.mTag = aTag;
|
|
|
|
options.mIcon = aIcon;
|
|
|
|
nsRefPtr<Notification> notification = Notification::CreateInternal(mWindow,
|
|
|
|
aID,
|
|
|
|
aTitle,
|
|
|
|
options);
|
|
|
|
JSAutoCompartment ac(aCx, mGlobal);
|
2013-11-11 00:04:41 -08:00
|
|
|
JS::Rooted<JSObject*> scope(aCx, mGlobal);
|
|
|
|
JS::Rooted<JSObject*> element(aCx, notification->WrapObject(aCx, scope));
|
2013-10-02 18:27:53 -07:00
|
|
|
NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
|
|
|
|
|
|
|
|
if (!JS_DefineElement(aCx, mNotifications, mCount++,
|
|
|
|
JS::ObjectValue(*element), nullptr, nullptr, 0)) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHOD Done(JSContext* aCx)
|
|
|
|
{
|
|
|
|
JSAutoCompartment ac(aCx, mGlobal);
|
2013-11-19 10:39:51 -08:00
|
|
|
JS::Rooted<JS::Value> result(aCx, JS::ObjectValue(*mNotifications));
|
2013-10-02 18:27:53 -07:00
|
|
|
mPromise->MaybeResolve(aCx, result);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
~NotificationStorageCallback()
|
|
|
|
{
|
|
|
|
DropData();
|
|
|
|
}
|
|
|
|
|
|
|
|
void HoldData()
|
|
|
|
{
|
|
|
|
mozilla::HoldJSObjects(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DropData()
|
|
|
|
{
|
|
|
|
mGlobal = nullptr;
|
|
|
|
mNotifications = nullptr;
|
|
|
|
mozilla::DropJSObjects(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t mCount;
|
|
|
|
JS::Heap<JSObject *> mGlobal;
|
|
|
|
nsCOMPtr<nsPIDOMWindow> mWindow;
|
|
|
|
nsRefPtr<Promise> mPromise;
|
|
|
|
JS::Heap<JSObject *> mNotifications;
|
|
|
|
};
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(NotificationStorageCallback)
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(NotificationStorageCallback)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(NotificationStorageCallback)
|
|
|
|
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NotificationStorageCallback)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsINotificationStorageCallback)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
|
|
|
NS_INTERFACE_MAP_END
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(NotificationStorageCallback)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mNotifications)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(NotificationStorageCallback)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(NotificationStorageCallback)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
|
|
|
|
tmp->DropData();
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
|
2013-03-18 06:24:54 -07:00
|
|
|
class NotificationPermissionRequest : public nsIContentPermissionRequest,
|
|
|
|
public PCOMContentPermissionRequestChild,
|
|
|
|
public nsIRunnable
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
|
|
|
NS_DECL_NSICONTENTPERMISSIONREQUEST
|
|
|
|
NS_DECL_NSIRUNNABLE
|
|
|
|
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(NotificationPermissionRequest,
|
|
|
|
nsIContentPermissionRequest)
|
|
|
|
|
|
|
|
NotificationPermissionRequest(nsIPrincipal* aPrincipal, nsPIDOMWindow* aWindow,
|
|
|
|
NotificationPermissionCallback* aCallback)
|
|
|
|
: mPrincipal(aPrincipal), mWindow(aWindow),
|
2013-05-06 12:28:13 -07:00
|
|
|
mPermission(NotificationPermission::Default),
|
2013-03-18 06:24:54 -07:00
|
|
|
mCallback(aCallback) {}
|
|
|
|
|
|
|
|
virtual ~NotificationPermissionRequest() {}
|
|
|
|
|
|
|
|
bool Recv__delete__(const bool& aAllow);
|
|
|
|
void IPDLRelease() { Release(); }
|
|
|
|
|
|
|
|
protected:
|
|
|
|
nsresult CallCallback();
|
2012-07-12 08:45:38 -07:00
|
|
|
nsresult DispatchCallback();
|
2013-03-18 06:24:54 -07:00
|
|
|
nsCOMPtr<nsIPrincipal> mPrincipal;
|
|
|
|
nsCOMPtr<nsPIDOMWindow> mWindow;
|
|
|
|
NotificationPermission mPermission;
|
|
|
|
nsRefPtr<NotificationPermissionCallback> mCallback;
|
|
|
|
};
|
|
|
|
|
|
|
|
class NotificationObserver : public nsIObserver
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
NS_DECL_NSIOBSERVER
|
|
|
|
|
|
|
|
NotificationObserver(Notification* aNotification)
|
|
|
|
: mNotification(aNotification) {}
|
|
|
|
|
|
|
|
virtual ~NotificationObserver() {}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
nsRefPtr<Notification> mNotification;
|
|
|
|
};
|
|
|
|
|
|
|
|
class NotificationTask : public nsIRunnable
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
NS_DECL_NSIRUNNABLE
|
|
|
|
|
|
|
|
enum NotificationAction {
|
|
|
|
eShow,
|
|
|
|
eClose
|
|
|
|
};
|
|
|
|
|
|
|
|
NotificationTask(Notification* aNotification, NotificationAction aAction)
|
|
|
|
: mNotification(aNotification), mAction(aAction) {}
|
|
|
|
|
|
|
|
virtual ~NotificationTask() {}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
nsRefPtr<Notification> mNotification;
|
|
|
|
NotificationAction mAction;
|
|
|
|
};
|
|
|
|
|
|
|
|
uint32_t Notification::sCount = 0;
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_1(NotificationPermissionRequest, mWindow)
|
|
|
|
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NotificationPermissionRequest)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIRunnable)
|
|
|
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
|
|
|
|
NS_INTERFACE_MAP_END
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(NotificationPermissionRequest)
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(NotificationPermissionRequest)
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
NotificationPermissionRequest::Run()
|
|
|
|
{
|
2013-05-23 15:38:00 -07:00
|
|
|
if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
|
2013-05-06 12:28:13 -07:00
|
|
|
mPermission = NotificationPermission::Granted;
|
2013-05-23 15:38:00 -07:00
|
|
|
} else {
|
|
|
|
// File are automatically granted permission.
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
|
|
mPrincipal->GetURI(getter_AddRefs(uri));
|
|
|
|
|
|
|
|
if (uri) {
|
|
|
|
bool isFile;
|
|
|
|
uri->SchemeIs("file", &isFile);
|
|
|
|
if (isFile) {
|
|
|
|
mPermission = NotificationPermission::Granted;
|
|
|
|
}
|
|
|
|
}
|
2012-07-12 08:45:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Grant permission if pref'ed on.
|
|
|
|
if (Preferences::GetBool("notification.prompt.testing", false)) {
|
|
|
|
if (Preferences::GetBool("notification.prompt.testing.allow", true)) {
|
2013-05-06 12:28:13 -07:00
|
|
|
mPermission = NotificationPermission::Granted;
|
2012-07-12 08:45:38 -07:00
|
|
|
} else {
|
2013-05-06 12:28:13 -07:00
|
|
|
mPermission = NotificationPermission::Denied;
|
2012-07-12 08:45:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-06 12:28:13 -07:00
|
|
|
if (mPermission != NotificationPermission::Default) {
|
2012-07-12 08:45:38 -07:00
|
|
|
return DispatchCallback();
|
|
|
|
}
|
|
|
|
|
2013-03-18 06:24:54 -07:00
|
|
|
if (XRE_GetProcessType() == GeckoProcessType_Content) {
|
|
|
|
// because owner implements nsITabChild, we can assume that it is
|
|
|
|
// the one and only TabChild.
|
2013-09-12 12:24:11 -07:00
|
|
|
TabChild* child = TabChild::GetFrom(mWindow->GetDocShell());
|
2013-03-18 06:24:54 -07:00
|
|
|
if (!child) {
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Retain a reference so the object isn't deleted without IPDL's knowledge.
|
|
|
|
// Corresponding release occurs in DeallocPContentPermissionRequest.
|
|
|
|
AddRef();
|
|
|
|
|
2013-12-09 22:03:21 -08:00
|
|
|
NS_NAMED_LITERAL_CSTRING(type, "desktop-notification");
|
|
|
|
NS_NAMED_LITERAL_CSTRING(access, "unused");
|
|
|
|
child->SendPContentPermissionRequestConstructor(this, type, access,
|
2013-03-18 06:24:54 -07:00
|
|
|
IPC::Principal(mPrincipal));
|
|
|
|
|
|
|
|
Sendprompt();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIContentPermissionPrompt> prompt =
|
|
|
|
do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
|
|
|
|
if (prompt) {
|
|
|
|
prompt->Prompt(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
NotificationPermissionRequest::GetPrincipal(nsIPrincipal** aRequestingPrincipal)
|
|
|
|
{
|
|
|
|
NS_ADDREF(*aRequestingPrincipal = mPrincipal);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
NotificationPermissionRequest::GetWindow(nsIDOMWindow** aRequestingWindow)
|
|
|
|
{
|
|
|
|
NS_ADDREF(*aRequestingWindow = mWindow);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
NotificationPermissionRequest::GetElement(nsIDOMElement** aElement)
|
|
|
|
{
|
2013-09-04 08:40:16 -07:00
|
|
|
NS_ENSURE_ARG_POINTER(aElement);
|
|
|
|
*aElement = nullptr;
|
|
|
|
return NS_OK;
|
2013-03-18 06:24:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
NotificationPermissionRequest::Cancel()
|
|
|
|
{
|
2013-05-06 12:28:13 -07:00
|
|
|
mPermission = NotificationPermission::Denied;
|
2012-07-12 08:45:38 -07:00
|
|
|
return DispatchCallback();
|
2013-03-18 06:24:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
NotificationPermissionRequest::Allow()
|
|
|
|
{
|
2013-05-06 12:28:13 -07:00
|
|
|
mPermission = NotificationPermission::Granted;
|
2012-07-12 08:45:38 -07:00
|
|
|
return DispatchCallback();
|
|
|
|
}
|
|
|
|
|
|
|
|
inline nsresult
|
|
|
|
NotificationPermissionRequest::DispatchCallback()
|
|
|
|
{
|
|
|
|
if (!mCallback) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2013-03-18 06:24:54 -07:00
|
|
|
nsCOMPtr<nsIRunnable> callbackRunnable = NS_NewRunnableMethod(this,
|
|
|
|
&NotificationPermissionRequest::CallCallback);
|
2012-07-12 08:45:38 -07:00
|
|
|
return NS_DispatchToMainThread(callbackRunnable);
|
2013-03-18 06:24:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
NotificationPermissionRequest::CallCallback()
|
|
|
|
{
|
|
|
|
ErrorResult rv;
|
|
|
|
mCallback->Call(mPermission, rv);
|
|
|
|
return rv.ErrorCode();
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2013-12-09 22:03:21 -08:00
|
|
|
NotificationPermissionRequest::GetAccess(nsACString& aAccess)
|
2013-03-18 06:24:54 -07:00
|
|
|
{
|
2013-12-09 22:03:21 -08:00
|
|
|
aAccess.AssignLiteral("unused");
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
NotificationPermissionRequest::GetType(nsACString& aType)
|
|
|
|
{
|
|
|
|
aType.AssignLiteral("desktop-notification");
|
|
|
|
return NS_OK;
|
2013-03-18 06:24:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
NotificationPermissionRequest::Recv__delete__(const bool& aAllow)
|
|
|
|
{
|
|
|
|
if (aAllow) {
|
|
|
|
(void) Allow();
|
|
|
|
} else {
|
|
|
|
(void) Cancel();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMPL_ISUPPORTS1(NotificationTask, nsIRunnable)
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
NotificationTask::Run()
|
|
|
|
{
|
|
|
|
switch (mAction) {
|
|
|
|
case eShow:
|
2013-10-02 18:27:53 -07:00
|
|
|
mNotification->ShowInternal();
|
|
|
|
break;
|
2013-03-18 06:24:54 -07:00
|
|
|
case eClose:
|
2013-10-02 18:27:53 -07:00
|
|
|
mNotification->CloseInternal();
|
|
|
|
break;
|
2013-03-18 06:24:54 -07:00
|
|
|
default:
|
2013-06-28 18:38:30 -07:00
|
|
|
MOZ_CRASH("Unexpected action for NotificationTask.");
|
2013-03-18 06:24:54 -07:00
|
|
|
}
|
2013-10-02 18:27:53 -07:00
|
|
|
return NS_OK;
|
2013-03-18 06:24:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMPL_ISUPPORTS1(NotificationObserver, nsIObserver)
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
NotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
|
2014-01-04 07:02:17 -08:00
|
|
|
const char16_t* aData)
|
2013-03-18 06:24:54 -07:00
|
|
|
{
|
|
|
|
if (!strcmp("alertclickcallback", aTopic)) {
|
|
|
|
mNotification->DispatchTrustedEvent(NS_LITERAL_STRING("click"));
|
|
|
|
} else if (!strcmp("alertfinished", aTopic)) {
|
|
|
|
mNotification->mIsClosed = true;
|
|
|
|
mNotification->DispatchTrustedEvent(NS_LITERAL_STRING("close"));
|
|
|
|
} else if (!strcmp("alertshow", aTopic)) {
|
|
|
|
mNotification->DispatchTrustedEvent(NS_LITERAL_STRING("show"));
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2013-10-02 18:27:53 -07:00
|
|
|
Notification::Notification(const nsAString& aID, const nsAString& aTitle, const nsAString& aBody,
|
2013-03-18 06:24:54 -07:00
|
|
|
NotificationDirection aDir, const nsAString& aLang,
|
2014-01-06 18:53:23 -08:00
|
|
|
const nsAString& aTag, const nsAString& aIconUrl,
|
|
|
|
nsPIDOMWindow* aWindow)
|
|
|
|
: nsDOMEventTargetHelper(aWindow),
|
|
|
|
mID(aID), mTitle(aTitle), mBody(aBody), mDir(aDir), mLang(aLang),
|
2013-03-18 06:24:54 -07:00
|
|
|
mTag(aTag), mIconUrl(aIconUrl), mIsClosed(false)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2013-10-02 18:27:53 -07:00
|
|
|
// static
|
2013-03-18 06:24:54 -07:00
|
|
|
already_AddRefed<Notification>
|
|
|
|
Notification::Constructor(const GlobalObject& aGlobal,
|
|
|
|
const nsAString& aTitle,
|
|
|
|
const NotificationOptions& aOptions,
|
|
|
|
ErrorResult& aRv)
|
|
|
|
{
|
2013-10-02 18:27:53 -07:00
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
|
|
|
|
MOZ_ASSERT(window, "Window should not be null.");
|
|
|
|
nsRefPtr<Notification> notification = CreateInternal(window,
|
|
|
|
EmptyString(),
|
|
|
|
aTitle,
|
|
|
|
aOptions);
|
|
|
|
|
|
|
|
// Queue a task to show the notification.
|
|
|
|
nsCOMPtr<nsIRunnable> showNotificationTask =
|
|
|
|
new NotificationTask(notification, NotificationTask::eShow);
|
|
|
|
NS_DispatchToCurrentThread(showNotificationTask);
|
|
|
|
|
|
|
|
// Persist the notification.
|
|
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsINotificationStorage> notificationStorage =
|
|
|
|
do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID, &rv);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
aRv.Throw(rv);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsString origin;
|
|
|
|
aRv = GetOrigin(window, origin);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsString id;
|
|
|
|
notification->GetID(id);
|
|
|
|
aRv = notificationStorage->Put(origin,
|
|
|
|
id,
|
|
|
|
aTitle,
|
|
|
|
DirectionToString(aOptions.mDir),
|
|
|
|
aOptions.mLang,
|
|
|
|
aOptions.mBody,
|
|
|
|
aOptions.mTag,
|
|
|
|
aOptions.mIcon);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return notification.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<Notification>
|
|
|
|
Notification::CreateInternal(nsPIDOMWindow* aWindow,
|
|
|
|
const nsAString& aID,
|
|
|
|
const nsAString& aTitle,
|
|
|
|
const NotificationOptions& aOptions)
|
|
|
|
{
|
|
|
|
nsString id;
|
|
|
|
if (!aID.IsEmpty()) {
|
|
|
|
id = aID;
|
2013-03-18 06:24:54 -07:00
|
|
|
} else {
|
2013-10-02 18:27:53 -07:00
|
|
|
nsCOMPtr<nsIUUIDGenerator> uuidgen =
|
|
|
|
do_GetService("@mozilla.org/uuid-generator;1");
|
|
|
|
NS_ENSURE_TRUE(uuidgen, nullptr);
|
|
|
|
nsID uuid;
|
|
|
|
nsresult rv = uuidgen->GenerateUUIDInPlace(&uuid);
|
|
|
|
NS_ENSURE_SUCCESS(rv, nullptr);
|
|
|
|
|
|
|
|
char buffer[NSID_LENGTH];
|
|
|
|
uuid.ToProvidedString(buffer);
|
|
|
|
NS_ConvertASCIItoUTF16 convertedID(buffer);
|
|
|
|
id = convertedID;
|
2013-03-18 06:24:54 -07:00
|
|
|
}
|
|
|
|
|
2013-10-02 18:27:53 -07:00
|
|
|
nsRefPtr<Notification> notification = new Notification(id,
|
|
|
|
aTitle,
|
2013-03-18 06:24:54 -07:00
|
|
|
aOptions.mBody,
|
|
|
|
aOptions.mDir,
|
|
|
|
aOptions.mLang,
|
2013-10-02 18:27:53 -07:00
|
|
|
aOptions.mTag,
|
2014-01-06 18:53:23 -08:00
|
|
|
aOptions.mIcon,
|
|
|
|
aWindow);
|
2013-03-18 06:24:54 -07:00
|
|
|
return notification.forget();
|
|
|
|
}
|
|
|
|
|
2013-11-11 15:56:21 -08:00
|
|
|
nsIPrincipal*
|
|
|
|
Notification::GetPrincipal()
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(GetOwner());
|
|
|
|
NS_ENSURE_TRUE(sop, nullptr);
|
|
|
|
return sop->GetPrincipal();
|
|
|
|
}
|
|
|
|
|
2013-10-02 18:27:53 -07:00
|
|
|
void
|
2013-03-18 06:24:54 -07:00
|
|
|
Notification::ShowInternal()
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIAlertsService> alertService =
|
|
|
|
do_GetService(NS_ALERTSERVICE_CONTRACTID);
|
|
|
|
|
|
|
|
ErrorResult result;
|
|
|
|
if (GetPermissionInternal(GetOwner(), result) !=
|
2013-05-06 12:28:13 -07:00
|
|
|
NotificationPermission::Granted || !alertService) {
|
2013-03-18 06:24:54 -07:00
|
|
|
// We do not have permission to show a notification or alert service
|
|
|
|
// is not available.
|
2013-10-02 18:27:53 -07:00
|
|
|
DispatchTrustedEvent(NS_LITERAL_STRING("error"));
|
|
|
|
return;
|
2013-03-18 06:24:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult rv;
|
|
|
|
nsAutoString absoluteUrl;
|
|
|
|
if (mIconUrl.Length() > 0) {
|
|
|
|
// Resolve image URL against document base URI.
|
|
|
|
nsIDocument* doc = GetOwner()->GetExtantDoc();
|
2013-10-02 18:27:53 -07:00
|
|
|
if (doc) {
|
|
|
|
nsCOMPtr<nsIURI> baseUri = doc->GetBaseURI();
|
|
|
|
if (baseUri) {
|
|
|
|
nsCOMPtr<nsIURI> srcUri;
|
|
|
|
rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(srcUri),
|
|
|
|
mIconUrl, doc, baseUri);
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
nsAutoCString src;
|
|
|
|
srcUri->GetSpec(src);
|
|
|
|
absoluteUrl = NS_ConvertUTF8toUTF16(src);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-18 06:24:54 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-10 07:49:09 -07:00
|
|
|
nsCOMPtr<nsIObserver> observer = new NotificationObserver(this);
|
|
|
|
|
2013-03-18 06:24:54 -07:00
|
|
|
nsString alertName;
|
|
|
|
rv = GetAlertName(alertName);
|
2013-10-02 18:27:53 -07:00
|
|
|
NS_ENSURE_SUCCESS_VOID(rv);
|
2013-03-18 06:24:54 -07:00
|
|
|
|
2013-09-10 07:49:09 -07:00
|
|
|
#ifdef MOZ_B2G
|
|
|
|
nsCOMPtr<nsIAppNotificationService> appNotifier =
|
|
|
|
do_GetService("@mozilla.org/system-alerts-service;1");
|
|
|
|
if (appNotifier) {
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window = GetOwner();
|
|
|
|
uint32_t appId = (window.get())->GetDoc()->NodePrincipal()->GetAppId();
|
|
|
|
|
|
|
|
if (appId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
|
|
|
|
nsCOMPtr<nsIAppsService> appsService = do_GetService("@mozilla.org/AppsService;1");
|
|
|
|
nsString manifestUrl = EmptyString();
|
2013-10-02 18:27:53 -07:00
|
|
|
rv = appsService->GetManifestURLByLocalId(appId, manifestUrl);
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
mozilla::AutoSafeJSContext cx;
|
2013-11-11 00:04:41 -08:00
|
|
|
JS::Rooted<JS::Value> val(cx);
|
2013-10-02 18:27:53 -07:00
|
|
|
AppNotificationServiceOptions ops;
|
|
|
|
ops.mTextClickable = true;
|
|
|
|
ops.mManifestURL = manifestUrl;
|
|
|
|
ops.mId = alertName;
|
|
|
|
ops.mDir = DirectionToString(mDir);
|
|
|
|
ops.mLang = mLang;
|
2014-01-09 07:55:44 -08:00
|
|
|
ops.mTag = mTag;
|
2013-10-02 18:27:53 -07:00
|
|
|
|
|
|
|
if (!ops.ToObject(cx, JS::NullPtr(), &val)) {
|
|
|
|
NS_WARNING("Converting dict to object failed!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
appNotifier->ShowAppNotification(mIconUrl, mTitle, mBody,
|
|
|
|
observer, val);
|
|
|
|
return;
|
2013-10-11 11:12:13 -07:00
|
|
|
}
|
2013-09-10 07:49:09 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2013-03-18 06:24:54 -07:00
|
|
|
// In the case of IPC, the parent process uses the cookie to map to
|
|
|
|
// nsIObserver. Thus the cookie must be unique to differentiate observers.
|
|
|
|
nsString uniqueCookie = NS_LITERAL_STRING("notification:");
|
|
|
|
uniqueCookie.AppendInt(sCount++);
|
2013-10-02 18:27:53 -07:00
|
|
|
alertService->ShowAlertNotification(absoluteUrl, mTitle, mBody, true,
|
|
|
|
uniqueCookie, observer, alertName,
|
2013-11-11 15:56:21 -08:00
|
|
|
DirectionToString(mDir), mLang,
|
|
|
|
GetPrincipal());
|
2013-03-18 06:24:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Notification::RequestPermission(const GlobalObject& aGlobal,
|
|
|
|
const Optional<OwningNonNull<NotificationPermissionCallback> >& aCallback,
|
|
|
|
ErrorResult& aRv)
|
|
|
|
{
|
|
|
|
// Get principal from global to make permission request for notifications.
|
2013-08-22 22:17:08 -07:00
|
|
|
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
|
|
|
|
nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aGlobal.GetAsSupports());
|
2013-03-18 06:24:54 -07:00
|
|
|
if (!sop) {
|
|
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
|
|
|
|
|
|
|
|
NotificationPermissionCallback* permissionCallback = nullptr;
|
|
|
|
if (aCallback.WasPassed()) {
|
2013-06-19 11:48:43 -07:00
|
|
|
permissionCallback = &aCallback.Value();
|
2013-03-18 06:24:54 -07:00
|
|
|
}
|
|
|
|
nsCOMPtr<nsIRunnable> request =
|
|
|
|
new NotificationPermissionRequest(principal, window, permissionCallback);
|
|
|
|
|
|
|
|
NS_DispatchToMainThread(request);
|
|
|
|
}
|
|
|
|
|
|
|
|
NotificationPermission
|
|
|
|
Notification::GetPermission(const GlobalObject& aGlobal, ErrorResult& aRv)
|
|
|
|
{
|
2013-08-22 22:17:08 -07:00
|
|
|
return GetPermissionInternal(aGlobal.GetAsSupports(), aRv);
|
2013-03-18 06:24:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
NotificationPermission
|
|
|
|
Notification::GetPermissionInternal(nsISupports* aGlobal, ErrorResult& aRv)
|
|
|
|
{
|
|
|
|
// Get principal from global to check permission for notifications.
|
|
|
|
nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aGlobal);
|
|
|
|
if (!sop) {
|
|
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
2013-05-06 12:28:13 -07:00
|
|
|
return NotificationPermission::Denied;
|
2013-03-18 06:24:54 -07:00
|
|
|
}
|
|
|
|
|
2013-05-23 15:38:00 -07:00
|
|
|
nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
|
|
|
|
if (nsContentUtils::IsSystemPrincipal(principal)) {
|
2013-05-06 12:28:13 -07:00
|
|
|
return NotificationPermission::Granted;
|
2013-05-23 15:38:00 -07:00
|
|
|
} else {
|
|
|
|
// Allow files to show notifications by default.
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
|
|
principal->GetURI(getter_AddRefs(uri));
|
|
|
|
if (uri) {
|
|
|
|
bool isFile;
|
|
|
|
uri->SchemeIs("file", &isFile);
|
|
|
|
if (isFile) {
|
|
|
|
return NotificationPermission::Granted;
|
|
|
|
}
|
|
|
|
}
|
2012-07-12 08:45:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// We also allow notifications is they are pref'ed on.
|
|
|
|
if (Preferences::GetBool("notification.prompt.testing", false)) {
|
|
|
|
if (Preferences::GetBool("notification.prompt.testing.allow", true)) {
|
2013-05-06 12:28:13 -07:00
|
|
|
return NotificationPermission::Granted;
|
2012-07-12 08:45:38 -07:00
|
|
|
} else {
|
2013-05-06 12:28:13 -07:00
|
|
|
return NotificationPermission::Denied;
|
2012-07-12 08:45:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-18 06:24:54 -07:00
|
|
|
uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION;
|
|
|
|
|
2013-08-15 13:15:07 -07:00
|
|
|
nsCOMPtr<nsIPermissionManager> permissionManager =
|
|
|
|
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
|
2013-03-18 06:24:54 -07:00
|
|
|
|
2013-08-15 13:15:07 -07:00
|
|
|
permissionManager->TestPermissionFromPrincipal(principal,
|
|
|
|
"desktop-notification",
|
|
|
|
&permission);
|
2013-03-18 06:24:54 -07:00
|
|
|
|
|
|
|
// Convert the result to one of the enum types.
|
|
|
|
switch (permission) {
|
|
|
|
case nsIPermissionManager::ALLOW_ACTION:
|
2013-05-06 12:28:13 -07:00
|
|
|
return NotificationPermission::Granted;
|
2013-03-18 06:24:54 -07:00
|
|
|
case nsIPermissionManager::DENY_ACTION:
|
2013-05-06 12:28:13 -07:00
|
|
|
return NotificationPermission::Denied;
|
2013-03-18 06:24:54 -07:00
|
|
|
default:
|
2013-05-06 12:28:13 -07:00
|
|
|
return NotificationPermission::Default;
|
2013-03-18 06:24:54 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-02 18:27:53 -07:00
|
|
|
already_AddRefed<Promise>
|
|
|
|
Notification::Get(const GlobalObject& aGlobal,
|
|
|
|
const GetNotificationOptions& aFilter,
|
|
|
|
ErrorResult& aRv)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
|
|
|
|
MOZ_ASSERT(window);
|
|
|
|
nsIDocument* doc = window->GetExtantDoc();
|
|
|
|
if (!doc) {
|
|
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsString origin;
|
|
|
|
aRv = GetOrigin(window, origin);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsINotificationStorage> notificationStorage =
|
|
|
|
do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID, &rv);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
aRv.Throw(rv);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsRefPtr<Promise> promise = new Promise(window);
|
|
|
|
nsCOMPtr<nsINotificationStorageCallback> callback =
|
|
|
|
new NotificationStorageCallback(aGlobal, window, promise);
|
|
|
|
nsString tag = aFilter.mTag.WasPassed() ?
|
|
|
|
aFilter.mTag.Value() :
|
|
|
|
EmptyString();
|
|
|
|
aRv = notificationStorage->Get(origin, tag, callback);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return promise.forget();
|
|
|
|
}
|
|
|
|
|
2013-03-18 06:24:54 -07:00
|
|
|
bool
|
|
|
|
Notification::PrefEnabled()
|
|
|
|
{
|
|
|
|
return Preferences::GetBool("dom.webnotifications.enabled", false);
|
|
|
|
}
|
|
|
|
|
|
|
|
JSObject*
|
2013-04-25 09:29:54 -07:00
|
|
|
Notification::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
|
2013-03-18 06:24:54 -07:00
|
|
|
{
|
|
|
|
return mozilla::dom::NotificationBinding::Wrap(aCx, aScope, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Notification::Close()
|
|
|
|
{
|
|
|
|
// Queue a task to close the notification.
|
2013-12-05 15:26:04 -08:00
|
|
|
nsCOMPtr<nsIRunnable> closeNotificationTask =
|
2013-03-18 06:24:54 -07:00
|
|
|
new NotificationTask(this, NotificationTask::eClose);
|
2013-12-05 15:26:04 -08:00
|
|
|
NS_DispatchToMainThread(closeNotificationTask);
|
2013-03-18 06:24:54 -07:00
|
|
|
}
|
|
|
|
|
2013-10-02 18:27:53 -07:00
|
|
|
void
|
2013-03-18 06:24:54 -07:00
|
|
|
Notification::CloseInternal()
|
|
|
|
{
|
|
|
|
if (!mIsClosed) {
|
2013-10-02 18:27:53 -07:00
|
|
|
nsresult rv;
|
|
|
|
// Don't bail out if notification storage fails, since we still
|
|
|
|
// want to send the close event through the alert service.
|
|
|
|
nsCOMPtr<nsINotificationStorage> notificationStorage =
|
|
|
|
do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID);
|
|
|
|
if (notificationStorage) {
|
|
|
|
nsString origin;
|
|
|
|
rv = GetOrigin(GetOwner(), origin);
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
notificationStorage->Delete(origin, mID);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-18 06:24:54 -07:00
|
|
|
nsCOMPtr<nsIAlertsService> alertService =
|
|
|
|
do_GetService(NS_ALERTSERVICE_CONTRACTID);
|
|
|
|
if (alertService) {
|
|
|
|
nsString alertName;
|
2013-10-02 18:27:53 -07:00
|
|
|
rv = GetAlertName(alertName);
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
2013-11-11 15:56:21 -08:00
|
|
|
alertService->CloseAlert(alertName, GetPrincipal());
|
2013-10-02 18:27:53 -07:00
|
|
|
}
|
2013-10-07 11:41:13 -07:00
|
|
|
}
|
2013-10-02 18:27:53 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
2013-10-02 18:27:53 -07:00
|
|
|
Notification::GetOrigin(nsPIDOMWindow* aWindow, nsString& aOrigin)
|
2013-10-02 18:27:53 -07:00
|
|
|
{
|
2013-12-05 15:26:04 -08:00
|
|
|
if (!aWindow) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2013-10-02 18:27:53 -07:00
|
|
|
nsresult rv;
|
|
|
|
nsIDocument* doc = aWindow->GetExtantDoc();
|
|
|
|
NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
|
|
|
|
nsIPrincipal* principal = doc->NodePrincipal();
|
|
|
|
NS_ENSURE_TRUE(principal, NS_ERROR_UNEXPECTED);
|
2013-10-07 11:41:13 -07:00
|
|
|
|
2013-10-02 18:27:53 -07:00
|
|
|
uint16_t appStatus = principal->GetAppStatus();
|
|
|
|
uint32_t appId = principal->GetAppId();
|
2013-10-07 11:41:13 -07:00
|
|
|
|
2013-10-02 18:27:53 -07:00
|
|
|
if (appStatus == nsIPrincipal::APP_STATUS_NOT_INSTALLED ||
|
|
|
|
appId == nsIScriptSecurityManager::NO_APP_ID ||
|
|
|
|
appId == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
|
|
|
|
rv = nsContentUtils::GetUTFOrigin(principal, aOrigin);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
} else {
|
|
|
|
// If we are in "app code", use manifest URL as unique origin since
|
|
|
|
// multiple apps can share the same origin but not same notifications.
|
|
|
|
nsCOMPtr<nsIAppsService> appsService =
|
|
|
|
do_GetService("@mozilla.org/AppsService;1", &rv);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
appsService->GetManifestURLByLocalId(appId, aOrigin);
|
|
|
|
}
|
2013-10-07 11:41:13 -07:00
|
|
|
|
2013-10-02 18:27:53 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
Notification::GetAlertName(nsString& aAlertName)
|
|
|
|
{
|
|
|
|
// Get the notification name that is unique per origin + tag/ID.
|
|
|
|
// The name of the alert is of the form origin#tag/ID.
|
|
|
|
nsresult rv = GetOrigin(GetOwner(), aAlertName);
|
2013-03-18 06:24:54 -07:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
aAlertName.AppendLiteral("#");
|
2013-10-02 18:27:53 -07:00
|
|
|
if (!mTag.IsEmpty()) {
|
|
|
|
aAlertName.Append(NS_LITERAL_STRING("tag:"));
|
|
|
|
aAlertName.Append(mTag);
|
|
|
|
} else {
|
|
|
|
aAlertName.Append(NS_LITERAL_STRING("notag:"));
|
|
|
|
aAlertName.Append(mID);
|
|
|
|
}
|
2013-03-18 06:24:54 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace dom
|
|
|
|
} // namespace mozilla
|
|
|
|
|