/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/. */
// Skip scopes that don't match when called from getNotifications(). if (!mScope.IsEmpty() && !mScope.Equals(aServiceWorkerRegistrationScope)) { return NS_OK;
}
class NotificationStorageCallback final : public ScopeCheckingGetCallback { public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(NotificationStorageCallback)
NS_IMETHOD Done() final {
AutoTArray<RefPtr<Notification>, 5> notifications;
for (uint32_t i = 0; i < mStrings.Length(); ++i) { auto result = Notification::ConstructFromFields(
mWindow, mStrings[i].mID, mStrings[i].mTitle, mStrings[i].mDir,
mStrings[i].mLang, mStrings[i].mBody, mStrings[i].mTag,
mStrings[i].mIcon, mStrings[i].mData,
mStrings[i].mServiceWorkerRegistrationScope); if (result.isErr()) { continue;
}
RefPtr<Notification> n = result.unwrap();
notifications.AppendElement(n.forget());
}
nsresult rv = notificationStorage->Get(mOrigin, mTag, mCallback); // XXXnsm Is it guaranteed mCallback will be called in case of failure?
Unused << NS_WARN_IF(NS_FAILED(rv)); return rv;
}
};
class NotificationPermissionRequest : public ContentPermissionRequestBase, public nsIRunnable, public nsINamed { public:
NS_DECL_NSIRUNNABLE
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(NotificationPermissionRequest,
ContentPermissionRequestBase)
// Subclass that can be directly dispatched to child workers from the main // thread. class NotificationWorkerRunnable : public MainThreadWorkerRunnable { protected: explicit NotificationWorkerRunnable(
WorkerPrivate* aWorkerPrivate, constchar* aName = "NotificationWorkerRunnable")
: MainThreadWorkerRunnable(aName) {}
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
aWorkerPrivate->AssertIsOnWorkerThread(); // WorkerScope might start dying at the moment. And WorkerRunInternal() // should not be executed once WorkerScope is dying, since // WorkerRunInternal() might access resources which already been freed // during WorkerRef::Notify(). if (aWorkerPrivate->GlobalScope() &&
!aWorkerPrivate->GlobalScope()->IsDying()) {
WorkerRunInternal(aWorkerPrivate);
} returntrue;
}
// We can't call ShowPrompt() directly here since our logic for determining // whether to display a prompt depends on the checks above as well as the // result of CheckPromptPrefs(). So we have to manually check the prompt // prefs and decide what to do based on that.
PromptResult pr = CheckPromptPrefs(); switch (pr) { case PromptResult::Granted:
mPermission = NotificationPermission::Granted; break; case PromptResult::Denied:
mPermission = NotificationPermission::Denied; break; default: // ignore break;
}
if (!mHasValidTransientUserGestureActivation &&
!StaticPrefs::dom_webnotifications_requireuserinteraction()) {
nsCOMPtr<Document> doc = mWindow->GetExtantDoc(); if (doc) {
doc->WarnOnceAbout(Document::eNotificationsRequireUserGestureDeprecation);
}
}
if (mPermission != NotificationPermission::Default) { return DispatchResolvePromise();
}
NS_IMETHODIMP
NotificationPermissionRequest::Cancel() { // `Cancel` is called if the user denied permission or dismissed the // permission request. To distinguish between the two, we set the // permission to "default" and query the permission manager in // `ResolvePromise`.
mPermission = NotificationPermission::Default; return DispatchResolvePromise();
}
nsresult NotificationPermissionRequest::ResolvePromise() {
nsresult rv = NS_OK; // This will still be "default" if the user dismissed the doorhanger, // or "denied" otherwise. if (mPermission == NotificationPermission::Default) { // When the front-end has decided to deny the permission request // automatically and we are not handling user input, then log a // warning in the current document that this happened because // Notifications require a user gesture. if (!mHasValidTransientUserGestureActivation &&
StaticPrefs::dom_webnotifications_requireuserinteraction()) {
nsCOMPtr<Document> doc = mWindow->GetExtantDoc(); if (doc) {
nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, "DOM"_ns,
doc, nsContentUtils::eDOM_PROPERTIES, "NotificationsRequireUserGesture");
}
}
// May be called on any thread. // static
already_AddRefed<Notification> Notification::Constructor( const GlobalObject& aGlobal, const nsAString& aTitle, const NotificationOptions& aOptions, ErrorResult& aRv) { // FIXME(nsm): If the sticky flag is set, throw an error.
RefPtr<ServiceWorkerGlobalScope> scope;
UNWRAP_OBJECT(ServiceWorkerGlobalScope, aGlobal.Get(), scope); if (scope) {
aRv.ThrowTypeError( "Notification constructor cannot be used in ServiceWorkerGlobalScope. " "Use registration.showNotification() instead."); return nullptr;
}
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
RefPtr<Notification> notification =
Create(aGlobal.Context(), global, aTitle, aOptions, u""_ns, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr;
}
char buffer[NSID_LENGTH];
uuid.ToProvidedString(buffer);
NS_ConvertASCIItoUTF16 convertedID(buffer);
id = convertedID;
}
// Step 20: Set notification’s silent preference to options["silent"]. bool silent = false; if (StaticPrefs::dom_webnotifications_silent_enabled()) {
silent = aOptions.mSilent;
}
nsTArray<uint32_t> vibrate; if (StaticPrefs::dom_webnotifications_vibrate_enabled() &&
aOptions.mVibrate.WasPassed()) { // Step 4: If options["silent"] is true and options["vibrate"] exists, then // throw a TypeError. if (silent) {
aRv.ThrowTypeError( "Silent notifications must not specify vibration patterns."); return nullptr;
}
// Step 17: If options["vibrate"] exists, then validate and normalize it and // set notification’s vibration pattern to the return value. const OwningUnsignedLongOrUnsignedLongSequence& value =
aOptions.mVibrate.Value(); if (value.IsUnsignedLong()) {
AutoTArray<uint32_t, 1> array;
array.AppendElement(value.GetAsUnsignedLong());
vibrate = SanitizeVibratePattern(array);
} else {
vibrate = SanitizeVibratePattern(value.GetAsUnsignedLongSequence());
}
}
// Step 15: If options["icon"] exists, then parse it using baseURL, and if // that does not return failure, set notification’s icon URL to the return // value. (Otherwise icon URL is not set.)
nsString iconUrl = aOptions.mIcon;
ResolveIconURL(aGlobal, iconUrl);
/* static */ bool Notification::RequestPermissionEnabledForScope(JSContext* aCx,
JSObject* /* unused */) { // requestPermission() is not allowed on workers. The calling page should ask // for permission on the worker's behalf. This is to prevent 'which window // should show the browser pop-up'. See discussion: // http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2013-October/041272.html return NS_IsMainThread();
}
// Get principal from global to make permission request for notifications.
nsCOMPtr<nsPIDOMWindowInner> window =
do_QueryInterface(aGlobal.GetAsSupports());
nsCOMPtr<nsIScriptObjectPrincipal> sop =
do_QueryInterface(aGlobal.GetAsSupports()); if (!sop || !window) {
aRv.Throw(NS_ERROR_UNEXPECTED); return nullptr;
}
nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
nsCOMPtr<nsIPrincipal> effectiveStoragePrincipal =
sop->GetEffectiveStoragePrincipal(); if (!principal || !effectiveStoragePrincipal) {
aRv.Throw(NS_ERROR_UNEXPECTED); return nullptr;
}
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(worker);
RefPtr<GetPermissionRunnable> r = new GetPermissionRunnable(
worker, worker->UseRegularPrincipal(), aPurpose);
r->Dispatch(worker, Canceling, aRv); if (aRv.Failed()) { return NotificationPermission::Denied;
}
return r->GetPermission();
}
/* static */
NotificationPermission Notification::GetPermissionInternal(
nsPIDOMWindowInner* aWindow, PermissionCheckPurpose aPurpose,
ErrorResult& aRv) { // Get principal from global to check permission for notifications.
nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow); if (!sop) {
aRv.Throw(NS_ERROR_UNEXPECTED); return NotificationPermission::Denied;
}
nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
nsCOMPtr<nsIPrincipal> effectiveStoragePrincipal =
sop->GetEffectiveStoragePrincipal(); if (!principal || !effectiveStoragePrincipal) {
aRv.Throw(NS_ERROR_UNEXPECTED); return NotificationPermission::Denied;
}
// XXXnsm If I understand correctly, the character encoding for resolving // URIs in new specs is dictated by the URL spec, which states that unless // the URL parser is passed an override encoding, the charset to be used is // UTF-8. The new Notification icon/sound specification just says to use the // Fetch API, where the Request constructor defers to URL parsing specifying // the API base URL and no override encoding. So we've to use UTF-8 on // workers, but for backwards compat keeping it document charset on main // thread. auto encoding = UTF_8_ENCODING;
if (nsCOMPtr<nsPIDOMWindowInner> window = aGlobal->GetAsInnerWindow()) { if (RefPtr<Document> doc = window->GetExtantDoc()) {
baseUri = doc->GetBaseURI();
encoding = doc->GetDocumentCharacterSet();
} else {
NS_WARNING("No document found for main thread notification!"); return NS_ERROR_FAILURE;
}
} elseif (WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate()) {
baseUri = workerPrivate->GetBaseURI();
}
if (baseUri) { if (aIconUrl.Length() > 0) {
nsCOMPtr<nsIURI> srcUri;
rv = NS_NewURI(getter_AddRefs(srcUri), aIconUrl, encoding, baseUri); if (NS_SUCCEEDED(rv)) {
nsAutoCString src;
srcUri->GetSpec(src);
CopyUTF8toUTF16(src, aIconUrl);
}
}
}
nsCOMPtr<nsINotificationStorageCallback> callback = new NotificationStorageCallback(aWindow->AsGlobal(), aScope, promise);
RefPtr<NotificationGetRunnable> r = new NotificationGetRunnable(
origin, aFilter.mTag, callback, doc->IsInPrivateBrowsing());
aRv = aWindow->AsGlobal()->Dispatch(r.forget()); if (NS_WARN_IF(aRv.Failed())) { return nullptr;
}
return promise.forget();
}
class WorkerGetResultRunnable final : public NotificationWorkerRunnable {
RefPtr<PromiseWorkerProxy> mPromiseProxy; const nsTArray<NotificationStrings> mStrings;
void WorkerRunInternal(WorkerPrivate* aWorkerPrivate) override {
RefPtr<Promise> workerPromise = mPromiseProxy->GetWorkerPromise(); // Once Worker had already started shutdown, workerPromise would be nullptr if (!workerPromise) { return;
}
AutoTArray<RefPtr<Notification>, 5> notifications; for (uint32_t i = 0; i < mStrings.Length(); ++i) { auto result = Notification::ConstructFromFields(
aWorkerPrivate->GlobalScope(), mStrings[i].mID, mStrings[i].mTitle,
mStrings[i].mDir, mStrings[i].mLang, mStrings[i].mBody,
mStrings[i].mTag, mStrings[i].mIcon, mStrings[i].mData, /* mStrings[i].mBehavior, not
* supported */
mStrings[i].mServiceWorkerRegistrationScope); if (result.isErr()) { continue;
}
RefPtr<Notification> n = result.unwrap();
notifications.AppendElement(n.forget());
}
RefPtr<WorkerGetRunnable> r = new WorkerGetRunnable(proxy, aFilter.mTag, aScope); // Since this is called from script via // ServiceWorkerRegistration::GetNotifications, we can assert dispatch.
MOZ_ALWAYS_SUCCEEDS(aWorkerPrivate->DispatchToMainThread(r.forget())); return p.forget();
}
if (data.isGCThing()) {
mozilla::HoldJSObjects(this);
}
mData = data;
} if (mData.isNull()) {
aRetval.setNull(); return;
}
aRetval.set(mData);
}
void Notification::InitFromJSVal(JSContext* aCx, JS::Handle<JS::Value> aData,
ErrorResult& aRv) { if (!mDataAsBase64.IsEmpty() || aData.isNull()) { return;
}
RefPtr<nsStructuredCloneContainer> dataObjectContainer = new nsStructuredCloneContainer();
aRv = dataObjectContainer->InitFromJSVal(aData, aCx); if (NS_WARN_IF(aRv.Failed())) { return;
}
aRv = dataObjectContainer->GetDataAsBase64(mDataAsBase64); if (NS_WARN_IF(aRv.Failed())) { return;
}
}
Result<Ok, QMResult> Notification::InitFromBase64(const nsAString& aData) {
MOZ_ASSERT(mDataAsBase64.IsEmpty()); if (aData.IsEmpty()) { // No data; skipping return Ok();
}
// To and fro to ensure it is valid base64.
RefPtr<nsStructuredCloneContainer> container = new nsStructuredCloneContainer();
QM_TRY(QM_TO_RESULT(
container->InitFromBase64(aData, JS_STRUCTURED_CLONE_VERSION)));
QM_TRY(QM_TO_RESULT(container->GetDataAsBase64(mDataAsBase64)));
// Step 2: Let promise be a new promise in this’s relevant Realm.
RefPtr<Promise> p = Promise::Create(aGlobal, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr;
}
// Step 3: If this’s active worker is null, then reject promise with a // TypeError and return promise. if (!aDescriptor.GetActive()) {
aRv.ThrowTypeError<MSG_NO_ACTIVE_WORKER>(NS_ConvertUTF16toUTF8(aScope)); return nullptr;
}
// Step 4: Let notification be the result of creating a notification with a // settings object given title, options, and this’s relevant settings object. // If this threw an exception, then reject promise with that exception and // return promise. // // Step 5: Set notification’s service worker registration to this. // // Note: We currently use the scope as the unique identifier for the // registration (and there currently is no durable registration identifier, // so this is necessary), which is why we pass in the scope. See // https://github.com/whatwg/notifications/issues/205 for some scope-related // discussion. // // XXX: We create Notification object almost solely to share the parameter // normalization steps. It would be nice to export that and skip creating // object here.
RefPtr<Notification> notification =
Create(aCx, aGlobal, aTitle, aOptions, aScope, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr;
}
if (!notification->CreateActor() || !notification->SendShow(p)) { return nullptr;
}
// Make a structured clone of the aOptions.mData object
JS::Rooted<JS::Value> data(aCx, aOptions.mData);
notification->InitFromJSVal(aCx, data, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr;
}
// Note: We are not using the typical PBackground managed actor here as we // want the actor to be in the main thread of the main process. Instead we // pass the endpoint to PBackground, it dispatches a runnable to the main // thread, and the endpoint is bound there.
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.