diff options
Diffstat (limited to 'dom/workers/ServiceWorkerClient.cpp')
-rw-r--r-- | dom/workers/ServiceWorkerClient.cpp | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/dom/workers/ServiceWorkerClient.cpp b/dom/workers/ServiceWorkerClient.cpp new file mode 100644 index 000000000..660512a5f --- /dev/null +++ b/dom/workers/ServiceWorkerClient.cpp @@ -0,0 +1,232 @@ +/* -*- 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/. + */ + +#include "ServiceWorkerClient.h" +#include "ServiceWorkerContainer.h" + +#include "mozilla/dom/MessageEvent.h" +#include "mozilla/dom/Navigator.h" +#include "mozilla/dom/ServiceWorkerMessageEvent.h" +#include "mozilla/dom/ServiceWorkerMessageEventBinding.h" +#include "nsGlobalWindow.h" +#include "nsIBrowserDOMWindow.h" +#include "nsIDocument.h" +#include "ServiceWorker.h" +#include "ServiceWorkerPrivate.h" +#include "WorkerPrivate.h" + +using namespace mozilla; +using namespace mozilla::dom; +using namespace mozilla::dom::workers; + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ServiceWorkerClient, mOwner) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(ServiceWorkerClient) +NS_IMPL_CYCLE_COLLECTING_RELEASE(ServiceWorkerClient) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerClient) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +ServiceWorkerClientInfo::ServiceWorkerClientInfo(nsIDocument* aDoc) + : mWindowId(0) + , mFrameType(FrameType::None) +{ + MOZ_ASSERT(aDoc); + nsresult rv = aDoc->GetOrCreateId(mClientId); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to get the UUID of the document."); + } + + RefPtr<nsGlobalWindow> innerWindow = nsGlobalWindow::Cast(aDoc->GetInnerWindow()); + if (innerWindow) { + // XXXcatalinb: The inner window can be null if the document is navigating + // and was detached. + mWindowId = innerWindow->WindowID(); + } + + nsCOMPtr<nsIURI> originalURI = aDoc->GetOriginalURI(); + if (originalURI) { + nsAutoCString spec; + originalURI->GetSpec(spec); + CopyUTF8toUTF16(spec, mUrl); + } + mVisibilityState = aDoc->VisibilityState(); + + ErrorResult result; + mFocused = aDoc->HasFocus(result); + if (result.Failed()) { + NS_WARNING("Failed to get focus information."); + } + + RefPtr<nsGlobalWindow> outerWindow = nsGlobalWindow::Cast(aDoc->GetWindow()); + if (!outerWindow) { + MOZ_ASSERT(mFrameType == FrameType::None); + } else if (!outerWindow->IsTopLevelWindow()) { + mFrameType = FrameType::Nested; + } else if (outerWindow->HadOriginalOpener()) { + mFrameType = FrameType::Auxiliary; + } else { + mFrameType = FrameType::Top_level; + } +} + +JSObject* +ServiceWorkerClient::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return ClientBinding::Wrap(aCx, this, aGivenProto); +} + +namespace { + +class ServiceWorkerClientPostMessageRunnable final + : public Runnable + , public StructuredCloneHolder +{ + uint64_t mWindowId; + +public: + explicit ServiceWorkerClientPostMessageRunnable(uint64_t aWindowId) + : StructuredCloneHolder(CloningSupported, TransferringSupported, + StructuredCloneScope::SameProcessDifferentThread) + , mWindowId(aWindowId) + {} + + NS_IMETHOD + Run() override + { + AssertIsOnMainThread(); + nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId); + if (!window) { + return NS_ERROR_FAILURE; + } + + ErrorResult result; + dom::Navigator* navigator = window->GetNavigator(result); + if (NS_WARN_IF(result.Failed())) { + return result.StealNSResult(); + } + + RefPtr<ServiceWorkerContainer> container = navigator->ServiceWorker(); + AutoJSAPI jsapi; + if (NS_WARN_IF(!jsapi.Init(window))) { + return NS_ERROR_FAILURE; + } + JSContext* cx = jsapi.cx(); + + return DispatchDOMEvent(cx, container); + } + +private: + NS_IMETHOD + DispatchDOMEvent(JSContext* aCx, ServiceWorkerContainer* aTargetContainer) + { + AssertIsOnMainThread(); + + MOZ_ASSERT(aTargetContainer->GetParentObject(), + "How come we don't have a window here?!"); + + JS::Rooted<JS::Value> messageData(aCx); + ErrorResult rv; + Read(aTargetContainer->GetParentObject(), aCx, &messageData, rv); + if (NS_WARN_IF(rv.Failed())) { + xpc::Throw(aCx, rv.StealNSResult()); + return NS_ERROR_FAILURE; + } + + RootedDictionary<ServiceWorkerMessageEventInit> init(aCx); + + nsCOMPtr<nsIPrincipal> principal = aTargetContainer->GetParentObject()->PrincipalOrNull(); + NS_WARNING_ASSERTION(principal, "Why is the principal null here?"); + + bool isNullPrincipal = false; + bool isSystemPrincipal = false; + if (principal) { + isNullPrincipal = principal->GetIsNullPrincipal(); + MOZ_ASSERT(!isNullPrincipal); + isSystemPrincipal = principal->GetIsSystemPrincipal(); + MOZ_ASSERT(!isSystemPrincipal); + } + + init.mData = messageData; + nsAutoCString origin; + if (principal && !isNullPrincipal && !isSystemPrincipal) { + principal->GetOrigin(origin); + } + init.mOrigin = NS_ConvertUTF8toUTF16(origin); + + RefPtr<ServiceWorker> serviceWorker = aTargetContainer->GetController(); + if (serviceWorker) { + init.mSource.SetValue().SetAsServiceWorker() = serviceWorker; + } + + if (!TakeTransferredPortsAsSequence(init.mPorts)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + RefPtr<ServiceWorkerMessageEvent> event = + ServiceWorkerMessageEvent::Constructor(aTargetContainer, + NS_LITERAL_STRING("message"), + init); + + event->SetTrusted(true); + bool status = false; + aTargetContainer->DispatchEvent(static_cast<dom::Event*>(event.get()), + &status); + + if (!status) { + return NS_ERROR_FAILURE; + } + + return NS_OK; + } +}; + +} // namespace + +void +ServiceWorkerClient::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage, + const Optional<Sequence<JS::Value>>& aTransferable, + ErrorResult& aRv) +{ + WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); + MOZ_ASSERT(workerPrivate); + workerPrivate->AssertIsOnWorkerThread(); + + JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue()); + if (aTransferable.WasPassed()) { + const Sequence<JS::Value>& realTransferable = aTransferable.Value(); + + JS::HandleValueArray elements = + JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(), + realTransferable.Elements()); + + JSObject* array = JS_NewArrayObject(aCx, elements); + if (!array) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + + transferable.setObject(*array); + } + + RefPtr<ServiceWorkerClientPostMessageRunnable> runnable = + new ServiceWorkerClientPostMessageRunnable(mWindowId); + + runnable->Write(aCx, aMessage, transferable, JS::CloneDataPolicy().denySharedArrayBuffer(), + aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + aRv = workerPrivate->DispatchToMainThread(runnable.forget()); + if (NS_WARN_IF(aRv.Failed())) { + return; + } +} + |