mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-02 15:15:23 +00:00
f9035eb119
For top-level navigations, we need to compute the client ID when we decide to intercept the document load, and we need to make sure the document that will be created later will end up using that same ID.
211 lines
6.0 KiB
C++
211 lines
6.0 KiB
C++
/* -*- 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 "nsIDocument.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)
|
|
{
|
|
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 = static_cast<nsGlobalWindow*>(aDoc->GetInnerWindow());
|
|
if (innerWindow) {
|
|
// XXXcatalinb: The inner window can be null if the document is navigating
|
|
// and was detached.
|
|
mWindowId = innerWindow->WindowID();
|
|
}
|
|
|
|
aDoc->GetURL(mUrl);
|
|
mVisibilityState = aDoc->VisibilityState();
|
|
|
|
ErrorResult result;
|
|
mFocused = aDoc->HasFocus(result);
|
|
if (result.Failed()) {
|
|
NS_WARNING("Failed to get focus information.");
|
|
}
|
|
|
|
RefPtr<nsGlobalWindow> outerWindow = static_cast<nsGlobalWindow*>(aDoc->GetWindow());
|
|
MOZ_ASSERT(outerWindow);
|
|
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 nsRunnable
|
|
, public StructuredCloneHolder
|
|
{
|
|
uint64_t mWindowId;
|
|
|
|
public:
|
|
explicit ServiceWorkerClientPostMessageRunnable(uint64_t aWindowId)
|
|
: StructuredCloneHolder(CloningSupported, TransferringSupported,
|
|
SameProcessDifferentThread)
|
|
, mWindowId(aWindowId)
|
|
{}
|
|
|
|
NS_IMETHOD
|
|
Run()
|
|
{
|
|
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();
|
|
|
|
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);
|
|
|
|
init.mData = messageData;
|
|
init.mOrigin.Construct(EmptyString());
|
|
init.mLastEventId.Construct(EmptyString());
|
|
init.mPorts.Construct();
|
|
init.mPorts.Value().SetNull();
|
|
|
|
RefPtr<ServiceWorker> serviceWorker = aTargetContainer->GetController();
|
|
init.mSource.Construct();
|
|
if (serviceWorker) {
|
|
init.mSource.Value().SetValue().SetAsServiceWorker() = serviceWorker;
|
|
} else {
|
|
init.mSource.Value().SetNull();
|
|
}
|
|
|
|
RefPtr<ServiceWorkerMessageEvent> event =
|
|
ServiceWorkerMessageEvent::Constructor(aTargetContainer,
|
|
NS_LITERAL_STRING("message"), init, rv);
|
|
|
|
nsTArray<RefPtr<MessagePort>> ports = TakeTransferredPorts();
|
|
|
|
RefPtr<MessagePortList> portList =
|
|
new MessagePortList(static_cast<dom::Event*>(event.get()),
|
|
ports);
|
|
event->SetPorts(portList);
|
|
|
|
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, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return;
|
|
}
|
|
|
|
aRv = NS_DispatchToMainThread(runnable);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return;
|
|
}
|
|
}
|
|
|