Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/websocket/WebSocket.cpp
Line
Count
Source (jump to first uncovered line)
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
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "WebSocket.h"
8
#include "mozilla/dom/WebSocketBinding.h"
9
#include "mozilla/net/WebSocketChannel.h"
10
11
#include "jsapi.h"
12
#include "jsfriendapi.h"
13
#include "mozilla/DOMEventTargetHelper.h"
14
#include "mozilla/net/WebSocketChannel.h"
15
#include "mozilla/dom/File.h"
16
#include "mozilla/dom/MessageEvent.h"
17
#include "mozilla/dom/MessageEventBinding.h"
18
#include "mozilla/dom/nsCSPContext.h"
19
#include "mozilla/dom/nsCSPUtils.h"
20
#include "mozilla/dom/ScriptSettings.h"
21
#include "mozilla/dom/WorkerPrivate.h"
22
#include "mozilla/dom/WorkerRef.h"
23
#include "mozilla/dom/WorkerRunnable.h"
24
#include "mozilla/dom/WorkerScope.h"
25
#include "nsAutoPtr.h"
26
#include "mozilla/LoadInfo.h"
27
#include "nsGlobalWindow.h"
28
#include "nsIScriptGlobalObject.h"
29
#include "nsIDOMWindow.h"
30
#include "nsIDocument.h"
31
#include "nsXPCOM.h"
32
#include "nsIXPConnect.h"
33
#include "nsContentUtils.h"
34
#include "nsError.h"
35
#include "nsIScriptObjectPrincipal.h"
36
#include "nsIURL.h"
37
#include "nsThreadUtils.h"
38
#include "nsIPromptFactory.h"
39
#include "nsIWindowWatcher.h"
40
#include "nsIPrompt.h"
41
#include "nsIStringBundle.h"
42
#include "nsIConsoleService.h"
43
#include "mozilla/dom/CloseEvent.h"
44
#include "mozilla/net/WebSocketEventService.h"
45
#include "nsICryptoHash.h"
46
#include "nsJSUtils.h"
47
#include "nsIScriptError.h"
48
#include "nsNetUtil.h"
49
#include "nsIAuthPrompt.h"
50
#include "nsIAuthPrompt2.h"
51
#include "nsILoadGroup.h"
52
#include "mozilla/Preferences.h"
53
#include "xpcpublic.h"
54
#include "nsContentPolicyUtils.h"
55
#include "nsWrapperCacheInlines.h"
56
#include "nsIObserverService.h"
57
#include "nsIEventTarget.h"
58
#include "nsIInterfaceRequestor.h"
59
#include "nsIObserver.h"
60
#include "nsIRequest.h"
61
#include "nsIThreadRetargetableRequest.h"
62
#include "nsIWebSocketChannel.h"
63
#include "nsIWebSocketListener.h"
64
#include "nsProxyRelease.h"
65
#include "nsWeakReference.h"
66
67
0
#define OPEN_EVENT_STRING NS_LITERAL_STRING("open")
68
0
#define MESSAGE_EVENT_STRING NS_LITERAL_STRING("message")
69
0
#define ERROR_EVENT_STRING NS_LITERAL_STRING("error")
70
0
#define CLOSE_EVENT_STRING NS_LITERAL_STRING("close")
71
72
using namespace mozilla::net;
73
74
namespace mozilla {
75
namespace dom {
76
77
class WebSocketImpl final : public nsIInterfaceRequestor
78
                          , public nsIWebSocketListener
79
                          , public nsIObserver
80
                          , public nsSupportsWeakReference
81
                          , public nsIRequest
82
                          , public nsIEventTarget
83
{
84
public:
85
  NS_DECL_NSIINTERFACEREQUESTOR
86
  NS_DECL_NSIWEBSOCKETLISTENER
87
  NS_DECL_NSIOBSERVER
88
  NS_DECL_NSIREQUEST
89
  NS_DECL_THREADSAFE_ISUPPORTS
90
  NS_DECL_NSIEVENTTARGET_FULL
91
92
  explicit WebSocketImpl(WebSocket* aWebSocket)
93
  : mWebSocket(aWebSocket)
94
  , mIsServerSide(false)
95
  , mSecure(false)
96
  , mOnCloseScheduled(false)
97
  , mFailed(false)
98
  , mDisconnectingOrDisconnected(false)
99
  , mCloseEventWasClean(false)
100
  , mCloseEventCode(nsIWebSocketChannel::CLOSE_ABNORMAL)
101
  , mPort(0)
102
  , mScriptLine(0)
103
  , mScriptColumn(0)
104
  , mInnerWindowID(0)
105
  , mPrivateBrowsing(false)
106
  , mIsMainThread(true)
107
  , mMutex("WebSocketImpl::mMutex")
108
  , mWorkerShuttingDown(false)
109
0
  {
110
0
    if (!NS_IsMainThread()) {
111
0
      mIsMainThread = false;
112
0
    }
113
0
  }
114
115
  void AssertIsOnTargetThread() const
116
0
  {
117
0
    MOZ_ASSERT(IsTargetThread());
118
0
  }
119
120
  bool IsTargetThread() const;
121
122
  nsresult Init(JSContext* aCx,
123
                nsIPrincipal* aLoadingPrincipal,
124
                nsIPrincipal* aPrincipal,
125
                bool aIsServerSide,
126
                const nsAString& aURL,
127
                nsTArray<nsString>& aProtocolArray,
128
                const nsACString& aScriptFile,
129
                uint32_t aScriptLine,
130
                uint32_t aScriptColumn);
131
132
  nsresult AsyncOpen(nsIPrincipal* aPrincipal, uint64_t aInnerWindowID,
133
                     nsITransportProvider* aTransportProvider,
134
                     const nsACString& aNegotiatedExtensions);
135
136
  nsresult ParseURL(const nsAString& aURL);
137
  nsresult InitializeConnection(nsIPrincipal* aPrincipal);
138
139
  // These methods when called can release the WebSocket object
140
  void FailConnection(uint16_t reasonCode,
141
                      const nsACString& aReasonString = EmptyCString());
142
  nsresult CloseConnection(uint16_t reasonCode,
143
                           const nsACString& aReasonString = EmptyCString());
144
  void Disconnect();
145
  void DisconnectInternal();
146
147
  nsresult ConsoleError();
148
  void PrintErrorOnConsole(const char* aBundleURI,
149
                           const char* aError,
150
                           const char16_t** aFormatStrings,
151
                           uint32_t aFormatStringsLen);
152
153
  nsresult DoOnMessageAvailable(const nsACString& aMsg,
154
                                bool isBinary);
155
156
  // ConnectionCloseEvents: 'error' event if needed, then 'close' event.
157
  nsresult ScheduleConnectionCloseEvents(nsISupports* aContext,
158
                                         nsresult aStatusCode);
159
  // 2nd half of ScheduleConnectionCloseEvents, run in its own event.
160
  void DispatchConnectionCloseEvents();
161
162
  nsresult UpdateURI();
163
164
  void AddRefObject();
165
  void ReleaseObject();
166
167
  bool RegisterWorkerRef(WorkerPrivate* aWorkerPrivate);
168
  void UnregisterWorkerRef();
169
170
  nsresult CancelInternal();
171
172
  nsresult GetLoadingPrincipal(nsIPrincipal** aPrincipal);
173
174
  RefPtr<WebSocket> mWebSocket;
175
176
  nsCOMPtr<nsIWebSocketChannel> mChannel;
177
178
  bool mIsServerSide; // True if we're implementing the server side of a
179
                      // websocket connection
180
181
  bool mSecure; // if true it is using SSL and the wss scheme,
182
                // otherwise it is using the ws scheme with no SSL
183
184
  bool mOnCloseScheduled;
185
  bool mFailed;
186
  bool mDisconnectingOrDisconnected;
187
188
  // Set attributes of DOM 'onclose' message
189
  bool      mCloseEventWasClean;
190
  nsString  mCloseEventReason;
191
  uint16_t  mCloseEventCode;
192
193
  nsCString mAsciiHost;  // hostname
194
  uint32_t  mPort;
195
  nsCString mResource; // [filepath[?query]]
196
  nsString  mUTF16Origin;
197
198
  nsCString mURI;
199
  nsCString mRequestedProtocolList;
200
201
  nsWeakPtr mOriginDocument;
202
203
  // Web Socket owner information:
204
  // - the script file name, UTF8 encoded.
205
  // - source code line number and column number where the Web Socket object
206
  //   was constructed.
207
  // - the ID of the inner window where the script lives. Note that this may not
208
  //   be the same as the Web Socket owner window.
209
  // These attributes are used for error reporting.
210
  nsCString mScriptFile;
211
  uint32_t mScriptLine;
212
  uint32_t mScriptColumn;
213
  uint64_t mInnerWindowID;
214
  bool mPrivateBrowsing;
215
216
  RefPtr<ThreadSafeWorkerRef> mWorkerRef;
217
218
  nsWeakPtr mWeakLoadGroup;
219
220
  bool mIsMainThread;
221
222
  // This mutex protects mWorkerShuttingDown.
223
  mozilla::Mutex mMutex;
224
  bool mWorkerShuttingDown;
225
226
  RefPtr<WebSocketEventService> mService;
227
228
  // For dispatching runnables to main thread.
229
  nsCOMPtr<nsIEventTarget> mMainThreadEventTarget;
230
231
private:
232
  ~WebSocketImpl()
233
0
  {
234
0
    // If we threw during Init we never called disconnect
235
0
    if (!mDisconnectingOrDisconnected) {
236
0
      Disconnect();
237
0
    }
238
0
  }
239
};
240
241
NS_IMPL_ISUPPORTS(WebSocketImpl,
242
                  nsIInterfaceRequestor,
243
                  nsIWebSocketListener,
244
                  nsIObserver,
245
                  nsISupportsWeakReference,
246
                  nsIRequest,
247
                  nsIEventTarget)
248
249
class CallDispatchConnectionCloseEvents final : public CancelableRunnable
250
{
251
public:
252
  explicit CallDispatchConnectionCloseEvents(WebSocketImpl* aWebSocketImpl)
253
    : CancelableRunnable("dom::CallDispatchConnectionCloseEvents")
254
    , mWebSocketImpl(aWebSocketImpl)
255
0
  {
256
0
    aWebSocketImpl->AssertIsOnTargetThread();
257
0
  }
258
259
  NS_IMETHOD Run() override
260
0
  {
261
0
    mWebSocketImpl->AssertIsOnTargetThread();
262
0
    mWebSocketImpl->DispatchConnectionCloseEvents();
263
0
    return NS_OK;
264
0
  }
265
266
private:
267
  RefPtr<WebSocketImpl> mWebSocketImpl;
268
};
269
270
//-----------------------------------------------------------------------------
271
// WebSocketImpl
272
//-----------------------------------------------------------------------------
273
274
namespace {
275
276
class PrintErrorOnConsoleRunnable final : public WorkerMainThreadRunnable
277
{
278
public:
279
  PrintErrorOnConsoleRunnable(WebSocketImpl* aImpl,
280
                              const char* aBundleURI,
281
                              const char* aError,
282
                              const char16_t** aFormatStrings,
283
                              uint32_t aFormatStringsLen)
284
    : WorkerMainThreadRunnable(aImpl->mWorkerRef->Private(),
285
                               NS_LITERAL_CSTRING("WebSocket :: print error on console"))
286
    , mImpl(aImpl)
287
    , mBundleURI(aBundleURI)
288
    , mError(aError)
289
    , mFormatStrings(aFormatStrings)
290
    , mFormatStringsLen(aFormatStringsLen)
291
0
  { }
292
293
  bool MainThreadRun() override
294
0
  {
295
0
    mImpl->PrintErrorOnConsole(mBundleURI, mError, mFormatStrings,
296
0
                               mFormatStringsLen);
297
0
    return true;
298
0
  }
299
300
private:
301
  // Raw pointer because this runnable is sync.
302
  WebSocketImpl* mImpl;
303
304
  const char* mBundleURI;
305
  const char* mError;
306
  const char16_t** mFormatStrings;
307
  uint32_t mFormatStringsLen;
308
};
309
310
} // namespace
311
312
void
313
WebSocketImpl::PrintErrorOnConsole(const char *aBundleURI,
314
                                   const char *aError,
315
                                   const char16_t **aFormatStrings,
316
                                   uint32_t aFormatStringsLen)
317
0
{
318
0
  // This method must run on the main thread.
319
0
320
0
  if (!NS_IsMainThread()) {
321
0
    MOZ_ASSERT(mWorkerRef);
322
0
323
0
    RefPtr<PrintErrorOnConsoleRunnable> runnable =
324
0
      new PrintErrorOnConsoleRunnable(this, aBundleURI, aError, aFormatStrings,
325
0
                                      aFormatStringsLen);
326
0
    ErrorResult rv;
327
0
    runnable->Dispatch(Killing, rv);
328
0
    // XXXbz this seems totally broken.  We should be propagating this out, but
329
0
    // none of our callers really propagate anything usefully.  Come to think of
330
0
    // it, why is this a syncrunnable anyway?  Can't this be a fire-and-forget
331
0
    // runnable??
332
0
    rv.SuppressException();
333
0
    return;
334
0
  }
335
0
336
0
  nsresult rv;
337
0
  nsCOMPtr<nsIStringBundleService> bundleService =
338
0
    do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
339
0
  NS_ENSURE_SUCCESS_VOID(rv);
340
0
341
0
  nsCOMPtr<nsIStringBundle> strBundle;
342
0
  rv = bundleService->CreateBundle(aBundleURI, getter_AddRefs(strBundle));
343
0
  NS_ENSURE_SUCCESS_VOID(rv);
344
0
345
0
  nsCOMPtr<nsIConsoleService> console(
346
0
    do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv));
347
0
  NS_ENSURE_SUCCESS_VOID(rv);
348
0
349
0
  nsCOMPtr<nsIScriptError> errorObject(
350
0
    do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv));
351
0
  NS_ENSURE_SUCCESS_VOID(rv);
352
0
353
0
  // Localize the error message
354
0
  nsAutoString message;
355
0
  if (aFormatStrings) {
356
0
    rv = strBundle->FormatStringFromName(aError, aFormatStrings,
357
0
                                         aFormatStringsLen, message);
358
0
  } else {
359
0
    rv = strBundle->GetStringFromName(aError, message);
360
0
  }
361
0
  NS_ENSURE_SUCCESS_VOID(rv);
362
0
363
0
  if (mInnerWindowID) {
364
0
    rv = errorObject->InitWithWindowID(message,
365
0
                                       NS_ConvertUTF8toUTF16(mScriptFile),
366
0
                                       EmptyString(), mScriptLine,
367
0
                                       mScriptColumn,
368
0
                                       nsIScriptError::errorFlag, "Web Socket",
369
0
                                       mInnerWindowID);
370
0
  } else {
371
0
    rv = errorObject->Init(message,
372
0
                           NS_ConvertUTF8toUTF16(mScriptFile),
373
0
                           EmptyString(), mScriptLine, mScriptColumn,
374
0
                           nsIScriptError::errorFlag, "Web Socket",
375
0
                           mPrivateBrowsing);
376
0
  }
377
0
378
0
  NS_ENSURE_SUCCESS_VOID(rv);
379
0
380
0
  // print the error message directly to the JS console
381
0
  rv = console->LogMessage(errorObject);
382
0
  NS_ENSURE_SUCCESS_VOID(rv);
383
0
}
384
385
namespace {
386
387
class CancelWebSocketRunnable final : public Runnable
388
{
389
public:
390
  CancelWebSocketRunnable(nsIWebSocketChannel* aChannel,
391
                          uint16_t aReasonCode,
392
                          const nsACString& aReasonString)
393
    : Runnable("dom::CancelWebSocketRunnable")
394
    , mChannel(aChannel)
395
    , mReasonCode(aReasonCode)
396
    , mReasonString(aReasonString)
397
0
  {}
398
399
  NS_IMETHOD Run() override
400
0
  {
401
0
    nsresult rv = mChannel->Close(mReasonCode, mReasonString);
402
0
    if (NS_FAILED(rv)) {
403
0
      NS_WARNING("Failed to dispatch the close message");
404
0
    }
405
0
    return NS_OK;
406
0
  }
407
408
private:
409
  nsCOMPtr<nsIWebSocketChannel> mChannel;
410
  uint16_t mReasonCode;
411
  nsCString mReasonString;
412
};
413
414
class MOZ_STACK_CLASS MaybeDisconnect
415
{
416
public:
417
  explicit MaybeDisconnect(WebSocketImpl* aImpl)
418
    : mImpl(aImpl)
419
0
  {
420
0
  }
421
422
  ~MaybeDisconnect()
423
0
  {
424
0
    bool toDisconnect = false;
425
0
426
0
    {
427
0
      MutexAutoLock lock(mImpl->mMutex);
428
0
      toDisconnect = mImpl->mWorkerShuttingDown;
429
0
    }
430
0
431
0
    if (toDisconnect) {
432
0
      mImpl->Disconnect();
433
0
    }
434
0
  }
435
436
private:
437
  WebSocketImpl* mImpl;
438
};
439
440
class CloseConnectionRunnable final : public Runnable
441
{
442
public:
443
  CloseConnectionRunnable(WebSocketImpl* aImpl,
444
                          uint16_t aReasonCode,
445
                          const nsACString& aReasonString)
446
    : Runnable("dom::CloseConnectionRunnable")
447
    , mImpl(aImpl)
448
    , mReasonCode(aReasonCode)
449
    , mReasonString(aReasonString)
450
0
  {}
451
452
  NS_IMETHOD Run() override
453
0
  {
454
0
    return mImpl->CloseConnection(mReasonCode, mReasonString);
455
0
  }
456
457
private:
458
  RefPtr<WebSocketImpl> mImpl;
459
  uint16_t mReasonCode;
460
  const nsCString mReasonString;
461
};
462
463
} // namespace
464
465
nsresult
466
WebSocketImpl::CloseConnection(uint16_t aReasonCode,
467
                               const nsACString& aReasonString)
468
0
{
469
0
  if (!IsTargetThread()) {
470
0
    nsCOMPtr<nsIRunnable> runnable =
471
0
      new CloseConnectionRunnable(this, aReasonCode, aReasonString);
472
0
    return Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
473
0
  }
474
0
475
0
  AssertIsOnTargetThread();
476
0
477
0
  if (mDisconnectingOrDisconnected) {
478
0
    return NS_OK;
479
0
  }
480
0
481
0
  // If this method is called because the worker is going away, we will not
482
0
  // receive the OnStop() method and we have to disconnect the WebSocket and
483
0
  // release the ThreadSafeWorkerRef.
484
0
  MaybeDisconnect md(this);
485
0
486
0
  uint16_t readyState = mWebSocket->ReadyState();
487
0
  if (readyState == WebSocket::CLOSING ||
488
0
      readyState == WebSocket::CLOSED) {
489
0
    return NS_OK;
490
0
  }
491
0
492
0
  // The common case...
493
0
  if (mChannel) {
494
0
    mWebSocket->SetReadyState(WebSocket::CLOSING);
495
0
496
0
    // The channel has to be closed on the main-thread.
497
0
498
0
    if (NS_IsMainThread()) {
499
0
      return mChannel->Close(aReasonCode, aReasonString);
500
0
    }
501
0
502
0
    RefPtr<CancelWebSocketRunnable> runnable =
503
0
      new CancelWebSocketRunnable(mChannel, aReasonCode, aReasonString);
504
0
    return NS_DispatchToMainThread(runnable);
505
0
  }
506
0
507
0
  // No channel, but not disconnected: canceled or failed early
508
0
  MOZ_ASSERT(readyState == WebSocket::CONNECTING,
509
0
             "Should only get here for early websocket cancel/error");
510
0
511
0
  // Server won't be sending us a close code, so use what's passed in here.
512
0
  mCloseEventCode = aReasonCode;
513
0
  CopyUTF8toUTF16(aReasonString, mCloseEventReason);
514
0
515
0
  mWebSocket->SetReadyState(WebSocket::CLOSING);
516
0
517
0
  ScheduleConnectionCloseEvents(
518
0
                    nullptr,
519
0
                    (aReasonCode == nsIWebSocketChannel::CLOSE_NORMAL ||
520
0
                     aReasonCode == nsIWebSocketChannel::CLOSE_GOING_AWAY) ?
521
0
                     NS_OK : NS_ERROR_FAILURE);
522
0
523
0
  return NS_OK;
524
0
}
525
526
nsresult
527
WebSocketImpl::ConsoleError()
528
0
{
529
0
  AssertIsOnTargetThread();
530
0
531
0
  {
532
0
    MutexAutoLock lock(mMutex);
533
0
    if (mWorkerShuttingDown) {
534
0
      // Too late to report anything, bail out.
535
0
      return NS_OK;
536
0
    }
537
0
  }
538
0
539
0
  NS_ConvertUTF8toUTF16 specUTF16(mURI);
540
0
  const char16_t* formatStrings[] = { specUTF16.get() };
541
0
542
0
  if (mWebSocket->ReadyState() < WebSocket::OPEN) {
543
0
    PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
544
0
                        "connectionFailure",
545
0
                        formatStrings, ArrayLength(formatStrings));
546
0
  } else {
547
0
    PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
548
0
                        "netInterrupt",
549
0
                        formatStrings, ArrayLength(formatStrings));
550
0
  }
551
0
  /// todo some specific errors - like for message too large
552
0
  return NS_OK;
553
0
}
554
555
void
556
WebSocketImpl::FailConnection(uint16_t aReasonCode,
557
                              const nsACString& aReasonString)
558
0
{
559
0
  AssertIsOnTargetThread();
560
0
561
0
  if (mDisconnectingOrDisconnected) {
562
0
    return;
563
0
  }
564
0
565
0
  ConsoleError();
566
0
  mFailed = true;
567
0
  CloseConnection(aReasonCode, aReasonString);
568
0
}
569
570
namespace {
571
572
class DisconnectInternalRunnable final : public WorkerMainThreadRunnable
573
{
574
public:
575
  explicit DisconnectInternalRunnable(WebSocketImpl* aImpl)
576
    : WorkerMainThreadRunnable(GetCurrentThreadWorkerPrivate(),
577
                               NS_LITERAL_CSTRING("WebSocket :: disconnect"))
578
    , mImpl(aImpl)
579
0
  { }
580
581
  bool MainThreadRun() override
582
0
  {
583
0
    mImpl->DisconnectInternal();
584
0
    return true;
585
0
  }
586
587
private:
588
  // A raw pointer because this runnable is sync.
589
  WebSocketImpl* mImpl;
590
};
591
592
} // namespace
593
594
void
595
WebSocketImpl::Disconnect()
596
0
{
597
0
  if (mDisconnectingOrDisconnected) {
598
0
    return;
599
0
  }
600
0
601
0
  AssertIsOnTargetThread();
602
0
603
0
  // DontKeepAliveAnyMore() and DisconnectInternal() can release the object. So
604
0
  // hold a reference to this until the end of the method.
605
0
  RefPtr<WebSocketImpl> kungfuDeathGrip = this;
606
0
607
0
  // Disconnect can be called from some control event (such as a callback from
608
0
  // StrongWorkerRef). This will be schedulated before any other sync/async
609
0
  // runnable. In order to prevent some double Disconnect() calls, we use this
610
0
  // boolean.
611
0
  mDisconnectingOrDisconnected = true;
612
0
613
0
  // DisconnectInternal touches observers and nsILoadGroup and it must run on
614
0
  // the main thread.
615
0
616
0
  if (NS_IsMainThread()) {
617
0
    DisconnectInternal();
618
0
619
0
    // If we haven't called WebSocket::DisconnectFromOwner yet, update
620
0
    // web socket count here.
621
0
    if (mWebSocket->GetOwner()) {
622
0
      mWebSocket->GetOwner()->UpdateWebSocketCount(-1);
623
0
    }
624
0
  } else {
625
0
    RefPtr<DisconnectInternalRunnable> runnable =
626
0
      new DisconnectInternalRunnable(this);
627
0
    ErrorResult rv;
628
0
    runnable->Dispatch(Killing, rv);
629
0
    // XXXbz this seems totally broken.  We should be propagating this out, but
630
0
    // where to, exactly?
631
0
    rv.SuppressException();
632
0
  }
633
0
634
0
  NS_ReleaseOnMainThreadSystemGroup("WebSocketImpl::mChannel",
635
0
                                    mChannel.forget());
636
0
  NS_ReleaseOnMainThreadSystemGroup("WebSocketImpl::mService",
637
0
                                    mService.forget());
638
0
639
0
  mWebSocket->DontKeepAliveAnyMore();
640
0
  mWebSocket->mImpl = nullptr;
641
0
642
0
  if (mWorkerRef) {
643
0
    UnregisterWorkerRef();
644
0
  }
645
0
646
0
  // We want to release the WebSocket in the correct thread.
647
0
  mWebSocket = nullptr;
648
0
}
649
650
void
651
WebSocketImpl::DisconnectInternal()
652
0
{
653
0
  AssertIsOnMainThread();
654
0
655
0
  nsCOMPtr<nsILoadGroup> loadGroup = do_QueryReferent(mWeakLoadGroup);
656
0
  if (loadGroup) {
657
0
    loadGroup->RemoveRequest(this, nullptr, NS_OK);
658
0
    // mWeakLoadGroup has to be release on main-thread because WeakReferences
659
0
    // are not thread-safe.
660
0
    mWeakLoadGroup = nullptr;
661
0
  }
662
0
663
0
  if (!mWorkerRef) {
664
0
    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
665
0
    if (os) {
666
0
      os->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
667
0
      os->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC);
668
0
    }
669
0
  }
670
0
}
671
672
//-----------------------------------------------------------------------------
673
// WebSocketImpl::nsIWebSocketListener methods:
674
//-----------------------------------------------------------------------------
675
676
nsresult
677
WebSocketImpl::DoOnMessageAvailable(const nsACString& aMsg, bool isBinary)
678
0
{
679
0
  AssertIsOnTargetThread();
680
0
681
0
  if (mDisconnectingOrDisconnected) {
682
0
    return NS_OK;
683
0
  }
684
0
685
0
  int16_t readyState = mWebSocket->ReadyState();
686
0
  if (readyState == WebSocket::CLOSED) {
687
0
    NS_ERROR("Received message after CLOSED");
688
0
    return NS_ERROR_UNEXPECTED;
689
0
  }
690
0
691
0
  if (readyState == WebSocket::OPEN) {
692
0
    // Dispatch New Message
693
0
    nsresult rv = mWebSocket->CreateAndDispatchMessageEvent(aMsg, isBinary);
694
0
    if (NS_FAILED(rv)) {
695
0
      NS_WARNING("Failed to dispatch the message event");
696
0
    }
697
0
698
0
    return NS_OK;
699
0
  }
700
0
701
0
  // CLOSING should be the only other state where it's possible to get msgs
702
0
  // from channel: Spec says to drop them.
703
0
  MOZ_ASSERT(readyState == WebSocket::CLOSING,
704
0
             "Received message while CONNECTING or CLOSED");
705
0
  return NS_OK;
706
0
}
707
708
NS_IMETHODIMP
709
WebSocketImpl::OnMessageAvailable(nsISupports* aContext,
710
                                  const nsACString& aMsg)
711
0
{
712
0
  AssertIsOnTargetThread();
713
0
714
0
  if (mDisconnectingOrDisconnected) {
715
0
    return NS_OK;
716
0
  }
717
0
718
0
  return DoOnMessageAvailable(aMsg, false);
719
0
}
720
721
NS_IMETHODIMP
722
WebSocketImpl::OnBinaryMessageAvailable(nsISupports* aContext,
723
                                        const nsACString& aMsg)
724
0
{
725
0
  AssertIsOnTargetThread();
726
0
727
0
  if (mDisconnectingOrDisconnected) {
728
0
    return NS_OK;
729
0
  }
730
0
731
0
  return DoOnMessageAvailable(aMsg, true);
732
0
}
733
734
NS_IMETHODIMP
735
WebSocketImpl::OnStart(nsISupports* aContext)
736
0
{
737
0
  AssertIsOnTargetThread();
738
0
739
0
  if (mDisconnectingOrDisconnected) {
740
0
    return NS_OK;
741
0
  }
742
0
743
0
  int16_t readyState = mWebSocket->ReadyState();
744
0
745
0
  // This is the only function that sets OPEN, and should be called only once
746
0
  MOZ_ASSERT(readyState != WebSocket::OPEN,
747
0
             "readyState already OPEN! OnStart called twice?");
748
0
749
0
  // Nothing to do if we've already closed/closing
750
0
  if (readyState != WebSocket::CONNECTING) {
751
0
    return NS_OK;
752
0
  }
753
0
754
0
  // Attempt to kill "ghost" websocket: but usually too early for check to fail
755
0
  nsresult rv = mWebSocket->CheckInnerWindowCorrectness();
756
0
  if (NS_FAILED(rv)) {
757
0
    CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY);
758
0
    return rv;
759
0
  }
760
0
761
0
  if (!mRequestedProtocolList.IsEmpty()) {
762
0
    rv = mChannel->GetProtocol(mWebSocket->mEstablishedProtocol);
763
0
    MOZ_ASSERT(NS_SUCCEEDED(rv));
764
0
  }
765
0
766
0
  rv = mChannel->GetExtensions(mWebSocket->mEstablishedExtensions);
767
0
  MOZ_ASSERT(NS_SUCCEEDED(rv));
768
0
  UpdateURI();
769
0
770
0
  mWebSocket->SetReadyState(WebSocket::OPEN);
771
0
772
0
  mService->WebSocketOpened(mChannel->Serial(),mInnerWindowID,
773
0
                            mWebSocket->mEffectiveURL,
774
0
                            mWebSocket->mEstablishedProtocol,
775
0
                            mWebSocket->mEstablishedExtensions);
776
0
777
0
  // Let's keep the object alive because the webSocket can be CCed in the
778
0
  // onopen callback.
779
0
  RefPtr<WebSocket> webSocket = mWebSocket;
780
0
781
0
  // Call 'onopen'
782
0
  rv = webSocket->CreateAndDispatchSimpleEvent(OPEN_EVENT_STRING);
783
0
  if (NS_FAILED(rv)) {
784
0
    NS_WARNING("Failed to dispatch the open event");
785
0
  }
786
0
787
0
  webSocket->UpdateMustKeepAlive();
788
0
  return NS_OK;
789
0
}
790
791
NS_IMETHODIMP
792
WebSocketImpl::OnStop(nsISupports* aContext, nsresult aStatusCode)
793
0
{
794
0
  AssertIsOnTargetThread();
795
0
796
0
  if (mDisconnectingOrDisconnected) {
797
0
    return NS_OK;
798
0
  }
799
0
800
0
  // We can be CONNECTING here if connection failed.
801
0
  // We can be OPEN if we have encountered a fatal protocol error
802
0
  // We can be CLOSING if close() was called and/or server initiated close.
803
0
  MOZ_ASSERT(mWebSocket->ReadyState() != WebSocket::CLOSED,
804
0
             "Shouldn't already be CLOSED when OnStop called");
805
0
806
0
  return ScheduleConnectionCloseEvents(aContext, aStatusCode);
807
0
}
808
809
nsresult
810
WebSocketImpl::ScheduleConnectionCloseEvents(nsISupports* aContext,
811
                                             nsresult aStatusCode)
812
0
{
813
0
  AssertIsOnTargetThread();
814
0
815
0
  // no-op if some other code has already initiated close event
816
0
  if (!mOnCloseScheduled) {
817
0
    mCloseEventWasClean = NS_SUCCEEDED(aStatusCode);
818
0
819
0
    if (aStatusCode == NS_BASE_STREAM_CLOSED) {
820
0
      // don't generate an error event just because of an unclean close
821
0
      aStatusCode = NS_OK;
822
0
    }
823
0
824
0
    if (aStatusCode == NS_ERROR_NET_INADEQUATE_SECURITY) {
825
0
      // TLS negotiation failed so we need to set status code to 1015.
826
0
      mCloseEventCode = 1015;
827
0
    }
828
0
829
0
    if (NS_FAILED(aStatusCode)) {
830
0
      ConsoleError();
831
0
      mFailed = true;
832
0
    }
833
0
834
0
    mOnCloseScheduled = true;
835
0
836
0
    NS_DispatchToCurrentThread(new CallDispatchConnectionCloseEvents(this));
837
0
  }
838
0
839
0
  return NS_OK;
840
0
}
841
842
NS_IMETHODIMP
843
WebSocketImpl::OnAcknowledge(nsISupports *aContext, uint32_t aSize)
844
0
{
845
0
  AssertIsOnTargetThread();
846
0
847
0
  if (mDisconnectingOrDisconnected) {
848
0
    return NS_OK;
849
0
  }
850
0
851
0
  MOZ_RELEASE_ASSERT(mWebSocket->mOutgoingBufferedAmount.isValid());
852
0
  if (aSize > mWebSocket->mOutgoingBufferedAmount.value()) {
853
0
    return NS_ERROR_UNEXPECTED;
854
0
  }
855
0
856
0
  mWebSocket->mOutgoingBufferedAmount -= aSize;
857
0
  if (!mWebSocket->mOutgoingBufferedAmount.isValid()) {
858
0
    return NS_ERROR_UNEXPECTED;
859
0
  }
860
0
861
0
  return NS_OK;
862
0
}
863
864
NS_IMETHODIMP
865
WebSocketImpl::OnServerClose(nsISupports *aContext, uint16_t aCode,
866
                             const nsACString &aReason)
867
0
{
868
0
  AssertIsOnTargetThread();
869
0
870
0
  if (mDisconnectingOrDisconnected) {
871
0
    return NS_OK;
872
0
  }
873
0
874
0
  int16_t readyState = mWebSocket->ReadyState();
875
0
876
0
  MOZ_ASSERT(readyState != WebSocket::CONNECTING,
877
0
             "Received server close before connected?");
878
0
  MOZ_ASSERT(readyState != WebSocket::CLOSED,
879
0
             "Received server close after already closed!");
880
0
881
0
  // store code/string for onclose DOM event
882
0
  mCloseEventCode = aCode;
883
0
  CopyUTF8toUTF16(aReason, mCloseEventReason);
884
0
885
0
  if (readyState == WebSocket::OPEN) {
886
0
    // Server initiating close.
887
0
    // RFC 6455, 5.5.1: "When sending a Close frame in response, the endpoint
888
0
    // typically echos the status code it received".
889
0
    // But never send certain codes, per section 7.4.1
890
0
    if (aCode == 1005 || aCode == 1006 || aCode == 1015) {
891
0
      CloseConnection(0, EmptyCString());
892
0
    } else {
893
0
      CloseConnection(aCode, aReason);
894
0
    }
895
0
  } else {
896
0
    // We initiated close, and server has replied: OnStop does rest of the work.
897
0
    MOZ_ASSERT(readyState == WebSocket::CLOSING, "unknown state");
898
0
  }
899
0
900
0
  return NS_OK;
901
0
}
902
903
//-----------------------------------------------------------------------------
904
// WebSocketImpl::nsIInterfaceRequestor
905
//-----------------------------------------------------------------------------
906
907
NS_IMETHODIMP
908
WebSocketImpl::GetInterface(const nsIID& aIID, void** aResult)
909
0
{
910
0
  AssertIsOnMainThread();
911
0
912
0
  if (!mWebSocket || mWebSocket->ReadyState() == WebSocket::CLOSED) {
913
0
    return NS_ERROR_FAILURE;
914
0
  }
915
0
916
0
  if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
917
0
      aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
918
0
    nsCOMPtr<nsPIDOMWindowInner> win = mWebSocket->GetWindowIfCurrent();
919
0
    if (!win) {
920
0
      return NS_ERROR_NOT_AVAILABLE;
921
0
    }
922
0
923
0
    nsresult rv;
924
0
    nsCOMPtr<nsIPromptFactory> wwatch =
925
0
      do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
926
0
    NS_ENSURE_SUCCESS(rv, rv);
927
0
928
0
    nsCOMPtr<nsPIDOMWindowOuter> outerWindow = win->GetOuterWindow();
929
0
    return wwatch->GetPrompt(outerWindow, aIID, aResult);
930
0
  }
931
0
932
0
  return QueryInterface(aIID, aResult);
933
0
}
934
935
////////////////////////////////////////////////////////////////////////////////
936
// WebSocket
937
////////////////////////////////////////////////////////////////////////////////
938
939
WebSocket::WebSocket(nsPIDOMWindowInner* aOwnerWindow)
940
  : DOMEventTargetHelper(aOwnerWindow)
941
  , mIsMainThread(true)
942
  , mKeepingAlive(false)
943
  , mCheckMustKeepAlive(true)
944
  , mOutgoingBufferedAmount(0)
945
  , mBinaryType(dom::BinaryType::Blob)
946
  , mMutex("WebSocket::mMutex")
947
  , mReadyState(CONNECTING)
948
0
{
949
0
  mImpl = new WebSocketImpl(this);
950
0
  mIsMainThread = mImpl->mIsMainThread;
951
0
}
952
953
WebSocket::~WebSocket()
954
0
{
955
0
}
956
957
JSObject*
958
WebSocket::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto)
959
0
{
960
0
  return WebSocket_Binding::Wrap(cx, this, aGivenProto);
961
0
}
962
963
void
964
WebSocket::BindToOwner(nsIGlobalObject* aNew)
965
0
{
966
0
  auto scopeExit = MakeScopeExit([&] {
967
0
    DOMEventTargetHelper::BindToOwner(aNew);
968
0
  });
969
0
970
0
  // If we're disconnected, then there is no state to update.
971
0
  if (!mImpl || mImpl->mDisconnectingOrDisconnected) {
972
0
    return;
973
0
  }
974
0
975
0
  // Update state on the old window.
976
0
  if (GetOwner()) {
977
0
    GetOwner()->UpdateWebSocketCount(-1);
978
0
  }
979
0
980
0
  // Update state on the new window
981
0
  nsCOMPtr<nsPIDOMWindowInner> newWindow = do_QueryInterface(aNew);
982
0
  if (newWindow) {
983
0
    newWindow->UpdateWebSocketCount(1);
984
0
  }
985
0
}
986
987
//---------------------------------------------------------------------------
988
// WebIDL
989
//---------------------------------------------------------------------------
990
991
// Constructor:
992
already_AddRefed<WebSocket>
993
WebSocket::Constructor(const GlobalObject& aGlobal,
994
                       const nsAString& aUrl,
995
                       ErrorResult& aRv)
996
0
{
997
0
  Sequence<nsString> protocols;
998
0
  return WebSocket::ConstructorCommon(aGlobal, aUrl, protocols, nullptr,
999
0
                                      EmptyCString(), aRv);
1000
0
}
1001
1002
already_AddRefed<WebSocket>
1003
WebSocket::Constructor(const GlobalObject& aGlobal,
1004
                       const nsAString& aUrl,
1005
                       const nsAString& aProtocol,
1006
                       ErrorResult& aRv)
1007
0
{
1008
0
  Sequence<nsString> protocols;
1009
0
  if (!protocols.AppendElement(aProtocol, fallible)) {
1010
0
    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
1011
0
    return nullptr;
1012
0
  }
1013
0
1014
0
  return WebSocket::ConstructorCommon(aGlobal, aUrl, protocols, nullptr,
1015
0
                                      EmptyCString(), aRv);
1016
0
}
1017
1018
already_AddRefed<WebSocket>
1019
WebSocket::Constructor(const GlobalObject& aGlobal,
1020
                       const nsAString& aUrl,
1021
                       const Sequence<nsString>& aProtocols,
1022
                       ErrorResult& aRv)
1023
0
{
1024
0
  return WebSocket::ConstructorCommon(aGlobal, aUrl, aProtocols, nullptr,
1025
0
                                      EmptyCString(), aRv);
1026
0
}
1027
1028
already_AddRefed<WebSocket>
1029
WebSocket::CreateServerWebSocket(const GlobalObject& aGlobal,
1030
                                 const nsAString& aUrl,
1031
                                 const Sequence<nsString>& aProtocols,
1032
                                 nsITransportProvider* aTransportProvider,
1033
                                 const nsAString& aNegotiatedExtensions,
1034
                                 ErrorResult& aRv)
1035
0
{
1036
0
  return WebSocket::ConstructorCommon(aGlobal, aUrl, aProtocols, aTransportProvider,
1037
0
                                      NS_ConvertUTF16toUTF8(aNegotiatedExtensions), aRv);
1038
0
}
1039
1040
namespace {
1041
1042
// This class is used to clear any exception.
1043
class MOZ_STACK_CLASS ClearException
1044
{
1045
public:
1046
  explicit ClearException(JSContext* aCx)
1047
    : mCx(aCx)
1048
0
  {
1049
0
  }
1050
1051
  ~ClearException()
1052
0
  {
1053
0
    JS_ClearPendingException(mCx);
1054
0
  }
1055
1056
private:
1057
  JSContext* mCx;
1058
};
1059
1060
class WebSocketMainThreadRunnable : public WorkerMainThreadRunnable
1061
{
1062
public:
1063
  WebSocketMainThreadRunnable(WorkerPrivate* aWorkerPrivate,
1064
                              const nsACString& aTelemetryKey)
1065
    : WorkerMainThreadRunnable(aWorkerPrivate, aTelemetryKey)
1066
0
  {
1067
0
    MOZ_ASSERT(aWorkerPrivate);
1068
0
    aWorkerPrivate->AssertIsOnWorkerThread();
1069
0
  }
1070
1071
  bool MainThreadRun() override
1072
0
  {
1073
0
    AssertIsOnMainThread();
1074
0
1075
0
    // Walk up to our containing page
1076
0
    WorkerPrivate* wp = mWorkerPrivate;
1077
0
    while (wp->GetParent()) {
1078
0
      wp = wp->GetParent();
1079
0
    }
1080
0
1081
0
    nsPIDOMWindowInner* window = wp->GetWindow();
1082
0
    if (window) {
1083
0
      return InitWithWindow(window);
1084
0
    }
1085
0
1086
0
    return InitWindowless(wp);
1087
0
  }
1088
1089
protected:
1090
  virtual bool InitWithWindow(nsPIDOMWindowInner* aWindow) = 0;
1091
1092
  virtual bool InitWindowless(WorkerPrivate* aTopLevelWorkerPrivate) = 0;
1093
};
1094
1095
class InitRunnable final : public WebSocketMainThreadRunnable
1096
{
1097
public:
1098
  InitRunnable(WorkerPrivate* aWorkerPrivate,
1099
               WebSocketImpl* aImpl,
1100
               bool aIsServerSide,
1101
               const nsAString& aURL,
1102
               nsTArray<nsString>& aProtocolArray,
1103
               const nsACString& aScriptFile, uint32_t aScriptLine,
1104
               uint32_t aScriptColumn)
1105
    : WebSocketMainThreadRunnable(aWorkerPrivate,
1106
                                  NS_LITERAL_CSTRING("WebSocket :: init"))
1107
    , mImpl(aImpl)
1108
    , mIsServerSide(aIsServerSide)
1109
    , mURL(aURL)
1110
    , mProtocolArray(aProtocolArray)
1111
    , mScriptFile(aScriptFile)
1112
    , mScriptLine(aScriptLine)
1113
    , mScriptColumn(aScriptColumn)
1114
    , mErrorCode(NS_OK)
1115
0
  {
1116
0
    MOZ_ASSERT(mWorkerPrivate);
1117
0
    mWorkerPrivate->AssertIsOnWorkerThread();
1118
0
  }
1119
1120
  nsresult
1121
  ErrorCode() const
1122
0
  {
1123
0
    return mErrorCode;
1124
0
  }
1125
1126
protected:
1127
  virtual bool InitWithWindow(nsPIDOMWindowInner* aWindow) override
1128
0
  {
1129
0
    AutoJSAPI jsapi;
1130
0
    if (NS_WARN_IF(!jsapi.Init(aWindow))) {
1131
0
      mErrorCode = NS_ERROR_FAILURE;
1132
0
      return true;
1133
0
    }
1134
0
1135
0
    ClearException ce(jsapi.cx());
1136
0
1137
0
    nsIDocument* doc = aWindow->GetExtantDoc();
1138
0
    if (!doc) {
1139
0
      mErrorCode = NS_ERROR_FAILURE;
1140
0
      return true;
1141
0
    }
1142
0
1143
0
    mErrorCode =
1144
0
      mImpl->Init(jsapi.cx(), mWorkerPrivate->GetPrincipal(),
1145
0
                  doc->NodePrincipal(), mIsServerSide, mURL, mProtocolArray,
1146
0
                  mScriptFile, mScriptLine, mScriptColumn);
1147
0
    return true;
1148
0
  }
1149
1150
  virtual bool InitWindowless(WorkerPrivate* aTopLevelWorkerPrivate) override
1151
0
  {
1152
0
    MOZ_ASSERT(NS_IsMainThread());
1153
0
    MOZ_ASSERT(aTopLevelWorkerPrivate && !aTopLevelWorkerPrivate->GetWindow());
1154
0
1155
0
    mErrorCode =
1156
0
      mImpl->Init(nullptr, mWorkerPrivate->GetPrincipal(),
1157
0
                  aTopLevelWorkerPrivate->GetPrincipal(), mIsServerSide, mURL,
1158
0
                  mProtocolArray, mScriptFile, mScriptLine, mScriptColumn);
1159
0
    return true;
1160
0
  }
1161
1162
  // Raw pointer. This worker runnable runs synchronously.
1163
  WebSocketImpl* mImpl;
1164
1165
  bool mIsServerSide;
1166
  const nsAString& mURL;
1167
  nsTArray<nsString>& mProtocolArray;
1168
  nsCString mScriptFile;
1169
  uint32_t mScriptLine;
1170
  uint32_t mScriptColumn;
1171
  nsresult mErrorCode;
1172
};
1173
1174
class ConnectRunnable final : public WebSocketMainThreadRunnable
1175
{
1176
public:
1177
  ConnectRunnable(WorkerPrivate* aWorkerPrivate, WebSocketImpl* aImpl)
1178
    : WebSocketMainThreadRunnable(aWorkerPrivate,
1179
                                  NS_LITERAL_CSTRING("WebSocket :: init"))
1180
    , mImpl(aImpl)
1181
    , mConnectionFailed(true)
1182
0
  {
1183
0
    MOZ_ASSERT(mWorkerPrivate);
1184
0
    mWorkerPrivate->AssertIsOnWorkerThread();
1185
0
  }
1186
1187
  bool
1188
  ConnectionFailed() const
1189
0
  {
1190
0
    return mConnectionFailed;
1191
0
  }
1192
1193
protected:
1194
  virtual bool InitWithWindow(nsPIDOMWindowInner* aWindow) override
1195
0
  {
1196
0
    nsIDocument* doc = aWindow->GetExtantDoc();
1197
0
    if (!doc) {
1198
0
      return true;
1199
0
    }
1200
0
1201
0
    mConnectionFailed =
1202
0
      NS_FAILED(mImpl->InitializeConnection(doc->NodePrincipal()));
1203
0
    return true;
1204
0
  }
1205
1206
  virtual bool InitWindowless(WorkerPrivate* aTopLevelWorkerPrivate) override
1207
0
  {
1208
0
    MOZ_ASSERT(NS_IsMainThread());
1209
0
    MOZ_ASSERT(aTopLevelWorkerPrivate && !aTopLevelWorkerPrivate->GetWindow());
1210
0
1211
0
    mConnectionFailed =
1212
0
      NS_FAILED(mImpl->InitializeConnection(aTopLevelWorkerPrivate->GetPrincipal()));
1213
0
    return true;
1214
0
  }
1215
1216
  // Raw pointer. This worker runnable runs synchronously.
1217
  WebSocketImpl* mImpl;
1218
1219
  bool mConnectionFailed;
1220
};
1221
1222
class AsyncOpenRunnable final : public WebSocketMainThreadRunnable
1223
{
1224
public:
1225
  explicit AsyncOpenRunnable(WebSocketImpl* aImpl)
1226
    : WebSocketMainThreadRunnable(aImpl->mWorkerRef->Private(),
1227
                                  NS_LITERAL_CSTRING("WebSocket :: AsyncOpen"))
1228
    , mImpl(aImpl)
1229
    , mErrorCode(NS_OK)
1230
0
  {
1231
0
    MOZ_ASSERT(mWorkerPrivate);
1232
0
    mWorkerPrivate->AssertIsOnWorkerThread();
1233
0
  }
1234
1235
  nsresult
1236
  ErrorCode() const
1237
0
  {
1238
0
    return mErrorCode;
1239
0
  }
1240
1241
protected:
1242
  virtual bool InitWithWindow(nsPIDOMWindowInner* aWindow) override
1243
0
  {
1244
0
    AssertIsOnMainThread();
1245
0
    MOZ_ASSERT(aWindow);
1246
0
1247
0
    nsIDocument* doc = aWindow->GetExtantDoc();
1248
0
    if (!doc) {
1249
0
      mErrorCode = NS_ERROR_FAILURE;
1250
0
      return true;
1251
0
    }
1252
0
1253
0
    nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
1254
0
    if (!principal) {
1255
0
      mErrorCode = NS_ERROR_FAILURE;
1256
0
      return true;
1257
0
    }
1258
0
1259
0
    uint64_t windowID = 0;
1260
0
    nsCOMPtr<nsPIDOMWindowOuter> topWindow = aWindow->GetScriptableTop();
1261
0
    nsCOMPtr<nsPIDOMWindowInner> topInner;
1262
0
    if (topWindow) {
1263
0
      topInner = topWindow->GetCurrentInnerWindow();
1264
0
    }
1265
0
1266
0
    if (topInner) {
1267
0
      windowID = topInner->WindowID();
1268
0
    }
1269
0
1270
0
    mErrorCode = mImpl->AsyncOpen(principal, windowID, nullptr, EmptyCString());
1271
0
    return true;
1272
0
  }
1273
1274
  virtual bool InitWindowless(WorkerPrivate* aTopLevelWorkerPrivate) override
1275
0
  {
1276
0
    MOZ_ASSERT(NS_IsMainThread());
1277
0
    MOZ_ASSERT(aTopLevelWorkerPrivate && !aTopLevelWorkerPrivate->GetWindow());
1278
0
1279
0
    mErrorCode =
1280
0
      mImpl->AsyncOpen(aTopLevelWorkerPrivate->GetPrincipal(), 0, nullptr,
1281
0
                       EmptyCString());
1282
0
    return true;
1283
0
  }
1284
1285
private:
1286
  // Raw pointer. This worker runs synchronously.
1287
  WebSocketImpl* mImpl;
1288
1289
  nsresult mErrorCode;
1290
};
1291
1292
} // namespace
1293
1294
already_AddRefed<WebSocket>
1295
WebSocket::ConstructorCommon(const GlobalObject& aGlobal,
1296
                             const nsAString& aUrl,
1297
                             const Sequence<nsString>& aProtocols,
1298
                             nsITransportProvider* aTransportProvider,
1299
                             const nsACString& aNegotiatedExtensions,
1300
                             ErrorResult& aRv)
1301
0
{
1302
0
  MOZ_ASSERT_IF(!aTransportProvider, aNegotiatedExtensions.IsEmpty());
1303
0
  nsCOMPtr<nsIPrincipal> principal;
1304
0
  nsCOMPtr<nsPIDOMWindowInner> ownerWindow;
1305
0
1306
0
  if (NS_IsMainThread()) {
1307
0
    nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal =
1308
0
      do_QueryInterface(aGlobal.GetAsSupports());
1309
0
    if (!scriptPrincipal) {
1310
0
      aRv.Throw(NS_ERROR_FAILURE);
1311
0
      return nullptr;
1312
0
    }
1313
0
1314
0
    principal = scriptPrincipal->GetPrincipal();
1315
0
    if (!principal) {
1316
0
      aRv.Throw(NS_ERROR_FAILURE);
1317
0
      return nullptr;
1318
0
    }
1319
0
1320
0
    nsCOMPtr<nsIScriptGlobalObject> sgo =
1321
0
      do_QueryInterface(aGlobal.GetAsSupports());
1322
0
    if (!sgo) {
1323
0
      aRv.Throw(NS_ERROR_FAILURE);
1324
0
      return nullptr;
1325
0
    }
1326
0
1327
0
    ownerWindow = do_QueryInterface(aGlobal.GetAsSupports());
1328
0
    if (!ownerWindow) {
1329
0
      aRv.Throw(NS_ERROR_FAILURE);
1330
0
      return nullptr;
1331
0
    }
1332
0
  }
1333
0
1334
0
  nsTArray<nsString> protocolArray;
1335
0
1336
0
  for (uint32_t index = 0, len = aProtocols.Length(); index < len; ++index) {
1337
0
1338
0
    const nsString& protocolElement = aProtocols[index];
1339
0
1340
0
    if (protocolElement.IsEmpty()) {
1341
0
      aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
1342
0
      return nullptr;
1343
0
    }
1344
0
    if (protocolArray.Contains(protocolElement)) {
1345
0
      aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
1346
0
      return nullptr;
1347
0
    }
1348
0
    if (protocolElement.FindChar(',') != -1)  /* interferes w/list */ {
1349
0
      aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
1350
0
      return nullptr;
1351
0
    }
1352
0
1353
0
    protocolArray.AppendElement(protocolElement);
1354
0
  }
1355
0
1356
0
  RefPtr<WebSocket> webSocket = new WebSocket(ownerWindow);
1357
0
  RefPtr<WebSocketImpl> webSocketImpl = webSocket->mImpl;
1358
0
1359
0
  bool connectionFailed = true;
1360
0
1361
0
  if (NS_IsMainThread()) {
1362
0
    // We're keeping track of all main thread web sockets to be able to
1363
0
    // avoid throttling timeouts when we have active web sockets.
1364
0
    webSocket->GetOwner()->UpdateWebSocketCount(1);
1365
0
1366
0
    nsCOMPtr<nsIPrincipal> loadingPrincipal;
1367
0
    aRv = webSocketImpl->GetLoadingPrincipal(getter_AddRefs(loadingPrincipal));
1368
0
    if (NS_WARN_IF(aRv.Failed())) {
1369
0
      return nullptr;
1370
0
    }
1371
0
1372
0
    aRv =
1373
0
      webSocketImpl->Init(aGlobal.Context(), loadingPrincipal, principal,
1374
0
                          !!aTransportProvider, aUrl, protocolArray,
1375
0
                          EmptyCString(), 0, 0);
1376
0
1377
0
    if (NS_WARN_IF(aRv.Failed())) {
1378
0
      return nullptr;
1379
0
    }
1380
0
1381
0
    // the constructor should throw a SYNTAX_ERROR only if it fails to parse the
1382
0
    // url parameter, so don't throw if InitializeConnection fails, and call
1383
0
    // onerror/onclose asynchronously
1384
0
    connectionFailed =
1385
0
      NS_FAILED(webSocketImpl->InitializeConnection(principal));
1386
0
  } else {
1387
0
    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
1388
0
    MOZ_ASSERT(workerPrivate);
1389
0
1390
0
    unsigned lineno, column;
1391
0
    JS::AutoFilename file;
1392
0
    if (!JS::DescribeScriptedCaller(aGlobal.Context(), &file, &lineno,
1393
0
                                    &column)) {
1394
0
      NS_WARNING("Failed to get line number and filename in workers.");
1395
0
    }
1396
0
1397
0
    RefPtr<InitRunnable> runnable =
1398
0
      new InitRunnable(workerPrivate, webSocketImpl, !!aTransportProvider, aUrl,
1399
0
                       protocolArray, nsDependentCString(file.get()), lineno,
1400
0
                       column);
1401
0
    runnable->Dispatch(Canceling, aRv);
1402
0
    if (NS_WARN_IF(aRv.Failed())) {
1403
0
      return nullptr;
1404
0
    }
1405
0
1406
0
    aRv = runnable->ErrorCode();
1407
0
    if (NS_WARN_IF(aRv.Failed())) {
1408
0
      return nullptr;
1409
0
    }
1410
0
1411
0
    if (NS_WARN_IF(!webSocketImpl->RegisterWorkerRef(workerPrivate))) {
1412
0
      // The worker is shutting down.
1413
0
      aRv.Throw(NS_ERROR_FAILURE);
1414
0
      return nullptr;
1415
0
    }
1416
0
1417
0
    RefPtr<ConnectRunnable> connectRunnable =
1418
0
      new ConnectRunnable(workerPrivate, webSocketImpl);
1419
0
    connectRunnable->Dispatch(Canceling, aRv);
1420
0
    if (NS_WARN_IF(aRv.Failed())) {
1421
0
      return nullptr;
1422
0
    }
1423
0
1424
0
    connectionFailed = connectRunnable->ConnectionFailed();
1425
0
  }
1426
0
1427
0
  // It can be that we have been already disconnected because the WebSocket is
1428
0
  // gone away while we where initializing the webSocket.
1429
0
  if (!webSocket->mImpl) {
1430
0
    aRv.Throw(NS_ERROR_FAILURE);
1431
0
    return nullptr;
1432
0
  }
1433
0
1434
0
  // We don't return an error if the connection just failed. Instead we dispatch
1435
0
  // an event.
1436
0
  if (connectionFailed) {
1437
0
    webSocket->mImpl->FailConnection(nsIWebSocketChannel::CLOSE_ABNORMAL);
1438
0
  }
1439
0
1440
0
  // If we don't have a channel, the connection is failed and onerror() will be
1441
0
  // called asynchrounsly.
1442
0
  if (!webSocket->mImpl->mChannel) {
1443
0
    return webSocket.forget();
1444
0
  }
1445
0
1446
0
  class MOZ_STACK_CLASS ClearWebSocket
1447
0
  {
1448
0
  public:
1449
0
    explicit ClearWebSocket(WebSocketImpl* aWebSocketImpl)
1450
0
      : mWebSocketImpl(aWebSocketImpl)
1451
0
      , mDone(false)
1452
0
    {
1453
0
    }
1454
0
1455
0
    void Done()
1456
0
    {
1457
0
       mDone = true;
1458
0
    }
1459
0
1460
0
    ~ClearWebSocket()
1461
0
    {
1462
0
      if (!mDone) {
1463
0
        mWebSocketImpl->mChannel = nullptr;
1464
0
        mWebSocketImpl->FailConnection(nsIWebSocketChannel::CLOSE_ABNORMAL);
1465
0
      }
1466
0
    }
1467
0
1468
0
    WebSocketImpl* mWebSocketImpl;
1469
0
    bool mDone;
1470
0
  };
1471
0
1472
0
  ClearWebSocket cws(webSocket->mImpl);
1473
0
1474
0
  // This operation must be done on the correct thread. The rest must run on the
1475
0
  // main-thread.
1476
0
  aRv = webSocket->mImpl->mChannel->SetNotificationCallbacks(webSocket->mImpl);
1477
0
  if (NS_WARN_IF(aRv.Failed())) {
1478
0
    return nullptr;
1479
0
  }
1480
0
1481
0
  if (NS_IsMainThread()) {
1482
0
    MOZ_ASSERT(principal);
1483
0
1484
0
    nsPIDOMWindowOuter* outerWindow = ownerWindow->GetOuterWindow();
1485
0
1486
0
    uint64_t windowID = 0;
1487
0
    nsCOMPtr<nsPIDOMWindowOuter> topWindow = outerWindow->GetScriptableTop();
1488
0
    nsCOMPtr<nsPIDOMWindowInner> topInner;
1489
0
    if (topWindow) {
1490
0
      topInner = topWindow->GetCurrentInnerWindow();
1491
0
    }
1492
0
1493
0
    if (topInner) {
1494
0
      windowID = topInner->WindowID();
1495
0
    }
1496
0
1497
0
    aRv = webSocket->mImpl->AsyncOpen(principal, windowID, aTransportProvider,
1498
0
                                      aNegotiatedExtensions);
1499
0
  } else {
1500
0
    MOZ_ASSERT(!aTransportProvider && aNegotiatedExtensions.IsEmpty(),
1501
0
               "not yet implemented");
1502
0
    RefPtr<AsyncOpenRunnable> runnable =
1503
0
      new AsyncOpenRunnable(webSocket->mImpl);
1504
0
    runnable->Dispatch(Canceling, aRv);
1505
0
    if (NS_WARN_IF(aRv.Failed())) {
1506
0
      return nullptr;
1507
0
    }
1508
0
1509
0
    aRv = runnable->ErrorCode();
1510
0
  }
1511
0
1512
0
  if (NS_WARN_IF(aRv.Failed())) {
1513
0
    return nullptr;
1514
0
  }
1515
0
1516
0
  // It can be that we have been already disconnected because the WebSocket is
1517
0
  // gone away while we where initializing the webSocket.
1518
0
  if (!webSocket->mImpl) {
1519
0
    aRv.Throw(NS_ERROR_FAILURE);
1520
0
    return nullptr;
1521
0
  }
1522
0
1523
0
  // Let's inform devtools about this new active WebSocket.
1524
0
  webSocket->mImpl->mService->WebSocketCreated(webSocket->mImpl->mChannel->Serial(),
1525
0
                                               webSocket->mImpl->mInnerWindowID,
1526
0
                                               webSocket->mURI,
1527
0
                                               webSocket->mImpl->mRequestedProtocolList);
1528
0
  cws.Done();
1529
0
1530
0
  return webSocket.forget();
1531
0
}
1532
1533
NS_IMPL_CYCLE_COLLECTION_CLASS(WebSocket)
1534
1535
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WebSocket,
1536
0
                                                  DOMEventTargetHelper)
1537
0
  if (tmp->mImpl) {
1538
0
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImpl->mChannel)
1539
0
  }
1540
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1541
1542
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WebSocket,
1543
0
                                                DOMEventTargetHelper)
1544
0
  if (tmp->mImpl) {
1545
0
    NS_IMPL_CYCLE_COLLECTION_UNLINK(mImpl->mChannel)
1546
0
    tmp->mImpl->Disconnect();
1547
0
    MOZ_ASSERT(!tmp->mImpl);
1548
0
  }
1549
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1550
1551
bool
1552
WebSocket::IsCertainlyAliveForCC() const
1553
0
{
1554
0
  return mKeepingAlive;
1555
0
}
1556
1557
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebSocket)
1558
0
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
1559
1560
NS_IMPL_ADDREF_INHERITED(WebSocket, DOMEventTargetHelper)
1561
NS_IMPL_RELEASE_INHERITED(WebSocket, DOMEventTargetHelper)
1562
1563
void
1564
WebSocket::DisconnectFromOwner()
1565
0
{
1566
0
  AssertIsOnMainThread();
1567
0
1568
0
  // If we haven't called WebSocketImpl::Disconnect yet, update web
1569
0
  // socket count here.
1570
0
  if (mImpl && !mImpl->mDisconnectingOrDisconnected) {
1571
0
    GetOwner()->UpdateWebSocketCount(-1);
1572
0
  }
1573
0
1574
0
  DOMEventTargetHelper::DisconnectFromOwner();
1575
0
1576
0
  if (mImpl) {
1577
0
    mImpl->CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY);
1578
0
  }
1579
0
1580
0
  DontKeepAliveAnyMore();
1581
0
}
1582
1583
//-----------------------------------------------------------------------------
1584
// WebSocketImpl:: initialization
1585
//-----------------------------------------------------------------------------
1586
1587
nsresult
1588
WebSocketImpl::Init(JSContext* aCx,
1589
                    nsIPrincipal* aLoadingPrincipal,
1590
                    nsIPrincipal* aPrincipal,
1591
                    bool aIsServerSide,
1592
                    const nsAString& aURL,
1593
                    nsTArray<nsString>& aProtocolArray,
1594
                    const nsACString& aScriptFile,
1595
                    uint32_t aScriptLine,
1596
                    uint32_t aScriptColumn)
1597
0
{
1598
0
  AssertIsOnMainThread();
1599
0
  MOZ_ASSERT(aPrincipal);
1600
0
1601
0
  mService = WebSocketEventService::GetOrCreate();
1602
0
1603
0
  // We need to keep the implementation alive in case the init disconnects it
1604
0
  // because of some error.
1605
0
  RefPtr<WebSocketImpl> kungfuDeathGrip = this;
1606
0
1607
0
  // Attempt to kill "ghost" websocket: but usually too early for check to fail
1608
0
  nsresult rv = mWebSocket->CheckInnerWindowCorrectness();
1609
0
  NS_ENSURE_SUCCESS(rv, rv);
1610
0
1611
0
  // Shut down websocket if window is frozen or destroyed (only needed for
1612
0
  // "ghost" websockets--see bug 696085)
1613
0
  if (mIsMainThread) {
1614
0
    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1615
0
    if (NS_WARN_IF(!os)) {
1616
0
      return NS_ERROR_FAILURE;
1617
0
    }
1618
0
1619
0
    rv = os->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC, true);
1620
0
    NS_ENSURE_SUCCESS(rv, rv);
1621
0
1622
0
    rv = os->AddObserver(this, DOM_WINDOW_FROZEN_TOPIC, true);
1623
0
    NS_ENSURE_SUCCESS(rv, rv);
1624
0
  }
1625
0
1626
0
  if (!mIsMainThread) {
1627
0
    mScriptFile = aScriptFile;
1628
0
    mScriptLine = aScriptLine;
1629
0
    mScriptColumn = aScriptColumn;
1630
0
  } else {
1631
0
    MOZ_ASSERT(aCx);
1632
0
1633
0
    unsigned lineno, column;
1634
0
    JS::AutoFilename file;
1635
0
    if (JS::DescribeScriptedCaller(aCx, &file, &lineno, &column)) {
1636
0
      mScriptFile = file.get();
1637
0
      mScriptLine = lineno;
1638
0
      mScriptColumn = column;
1639
0
    }
1640
0
  }
1641
0
1642
0
  mIsServerSide = aIsServerSide;
1643
0
1644
0
  // If we don't have aCx, we are window-less, so we don't have a
1645
0
  // inner-windowID. This can happen in sharedWorkers and ServiceWorkers or in
1646
0
  // DedicateWorkers created by JSM.
1647
0
  if (aCx) {
1648
0
    mInnerWindowID = nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(aCx);
1649
0
  }
1650
0
1651
0
  mPrivateBrowsing = !!aPrincipal->OriginAttributesRef().mPrivateBrowsingId;
1652
0
1653
0
  // parses the url
1654
0
  rv = ParseURL(aURL);
1655
0
  NS_ENSURE_SUCCESS(rv, rv);
1656
0
1657
0
  nsCOMPtr<nsIDocument> originDoc = mWebSocket->GetDocumentIfCurrent();
1658
0
  if (!originDoc) {
1659
0
    rv = mWebSocket->CheckInnerWindowCorrectness();
1660
0
    NS_ENSURE_SUCCESS(rv, rv);
1661
0
  }
1662
0
  mOriginDocument = do_GetWeakReference(originDoc);
1663
0
1664
0
  if (!mIsServerSide) {
1665
0
    nsCOMPtr<nsIURI> uri;
1666
0
    {
1667
0
      nsresult rv = NS_NewURI(getter_AddRefs(uri), mURI);
1668
0
1669
0
      // We crash here because we are sure that mURI is a valid URI, so either we
1670
0
      // are OOM'ing or something else bad is happening.
1671
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
1672
0
        MOZ_CRASH();
1673
0
      }
1674
0
    }
1675
0
1676
0
    // The 'real' nsHttpChannel of the websocket gets opened in the parent.
1677
0
    // Since we don't serialize the CSP within child and parent and also not
1678
0
    // the context, we have to perform content policy checks here instead of
1679
0
    // AsyncOpen2().
1680
0
    // Please note that websockets can't follow redirects, hence there is no
1681
0
    // need to perform a CSP check after redirects.
1682
0
    nsCOMPtr<nsILoadInfo> secCheckLoadInfo =
1683
0
      new net::LoadInfo(aPrincipal, // loading principal
1684
0
                        aPrincipal, // triggering principal
1685
0
                        originDoc,
1686
0
                        nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
1687
0
                        nsIContentPolicy::TYPE_WEBSOCKET);
1688
0
1689
0
    int16_t shouldLoad = nsIContentPolicy::ACCEPT;
1690
0
    rv = NS_CheckContentLoadPolicy(uri,
1691
0
                                   secCheckLoadInfo,
1692
0
                                   EmptyCString(),
1693
0
                                   &shouldLoad,
1694
0
                                   nsContentUtils::GetContentPolicy());
1695
0
    NS_ENSURE_SUCCESS(rv, rv);
1696
0
1697
0
    if (NS_CP_REJECTED(shouldLoad)) {
1698
0
      // Disallowed by content policy
1699
0
      return NS_ERROR_CONTENT_BLOCKED;
1700
0
    }
1701
0
  }
1702
0
1703
0
  // Potentially the page uses the CSP directive 'upgrade-insecure-requests'.
1704
0
  // In such a case we have to upgrade ws: to wss: and also update mSecure
1705
0
  // to reflect that upgrade. Please note that we can not upgrade from ws:
1706
0
  // to wss: before performing content policy checks because CSP needs to
1707
0
  // send reports in case the scheme is about to be upgraded.
1708
0
  if (!mIsServerSide && !mSecure && originDoc &&
1709
0
      originDoc->GetUpgradeInsecureRequests(false)) {
1710
0
    // let's use the old specification before the upgrade for logging
1711
0
    NS_ConvertUTF8toUTF16 reportSpec(mURI);
1712
0
1713
0
    // upgrade the request from ws:// to wss:// and mark as secure
1714
0
    mURI.ReplaceSubstring("ws://", "wss://");
1715
0
    if (NS_WARN_IF(mURI.Find("wss://") != 0)) {
1716
0
      return NS_OK;
1717
0
    }
1718
0
    mSecure = true;
1719
0
1720
0
    const char16_t* params[] = { reportSpec.get(), u"wss" };
1721
0
    CSP_LogLocalizedStr("upgradeInsecureRequest",
1722
0
                        params, ArrayLength(params),
1723
0
                        EmptyString(), // aSourceFile
1724
0
                        EmptyString(), // aScriptSample
1725
0
                        0, // aLineNumber
1726
0
                        0, // aColumnNumber
1727
0
                        nsIScriptError::warningFlag,
1728
0
                        NS_LITERAL_CSTRING("upgradeInsecureRequest"),
1729
0
                        mInnerWindowID,
1730
0
                        mPrivateBrowsing);
1731
0
  }
1732
0
1733
0
  // Don't allow https:// to open ws://
1734
0
  if (!mIsServerSide && !mSecure &&
1735
0
      !Preferences::GetBool("network.websocket.allowInsecureFromHTTPS",
1736
0
                            false)) {
1737
0
    nsCOMPtr<nsIURI> originURI;
1738
0
    if (aLoadingPrincipal) {
1739
0
      aLoadingPrincipal->GetURI(getter_AddRefs(originURI));
1740
0
    }
1741
0
1742
0
    if (originURI) {
1743
0
      bool originIsHttps = false;
1744
0
      rv = originURI->SchemeIs("https", &originIsHttps);
1745
0
      NS_ENSURE_SUCCESS(rv, rv);
1746
0
1747
0
      if (originIsHttps) {
1748
0
        return NS_ERROR_DOM_SECURITY_ERR;
1749
0
      }
1750
0
    }
1751
0
  }
1752
0
1753
0
  // Assign the sub protocol list and scan it for illegal values
1754
0
  for (uint32_t index = 0; index < aProtocolArray.Length(); ++index) {
1755
0
    for (uint32_t i = 0; i < aProtocolArray[index].Length(); ++i) {
1756
0
      if (aProtocolArray[index][i] < static_cast<char16_t>(0x0021) ||
1757
0
          aProtocolArray[index][i] > static_cast<char16_t>(0x007E)) {
1758
0
        return NS_ERROR_DOM_SYNTAX_ERR;
1759
0
      }
1760
0
    }
1761
0
1762
0
    if (!mRequestedProtocolList.IsEmpty()) {
1763
0
      mRequestedProtocolList.AppendLiteral(", ");
1764
0
    }
1765
0
1766
0
    AppendUTF16toUTF8(aProtocolArray[index], mRequestedProtocolList);
1767
0
  }
1768
0
1769
0
  return NS_OK;
1770
0
}
1771
1772
nsresult
1773
WebSocketImpl::AsyncOpen(nsIPrincipal* aPrincipal, uint64_t aInnerWindowID,
1774
                         nsITransportProvider* aTransportProvider,
1775
                         const nsACString& aNegotiatedExtensions)
1776
0
{
1777
0
  MOZ_ASSERT(NS_IsMainThread(), "Not running on main thread");
1778
0
  MOZ_ASSERT_IF(!aTransportProvider, aNegotiatedExtensions.IsEmpty());
1779
0
1780
0
  nsCString asciiOrigin;
1781
0
  nsresult rv = nsContentUtils::GetASCIIOrigin(aPrincipal, asciiOrigin);
1782
0
  NS_ENSURE_SUCCESS(rv, rv);
1783
0
1784
0
  if (aTransportProvider) {
1785
0
    rv = mChannel->SetServerParameters(aTransportProvider,
1786
0
                                       aNegotiatedExtensions);
1787
0
    NS_ENSURE_SUCCESS(rv, rv);
1788
0
  }
1789
0
1790
0
  ToLowerCase(asciiOrigin);
1791
0
1792
0
  nsCOMPtr<nsIURI> uri;
1793
0
  if (!aTransportProvider) {
1794
0
    rv = NS_NewURI(getter_AddRefs(uri), mURI);
1795
0
    MOZ_ASSERT(NS_SUCCEEDED(rv));
1796
0
  }
1797
0
1798
0
  rv = mChannel->AsyncOpen(uri, asciiOrigin, aInnerWindowID, this, nullptr);
1799
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1800
0
    return NS_ERROR_CONTENT_BLOCKED;
1801
0
  }
1802
0
1803
0
  mInnerWindowID = aInnerWindowID;
1804
0
1805
0
  return NS_OK;
1806
0
}
1807
1808
//-----------------------------------------------------------------------------
1809
// WebSocketImpl methods:
1810
//-----------------------------------------------------------------------------
1811
1812
class nsAutoCloseWS final
1813
{
1814
public:
1815
  explicit nsAutoCloseWS(WebSocketImpl* aWebSocketImpl)
1816
    : mWebSocketImpl(aWebSocketImpl)
1817
0
  {}
1818
1819
  ~nsAutoCloseWS()
1820
0
  {
1821
0
    if (!mWebSocketImpl->mChannel) {
1822
0
      mWebSocketImpl->CloseConnection(nsIWebSocketChannel::CLOSE_INTERNAL_ERROR);
1823
0
    }
1824
0
  }
1825
private:
1826
  RefPtr<WebSocketImpl> mWebSocketImpl;
1827
};
1828
1829
nsresult
1830
WebSocketImpl::InitializeConnection(nsIPrincipal* aPrincipal)
1831
0
{
1832
0
  AssertIsOnMainThread();
1833
0
  MOZ_ASSERT(!mChannel, "mChannel should be null");
1834
0
1835
0
  nsCOMPtr<nsIWebSocketChannel> wsChannel;
1836
0
  nsAutoCloseWS autoClose(this);
1837
0
  nsresult rv;
1838
0
1839
0
  if (mSecure) {
1840
0
    wsChannel =
1841
0
      do_CreateInstance("@mozilla.org/network/protocol;1?name=wss", &rv);
1842
0
  } else {
1843
0
    wsChannel =
1844
0
      do_CreateInstance("@mozilla.org/network/protocol;1?name=ws", &rv);
1845
0
  }
1846
0
  NS_ENSURE_SUCCESS(rv, rv);
1847
0
1848
0
  // add ourselves to the document's load group and
1849
0
  // provide the http stack the loadgroup info too
1850
0
  nsCOMPtr<nsILoadGroup> loadGroup;
1851
0
  rv = GetLoadGroup(getter_AddRefs(loadGroup));
1852
0
  if (loadGroup) {
1853
0
    rv = wsChannel->SetLoadGroup(loadGroup);
1854
0
    NS_ENSURE_SUCCESS(rv, rv);
1855
0
    rv = loadGroup->AddRequest(this, nullptr);
1856
0
    NS_ENSURE_SUCCESS(rv, rv);
1857
0
1858
0
    mWeakLoadGroup = do_GetWeakReference(loadGroup);
1859
0
  }
1860
0
1861
0
  // manually adding loadinfo to the channel since it
1862
0
  // was not set during channel creation.
1863
0
  nsCOMPtr<nsIDocument> doc = do_QueryReferent(mOriginDocument);
1864
0
1865
0
  // mOriginDocument has to be release on main-thread because WeakReferences
1866
0
  // are not thread-safe.
1867
0
  mOriginDocument = nullptr;
1868
0
1869
0
1870
0
  // The TriggeringPrincipal for websockets must always be a script.
1871
0
  // Let's make sure that the doc's principal (if a doc exists)
1872
0
  // and aPrincipal are same origin.
1873
0
  MOZ_ASSERT(!doc || doc->NodePrincipal()->Equals(aPrincipal));
1874
0
1875
0
  rv = wsChannel->InitLoadInfo(doc,
1876
0
                               doc ? doc->NodePrincipal() : aPrincipal,
1877
0
                               aPrincipal,
1878
0
                               nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
1879
0
                               nsIContentPolicy::TYPE_WEBSOCKET);
1880
0
  MOZ_ASSERT(NS_SUCCEEDED(rv));
1881
0
1882
0
  if (!mRequestedProtocolList.IsEmpty()) {
1883
0
    rv = wsChannel->SetProtocol(mRequestedProtocolList);
1884
0
    NS_ENSURE_SUCCESS(rv, rv);
1885
0
  }
1886
0
1887
0
  nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(wsChannel);
1888
0
  NS_ENSURE_TRUE(rr, NS_ERROR_FAILURE);
1889
0
1890
0
  rv = rr->RetargetDeliveryTo(this);
1891
0
  NS_ENSURE_SUCCESS(rv, rv);
1892
0
1893
0
  mChannel = wsChannel;
1894
0
1895
0
  if (mIsMainThread && doc) {
1896
0
    mMainThreadEventTarget = doc->EventTargetFor(TaskCategory::Other);
1897
0
  }
1898
0
1899
0
  return NS_OK;
1900
0
}
1901
1902
void
1903
WebSocketImpl::DispatchConnectionCloseEvents()
1904
0
{
1905
0
  AssertIsOnTargetThread();
1906
0
1907
0
  if (mDisconnectingOrDisconnected) {
1908
0
    return;
1909
0
  }
1910
0
1911
0
  mWebSocket->SetReadyState(WebSocket::CLOSED);
1912
0
1913
0
  // Let's keep the object alive because the webSocket can be CCed in the
1914
0
  // onerror or in the onclose callback.
1915
0
  RefPtr<WebSocket> webSocket = mWebSocket;
1916
0
1917
0
  // Call 'onerror' if needed
1918
0
  if (mFailed) {
1919
0
    nsresult rv =
1920
0
      webSocket->CreateAndDispatchSimpleEvent(ERROR_EVENT_STRING);
1921
0
    if (NS_FAILED(rv)) {
1922
0
      NS_WARNING("Failed to dispatch the error event");
1923
0
    }
1924
0
  }
1925
0
1926
0
  nsresult rv = webSocket->CreateAndDispatchCloseEvent(mCloseEventWasClean,
1927
0
                                                       mCloseEventCode,
1928
0
                                                       mCloseEventReason);
1929
0
  if (NS_FAILED(rv)) {
1930
0
    NS_WARNING("Failed to dispatch the close event");
1931
0
  }
1932
0
1933
0
  webSocket->UpdateMustKeepAlive();
1934
0
  Disconnect();
1935
0
}
1936
1937
nsresult
1938
WebSocket::CreateAndDispatchSimpleEvent(const nsAString& aName)
1939
0
{
1940
0
  MOZ_ASSERT(mImpl);
1941
0
  AssertIsOnTargetThread();
1942
0
1943
0
  nsresult rv = CheckInnerWindowCorrectness();
1944
0
  if (NS_FAILED(rv)) {
1945
0
    return NS_OK;
1946
0
  }
1947
0
1948
0
  RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
1949
0
1950
0
  // it doesn't bubble, and it isn't cancelable
1951
0
  event->InitEvent(aName, false, false);
1952
0
  event->SetTrusted(true);
1953
0
1954
0
  ErrorResult err;
1955
0
  DispatchEvent(*event, err);
1956
0
  return err.StealNSResult();
1957
0
}
1958
1959
nsresult
1960
WebSocket::CreateAndDispatchMessageEvent(const nsACString& aData,
1961
                                         bool aIsBinary)
1962
0
{
1963
0
  MOZ_ASSERT(mImpl);
1964
0
  AssertIsOnTargetThread();
1965
0
1966
0
  AutoJSAPI jsapi;
1967
0
1968
0
  if (NS_IsMainThread()) {
1969
0
    if (NS_WARN_IF(!jsapi.Init(GetOwner()))) {
1970
0
      return NS_ERROR_FAILURE;
1971
0
    }
1972
0
  } else {
1973
0
    MOZ_ASSERT(!mIsMainThread);
1974
0
    MOZ_ASSERT(mImpl->mWorkerRef);
1975
0
    if (NS_WARN_IF(!jsapi.Init(mImpl->mWorkerRef->Private()->GlobalScope()))) {
1976
0
      return NS_ERROR_FAILURE;
1977
0
    }
1978
0
  }
1979
0
1980
0
  JSContext* cx = jsapi.cx();
1981
0
1982
0
  nsresult rv = CheckInnerWindowCorrectness();
1983
0
  if (NS_FAILED(rv)) {
1984
0
    return NS_OK;
1985
0
  }
1986
0
1987
0
  uint16_t messageType = nsIWebSocketEventListener::TYPE_STRING;
1988
0
1989
0
  // Create appropriate JS object for message
1990
0
  JS::Rooted<JS::Value> jsData(cx);
1991
0
  if (aIsBinary) {
1992
0
    if (mBinaryType == dom::BinaryType::Blob) {
1993
0
      messageType = nsIWebSocketEventListener::TYPE_BLOB;
1994
0
1995
0
      RefPtr<Blob> blob =
1996
0
        Blob::CreateStringBlob(GetOwner(), aData, EmptyString());
1997
0
      MOZ_ASSERT(blob);
1998
0
1999
0
      if (!ToJSValue(cx, blob, &jsData)) {
2000
0
        return NS_ERROR_FAILURE;
2001
0
      }
2002
0
2003
0
    } else if (mBinaryType == dom::BinaryType::Arraybuffer) {
2004
0
      messageType = nsIWebSocketEventListener::TYPE_ARRAYBUFFER;
2005
0
2006
0
      JS::Rooted<JSObject*> arrayBuf(cx);
2007
0
      nsresult rv = nsContentUtils::CreateArrayBuffer(cx, aData,
2008
0
                                                      arrayBuf.address());
2009
0
      NS_ENSURE_SUCCESS(rv, rv);
2010
0
      jsData.setObject(*arrayBuf);
2011
0
    } else {
2012
0
      MOZ_CRASH("Unknown binary type!");
2013
0
      return NS_ERROR_UNEXPECTED;
2014
0
    }
2015
0
  } else {
2016
0
    // JS string
2017
0
    NS_ConvertUTF8toUTF16 utf16Data(aData);
2018
0
    JSString* jsString;
2019
0
    jsString = JS_NewUCStringCopyN(cx, utf16Data.get(), utf16Data.Length());
2020
0
    NS_ENSURE_TRUE(jsString, NS_ERROR_FAILURE);
2021
0
2022
0
    jsData.setString(jsString);
2023
0
  }
2024
0
2025
0
  mImpl->mService->WebSocketMessageAvailable(mImpl->mChannel->Serial(),
2026
0
                                             mImpl->mInnerWindowID,
2027
0
                                             aData, messageType);
2028
0
2029
0
  // create an event that uses the MessageEvent interface,
2030
0
  // which does not bubble, is not cancelable, and has no default action
2031
0
2032
0
  RefPtr<MessageEvent> event = new MessageEvent(this, nullptr, nullptr);
2033
0
2034
0
  event->InitMessageEvent(nullptr, MESSAGE_EVENT_STRING, CanBubble::eNo,
2035
0
                          Cancelable::eNo, jsData, mImpl->mUTF16Origin,
2036
0
                          EmptyString(), nullptr,
2037
0
                          Sequence<OwningNonNull<MessagePort>>());
2038
0
  event->SetTrusted(true);
2039
0
2040
0
  ErrorResult err;
2041
0
  DispatchEvent(*event, err);
2042
0
  return err.StealNSResult();
2043
0
}
2044
2045
nsresult
2046
WebSocket::CreateAndDispatchCloseEvent(bool aWasClean,
2047
                                       uint16_t aCode,
2048
                                       const nsAString& aReason)
2049
0
{
2050
0
  AssertIsOnTargetThread();
2051
0
2052
0
  // This method is called by a runnable and it can happen that, in the
2053
0
  // meantime, GC unlinked this object, so mImpl could be null.
2054
0
  if (mImpl && mImpl->mChannel) {
2055
0
    mImpl->mService->WebSocketClosed(mImpl->mChannel->Serial(),
2056
0
                                     mImpl->mInnerWindowID,
2057
0
                                     aWasClean, aCode, aReason);
2058
0
  }
2059
0
2060
0
  nsresult rv = CheckInnerWindowCorrectness();
2061
0
  if (NS_FAILED(rv)) {
2062
0
    return NS_OK;
2063
0
  }
2064
0
2065
0
  CloseEventInit init;
2066
0
  init.mBubbles = false;
2067
0
  init.mCancelable = false;
2068
0
  init.mWasClean = aWasClean;
2069
0
  init.mCode = aCode;
2070
0
  init.mReason = aReason;
2071
0
2072
0
  RefPtr<CloseEvent> event =
2073
0
    CloseEvent::Constructor(this, CLOSE_EVENT_STRING, init);
2074
0
  event->SetTrusted(true);
2075
0
2076
0
  ErrorResult err;
2077
0
  DispatchEvent(*event, err);
2078
0
  return err.StealNSResult();
2079
0
}
2080
2081
nsresult
2082
WebSocketImpl::ParseURL(const nsAString& aURL)
2083
0
{
2084
0
  AssertIsOnMainThread();
2085
0
  NS_ENSURE_TRUE(!aURL.IsEmpty(), NS_ERROR_DOM_SYNTAX_ERR);
2086
0
2087
0
  if (mIsServerSide) {
2088
0
    mWebSocket->mURI = aURL;
2089
0
    CopyUTF16toUTF8(mWebSocket->mURI, mURI);
2090
0
2091
0
    return NS_OK;
2092
0
  }
2093
0
2094
0
  nsCOMPtr<nsIURI> uri;
2095
0
  nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL);
2096
0
  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
2097
0
2098
0
  nsCOMPtr<nsIURL> parsedURL = do_QueryInterface(uri, &rv);
2099
0
  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
2100
0
2101
0
  bool hasRef;
2102
0
  rv = parsedURL->GetHasRef(&hasRef);
2103
0
  NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && !hasRef,
2104
0
                 NS_ERROR_DOM_SYNTAX_ERR);
2105
0
2106
0
  nsAutoCString scheme;
2107
0
  rv = parsedURL->GetScheme(scheme);
2108
0
  NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && !scheme.IsEmpty(),
2109
0
                 NS_ERROR_DOM_SYNTAX_ERR);
2110
0
2111
0
  nsAutoCString host;
2112
0
  rv = parsedURL->GetAsciiHost(host);
2113
0
  NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && !host.IsEmpty(), NS_ERROR_DOM_SYNTAX_ERR);
2114
0
2115
0
  int32_t port;
2116
0
  rv = parsedURL->GetPort(&port);
2117
0
  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
2118
0
2119
0
  rv = NS_CheckPortSafety(port, scheme.get());
2120
0
  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
2121
0
2122
0
  nsAutoCString filePath;
2123
0
  rv = parsedURL->GetFilePath(filePath);
2124
0
  if (filePath.IsEmpty()) {
2125
0
    filePath.Assign('/');
2126
0
  }
2127
0
  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
2128
0
2129
0
  nsAutoCString query;
2130
0
  rv = parsedURL->GetQuery(query);
2131
0
  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
2132
0
2133
0
  if (scheme.LowerCaseEqualsLiteral("ws")) {
2134
0
     mSecure = false;
2135
0
     mPort = (port == -1) ? DEFAULT_WS_SCHEME_PORT : port;
2136
0
  } else if (scheme.LowerCaseEqualsLiteral("wss")) {
2137
0
    mSecure = true;
2138
0
    mPort = (port == -1) ? DEFAULT_WSS_SCHEME_PORT : port;
2139
0
  } else {
2140
0
    return NS_ERROR_DOM_SYNTAX_ERR;
2141
0
  }
2142
0
2143
0
  rv = nsContentUtils::GetUTFOrigin(parsedURL, mUTF16Origin);
2144
0
  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
2145
0
2146
0
  mAsciiHost = host;
2147
0
  ToLowerCase(mAsciiHost);
2148
0
2149
0
  mResource = filePath;
2150
0
  if (!query.IsEmpty()) {
2151
0
    mResource.Append('?');
2152
0
    mResource.Append(query);
2153
0
  }
2154
0
  uint32_t length = mResource.Length();
2155
0
  uint32_t i;
2156
0
  for (i = 0; i < length; ++i) {
2157
0
    if (mResource[i] < static_cast<char16_t>(0x0021) ||
2158
0
        mResource[i] > static_cast<char16_t>(0x007E)) {
2159
0
      return NS_ERROR_DOM_SYNTAX_ERR;
2160
0
    }
2161
0
  }
2162
0
2163
0
  rv = parsedURL->GetSpec(mURI);
2164
0
  MOZ_ASSERT(NS_SUCCEEDED(rv));
2165
0
2166
0
  CopyUTF8toUTF16(mURI, mWebSocket->mURI);
2167
0
  return NS_OK;
2168
0
}
2169
2170
//-----------------------------------------------------------------------------
2171
// Methods that keep alive the WebSocket object when:
2172
//   1. the object has registered event listeners that can be triggered
2173
//      ("strong event listeners");
2174
//   2. there are outgoing not sent messages.
2175
//-----------------------------------------------------------------------------
2176
2177
void
2178
WebSocket::UpdateMustKeepAlive()
2179
0
{
2180
0
  // Here we could not have mImpl.
2181
0
  MOZ_ASSERT(NS_IsMainThread() == mIsMainThread);
2182
0
2183
0
  if (!mCheckMustKeepAlive || !mImpl) {
2184
0
    return;
2185
0
  }
2186
0
2187
0
  bool shouldKeepAlive = false;
2188
0
  uint16_t readyState = ReadyState();
2189
0
2190
0
  if (mListenerManager) {
2191
0
    switch (readyState)
2192
0
    {
2193
0
      case CONNECTING:
2194
0
      {
2195
0
        if (mListenerManager->HasListenersFor(OPEN_EVENT_STRING) ||
2196
0
            mListenerManager->HasListenersFor(MESSAGE_EVENT_STRING) ||
2197
0
            mListenerManager->HasListenersFor(ERROR_EVENT_STRING) ||
2198
0
            mListenerManager->HasListenersFor(CLOSE_EVENT_STRING)) {
2199
0
          shouldKeepAlive = true;
2200
0
        }
2201
0
      }
2202
0
      break;
2203
0
2204
0
      case OPEN:
2205
0
      case CLOSING:
2206
0
      {
2207
0
        if (mListenerManager->HasListenersFor(MESSAGE_EVENT_STRING) ||
2208
0
            mListenerManager->HasListenersFor(ERROR_EVENT_STRING) ||
2209
0
            mListenerManager->HasListenersFor(CLOSE_EVENT_STRING) ||
2210
0
            mOutgoingBufferedAmount.value() != 0) {
2211
0
          shouldKeepAlive = true;
2212
0
        }
2213
0
      }
2214
0
      break;
2215
0
2216
0
      case CLOSED:
2217
0
      {
2218
0
        shouldKeepAlive = false;
2219
0
      }
2220
0
    }
2221
0
  }
2222
0
2223
0
  if (mKeepingAlive && !shouldKeepAlive) {
2224
0
    mKeepingAlive = false;
2225
0
    mImpl->ReleaseObject();
2226
0
  } else if (!mKeepingAlive && shouldKeepAlive) {
2227
0
    mKeepingAlive = true;
2228
0
    mImpl->AddRefObject();
2229
0
  }
2230
0
}
2231
2232
void
2233
WebSocket::DontKeepAliveAnyMore()
2234
0
{
2235
0
  // Here we could not have mImpl.
2236
0
  MOZ_ASSERT(NS_IsMainThread() == mIsMainThread);
2237
0
2238
0
  if (mKeepingAlive) {
2239
0
    MOZ_ASSERT(mImpl);
2240
0
2241
0
    mKeepingAlive = false;
2242
0
    mImpl->ReleaseObject();
2243
0
  }
2244
0
2245
0
  mCheckMustKeepAlive = false;
2246
0
}
2247
2248
void
2249
WebSocketImpl::AddRefObject()
2250
0
{
2251
0
  AssertIsOnTargetThread();
2252
0
  AddRef();
2253
0
}
2254
2255
void
2256
WebSocketImpl::ReleaseObject()
2257
0
{
2258
0
  AssertIsOnTargetThread();
2259
0
  Release();
2260
0
}
2261
2262
bool
2263
WebSocketImpl::RegisterWorkerRef(WorkerPrivate* aWorkerPrivate)
2264
0
{
2265
0
  MOZ_ASSERT(aWorkerPrivate);
2266
0
2267
0
  RefPtr<WebSocketImpl> self = this;
2268
0
2269
0
  // In workers we have to keep the worker alive using a strong reference in
2270
0
  // order to dispatch messages correctly.
2271
0
  RefPtr<StrongWorkerRef> workerRef =
2272
0
    StrongWorkerRef::Create(aWorkerPrivate, "WebSocketImpl", [self]()
2273
0
    {
2274
0
      {
2275
0
        MutexAutoLock lock(self->mMutex);
2276
0
        self->mWorkerShuttingDown = true;
2277
0
      }
2278
0
2279
0
      self->CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY,
2280
0
                            EmptyCString());
2281
0
    });
2282
0
  if (NS_WARN_IF(!workerRef)) {
2283
0
    return false;
2284
0
  }
2285
0
2286
0
  mWorkerRef = new ThreadSafeWorkerRef(workerRef);
2287
0
  MOZ_ASSERT(mWorkerRef);
2288
0
2289
0
  return true;
2290
0
}
2291
2292
void
2293
WebSocketImpl::UnregisterWorkerRef()
2294
0
{
2295
0
  MOZ_ASSERT(mDisconnectingOrDisconnected);
2296
0
  MOZ_ASSERT(mWorkerRef);
2297
0
  mWorkerRef->Private()->AssertIsOnWorkerThread();
2298
0
2299
0
  {
2300
0
    MutexAutoLock lock(mMutex);
2301
0
    mWorkerShuttingDown = true;
2302
0
  }
2303
0
2304
0
  // The DTOR of this StrongWorkerRef will release the worker for us.
2305
0
  mWorkerRef = nullptr;
2306
0
}
2307
2308
nsresult
2309
WebSocketImpl::UpdateURI()
2310
0
{
2311
0
  AssertIsOnTargetThread();
2312
0
2313
0
  // Check for Redirections
2314
0
  RefPtr<BaseWebSocketChannel> channel;
2315
0
  channel = static_cast<BaseWebSocketChannel*>(mChannel.get());
2316
0
  MOZ_ASSERT(channel);
2317
0
2318
0
  channel->GetEffectiveURL(mWebSocket->mEffectiveURL);
2319
0
  mSecure = channel->IsEncrypted();
2320
0
2321
0
  return NS_OK;
2322
0
}
2323
2324
void
2325
WebSocket::EventListenerAdded(nsAtom* aType)
2326
0
{
2327
0
  AssertIsOnTargetThread();
2328
0
  UpdateMustKeepAlive();
2329
0
}
2330
2331
void
2332
WebSocket::EventListenerRemoved(nsAtom* aType)
2333
0
{
2334
0
  AssertIsOnTargetThread();
2335
0
  UpdateMustKeepAlive();
2336
0
}
2337
2338
//-----------------------------------------------------------------------------
2339
// WebSocket - methods
2340
//-----------------------------------------------------------------------------
2341
2342
// webIDL: readonly attribute unsigned short readyState;
2343
uint16_t
2344
WebSocket::ReadyState()
2345
0
{
2346
0
  MutexAutoLock lock(mMutex);
2347
0
  return mReadyState;
2348
0
}
2349
2350
void
2351
WebSocket::SetReadyState(uint16_t aReadyState)
2352
0
{
2353
0
  MutexAutoLock lock(mMutex);
2354
0
  mReadyState = aReadyState;
2355
0
}
2356
2357
// webIDL: readonly attribute unsigned long bufferedAmount;
2358
uint32_t
2359
WebSocket::BufferedAmount() const
2360
0
{
2361
0
  AssertIsOnTargetThread();
2362
0
  MOZ_RELEASE_ASSERT(mOutgoingBufferedAmount.isValid());
2363
0
  return mOutgoingBufferedAmount.value();
2364
0
}
2365
2366
// webIDL: attribute BinaryType binaryType;
2367
dom::BinaryType
2368
WebSocket::BinaryType() const
2369
0
{
2370
0
  AssertIsOnTargetThread();
2371
0
  return mBinaryType;
2372
0
}
2373
2374
// webIDL: attribute BinaryType binaryType;
2375
void
2376
WebSocket::SetBinaryType(dom::BinaryType aData)
2377
0
{
2378
0
  AssertIsOnTargetThread();
2379
0
  mBinaryType = aData;
2380
0
}
2381
2382
// webIDL: readonly attribute DOMString url
2383
void
2384
WebSocket::GetUrl(nsAString& aURL)
2385
0
{
2386
0
  AssertIsOnTargetThread();
2387
0
2388
0
  if (mEffectiveURL.IsEmpty()) {
2389
0
    aURL = mURI;
2390
0
  } else {
2391
0
    aURL = mEffectiveURL;
2392
0
  }
2393
0
}
2394
2395
// webIDL: readonly attribute DOMString extensions;
2396
void
2397
WebSocket::GetExtensions(nsAString& aExtensions)
2398
0
{
2399
0
  AssertIsOnTargetThread();
2400
0
  CopyUTF8toUTF16(mEstablishedExtensions, aExtensions);
2401
0
}
2402
2403
// webIDL: readonly attribute DOMString protocol;
2404
void
2405
WebSocket::GetProtocol(nsAString& aProtocol)
2406
0
{
2407
0
  AssertIsOnTargetThread();
2408
0
  CopyUTF8toUTF16(mEstablishedProtocol, aProtocol);
2409
0
}
2410
2411
// webIDL: void send(DOMString data);
2412
void
2413
WebSocket::Send(const nsAString& aData,
2414
                ErrorResult& aRv)
2415
0
{
2416
0
  AssertIsOnTargetThread();
2417
0
2418
0
  NS_ConvertUTF16toUTF8 msgString(aData);
2419
0
  Send(nullptr, msgString, msgString.Length(), false, aRv);
2420
0
}
2421
2422
void
2423
WebSocket::Send(Blob& aData, ErrorResult& aRv)
2424
0
{
2425
0
  AssertIsOnTargetThread();
2426
0
2427
0
  nsCOMPtr<nsIInputStream> msgStream;
2428
0
  aData.CreateInputStream(getter_AddRefs(msgStream), aRv);
2429
0
  if (NS_WARN_IF(aRv.Failed())){
2430
0
    return;
2431
0
  }
2432
0
2433
0
  uint64_t msgLength = aData.GetSize(aRv);
2434
0
  if (NS_WARN_IF(aRv.Failed())){
2435
0
    return;
2436
0
  }
2437
0
2438
0
  if (msgLength > UINT32_MAX) {
2439
0
    aRv.Throw(NS_ERROR_FILE_TOO_BIG);
2440
0
    return;
2441
0
  }
2442
0
2443
0
  Send(msgStream, EmptyCString(), msgLength, true, aRv);
2444
0
}
2445
2446
void
2447
WebSocket::Send(const ArrayBuffer& aData,
2448
                ErrorResult& aRv)
2449
0
{
2450
0
  AssertIsOnTargetThread();
2451
0
2452
0
  aData.ComputeLengthAndData();
2453
0
2454
0
  static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
2455
0
2456
0
  uint32_t len = aData.Length();
2457
0
  char* data = reinterpret_cast<char*>(aData.Data());
2458
0
2459
0
  nsDependentCSubstring msgString(data, len);
2460
0
  Send(nullptr, msgString, len, true, aRv);
2461
0
}
2462
2463
void
2464
WebSocket::Send(const ArrayBufferView& aData,
2465
                ErrorResult& aRv)
2466
0
{
2467
0
  AssertIsOnTargetThread();
2468
0
2469
0
  aData.ComputeLengthAndData();
2470
0
2471
0
  static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
2472
0
2473
0
  uint32_t len = aData.Length();
2474
0
  char* data = reinterpret_cast<char*>(aData.Data());
2475
0
2476
0
  nsDependentCSubstring msgString(data, len);
2477
0
  Send(nullptr, msgString, len, true, aRv);
2478
0
}
2479
2480
void
2481
WebSocket::Send(nsIInputStream* aMsgStream,
2482
                const nsACString& aMsgString,
2483
                uint32_t aMsgLength,
2484
                bool aIsBinary,
2485
                ErrorResult& aRv)
2486
0
{
2487
0
  AssertIsOnTargetThread();
2488
0
2489
0
  int64_t readyState = ReadyState();
2490
0
  if (readyState == CONNECTING) {
2491
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2492
0
    return;
2493
0
  }
2494
0
2495
0
  // Always increment outgoing buffer len, even if closed
2496
0
  mOutgoingBufferedAmount += aMsgLength;
2497
0
  if (!mOutgoingBufferedAmount.isValid()) {
2498
0
    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
2499
0
    return;
2500
0
  }
2501
0
2502
0
  if (readyState == CLOSING ||
2503
0
      readyState == CLOSED) {
2504
0
    return;
2505
0
  }
2506
0
2507
0
  // We must have mImpl when connected.
2508
0
  MOZ_ASSERT(mImpl);
2509
0
  MOZ_ASSERT(readyState == OPEN, "Unknown state in WebSocket::Send");
2510
0
2511
0
  nsresult rv;
2512
0
  if (aMsgStream) {
2513
0
    rv = mImpl->mChannel->SendBinaryStream(aMsgStream, aMsgLength);
2514
0
  } else {
2515
0
    if (aIsBinary) {
2516
0
      rv = mImpl->mChannel->SendBinaryMsg(aMsgString);
2517
0
    } else {
2518
0
      rv = mImpl->mChannel->SendMsg(aMsgString);
2519
0
    }
2520
0
  }
2521
0
2522
0
  if (NS_FAILED(rv)) {
2523
0
    aRv.Throw(rv);
2524
0
    return;
2525
0
  }
2526
0
2527
0
  UpdateMustKeepAlive();
2528
0
}
2529
2530
// webIDL: void close(optional unsigned short code, optional DOMString reason):
2531
void
2532
WebSocket::Close(const Optional<uint16_t>& aCode,
2533
                 const Optional<nsAString>& aReason,
2534
                 ErrorResult& aRv)
2535
0
{
2536
0
  AssertIsOnTargetThread();
2537
0
2538
0
  // the reason code is optional, but if provided it must be in a specific range
2539
0
  uint16_t closeCode = 0;
2540
0
  if (aCode.WasPassed()) {
2541
0
    if (aCode.Value() != 1000 && (aCode.Value() < 3000 || aCode.Value() > 4999)) {
2542
0
      aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
2543
0
      return;
2544
0
    }
2545
0
    closeCode = aCode.Value();
2546
0
  }
2547
0
2548
0
  nsCString closeReason;
2549
0
  if (aReason.WasPassed()) {
2550
0
    CopyUTF16toUTF8(aReason.Value(), closeReason);
2551
0
2552
0
    // The API requires the UTF-8 string to be 123 or less bytes
2553
0
    if (closeReason.Length() > 123) {
2554
0
      aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
2555
0
      return;
2556
0
    }
2557
0
  }
2558
0
2559
0
  int64_t readyState = ReadyState();
2560
0
  if (readyState == CLOSING ||
2561
0
      readyState == CLOSED) {
2562
0
    return;
2563
0
  }
2564
0
2565
0
  // If we don't have mImpl, we are in a shutting down worker where we are still
2566
0
  // in CONNECTING state, but already disconnected internally.
2567
0
  if (!mImpl) {
2568
0
    MOZ_ASSERT(readyState == CONNECTING);
2569
0
    SetReadyState(CLOSING);
2570
0
    return;
2571
0
  }
2572
0
2573
0
  if (readyState == CONNECTING) {
2574
0
    mImpl->FailConnection(closeCode, closeReason);
2575
0
    return;
2576
0
  }
2577
0
2578
0
  MOZ_ASSERT(readyState == OPEN);
2579
0
  mImpl->CloseConnection(closeCode, closeReason);
2580
0
}
2581
2582
//-----------------------------------------------------------------------------
2583
// WebSocketImpl::nsIObserver
2584
//-----------------------------------------------------------------------------
2585
2586
NS_IMETHODIMP
2587
WebSocketImpl::Observe(nsISupports* aSubject,
2588
                       const char* aTopic,
2589
                       const char16_t* aData)
2590
0
{
2591
0
  AssertIsOnMainThread();
2592
0
2593
0
  int64_t readyState = mWebSocket->ReadyState();
2594
0
  if ((readyState == WebSocket::CLOSING) ||
2595
0
      (readyState == WebSocket::CLOSED)) {
2596
0
    return NS_OK;
2597
0
  }
2598
0
2599
0
  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aSubject);
2600
0
  if (!mWebSocket->GetOwner() || window != mWebSocket->GetOwner()) {
2601
0
    return NS_OK;
2602
0
  }
2603
0
2604
0
  if ((strcmp(aTopic, DOM_WINDOW_FROZEN_TOPIC) == 0) ||
2605
0
      (strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC) == 0))
2606
0
  {
2607
0
    CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY);
2608
0
  }
2609
0
2610
0
  return NS_OK;
2611
0
}
2612
2613
//-----------------------------------------------------------------------------
2614
// WebSocketImpl::nsIRequest
2615
//-----------------------------------------------------------------------------
2616
2617
NS_IMETHODIMP
2618
WebSocketImpl::GetName(nsACString& aName)
2619
0
{
2620
0
  AssertIsOnMainThread();
2621
0
2622
0
  CopyUTF16toUTF8(mWebSocket->mURI, aName);
2623
0
  return NS_OK;
2624
0
}
2625
2626
NS_IMETHODIMP
2627
WebSocketImpl::IsPending(bool* aValue)
2628
0
{
2629
0
  AssertIsOnTargetThread();
2630
0
2631
0
  int64_t readyState = mWebSocket->ReadyState();
2632
0
  *aValue = (readyState != WebSocket::CLOSED);
2633
0
  return NS_OK;
2634
0
}
2635
2636
NS_IMETHODIMP
2637
WebSocketImpl::GetStatus(nsresult* aStatus)
2638
0
{
2639
0
  AssertIsOnTargetThread();
2640
0
2641
0
  *aStatus = NS_OK;
2642
0
  return NS_OK;
2643
0
}
2644
2645
namespace {
2646
2647
class CancelRunnable final : public MainThreadWorkerRunnable
2648
{
2649
public:
2650
  CancelRunnable(ThreadSafeWorkerRef* aWorkerRef, WebSocketImpl* aImpl)
2651
    : MainThreadWorkerRunnable(aWorkerRef->Private())
2652
    , mImpl(aImpl)
2653
0
  {
2654
0
  }
2655
2656
  bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
2657
0
  {
2658
0
    aWorkerPrivate->AssertIsOnWorkerThread();
2659
0
    return !NS_FAILED(mImpl->CancelInternal());
2660
0
  }
2661
2662
private:
2663
  RefPtr<WebSocketImpl> mImpl;
2664
};
2665
2666
} // namespace
2667
2668
// Window closed, stop/reload button pressed, user navigated away from page, etc.
2669
NS_IMETHODIMP
2670
WebSocketImpl::Cancel(nsresult aStatus)
2671
0
{
2672
0
  AssertIsOnMainThread();
2673
0
2674
0
  if (!mIsMainThread) {
2675
0
    MOZ_ASSERT(mWorkerRef);
2676
0
    RefPtr<CancelRunnable> runnable =
2677
0
      new CancelRunnable(mWorkerRef, this);
2678
0
    if (!runnable->Dispatch()) {
2679
0
      return NS_ERROR_FAILURE;
2680
0
    }
2681
0
2682
0
    return NS_OK;
2683
0
  }
2684
0
2685
0
  return CancelInternal();
2686
0
}
2687
2688
nsresult
2689
WebSocketImpl::CancelInternal()
2690
0
{
2691
0
  AssertIsOnTargetThread();
2692
0
2693
0
   // If CancelInternal is called by a runnable, we may already be disconnected
2694
0
   // by the time it runs.
2695
0
  if (mDisconnectingOrDisconnected) {
2696
0
    return NS_OK;
2697
0
  }
2698
0
2699
0
  int64_t readyState = mWebSocket->ReadyState();
2700
0
  if (readyState == WebSocket::CLOSING || readyState == WebSocket::CLOSED) {
2701
0
    return NS_OK;
2702
0
  }
2703
0
2704
0
  return CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY);
2705
0
}
2706
2707
NS_IMETHODIMP
2708
WebSocketImpl::Suspend()
2709
0
{
2710
0
  AssertIsOnMainThread();
2711
0
  return NS_ERROR_NOT_IMPLEMENTED;
2712
0
}
2713
2714
NS_IMETHODIMP
2715
WebSocketImpl::Resume()
2716
0
{
2717
0
  AssertIsOnMainThread();
2718
0
  return NS_ERROR_NOT_IMPLEMENTED;
2719
0
}
2720
2721
NS_IMETHODIMP
2722
WebSocketImpl::GetLoadGroup(nsILoadGroup** aLoadGroup)
2723
0
{
2724
0
  AssertIsOnMainThread();
2725
0
2726
0
  *aLoadGroup = nullptr;
2727
0
2728
0
  if (mIsMainThread) {
2729
0
    nsCOMPtr<nsIDocument> doc = mWebSocket->GetDocumentIfCurrent();
2730
0
    if (doc) {
2731
0
      *aLoadGroup = doc->GetDocumentLoadGroup().take();
2732
0
    }
2733
0
2734
0
    return NS_OK;
2735
0
  }
2736
0
2737
0
  MOZ_ASSERT(mWorkerRef);
2738
0
2739
0
  // Walk up to our containing page
2740
0
  WorkerPrivate* wp = mWorkerRef->Private();
2741
0
  while (wp->GetParent()) {
2742
0
    wp = wp->GetParent();
2743
0
  }
2744
0
2745
0
  nsPIDOMWindowInner* window = wp->GetWindow();
2746
0
  if (!window) {
2747
0
    return NS_OK;
2748
0
  }
2749
0
2750
0
  nsIDocument* doc = window->GetExtantDoc();
2751
0
  if (doc) {
2752
0
    *aLoadGroup = doc->GetDocumentLoadGroup().take();
2753
0
  }
2754
0
2755
0
  return NS_OK;
2756
0
}
2757
2758
NS_IMETHODIMP
2759
WebSocketImpl::SetLoadGroup(nsILoadGroup* aLoadGroup)
2760
0
{
2761
0
  AssertIsOnMainThread();
2762
0
  return NS_ERROR_UNEXPECTED;
2763
0
}
2764
2765
NS_IMETHODIMP
2766
WebSocketImpl::GetLoadFlags(nsLoadFlags* aLoadFlags)
2767
0
{
2768
0
  AssertIsOnMainThread();
2769
0
2770
0
  *aLoadFlags = nsIRequest::LOAD_BACKGROUND;
2771
0
  return NS_OK;
2772
0
}
2773
2774
NS_IMETHODIMP
2775
WebSocketImpl::SetLoadFlags(nsLoadFlags aLoadFlags)
2776
0
{
2777
0
  AssertIsOnMainThread();
2778
0
2779
0
  // we won't change the load flags at all.
2780
0
  return NS_OK;
2781
0
}
2782
2783
namespace {
2784
2785
class WorkerRunnableDispatcher final : public WorkerRunnable
2786
{
2787
  RefPtr<WebSocketImpl> mWebSocketImpl;
2788
2789
public:
2790
  WorkerRunnableDispatcher(WebSocketImpl* aImpl, ThreadSafeWorkerRef* aWorkerRef,
2791
                           already_AddRefed<nsIRunnable> aEvent)
2792
    : WorkerRunnable(aWorkerRef->Private(), WorkerThreadUnchangedBusyCount)
2793
    , mWebSocketImpl(aImpl)
2794
    , mEvent(std::move(aEvent))
2795
0
  {
2796
0
  }
2797
2798
  bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
2799
0
  {
2800
0
    aWorkerPrivate->AssertIsOnWorkerThread();
2801
0
2802
0
    // No messages when disconnected.
2803
0
    if (mWebSocketImpl->mDisconnectingOrDisconnected) {
2804
0
      NS_WARNING("Dispatching a WebSocket event after the disconnection!");
2805
0
      return true;
2806
0
    }
2807
0
2808
0
    return !NS_FAILED(mEvent->Run());
2809
0
  }
2810
2811
  void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
2812
               bool aRunResult) override
2813
0
  {
2814
0
  }
2815
2816
  bool
2817
  PreDispatch(WorkerPrivate* aWorkerPrivate) override
2818
0
  {
2819
0
    // We don't call WorkerRunnable::PreDispatch because it would assert the
2820
0
    // wrong thing about which thread we're on.  We're on whichever thread the
2821
0
    // channel implementation is running on (probably the main thread or socket
2822
0
    // transport thread).
2823
0
    return true;
2824
0
  }
2825
2826
  void
2827
  PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
2828
0
  {
2829
0
    // We don't call WorkerRunnable::PreDispatch because it would assert the
2830
0
    // wrong thing about which thread we're on.  We're on whichever thread the
2831
0
    // channel implementation is running on (probably the main thread or socket
2832
0
    // transport thread).
2833
0
  }
2834
2835
private:
2836
  nsCOMPtr<nsIRunnable> mEvent;
2837
};
2838
2839
} // namespace
2840
2841
NS_IMETHODIMP
2842
WebSocketImpl::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
2843
0
{
2844
0
  nsCOMPtr<nsIRunnable> event(aEvent);
2845
0
  return Dispatch(event.forget(), aFlags);
2846
0
}
2847
2848
NS_IMETHODIMP
2849
WebSocketImpl::Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags)
2850
0
{
2851
0
  nsCOMPtr<nsIRunnable> event_ref(aEvent);
2852
0
  // If the target is the main-thread, we should try to dispatch the runnable
2853
0
  // to a labeled event target.
2854
0
  if (mIsMainThread) {
2855
0
    return mMainThreadEventTarget
2856
0
      ? mMainThreadEventTarget->Dispatch(event_ref.forget())
2857
0
      : GetMainThreadEventTarget()->Dispatch(event_ref.forget());
2858
0
  }
2859
0
2860
0
  MutexAutoLock lock(mMutex);
2861
0
  if (mWorkerShuttingDown) {
2862
0
    return NS_OK;
2863
0
  }
2864
0
2865
0
  MOZ_DIAGNOSTIC_ASSERT(mWorkerRef);
2866
0
2867
0
  // If the target is a worker, we have to use a custom WorkerRunnableDispatcher
2868
0
  // runnable.
2869
0
  RefPtr<WorkerRunnableDispatcher> event =
2870
0
    new WorkerRunnableDispatcher(this, mWorkerRef, event_ref.forget());
2871
0
2872
0
  if (!event->Dispatch()) {
2873
0
    return NS_ERROR_FAILURE;
2874
0
  }
2875
0
2876
0
  return NS_OK;
2877
0
}
2878
2879
NS_IMETHODIMP
2880
WebSocketImpl::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t)
2881
0
{
2882
0
  return NS_ERROR_NOT_IMPLEMENTED;
2883
0
}
2884
2885
NS_IMETHODIMP
2886
WebSocketImpl::IsOnCurrentThread(bool* aResult)
2887
0
{
2888
0
  *aResult = IsTargetThread();
2889
0
  return NS_OK;
2890
0
}
2891
2892
NS_IMETHODIMP_(bool)
2893
WebSocketImpl::IsOnCurrentThreadInfallible()
2894
0
{
2895
0
  return IsTargetThread();
2896
0
}
2897
2898
bool
2899
WebSocketImpl::IsTargetThread() const
2900
0
{
2901
0
  return NS_IsMainThread() == mIsMainThread;
2902
0
}
2903
2904
void
2905
WebSocket::AssertIsOnTargetThread() const
2906
0
{
2907
0
  MOZ_ASSERT(NS_IsMainThread() == mIsMainThread);
2908
0
}
2909
2910
nsresult
2911
WebSocketImpl::GetLoadingPrincipal(nsIPrincipal** aPrincipal)
2912
0
{
2913
0
  MOZ_ASSERT(NS_IsMainThread());
2914
0
  MOZ_ASSERT(mIsMainThread);
2915
0
2916
0
  // Check the principal's uri to determine if we were loaded from https.
2917
0
  nsCOMPtr<nsIGlobalObject> globalObject(GetEntryGlobal());
2918
0
  nsCOMPtr<nsIPrincipal> principal;
2919
0
2920
0
  if (globalObject) {
2921
0
    principal = globalObject->PrincipalOrNull();
2922
0
  }
2923
0
2924
0
  nsCOMPtr<nsPIDOMWindowInner> innerWindow;
2925
0
2926
0
  while (true) {
2927
0
    if (principal && !principal->GetIsNullPrincipal()) {
2928
0
      break;
2929
0
    }
2930
0
2931
0
    if (!innerWindow) {
2932
0
      innerWindow = do_QueryInterface(globalObject);
2933
0
      if (!innerWindow) {
2934
0
        // If we are in a XPConnect sandbox or in a JS component,
2935
0
        // innerWindow will be null. There is nothing on top of this to be
2936
0
        // considered.
2937
0
        break;
2938
0
      }
2939
0
    }
2940
0
2941
0
    nsCOMPtr<nsPIDOMWindowOuter> parentWindow =
2942
0
      innerWindow->GetScriptableParent();
2943
0
    if (NS_WARN_IF(!parentWindow)) {
2944
0
      return NS_ERROR_DOM_SECURITY_ERR;
2945
0
    }
2946
0
2947
0
    nsCOMPtr<nsPIDOMWindowInner> currentInnerWindow =
2948
0
      parentWindow->GetCurrentInnerWindow();
2949
0
    if (NS_WARN_IF(!currentInnerWindow)) {
2950
0
      return NS_ERROR_DOM_SECURITY_ERR;
2951
0
    }
2952
0
2953
0
    // We are at the top. Let's see if we have an opener window.
2954
0
    if (innerWindow == currentInnerWindow) {
2955
0
      ErrorResult error;
2956
0
      parentWindow =
2957
0
        nsGlobalWindowInner::Cast(innerWindow)->GetOpenerWindow(error);
2958
0
      if (NS_WARN_IF(error.Failed())) {
2959
0
        error.SuppressException();
2960
0
        return NS_ERROR_DOM_SECURITY_ERR;
2961
0
      }
2962
0
2963
0
      if (!parentWindow) {
2964
0
        break;
2965
0
      }
2966
0
2967
0
      if (parentWindow->GetScriptableTop() ==
2968
0
            innerWindow->GetScriptableTop()) {
2969
0
        break;
2970
0
      }
2971
0
2972
0
      currentInnerWindow = parentWindow->GetCurrentInnerWindow();
2973
0
      if (NS_WARN_IF(!currentInnerWindow)) {
2974
0
        return NS_ERROR_DOM_SECURITY_ERR;
2975
0
      }
2976
0
2977
0
      if (currentInnerWindow == innerWindow) {
2978
0
        // The opener may be the same outer window as the parent.
2979
0
        break;
2980
0
      }
2981
0
    }
2982
0
2983
0
    innerWindow = currentInnerWindow;
2984
0
2985
0
    nsCOMPtr<nsIDocument> document = innerWindow->GetExtantDoc();
2986
0
    if (NS_WARN_IF(!document)) {
2987
0
      return NS_ERROR_DOM_SECURITY_ERR;
2988
0
    }
2989
0
2990
0
    principal = document->NodePrincipal();
2991
0
  }
2992
0
2993
0
  principal.forget(aPrincipal);
2994
0
  return NS_OK;
2995
0
}
2996
2997
} // namespace dom
2998
} // namespace mozilla