/* -*- 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/. */
void ServiceWorkerContainer::ControllerChanged(ErrorResult& aRv) {
nsCOMPtr<nsIGlobalObject> go = GetParentObject(); if (!go) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return;
}
mControllerWorker = go->GetOrCreateServiceWorker(go->GetController().ref());
aRv = DispatchTrustedEvent(u"controllerchange"_ns);
}
using mozilla::dom::ipc::StructuredCloneData;
// A ReceivedMessage represents a message sent via // Client.postMessage(). It is used as used both for queuing of // incoming messages and as an interface to DispatchMessage(). struct MOZ_HEAP_CLASS ServiceWorkerContainer::ReceivedMessage { explicit ReceivedMessage(const ClientPostMessageArgs& aArgs)
: mServiceWorker(aArgs.serviceWorker()) {
mClonedData.CopyFromClonedMessageData(aArgs.clonedData());
}
// Note, we can't use GetGlobalIfValid() from the start here. If we // hit a storage failure we want to log a message with the final // scope string we put together below.
nsCOMPtr<nsIGlobalObject> global = GetParentObject(); if (!global) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return nullptr;
}
// Don't use NS_ConvertUTF16toUTF8 because that doesn't let us handle OOM.
nsAutoCString scriptURL; if (!AppendUTF16toUTF8(*compliantString, scriptURL, fallible)) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return nullptr;
}
// Never allow script URL with moz-extension scheme if support is fully // disabled by the 'extensions.background_service_worker.enabled' pref. if (scriptURI->SchemeIs("moz-extension") &&
!StaticPrefs::extensions_backgroundServiceWorker_enabled_AtStartup()) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); return nullptr;
}
// In ServiceWorkerContainer.register() the scope argument is parsed against // different base URLs depending on whether it was passed or not.
nsCOMPtr<nsIURI> scopeURI;
// Step 4. If none passed, parse against script's URL if (!aOptions.mScope.WasPassed()) {
constexpr auto defaultScope = "./"_ns;
rv = NS_NewURI(getter_AddRefs(scopeURI), defaultScope, nullptr, scriptURI); if (NS_WARN_IF(NS_FAILED(rv))) {
nsAutoCString spec;
scriptURI->GetSpec(spec);
aRv.ThrowTypeError<MSG_INVALID_SCOPE>(defaultScope, spec); return nullptr;
}
} else { // Step 5. Parse against entry settings object's base URL.
rv = NS_NewURI(getter_AddRefs(scopeURI), aOptions.mScope.Value(), nullptr,
baseURI); if (NS_WARN_IF(NS_FAILED(rv))) {
nsIURI* uri = baseURI ? baseURI : scriptURI;
nsAutoCString spec;
uri->GetSpec(spec);
aRv.ThrowTypeError<MSG_INVALID_SCOPE>(
NS_ConvertUTF16toUTF8(aOptions.mScope.Value()), spec); return nullptr;
}
}
// Strip the any ref from both the script and scope URLs.
nsCOMPtr<nsIURI> cloneWithoutRef;
aRv = NS_GetURIWithoutRef(scriptURI, getter_AddRefs(cloneWithoutRef)); if (aRv.Failed()) { return nullptr;
}
scriptURI = std::move(cloneWithoutRef);
aRv = NS_GetURIWithoutRef(scopeURI, getter_AddRefs(cloneWithoutRef)); if (aRv.Failed()) { return nullptr;
}
scopeURI = std::move(cloneWithoutRef);
// Get the string representation for both the script and scope since // we sanitized them above.
nsCString cleanedScopeURL;
aRv = scopeURI->GetSpec(cleanedScopeURL); if (aRv.Failed()) { return nullptr;
}
nsCString cleanedScriptURL;
aRv = scriptURI->GetSpec(cleanedScriptURL); if (aRv.Failed()) { return nullptr;
}
// Verify that the global is valid and has permission to store // data. We perform this late so that we can report the final // scope URL in any error message.
Unused << GetGlobalIfValid(aRv, [&](nsIGlobalObject* aGlobal) {
AutoTArray<nsString, 1> param;
CopyUTF8toUTF16(cleanedScopeURL, *param.AppendElement());
aGlobal->ReportToConsole(nsIScriptError::errorFlag, "Service Workers"_ns,
nsContentUtils::eDOM_PROPERTIES, "ServiceWorkerRegisterStorageError"_ns, param);
});
// TODO: For bug 1836707 we will move this tracking to ServiceWorkerManager // where it can establish the mapping between the job and our client info, // which will also work on workers. For now we leave this notification for // Windows only. if (auto* window = global->GetAsInnerWindow()) {
window->NoteCalledRegisterForServiceWorkerScope(cleanedScopeURL);
}
// Don't resolve the ready promise until the registration has // reached the right version. This ensures that the active // worker property is set correctly on the registration.
reg->WhenVersionReached(ipcDesc.version(), [outer, reg](bool aResult) {
outer->MaybeResolve(reg);
});
},
[outer](ResponseRejectReason&& aReason) { // IPC layer error
outer->MaybeReject(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
});
return mReadyPromise;
}
nsIGlobalObject* ServiceWorkerContainer::GetGlobalIfValid(
ErrorResult& aRv, const std::function<void(nsIGlobalObject*)>&& aStorageFailureCB) const {
nsIGlobalObject* global = GetOwnerGlobal(); if (NS_WARN_IF(!global)) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return nullptr;
}
if (NS_FAILED(CheckCurrentGlobalCorrectness())) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return nullptr;
}
// Don't allow a service worker to access service worker registrations // from a global with storage disabled. If these globals can access // the registration it increases the chance they can bypass the storage // block via postMessage(), etc. auto storageAllowed = global->GetStorageAccess(); if (NS_WARN_IF(storageAllowed != StorageAccess::eAllow &&
(!StaticPrefs::privacy_partition_serviceWorkers() ||
!StoragePartitioningEnabled(
storageAllowed, global->GetCookieJarSettings())))) { if (aStorageFailureCB) {
aStorageFailureCB(global);
}
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); return nullptr;
}
// Don't allow service workers for system principals.
nsIPrincipal* principal = global->PrincipalOrNull(); if (NS_WARN_IF(!principal || principal->IsSystemPrincipal())) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); return nullptr;
}
// When dispatching a message, either DOMContentLoaded has already // been fired, or someone called startMessages() or set onmessage. // Either way, a global object is supposed to be present. If it's // not, we'd fail to initialize the JS API and exit.
RunWithJSContext([this, message = std::move(aMessage)](
JSContext* const aCx, nsIGlobalObject* const aGlobal) {
ErrorResult result; bool deserializationFailed = false;
RootedDictionary<MessageEventInit> init(aCx); auto res = FillInMessageEventInit(aCx, aGlobal, *message, init, result); if (res.isErr()) {
deserializationFailed = res.unwrapErr();
MOZ_ASSERT_IF(deserializationFailed, init.mData.isNull());
MOZ_ASSERT_IF(deserializationFailed, init.mPorts.IsEmpty());
MOZ_ASSERT_IF(deserializationFailed, !init.mOrigin.IsEmpty());
MOZ_ASSERT_IF(deserializationFailed, !init.mSource.IsNull());
result.SuppressException();
if (!deserializationFailed && result.MaybeSetPendingException(aCx)) { return;
}
}
result = NS_OK;
DispatchEvent(*event, result); if (result.Failed()) {
result.SuppressException();
}
});
}
namespace {
nsresult FillInOriginNoSuffix(const ServiceWorkerDescriptor& aServiceWorker,
nsString& aOrigin) { using mozilla::ipc::PrincipalInfoToPrincipal;
nsresult rv;
auto principalOrErr =
PrincipalInfoToPrincipal(aServiceWorker.PrincipalInfo()); if (NS_WARN_IF(principalOrErr.isErr())) { return principalOrErr.unwrapErr();
}
Result<Ok, bool> ServiceWorkerContainer::FillInMessageEventInit(
JSContext* const aCx, nsIGlobalObject* const aGlobal,
ReceivedMessage& aMessage, MessageEventInit& aInit, ErrorResult& aRv) { // Determining the source and origin should preceed attempting deserialization // because on a "messageerror" event (i.e. when deserialization fails), the // dispatched message needs to contain such an origin and source, per spec: // // "If this throws an exception, catch it, fire an event named messageerror // at destination, using MessageEvent, with the origin attribute initialized // to origin and the source attribute initialized to source, and then abort // these steps." - 6.4 of postMessage // See: https://w3c.github.io/ServiceWorker/#service-worker-postmessage const RefPtr<ServiceWorker> serviceWorkerInstance =
aGlobal->GetOrCreateServiceWorker(aMessage.mServiceWorker); if (serviceWorkerInstance) {
aInit.mSource.SetValue().SetAsServiceWorker() = serviceWorkerInstance;
}
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.