Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 : * You can obtain one at http://mozilla.org/MPL/2.0/.
6 : */
7 :
8 : #include "ServiceWorkerClient.h"
9 : #include "ServiceWorkerContainer.h"
10 :
11 : #include "mozilla/dom/MessageEvent.h"
12 : #include "mozilla/dom/Navigator.h"
13 : #include "nsGlobalWindow.h"
14 : #include "nsIBrowserDOMWindow.h"
15 : #include "nsIDocument.h"
16 : #include "ServiceWorker.h"
17 : #include "ServiceWorkerPrivate.h"
18 : #include "WorkerPrivate.h"
19 :
20 : using namespace mozilla;
21 : using namespace mozilla::dom;
22 : using namespace mozilla::dom::workers;
23 :
24 0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ServiceWorkerClient, mOwner)
25 :
26 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(ServiceWorkerClient)
27 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(ServiceWorkerClient)
28 :
29 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerClient)
30 0 : NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
31 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
32 0 : NS_INTERFACE_MAP_END
33 :
34 0 : ServiceWorkerClientInfo::ServiceWorkerClientInfo(nsIDocument* aDoc, uint32_t aOrdinal)
35 : : mType(ClientType::Window)
36 : , mOrdinal(aOrdinal)
37 : , mWindowId(0)
38 0 : , mFrameType(FrameType::None)
39 : {
40 0 : MOZ_ASSERT(aDoc);
41 0 : nsresult rv = aDoc->GetOrCreateId(mClientId);
42 0 : if (NS_FAILED(rv)) {
43 0 : NS_WARNING("Failed to get the UUID of the document.");
44 : }
45 :
46 0 : RefPtr<nsGlobalWindow> innerWindow = nsGlobalWindow::Cast(aDoc->GetInnerWindow());
47 0 : if (innerWindow) {
48 : // XXXcatalinb: The inner window can be null if the document is navigating
49 : // and was detached.
50 0 : mWindowId = innerWindow->WindowID();
51 : }
52 :
53 0 : nsCOMPtr<nsIURI> originalURI = aDoc->GetOriginalURI();
54 0 : if (originalURI) {
55 0 : nsAutoCString spec;
56 0 : originalURI->GetSpec(spec);
57 0 : CopyUTF8toUTF16(spec, mUrl);
58 : }
59 0 : mVisibilityState = aDoc->VisibilityState();
60 :
61 0 : mLastFocusTime = aDoc->LastFocusTime();
62 :
63 0 : ErrorResult result;
64 0 : mFocused = aDoc->HasFocus(result);
65 0 : if (result.Failed()) {
66 0 : NS_WARNING("Failed to get focus information.");
67 : }
68 :
69 0 : MOZ_ASSERT_IF(mLastFocusTime.IsNull(), !mFocused);
70 0 : MOZ_ASSERT_IF(mFocused, !mLastFocusTime.IsNull());
71 :
72 0 : RefPtr<nsGlobalWindow> outerWindow = nsGlobalWindow::Cast(aDoc->GetWindow());
73 0 : if (!outerWindow) {
74 0 : MOZ_ASSERT(mFrameType == FrameType::None);
75 0 : } else if (!outerWindow->IsTopLevelWindow()) {
76 0 : mFrameType = FrameType::Nested;
77 0 : } else if (outerWindow->HadOriginalOpener()) {
78 0 : mFrameType = FrameType::Auxiliary;
79 : } else {
80 0 : mFrameType = FrameType::Top_level;
81 : }
82 0 : }
83 :
84 : bool
85 0 : ServiceWorkerClientInfo::operator<(const ServiceWorkerClientInfo& aRight) const
86 : {
87 : // Note: the mLastFocusTime comparisons are reversed because we need to
88 : // put most recently focused values first. The mOrdinal comparison is
89 : // normal, though, because otherwise we want normal creation order.
90 :
91 0 : if (mLastFocusTime == aRight.mLastFocusTime) {
92 0 : return mOrdinal < aRight.mOrdinal;
93 : }
94 :
95 0 : if (mLastFocusTime.IsNull()) {
96 0 : return false;
97 : }
98 :
99 0 : if (aRight.mLastFocusTime.IsNull()) {
100 0 : return true;
101 : }
102 :
103 0 : return mLastFocusTime > aRight.mLastFocusTime;
104 : }
105 :
106 : bool
107 0 : ServiceWorkerClientInfo::operator==(const ServiceWorkerClientInfo& aRight) const
108 : {
109 0 : return mLastFocusTime == aRight.mLastFocusTime &&
110 0 : mOrdinal == aRight.mOrdinal &&
111 0 : mClientId == aRight.mClientId;
112 : }
113 :
114 : JSObject*
115 0 : ServiceWorkerClient::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
116 : {
117 0 : return ClientBinding::Wrap(aCx, this, aGivenProto);
118 : }
119 :
120 : ClientType
121 0 : ServiceWorkerClient::Type() const
122 : {
123 0 : return mType;
124 : }
125 :
126 : namespace {
127 :
128 0 : class ServiceWorkerClientPostMessageRunnable final
129 : : public Runnable
130 : , public StructuredCloneHolder
131 : {
132 : const uint64_t mSourceID;
133 : const nsCString mSourceScope;
134 : const uint64_t mWindowId;
135 :
136 : public:
137 0 : ServiceWorkerClientPostMessageRunnable(uint64_t aSourceID,
138 : const nsACString& aSourceScope,
139 : uint64_t aWindowId)
140 0 : : mozilla::Runnable("ServiceWorkerClientPostMessageRunnable")
141 : , StructuredCloneHolder(CloningSupported,
142 : TransferringSupported,
143 : StructuredCloneScope::SameProcessDifferentThread)
144 : , mSourceID(aSourceID)
145 : , mSourceScope(aSourceScope)
146 0 : , mWindowId(aWindowId)
147 0 : {}
148 :
149 : NS_IMETHOD
150 0 : Run() override
151 : {
152 0 : AssertIsOnMainThread();
153 0 : nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
154 0 : if (!window) {
155 0 : return NS_ERROR_FAILURE;
156 : }
157 :
158 0 : dom::Navigator* navigator = window->Navigator();
159 0 : if (!navigator) {
160 0 : return NS_ERROR_FAILURE;
161 : }
162 :
163 0 : RefPtr<ServiceWorkerContainer> container = navigator->ServiceWorker();
164 0 : AutoJSAPI jsapi;
165 0 : if (NS_WARN_IF(!jsapi.Init(window))) {
166 0 : return NS_ERROR_FAILURE;
167 : }
168 0 : JSContext* cx = jsapi.cx();
169 :
170 0 : return DispatchDOMEvent(cx, window->AsInner(), container);
171 : }
172 :
173 : private:
174 : NS_IMETHOD
175 0 : DispatchDOMEvent(JSContext* aCx, nsPIDOMWindowInner* aWindow,
176 : ServiceWorkerContainer* aTargetContainer)
177 : {
178 0 : AssertIsOnMainThread();
179 :
180 0 : MOZ_ASSERT(aTargetContainer->GetParentObject(),
181 : "How come we don't have a window here?!");
182 :
183 0 : JS::Rooted<JS::Value> messageData(aCx);
184 0 : ErrorResult rv;
185 0 : Read(aTargetContainer->GetParentObject(), aCx, &messageData, rv);
186 0 : if (NS_WARN_IF(rv.Failed())) {
187 0 : xpc::Throw(aCx, rv.StealNSResult());
188 0 : return NS_ERROR_FAILURE;
189 : }
190 :
191 0 : RootedDictionary<MessageEventInit> init(aCx);
192 :
193 0 : nsCOMPtr<nsIPrincipal> principal = aTargetContainer->GetParentObject()->PrincipalOrNull();
194 0 : NS_WARNING_ASSERTION(principal, "Why is the principal null here?");
195 :
196 0 : bool isNullPrincipal = false;
197 0 : bool isSystemPrincipal = false;
198 0 : if (principal) {
199 0 : isNullPrincipal = principal->GetIsNullPrincipal();
200 0 : MOZ_ASSERT(!isNullPrincipal);
201 0 : isSystemPrincipal = principal->GetIsSystemPrincipal();
202 0 : MOZ_ASSERT(!isSystemPrincipal);
203 : }
204 :
205 0 : init.mData = messageData;
206 0 : nsAutoCString origin;
207 0 : if (principal && !isNullPrincipal && !isSystemPrincipal) {
208 0 : principal->GetOrigin(origin);
209 : }
210 0 : init.mOrigin = NS_ConvertUTF8toUTF16(origin);
211 :
212 :
213 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
214 0 : if (swm) {
215 : RefPtr<ServiceWorkerRegistrationInfo> reg =
216 0 : swm->GetRegistration(principal, mSourceScope);
217 0 : if (reg) {
218 0 : RefPtr<ServiceWorkerInfo> serviceWorker = reg->GetByID(mSourceID);
219 0 : if (serviceWorker) {
220 0 : init.mSource.SetValue().SetAsServiceWorker() =
221 0 : serviceWorker->GetOrCreateInstance(aWindow);
222 : }
223 : }
224 : }
225 :
226 0 : if (!TakeTransferredPortsAsSequence(init.mPorts)) {
227 0 : return NS_ERROR_OUT_OF_MEMORY;
228 : }
229 :
230 : RefPtr<MessageEvent> event =
231 0 : MessageEvent::Constructor(aTargetContainer, NS_LITERAL_STRING("message"),
232 0 : init);
233 :
234 0 : event->SetTrusted(true);
235 0 : bool status = false;
236 0 : aTargetContainer->DispatchEvent(static_cast<dom::Event*>(event.get()),
237 0 : &status);
238 :
239 0 : if (!status) {
240 0 : return NS_ERROR_FAILURE;
241 : }
242 :
243 0 : return NS_OK;
244 : }
245 : };
246 :
247 : } // namespace
248 :
249 : void
250 0 : ServiceWorkerClient::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
251 : const Sequence<JSObject*>& aTransferable,
252 : ErrorResult& aRv)
253 : {
254 0 : WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
255 0 : MOZ_ASSERT(workerPrivate);
256 0 : workerPrivate->AssertIsOnWorkerThread();
257 :
258 0 : JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
259 :
260 0 : aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
261 0 : &transferable);
262 0 : if (NS_WARN_IF(aRv.Failed())) {
263 0 : return;
264 : }
265 :
266 : // At the moment we only expose Client on ServiceWorker globals.
267 0 : MOZ_ASSERT(workerPrivate->IsServiceWorker());
268 0 : uint32_t serviceWorkerID = workerPrivate->ServiceWorkerID();
269 0 : nsCString scope = workerPrivate->ServiceWorkerScope();
270 :
271 : RefPtr<ServiceWorkerClientPostMessageRunnable> runnable =
272 : new ServiceWorkerClientPostMessageRunnable(serviceWorkerID, scope,
273 0 : mWindowId);
274 :
275 0 : runnable->Write(aCx, aMessage, transferable, JS::CloneDataPolicy().denySharedArrayBuffer(),
276 0 : aRv);
277 0 : if (NS_WARN_IF(aRv.Failed())) {
278 0 : return;
279 : }
280 :
281 0 : aRv = workerPrivate->DispatchToMainThread(runnable.forget());
282 0 : if (NS_WARN_IF(aRv.Failed())) {
283 0 : return;
284 : }
285 : }
286 :
|