1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set sw=2 ts=8 et tw=80 : */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Wellington Fernando de Macedo.
20 : * Portions created by the Initial Developer are Copyright (C) 2009
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Wellington Fernando de Macedo <wfernandom2004@gmail.com> (original author)
25 : * Patrick McManus <mcmanus@ducksong.com>
26 : * Jason Duell <jduell.mcbugs@gmail.com>
27 : *
28 : * Alternatively, the contents of this file may be used under the terms of
29 : * either of the GNU General Public License Version 2 or later (the "GPL"),
30 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 : * in which case the provisions of the GPL or the LGPL are applicable instead
32 : * of those above. If you wish to allow use of your version of this file only
33 : * under the terms of either the GPL or the LGPL, and not to allow others to
34 : * use your version of this file under the terms of the MPL, indicate your
35 : * decision by deleting the provisions above and replace them with the notice
36 : * and other provisions required by the GPL or the LGPL. If you do not delete
37 : * the provisions above, a recipient may use your version of this file under
38 : * the terms of any one of the MPL, the GPL or the LGPL.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 :
42 : #include "mozilla/Util.h"
43 :
44 : #include "nsWebSocket.h"
45 :
46 : #include "nsIScriptGlobalObject.h"
47 : #include "nsIDOMWindow.h"
48 : #include "nsIDocument.h"
49 : #include "nsXPCOM.h"
50 : #include "nsIXPConnect.h"
51 : #include "nsContentUtils.h"
52 : #include "nsEventDispatcher.h"
53 : #include "nsDOMError.h"
54 : #include "nsIScriptObjectPrincipal.h"
55 : #include "nsDOMClassInfoID.h"
56 : #include "jsapi.h"
57 : #include "nsIURL.h"
58 : #include "nsIPrivateDOMEvent.h"
59 : #include "nsICharsetConverterManager.h"
60 : #include "nsIUnicodeEncoder.h"
61 : #include "nsThreadUtils.h"
62 : #include "nsIDOMMessageEvent.h"
63 : #include "nsIPromptFactory.h"
64 : #include "nsIWindowWatcher.h"
65 : #include "nsIPrompt.h"
66 : #include "nsIStringBundle.h"
67 : #include "nsIConsoleService.h"
68 : #include "nsLayoutStatics.h"
69 : #include "nsIDOMCloseEvent.h"
70 : #include "nsICryptoHash.h"
71 : #include "jsdbgapi.h"
72 : #include "nsIJSContextStack.h"
73 : #include "nsJSUtils.h"
74 : #include "nsIScriptError.h"
75 : #include "nsNetUtil.h"
76 : #include "nsILoadGroup.h"
77 : #include "mozilla/Preferences.h"
78 : #include "nsDOMLists.h"
79 : #include "xpcpublic.h"
80 : #include "nsContentPolicyUtils.h"
81 : #include "nsContentErrors.h"
82 : #include "jstypedarray.h"
83 : #include "prmem.h"
84 : #include "nsDOMFile.h"
85 : #include "nsWrapperCacheInlines.h"
86 : #include "nsDOMEventTargetHelper.h"
87 : #include "nsIObserverService.h"
88 :
89 : using namespace mozilla;
90 :
91 : #define UTF_8_REPLACEMENT_CHAR static_cast<PRUnichar>(0xFFFD)
92 :
93 : #define TRUE_OR_FAIL_WEBSOCKET(x, ret) \
94 : PR_BEGIN_MACRO \
95 : if (NS_UNLIKELY(!(x))) { \
96 : NS_WARNING("TRUE_OR_FAIL_WEBSOCKET(" #x ") failed"); \
97 : FailConnection(nsIWebSocketChannel::CLOSE_INTERNAL_ERROR); \
98 : return ret; \
99 : } \
100 : PR_END_MACRO
101 :
102 : #define SUCCESS_OR_FAIL_WEBSOCKET(res, ret) \
103 : PR_BEGIN_MACRO \
104 : nsresult __rv = res; \
105 : if (NS_FAILED(__rv)) { \
106 : NS_ENSURE_SUCCESS_BODY(res, ret) \
107 : FailConnection(nsIWebSocketChannel::CLOSE_INTERNAL_ERROR); \
108 : return ret; \
109 : } \
110 : PR_END_MACRO
111 :
112 : nsresult
113 0 : nsWebSocket::PrintErrorOnConsole(const char *aBundleURI,
114 : const PRUnichar *aError,
115 : const PRUnichar **aFormatStrings,
116 : PRUint32 aFormatStringsLen)
117 : {
118 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
119 : nsresult rv;
120 :
121 : nsCOMPtr<nsIStringBundleService> bundleService =
122 0 : do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
123 0 : NS_ENSURE_SUCCESS(rv, rv);
124 :
125 0 : nsCOMPtr<nsIStringBundle> strBundle;
126 0 : rv = bundleService->CreateBundle(aBundleURI, getter_AddRefs(strBundle));
127 0 : NS_ENSURE_SUCCESS(rv, rv);
128 :
129 : nsCOMPtr<nsIConsoleService> console(
130 0 : do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv));
131 0 : NS_ENSURE_SUCCESS(rv, rv);
132 :
133 : nsCOMPtr<nsIScriptError> errorObject(
134 0 : do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv));
135 0 : NS_ENSURE_SUCCESS(rv, rv);
136 :
137 : // Localize the error message
138 0 : nsXPIDLString message;
139 0 : if (aFormatStrings) {
140 0 : rv = strBundle->FormatStringFromName(aError, aFormatStrings,
141 : aFormatStringsLen,
142 0 : getter_Copies(message));
143 : } else {
144 0 : rv = strBundle->GetStringFromName(aError, getter_Copies(message));
145 : }
146 0 : NS_ENSURE_SUCCESS(rv, rv);
147 :
148 0 : rv = errorObject->InitWithWindowID(message.get(),
149 0 : NS_ConvertUTF8toUTF16(mScriptFile).get(),
150 : nsnull, mScriptLine, 0,
151 : nsIScriptError::errorFlag, "Web Socket",
152 0 : mInnerWindowID);
153 0 : NS_ENSURE_SUCCESS(rv, rv);
154 :
155 : // print the error message directly to the JS console
156 0 : rv = console->LogMessage(errorObject);
157 0 : NS_ENSURE_SUCCESS(rv, rv);
158 :
159 0 : return NS_OK;
160 : }
161 :
162 : // when this is called the browser side wants no more part of it
163 : nsresult
164 0 : nsWebSocket::CloseConnection(PRUint16 aReasonCode,
165 : const nsACString& aReasonString)
166 : {
167 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
168 0 : if (mDisconnected)
169 0 : return NS_OK;
170 :
171 : // Disconnect() can release this object, so we keep a
172 : // reference until the end of the method
173 0 : nsRefPtr<nsWebSocket> kungfuDeathGrip = this;
174 :
175 0 : if (mReadyState == nsIWebSocket::CONNECTING) {
176 0 : SetReadyState(nsIWebSocket::CLOSED);
177 0 : if (mChannel) {
178 0 : mChannel->Close(aReasonCode, aReasonString);
179 : }
180 0 : Disconnect();
181 0 : return NS_OK;
182 : }
183 :
184 0 : SetReadyState(nsIWebSocket::CLOSING);
185 :
186 0 : if (mDisconnected) {
187 0 : SetReadyState(nsIWebSocket::CLOSED);
188 0 : Disconnect();
189 0 : return NS_OK;
190 : }
191 :
192 0 : return mChannel->Close(aReasonCode, aReasonString);
193 : }
194 :
195 : nsresult
196 0 : nsWebSocket::ConsoleError()
197 : {
198 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
199 : nsresult rv;
200 :
201 0 : nsCAutoString targetSpec;
202 0 : rv = mURI->GetSpec(targetSpec);
203 0 : if (NS_FAILED(rv)) {
204 0 : NS_WARNING("Failed to get targetSpec");
205 : } else {
206 0 : NS_ConvertUTF8toUTF16 specUTF16(targetSpec);
207 0 : const PRUnichar *formatStrings[] = { specUTF16.get() };
208 :
209 0 : if (mReadyState < nsIWebSocket::OPEN) {
210 : PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
211 0 : NS_LITERAL_STRING("connectionFailure").get(),
212 0 : formatStrings, ArrayLength(formatStrings));
213 : } else {
214 : PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
215 0 : NS_LITERAL_STRING("netInterrupt").get(),
216 0 : formatStrings, ArrayLength(formatStrings));
217 : }
218 : }
219 : /// todo some specific errors - like for message too large
220 0 : return rv;
221 : }
222 :
223 :
224 : nsresult
225 0 : nsWebSocket::FailConnection(PRUint16 aReasonCode,
226 : const nsACString& aReasonString)
227 : {
228 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
229 0 : ConsoleError();
230 :
231 0 : CloseConnection(aReasonCode, aReasonString);
232 :
233 0 : nsresult rv = CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("error"));
234 0 : if (NS_FAILED(rv))
235 0 : NS_WARNING("Failed to dispatch the error event");
236 :
237 0 : return NS_OK;
238 : }
239 :
240 : nsresult
241 0 : nsWebSocket::Disconnect()
242 : {
243 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
244 :
245 0 : if (mDisconnected)
246 0 : return NS_OK;
247 :
248 0 : nsCOMPtr<nsILoadGroup> loadGroup;
249 0 : GetLoadGroup(getter_AddRefs(loadGroup));
250 0 : if (loadGroup)
251 0 : loadGroup->RemoveRequest(this, nsnull, NS_OK);
252 :
253 0 : nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
254 0 : if (os) {
255 0 : os->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
256 0 : os->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC);
257 : }
258 :
259 : // DontKeepAliveAnyMore() can release the object. So hold a reference to this
260 : // until the end of the method.
261 0 : nsRefPtr<nsWebSocket> kungfuDeathGrip = this;
262 :
263 0 : DontKeepAliveAnyMore();
264 0 : mChannel = nsnull;
265 0 : mDisconnected = true;
266 :
267 0 : return NS_OK;
268 : }
269 :
270 : //-----------------------------------------------------------------------------
271 : // nsWebSocket::nsIWebSocketListener methods:
272 : //-----------------------------------------------------------------------------
273 :
274 : nsresult
275 0 : nsWebSocket::DoOnMessageAvailable(const nsACString & aMsg, bool isBinary)
276 : {
277 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
278 0 : NS_ABORT_IF_FALSE(!mDisconnected, "Received message after disconnecting");
279 :
280 0 : if (mReadyState == nsIWebSocket::OPEN) {
281 : // Dispatch New Message
282 0 : nsresult rv = CreateAndDispatchMessageEvent(aMsg, isBinary);
283 0 : if (NS_FAILED(rv)) {
284 0 : NS_WARNING("Failed to dispatch the message event");
285 : }
286 : } else {
287 : // CLOSING should be the only other state where it's possible to get msgs
288 : // from channel: Spec says to drop them.
289 0 : NS_ASSERTION(mReadyState == nsIWebSocket::CLOSING,
290 : "Received message while CONNECTING or CLOSED");
291 : }
292 :
293 0 : return NS_OK;
294 : }
295 :
296 : NS_IMETHODIMP
297 0 : nsWebSocket::OnMessageAvailable(nsISupports *aContext, const nsACString & aMsg)
298 : {
299 0 : return DoOnMessageAvailable(aMsg, false);
300 : }
301 :
302 : NS_IMETHODIMP
303 0 : nsWebSocket::OnBinaryMessageAvailable(nsISupports *aContext,
304 : const nsACString & aMsg)
305 : {
306 0 : return DoOnMessageAvailable(aMsg, true);
307 : }
308 :
309 : NS_IMETHODIMP
310 0 : nsWebSocket::OnStart(nsISupports *aContext)
311 : {
312 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
313 0 : if (mDisconnected)
314 0 : return NS_OK;
315 :
316 : // Attempt to kill "ghost" websocket: but usually too early for check to fail
317 0 : nsresult rv = CheckInnerWindowCorrectness();
318 0 : if (NS_FAILED(rv)) {
319 0 : FailConnectionQuietly();
320 0 : return rv;
321 : }
322 :
323 0 : if (!mRequestedProtocolList.IsEmpty()) {
324 0 : mChannel->GetProtocol(mEstablishedProtocol);
325 : }
326 :
327 0 : mChannel->GetExtensions(mEstablishedExtensions);
328 0 : UpdateURI();
329 :
330 0 : SetReadyState(nsIWebSocket::OPEN);
331 0 : return NS_OK;
332 : }
333 :
334 : NS_IMETHODIMP
335 0 : nsWebSocket::OnStop(nsISupports *aContext, nsresult aStatusCode)
336 : {
337 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
338 0 : if (mDisconnected)
339 0 : return NS_OK;
340 :
341 0 : mCloseEventWasClean = NS_SUCCEEDED(aStatusCode);
342 :
343 0 : if (aStatusCode == NS_BASE_STREAM_CLOSED &&
344 : mReadyState >= nsIWebSocket::CLOSING) {
345 : // don't generate an error event just because of an unclean close
346 0 : aStatusCode = NS_OK;
347 : }
348 :
349 0 : if (NS_FAILED(aStatusCode)) {
350 0 : ConsoleError();
351 0 : nsresult rv = CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("error"));
352 0 : if (NS_FAILED(rv))
353 0 : NS_WARNING("Failed to dispatch the error event");
354 : }
355 :
356 0 : SetReadyState(nsIWebSocket::CLOSED);
357 0 : Disconnect();
358 :
359 0 : return NS_OK;
360 : }
361 :
362 : NS_IMETHODIMP
363 0 : nsWebSocket::OnAcknowledge(nsISupports *aContext, PRUint32 aSize)
364 : {
365 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
366 :
367 0 : if (aSize > mOutgoingBufferedAmount)
368 0 : return NS_ERROR_UNEXPECTED;
369 :
370 0 : mOutgoingBufferedAmount -= aSize;
371 0 : return NS_OK;
372 : }
373 :
374 : NS_IMETHODIMP
375 0 : nsWebSocket::OnServerClose(nsISupports *aContext, PRUint16 aCode,
376 : const nsACString &aReason)
377 : {
378 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
379 :
380 0 : NS_ABORT_IF_FALSE(mReadyState != nsIWebSocket::CONNECTING,
381 : "Received server close before connected?");
382 :
383 0 : if (mReadyState == nsIWebSocket::CLOSED) {
384 0 : NS_WARNING("Received server close after already closed!");
385 0 : return NS_ERROR_UNEXPECTED;
386 : }
387 :
388 : // store code/string for onclose DOM event
389 0 : mCloseEventCode = aCode;
390 0 : CopyUTF8toUTF16(aReason, mCloseEventReason);
391 :
392 0 : if (mReadyState == nsIWebSocket::OPEN) {
393 : // Send reciprocal Close frame.
394 : // 5.5.1: "When sending a Close frame in response, the endpoint typically
395 : // echos the status code it received"
396 0 : CloseConnection(aCode, aReason);
397 : } else {
398 : // Nothing else to do: OnStop does the rest of the work.
399 0 : NS_ASSERTION (mReadyState == nsIWebSocket::CLOSING, "unknown state");
400 0 : NS_ASSERTION(!mDisconnected, "should not be disconnected during CLOSING");
401 : }
402 :
403 0 : return NS_OK;
404 : }
405 :
406 : //-----------------------------------------------------------------------------
407 : // nsWebSocket::nsIInterfaceRequestor
408 : //-----------------------------------------------------------------------------
409 :
410 : NS_IMETHODIMP
411 0 : nsWebSocket::GetInterface(const nsIID &aIID, void **aResult)
412 : {
413 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
414 :
415 0 : if (mDisconnected)
416 0 : return NS_ERROR_FAILURE;
417 :
418 0 : if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
419 0 : aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
420 : nsresult rv;
421 :
422 0 : nsIScriptContext* sc = GetContextForEventHandlers(&rv);
423 : nsCOMPtr<nsIDocument> doc =
424 0 : nsContentUtils::GetDocumentFromScriptContext(sc);
425 0 : if (!doc)
426 0 : return NS_ERROR_NOT_AVAILABLE;
427 :
428 : nsCOMPtr<nsIPromptFactory> wwatch =
429 0 : do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
430 0 : NS_ENSURE_SUCCESS(rv, rv);
431 :
432 0 : nsCOMPtr<nsPIDOMWindow> outerWindow = doc->GetWindow();
433 0 : return wwatch->GetPrompt(outerWindow, aIID, aResult);
434 : }
435 :
436 0 : return QueryInterface(aIID, aResult);
437 : }
438 :
439 : ////////////////////////////////////////////////////////////////////////////////
440 : // nsWebSocket
441 : ////////////////////////////////////////////////////////////////////////////////
442 :
443 0 : nsWebSocket::nsWebSocket() : mKeepingAlive(false),
444 : mCheckMustKeepAlive(true),
445 : mTriggeredCloseEvent(false),
446 : mDisconnected(false),
447 : mCloseEventWasClean(false),
448 : mCloseEventCode(nsIWebSocketChannel::CLOSE_ABNORMAL),
449 : mReadyState(nsIWebSocket::CONNECTING),
450 : mOutgoingBufferedAmount(0),
451 : mBinaryType(WS_BINARY_TYPE_BLOB),
452 : mScriptLine(0),
453 0 : mInnerWindowID(0)
454 : {
455 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
456 0 : nsLayoutStatics::AddRef();
457 0 : }
458 :
459 0 : nsWebSocket::~nsWebSocket()
460 : {
461 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
462 :
463 0 : Disconnect();
464 0 : nsLayoutStatics::Release();
465 0 : }
466 :
467 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsWebSocket)
468 :
469 0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsWebSocket)
470 0 : bool isBlack = tmp->IsBlack();
471 0 : if (isBlack|| tmp->mKeepingAlive) {
472 0 : if (tmp->mListenerManager) {
473 0 : tmp->mListenerManager->UnmarkGrayJSListeners();
474 0 : NS_UNMARK_LISTENER_WRAPPER(Open)
475 0 : NS_UNMARK_LISTENER_WRAPPER(Error)
476 0 : NS_UNMARK_LISTENER_WRAPPER(Message)
477 0 : NS_UNMARK_LISTENER_WRAPPER(Close)
478 : }
479 0 : if (!isBlack) {
480 0 : xpc_UnmarkGrayObject(tmp->PreservingWrapper() ?
481 0 : tmp->GetWrapperPreserveColor() :
482 0 : tmp->GetExpandoObjectPreserveColor());
483 : }
484 0 : return true;
485 : }
486 0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
487 :
488 0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsWebSocket)
489 0 : return tmp->IsBlack();
490 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
491 :
492 0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsWebSocket)
493 0 : return tmp->IsBlack();
494 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
495 :
496 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsWebSocket,
497 : nsDOMEventTargetHelper)
498 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
499 :
500 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsWebSocket,
501 : nsDOMEventTargetHelper)
502 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnOpenListener)
503 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnMessageListener)
504 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnCloseListener)
505 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnErrorListener)
506 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mPrincipal)
507 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mURI)
508 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChannel)
509 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
510 :
511 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsWebSocket,
512 : nsDOMEventTargetHelper)
513 0 : tmp->Disconnect();
514 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnOpenListener)
515 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnMessageListener)
516 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnCloseListener)
517 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnErrorListener)
518 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mPrincipal)
519 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mURI)
520 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mChannel)
521 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
522 :
523 : DOMCI_DATA(WebSocket, nsWebSocket)
524 :
525 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsWebSocket)
526 0 : NS_INTERFACE_MAP_ENTRY(nsIWebSocket)
527 0 : NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer)
528 0 : NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
529 0 : NS_INTERFACE_MAP_ENTRY(nsIWebSocketListener)
530 0 : NS_INTERFACE_MAP_ENTRY(nsIObserver)
531 0 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
532 0 : NS_INTERFACE_MAP_ENTRY(nsIRequest)
533 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(WebSocket)
534 0 : NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
535 :
536 0 : NS_IMPL_ADDREF_INHERITED(nsWebSocket, nsDOMEventTargetHelper)
537 0 : NS_IMPL_RELEASE_INHERITED(nsWebSocket, nsDOMEventTargetHelper)
538 :
539 : void
540 0 : nsWebSocket::DisconnectFromOwner()
541 : {
542 0 : nsDOMEventTargetHelper::DisconnectFromOwner();
543 0 : NS_DISCONNECT_EVENT_HANDLER(Open)
544 0 : NS_DISCONNECT_EVENT_HANDLER(Message)
545 0 : NS_DISCONNECT_EVENT_HANDLER(Close)
546 0 : NS_DISCONNECT_EVENT_HANDLER(Error)
547 0 : FailConnectionQuietly();
548 0 : DontKeepAliveAnyMore();
549 0 : }
550 :
551 : //-----------------------------------------------------------------------------
552 : // nsWebSocket::nsIJSNativeInitializer methods:
553 : //-----------------------------------------------------------------------------
554 :
555 : /**
556 : * This Initialize method is called from XPConnect via nsIJSNativeInitializer.
557 : * It is used for constructing our nsWebSocket from JavaScript. It expects a URL
558 : * string parameter and an optional protocol parameter which may be a string or
559 : * an array of strings. It also initializes the principal, the script context and
560 : * the window owner.
561 : */
562 : NS_IMETHODIMP
563 0 : nsWebSocket::Initialize(nsISupports* aOwner,
564 : JSContext* aContext,
565 : JSObject* aObject,
566 : PRUint32 aArgc,
567 : jsval* aArgv)
568 : {
569 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
570 0 : nsAutoString urlParam;
571 :
572 0 : if (!PrefEnabled()) {
573 0 : return NS_ERROR_DOM_SECURITY_ERR;
574 : }
575 :
576 0 : if (aArgc != 1 && aArgc != 2) {
577 0 : return NS_ERROR_DOM_SYNTAX_ERR;
578 : }
579 :
580 0 : JSAutoRequest ar(aContext);
581 :
582 0 : JSString* jsstr = JS_ValueToString(aContext, aArgv[0]);
583 0 : if (!jsstr) {
584 0 : return NS_ERROR_DOM_SYNTAX_ERR;
585 : }
586 :
587 0 : JS::Anchor<JSString *> deleteProtector(jsstr);
588 : size_t length;
589 0 : const jschar *chars = JS_GetStringCharsAndLength(aContext, jsstr, &length);
590 0 : if (!chars) {
591 0 : return NS_ERROR_OUT_OF_MEMORY;
592 : }
593 :
594 0 : urlParam.Assign(chars, length);
595 0 : deleteProtector.clear();
596 :
597 0 : nsCOMPtr<nsPIDOMWindow> ownerWindow = do_QueryInterface(aOwner);
598 0 : NS_ENSURE_STATE(ownerWindow);
599 :
600 0 : nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aOwner);
601 0 : NS_ENSURE_STATE(sgo);
602 0 : nsCOMPtr<nsIScriptContext> scriptContext = sgo->GetContext();
603 0 : NS_ENSURE_STATE(scriptContext);
604 :
605 0 : nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal(do_QueryInterface(aOwner));
606 0 : NS_ENSURE_STATE(scriptPrincipal);
607 0 : nsCOMPtr<nsIPrincipal> principal = scriptPrincipal->GetPrincipal();
608 0 : NS_ENSURE_STATE(principal);
609 :
610 0 : nsTArray<nsString> protocolArray;
611 :
612 0 : if (aArgc == 2) {
613 : JSObject *jsobj;
614 :
615 0 : if (JSVAL_IS_OBJECT(aArgv[1]) &&
616 0 : (jsobj = JSVAL_TO_OBJECT(aArgv[1])) &&
617 0 : JS_IsArrayObject(aContext, jsobj)) {
618 : uint32_t len;
619 0 : JS_GetArrayLength(aContext, jsobj, &len);
620 :
621 0 : for (PRUint32 index = 0; index < len; ++index) {
622 : jsval value;
623 :
624 0 : if (!JS_GetElement(aContext, jsobj, index, &value))
625 0 : return NS_ERROR_DOM_SYNTAX_ERR;
626 :
627 0 : jsstr = JS_ValueToString(aContext, value);
628 0 : if (!jsstr)
629 0 : return NS_ERROR_DOM_SYNTAX_ERR;
630 :
631 0 : deleteProtector.set(jsstr);
632 0 : chars = JS_GetStringCharsAndLength(aContext, jsstr, &length);
633 0 : if (!chars)
634 0 : return NS_ERROR_OUT_OF_MEMORY;
635 :
636 0 : nsDependentString protocolElement(chars, length);
637 0 : if (protocolElement.IsEmpty())
638 0 : return NS_ERROR_DOM_SYNTAX_ERR;
639 0 : if (protocolArray.Contains(protocolElement))
640 0 : return NS_ERROR_DOM_SYNTAX_ERR;
641 0 : if (protocolElement.FindChar(',') != -1) /* interferes w/list */
642 0 : return NS_ERROR_DOM_SYNTAX_ERR;
643 0 : protocolArray.AppendElement(protocolElement);
644 0 : deleteProtector.clear();
645 : }
646 : } else {
647 0 : jsstr = JS_ValueToString(aContext, aArgv[1]);
648 0 : if (!jsstr)
649 0 : return NS_ERROR_DOM_SYNTAX_ERR;
650 :
651 0 : deleteProtector.set(jsstr);
652 0 : chars = JS_GetStringCharsAndLength(aContext, jsstr, &length);
653 0 : if (!chars)
654 0 : return NS_ERROR_OUT_OF_MEMORY;
655 :
656 0 : nsDependentString protocolElement(chars, length);
657 0 : if (protocolElement.IsEmpty())
658 0 : return NS_ERROR_DOM_SYNTAX_ERR;
659 0 : if (protocolElement.FindChar(',') != -1) /* interferes w/list */
660 0 : return NS_ERROR_DOM_SYNTAX_ERR;
661 0 : protocolArray.AppendElement(protocolElement);
662 : }
663 : }
664 :
665 0 : return Init(principal, scriptContext, ownerWindow, urlParam, protocolArray);
666 : }
667 :
668 : //-----------------------------------------------------------------------------
669 : // nsWebSocket methods:
670 : //-----------------------------------------------------------------------------
671 :
672 : class nsAutoCloseWS
673 : {
674 : public:
675 0 : nsAutoCloseWS(nsWebSocket *aWebSocket)
676 0 : : mWebSocket(aWebSocket)
677 0 : {}
678 :
679 0 : ~nsAutoCloseWS()
680 0 : {
681 0 : if (!mWebSocket->mChannel) {
682 0 : mWebSocket->CloseConnection(nsIWebSocketChannel::CLOSE_INTERNAL_ERROR);
683 : }
684 0 : }
685 : private:
686 : nsRefPtr<nsWebSocket> mWebSocket;
687 : };
688 :
689 : nsresult
690 0 : nsWebSocket::EstablishConnection()
691 : {
692 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
693 0 : NS_ABORT_IF_FALSE(!mChannel, "mChannel should be null");
694 :
695 : nsresult rv;
696 :
697 0 : nsCOMPtr<nsIWebSocketChannel> wsChannel;
698 0 : nsAutoCloseWS autoClose(this);
699 :
700 0 : if (mSecure) {
701 : wsChannel =
702 0 : do_CreateInstance("@mozilla.org/network/protocol;1?name=wss", &rv);
703 : } else {
704 : wsChannel =
705 0 : do_CreateInstance("@mozilla.org/network/protocol;1?name=ws", &rv);
706 : }
707 0 : NS_ENSURE_SUCCESS(rv, rv);
708 :
709 0 : rv = wsChannel->SetNotificationCallbacks(this);
710 0 : NS_ENSURE_SUCCESS(rv, rv);
711 :
712 : // add ourselves to the document's load group and
713 : // provide the http stack the loadgroup info too
714 0 : nsCOMPtr<nsILoadGroup> loadGroup;
715 0 : rv = GetLoadGroup(getter_AddRefs(loadGroup));
716 0 : if (loadGroup) {
717 0 : rv = wsChannel->SetLoadGroup(loadGroup);
718 0 : NS_ENSURE_SUCCESS(rv, rv);
719 0 : rv = loadGroup->AddRequest(this, nsnull);
720 0 : NS_ENSURE_SUCCESS(rv, rv);
721 : }
722 :
723 0 : if (!mRequestedProtocolList.IsEmpty()) {
724 0 : rv = wsChannel->SetProtocol(mRequestedProtocolList);
725 0 : NS_ENSURE_SUCCESS(rv, rv);
726 : }
727 :
728 0 : nsCString asciiOrigin;
729 0 : rv = nsContentUtils::GetASCIIOrigin(mPrincipal, asciiOrigin);
730 0 : NS_ENSURE_SUCCESS(rv, rv);
731 :
732 0 : ToLowerCase(asciiOrigin);
733 :
734 0 : rv = wsChannel->AsyncOpen(mURI, asciiOrigin, this, nsnull);
735 0 : NS_ENSURE_SUCCESS(rv, rv);
736 :
737 0 : mChannel = wsChannel;
738 :
739 0 : return NS_OK;
740 : }
741 :
742 : class nsWSCloseEvent : public nsRunnable
743 0 : {
744 : public:
745 0 : nsWSCloseEvent(nsWebSocket *aWebSocket, bool aWasClean,
746 : PRUint16 aCode, const nsString &aReason)
747 : : mWebSocket(aWebSocket),
748 : mWasClean(aWasClean),
749 : mCode(aCode),
750 0 : mReason(aReason)
751 0 : {}
752 :
753 0 : NS_IMETHOD Run()
754 : {
755 : nsresult rv = mWebSocket->CreateAndDispatchCloseEvent(mWasClean,
756 0 : mCode, mReason);
757 0 : mWebSocket->UpdateMustKeepAlive();
758 0 : return rv;
759 : }
760 :
761 : private:
762 : nsRefPtr<nsWebSocket> mWebSocket;
763 : bool mWasClean;
764 : PRUint16 mCode;
765 : nsString mReason;
766 : };
767 :
768 : nsresult
769 0 : nsWebSocket::CreateAndDispatchSimpleEvent(const nsString& aName)
770 : {
771 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
772 : nsresult rv;
773 :
774 0 : rv = CheckInnerWindowCorrectness();
775 0 : if (NS_FAILED(rv)) {
776 0 : return NS_OK;
777 : }
778 :
779 0 : nsCOMPtr<nsIDOMEvent> event;
780 0 : rv = NS_NewDOMEvent(getter_AddRefs(event), nsnull, nsnull);
781 0 : NS_ENSURE_SUCCESS(rv, rv);
782 :
783 : // it doesn't bubble, and it isn't cancelable
784 0 : rv = event->InitEvent(aName, false, false);
785 0 : NS_ENSURE_SUCCESS(rv, rv);
786 :
787 0 : nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
788 0 : rv = privateEvent->SetTrusted(true);
789 0 : NS_ENSURE_SUCCESS(rv, rv);
790 :
791 0 : return DispatchDOMEvent(nsnull, event, nsnull, nsnull);
792 : }
793 :
794 : nsresult
795 0 : nsWebSocket::CreateAndDispatchMessageEvent(const nsACString& aData,
796 : bool isBinary)
797 : {
798 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
799 : nsresult rv;
800 :
801 0 : rv = CheckInnerWindowCorrectness();
802 0 : if (NS_FAILED(rv))
803 0 : return NS_OK;
804 :
805 : // Get the JSContext
806 0 : nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(GetOwner());
807 0 : NS_ENSURE_TRUE(sgo, NS_ERROR_FAILURE);
808 :
809 0 : nsIScriptContext* scriptContext = sgo->GetContext();
810 0 : NS_ENSURE_TRUE(scriptContext, NS_ERROR_FAILURE);
811 :
812 0 : JSContext* cx = scriptContext->GetNativeContext();
813 0 : NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
814 :
815 : // Create appropriate JS object for message
816 : jsval jsData;
817 : {
818 0 : JSAutoRequest ar(cx);
819 0 : if (isBinary) {
820 0 : if (mBinaryType == WS_BINARY_TYPE_BLOB) {
821 0 : rv = CreateResponseBlob(aData, cx, jsData);
822 0 : NS_ENSURE_SUCCESS(rv, rv);
823 0 : } else if (mBinaryType == WS_BINARY_TYPE_ARRAYBUFFER) {
824 : JSObject *arrayBuf;
825 0 : rv = nsContentUtils::CreateArrayBuffer(cx, aData, &arrayBuf);
826 0 : NS_ENSURE_SUCCESS(rv, rv);
827 0 : jsData = OBJECT_TO_JSVAL(arrayBuf);
828 : } else {
829 0 : NS_RUNTIMEABORT("Unknown binary type!");
830 0 : return NS_ERROR_UNEXPECTED;
831 : }
832 : } else {
833 : // JS string
834 0 : NS_ConvertUTF8toUTF16 utf16Data(aData);
835 : JSString* jsString;
836 0 : jsString = JS_NewUCStringCopyN(cx, utf16Data.get(), utf16Data.Length());
837 0 : NS_ENSURE_TRUE(jsString, NS_ERROR_FAILURE);
838 :
839 0 : jsData = STRING_TO_JSVAL(jsString);
840 : }
841 : }
842 :
843 : // create an event that uses the MessageEvent interface,
844 : // which does not bubble, is not cancelable, and has no default action
845 :
846 0 : nsCOMPtr<nsIDOMEvent> event;
847 0 : rv = NS_NewDOMMessageEvent(getter_AddRefs(event), nsnull, nsnull);
848 0 : NS_ENSURE_SUCCESS(rv, rv);
849 :
850 0 : nsCOMPtr<nsIDOMMessageEvent> messageEvent = do_QueryInterface(event);
851 0 : rv = messageEvent->InitMessageEvent(NS_LITERAL_STRING("message"),
852 : false, false,
853 : jsData,
854 : mUTF16Origin,
855 0 : EmptyString(), nsnull);
856 0 : NS_ENSURE_SUCCESS(rv, rv);
857 :
858 0 : nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
859 0 : rv = privateEvent->SetTrusted(true);
860 0 : NS_ENSURE_SUCCESS(rv, rv);
861 :
862 0 : return DispatchDOMEvent(nsnull, event, nsnull, nsnull);
863 : }
864 :
865 : // Initial implementation: only stores to RAM, not file
866 : // TODO: bug 704447: large file support
867 : nsresult
868 0 : nsWebSocket::CreateResponseBlob(const nsACString& aData, JSContext *aCx,
869 : jsval &jsData)
870 : {
871 0 : PRUint32 blobLen = aData.Length();
872 0 : void *blobData = PR_Malloc(blobLen);
873 0 : nsCOMPtr<nsIDOMBlob> blob;
874 0 : if (blobData) {
875 0 : memcpy(blobData, aData.BeginReading(), blobLen);
876 0 : blob = new nsDOMMemoryFile(blobData, blobLen, EmptyString());
877 : } else {
878 0 : return NS_ERROR_OUT_OF_MEMORY;
879 : }
880 0 : JSObject* scope = JS_GetGlobalForScopeChain(aCx);
881 0 : return nsContentUtils::WrapNative(aCx, scope, blob, &jsData, nsnull, true);
882 : }
883 :
884 : nsresult
885 0 : nsWebSocket::CreateAndDispatchCloseEvent(bool aWasClean,
886 : PRUint16 aCode,
887 : const nsString &aReason)
888 : {
889 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
890 : nsresult rv;
891 :
892 0 : mTriggeredCloseEvent = true;
893 :
894 0 : rv = CheckInnerWindowCorrectness();
895 0 : if (NS_FAILED(rv)) {
896 0 : return NS_OK;
897 : }
898 :
899 : // create an event that uses the CloseEvent interface,
900 : // which does not bubble, is not cancelable, and has no default action
901 :
902 0 : nsCOMPtr<nsIDOMEvent> event;
903 0 : rv = NS_NewDOMCloseEvent(getter_AddRefs(event), nsnull, nsnull);
904 0 : NS_ENSURE_SUCCESS(rv, rv);
905 :
906 0 : nsCOMPtr<nsIDOMCloseEvent> closeEvent = do_QueryInterface(event);
907 0 : rv = closeEvent->InitCloseEvent(NS_LITERAL_STRING("close"),
908 : false, false,
909 0 : aWasClean, aCode, aReason);
910 0 : NS_ENSURE_SUCCESS(rv, rv);
911 :
912 0 : nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
913 0 : rv = privateEvent->SetTrusted(true);
914 0 : NS_ENSURE_SUCCESS(rv, rv);
915 :
916 0 : return DispatchDOMEvent(nsnull, event, nsnull, nsnull);
917 : }
918 :
919 : bool
920 0 : nsWebSocket::PrefEnabled()
921 : {
922 0 : return Preferences::GetBool("network.websocket.enabled", true);
923 : }
924 :
925 : void
926 0 : nsWebSocket::SetReadyState(PRUint16 aNewReadyState)
927 : {
928 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
929 : nsresult rv;
930 :
931 0 : if (mReadyState == aNewReadyState) {
932 0 : return;
933 : }
934 :
935 0 : NS_ABORT_IF_FALSE((aNewReadyState == nsIWebSocket::OPEN) ||
936 : (aNewReadyState == nsIWebSocket::CLOSING) ||
937 : (aNewReadyState == nsIWebSocket::CLOSED),
938 : "unexpected readyState");
939 :
940 0 : if (aNewReadyState == nsIWebSocket::OPEN) {
941 0 : NS_ABORT_IF_FALSE(mReadyState == nsIWebSocket::CONNECTING,
942 : "unexpected readyState transition");
943 0 : mReadyState = aNewReadyState;
944 :
945 0 : rv = CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("open"));
946 0 : if (NS_FAILED(rv)) {
947 0 : NS_WARNING("Failed to dispatch the open event");
948 : }
949 0 : UpdateMustKeepAlive();
950 0 : return;
951 : }
952 :
953 0 : if (aNewReadyState == nsIWebSocket::CLOSING) {
954 0 : NS_ABORT_IF_FALSE((mReadyState == nsIWebSocket::CONNECTING) ||
955 : (mReadyState == nsIWebSocket::OPEN),
956 : "unexpected readyState transition");
957 0 : mReadyState = aNewReadyState;
958 0 : return;
959 : }
960 :
961 0 : if (aNewReadyState == nsIWebSocket::CLOSED) {
962 0 : mReadyState = aNewReadyState;
963 :
964 : // The close event must be dispatched asynchronously.
965 : rv = NS_DispatchToMainThread(new nsWSCloseEvent(this, mCloseEventWasClean,
966 : mCloseEventCode,
967 0 : mCloseEventReason),
968 0 : NS_DISPATCH_NORMAL);
969 0 : if (NS_FAILED(rv)) {
970 0 : NS_WARNING("Failed to dispatch the close event");
971 0 : mTriggeredCloseEvent = true;
972 0 : UpdateMustKeepAlive();
973 : }
974 : }
975 : }
976 :
977 : nsresult
978 0 : nsWebSocket::ParseURL(const nsString& aURL)
979 : {
980 : nsresult rv;
981 :
982 0 : NS_ENSURE_TRUE(!aURL.IsEmpty(), NS_ERROR_DOM_SYNTAX_ERR);
983 :
984 0 : nsCOMPtr<nsIURI> uri;
985 0 : rv = NS_NewURI(getter_AddRefs(uri), aURL);
986 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
987 :
988 0 : nsCOMPtr<nsIURL> parsedURL(do_QueryInterface(uri, &rv));
989 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
990 :
991 0 : nsCAutoString fragment;
992 0 : rv = parsedURL->GetRef(fragment);
993 0 : NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && fragment.IsEmpty(),
994 : NS_ERROR_DOM_SYNTAX_ERR);
995 :
996 0 : nsCAutoString scheme;
997 0 : rv = parsedURL->GetScheme(scheme);
998 0 : NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && !scheme.IsEmpty(),
999 : NS_ERROR_DOM_SYNTAX_ERR);
1000 :
1001 0 : nsCAutoString host;
1002 0 : rv = parsedURL->GetAsciiHost(host);
1003 0 : NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && !host.IsEmpty(), NS_ERROR_DOM_SYNTAX_ERR);
1004 :
1005 : PRInt32 port;
1006 0 : rv = parsedURL->GetPort(&port);
1007 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
1008 :
1009 0 : rv = NS_CheckPortSafety(port, scheme.get());
1010 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
1011 :
1012 0 : nsCAutoString filePath;
1013 0 : rv = parsedURL->GetFilePath(filePath);
1014 0 : if (filePath.IsEmpty()) {
1015 0 : filePath.AssignLiteral("/");
1016 : }
1017 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
1018 :
1019 0 : nsCAutoString query;
1020 0 : rv = parsedURL->GetQuery(query);
1021 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
1022 :
1023 0 : if (scheme.LowerCaseEqualsLiteral("ws")) {
1024 0 : mSecure = false;
1025 0 : mPort = (port == -1) ? DEFAULT_WS_SCHEME_PORT : port;
1026 0 : } else if (scheme.LowerCaseEqualsLiteral("wss")) {
1027 0 : mSecure = true;
1028 0 : mPort = (port == -1) ? DEFAULT_WSS_SCHEME_PORT : port;
1029 : } else {
1030 0 : return NS_ERROR_DOM_SYNTAX_ERR;
1031 : }
1032 :
1033 0 : rv = nsContentUtils::GetUTFOrigin(parsedURL, mUTF16Origin);
1034 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
1035 :
1036 0 : mAsciiHost = host;
1037 0 : ToLowerCase(mAsciiHost);
1038 :
1039 0 : mResource = filePath;
1040 0 : if (!query.IsEmpty()) {
1041 0 : mResource.AppendLiteral("?");
1042 0 : mResource.Append(query);
1043 : }
1044 0 : PRUint32 length = mResource.Length();
1045 : PRUint32 i;
1046 0 : for (i = 0; i < length; ++i) {
1047 0 : if (mResource[i] < static_cast<PRUnichar>(0x0021) ||
1048 0 : mResource[i] > static_cast<PRUnichar>(0x007E)) {
1049 0 : return NS_ERROR_DOM_SYNTAX_ERR;
1050 : }
1051 : }
1052 :
1053 0 : mOriginalURL = aURL;
1054 0 : mURI = parsedURL;
1055 0 : return NS_OK;
1056 : }
1057 :
1058 : //-----------------------------------------------------------------------------
1059 : // Methods that keep alive the WebSocket object when:
1060 : // 1. the object has registered event listeners that can be triggered
1061 : // ("strong event listeners");
1062 : // 2. there are outgoing not sent messages.
1063 : //-----------------------------------------------------------------------------
1064 :
1065 : void
1066 0 : nsWebSocket::UpdateMustKeepAlive()
1067 : {
1068 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
1069 0 : if (!mCheckMustKeepAlive) {
1070 0 : return;
1071 : }
1072 :
1073 0 : bool shouldKeepAlive = false;
1074 :
1075 0 : if (mListenerManager) {
1076 0 : switch (mReadyState)
1077 : {
1078 : case nsIWebSocket::CONNECTING:
1079 : {
1080 0 : if (mListenerManager->HasListenersFor(NS_LITERAL_STRING("open")) ||
1081 0 : mListenerManager->HasListenersFor(NS_LITERAL_STRING("message")) ||
1082 0 : mListenerManager->HasListenersFor(NS_LITERAL_STRING("error")) ||
1083 0 : mListenerManager->HasListenersFor(NS_LITERAL_STRING("close"))) {
1084 0 : shouldKeepAlive = true;
1085 : }
1086 : }
1087 0 : break;
1088 :
1089 : case nsIWebSocket::OPEN:
1090 : case nsIWebSocket::CLOSING:
1091 : {
1092 0 : if (mListenerManager->HasListenersFor(NS_LITERAL_STRING("message")) ||
1093 0 : mListenerManager->HasListenersFor(NS_LITERAL_STRING("error")) ||
1094 0 : mListenerManager->HasListenersFor(NS_LITERAL_STRING("close")) ||
1095 : mOutgoingBufferedAmount != 0) {
1096 0 : shouldKeepAlive = true;
1097 : }
1098 : }
1099 0 : break;
1100 :
1101 : case nsIWebSocket::CLOSED:
1102 : {
1103 : shouldKeepAlive =
1104 0 : (!mTriggeredCloseEvent &&
1105 0 : mListenerManager->HasListenersFor(NS_LITERAL_STRING("close")));
1106 : }
1107 : }
1108 : }
1109 :
1110 0 : if (mKeepingAlive && !shouldKeepAlive) {
1111 0 : mKeepingAlive = false;
1112 0 : static_cast<nsIDOMEventTarget*>(this)->Release();
1113 0 : } else if (!mKeepingAlive && shouldKeepAlive) {
1114 0 : mKeepingAlive = true;
1115 0 : static_cast<nsIDOMEventTarget*>(this)->AddRef();
1116 : }
1117 : }
1118 :
1119 : void
1120 0 : nsWebSocket::DontKeepAliveAnyMore()
1121 : {
1122 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
1123 0 : if (mKeepingAlive) {
1124 0 : mKeepingAlive = false;
1125 0 : static_cast<nsIDOMEventTarget*>(this)->Release();
1126 : }
1127 0 : mCheckMustKeepAlive = false;
1128 0 : }
1129 :
1130 : void
1131 0 : nsWebSocket::FailConnectionQuietly()
1132 : {
1133 : // Fail without console error or JS onerror message: onmessage/onclose will
1134 : // also be blocked so long as CheckInnerWindowCorrectness is failing.
1135 0 : CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY);
1136 0 : }
1137 :
1138 : nsresult
1139 0 : nsWebSocket::UpdateURI()
1140 : {
1141 : // Check for Redirections
1142 0 : nsCOMPtr<nsIURI> uri;
1143 0 : nsresult rv = mChannel->GetURI(getter_AddRefs(uri));
1144 0 : NS_ENSURE_SUCCESS(rv, rv);
1145 :
1146 0 : nsCAutoString spec;
1147 0 : rv = uri->GetSpec(spec);
1148 0 : NS_ENSURE_SUCCESS(rv, rv);
1149 0 : CopyUTF8toUTF16(spec, mEffectiveURL);
1150 :
1151 0 : bool isWSS = false;
1152 0 : rv = uri->SchemeIs("wss", &isWSS);
1153 0 : NS_ENSURE_SUCCESS(rv, rv);
1154 0 : mSecure = isWSS ? true : false;
1155 :
1156 0 : return NS_OK;
1157 : }
1158 :
1159 : NS_IMETHODIMP
1160 0 : nsWebSocket::RemoveEventListener(const nsAString& aType,
1161 : nsIDOMEventListener* aListener,
1162 : bool aUseCapture)
1163 : {
1164 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
1165 : nsresult rv = nsDOMEventTargetHelper::RemoveEventListener(aType,
1166 : aListener,
1167 0 : aUseCapture);
1168 0 : if (NS_SUCCEEDED(rv)) {
1169 0 : UpdateMustKeepAlive();
1170 : }
1171 0 : return rv;
1172 : }
1173 :
1174 : NS_IMETHODIMP
1175 0 : nsWebSocket::AddEventListener(const nsAString& aType,
1176 : nsIDOMEventListener *aListener,
1177 : bool aUseCapture,
1178 : bool aWantsUntrusted,
1179 : PRUint8 optional_argc)
1180 : {
1181 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
1182 : nsresult rv = nsDOMEventTargetHelper::AddEventListener(aType,
1183 : aListener,
1184 : aUseCapture,
1185 : aWantsUntrusted,
1186 0 : optional_argc);
1187 0 : if (NS_SUCCEEDED(rv)) {
1188 0 : UpdateMustKeepAlive();
1189 : }
1190 0 : return rv;
1191 : }
1192 :
1193 : //-----------------------------------------------------------------------------
1194 : // nsWebSocket::nsIWebSocket methods:
1195 : //-----------------------------------------------------------------------------
1196 :
1197 : NS_IMETHODIMP
1198 0 : nsWebSocket::GetUrl(nsAString& aURL)
1199 : {
1200 0 : if (mEffectiveURL.IsEmpty()) {
1201 0 : aURL = mOriginalURL;
1202 : } else {
1203 0 : aURL = mEffectiveURL;
1204 : }
1205 0 : return NS_OK;
1206 : }
1207 :
1208 : NS_IMETHODIMP
1209 0 : nsWebSocket::GetExtensions(nsAString& aExtensions)
1210 : {
1211 0 : CopyUTF8toUTF16(mEstablishedExtensions, aExtensions);
1212 0 : return NS_OK;
1213 : }
1214 :
1215 : NS_IMETHODIMP
1216 0 : nsWebSocket::GetProtocol(nsAString& aProtocol)
1217 : {
1218 0 : CopyUTF8toUTF16(mEstablishedProtocol, aProtocol);
1219 0 : return NS_OK;
1220 : }
1221 :
1222 : NS_IMETHODIMP
1223 0 : nsWebSocket::GetReadyState(PRUint16 *aReadyState)
1224 : {
1225 0 : *aReadyState = mReadyState;
1226 0 : return NS_OK;
1227 : }
1228 :
1229 : NS_IMETHODIMP
1230 0 : nsWebSocket::GetBufferedAmount(PRUint32 *aBufferedAmount)
1231 : {
1232 0 : *aBufferedAmount = mOutgoingBufferedAmount;
1233 0 : return NS_OK;
1234 : }
1235 :
1236 : NS_IMETHODIMP
1237 0 : nsWebSocket::GetBinaryType(nsAString& aBinaryType)
1238 : {
1239 0 : switch (mBinaryType) {
1240 : case WS_BINARY_TYPE_ARRAYBUFFER:
1241 0 : aBinaryType.AssignLiteral("arraybuffer");
1242 0 : break;
1243 : case WS_BINARY_TYPE_BLOB:
1244 0 : aBinaryType.AssignLiteral("blob");
1245 0 : break;
1246 : default:
1247 0 : NS_ERROR("Should not happen");
1248 : }
1249 0 : return NS_OK;
1250 : }
1251 :
1252 : NS_IMETHODIMP
1253 0 : nsWebSocket::SetBinaryType(const nsAString& aBinaryType)
1254 : {
1255 0 : if (aBinaryType.EqualsLiteral("arraybuffer")) {
1256 0 : mBinaryType = WS_BINARY_TYPE_ARRAYBUFFER;
1257 0 : } else if (aBinaryType.EqualsLiteral("blob")) {
1258 0 : mBinaryType = WS_BINARY_TYPE_BLOB;
1259 : } else {
1260 0 : return NS_ERROR_INVALID_ARG;
1261 : }
1262 :
1263 0 : return NS_OK;
1264 : }
1265 :
1266 : #define NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(_eventlistenername, _eventlistener) \
1267 : NS_IMETHODIMP \
1268 : nsWebSocket::GetOn##_eventlistenername(nsIDOMEventListener * *aEventListener)\
1269 : { \
1270 : return GetInnerEventListener(_eventlistener, aEventListener); \
1271 : } \
1272 : \
1273 : NS_IMETHODIMP \
1274 : nsWebSocket::SetOn##_eventlistenername(nsIDOMEventListener * aEventListener) \
1275 : { \
1276 : return RemoveAddEventListener(NS_LITERAL_STRING(#_eventlistenername), \
1277 : _eventlistener, aEventListener); \
1278 : }
1279 :
1280 0 : NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(open, mOnOpenListener)
1281 0 : NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(error, mOnErrorListener)
1282 0 : NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(message, mOnMessageListener)
1283 0 : NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(close, mOnCloseListener)
1284 :
1285 : static bool
1286 0 : ContainsUnpairedSurrogates(const nsAString& aData)
1287 : {
1288 : // Check for unpaired surrogates.
1289 0 : PRUint32 i, length = aData.Length();
1290 0 : for (i = 0; i < length; ++i) {
1291 0 : if (NS_IS_LOW_SURROGATE(aData[i])) {
1292 0 : return true;
1293 : }
1294 0 : if (NS_IS_HIGH_SURROGATE(aData[i])) {
1295 0 : ++i;
1296 0 : if (i == length || !NS_IS_LOW_SURROGATE(aData[i])) {
1297 0 : return true;
1298 : }
1299 0 : continue;
1300 : }
1301 : }
1302 0 : return false;
1303 : }
1304 :
1305 : NS_IMETHODIMP
1306 0 : nsWebSocket::Send(nsIVariant *aData)
1307 : {
1308 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
1309 :
1310 0 : if (mReadyState == nsIWebSocket::CONNECTING) {
1311 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
1312 : }
1313 :
1314 0 : nsCString msgString;
1315 0 : nsCOMPtr<nsIInputStream> msgStream;
1316 : bool isBinary;
1317 : PRUint32 msgLen;
1318 0 : nsresult rv = GetSendParams(aData, msgString, msgStream, isBinary, msgLen);
1319 0 : NS_ENSURE_SUCCESS(rv, rv);
1320 :
1321 : // Always increment outgoing buffer len, even if closed
1322 0 : mOutgoingBufferedAmount += msgLen;
1323 :
1324 0 : if (mReadyState == nsIWebSocket::CLOSING ||
1325 : mReadyState == nsIWebSocket::CLOSED) {
1326 0 : return NS_OK;
1327 : }
1328 :
1329 0 : NS_ASSERTION(mReadyState == nsIWebSocket::OPEN,
1330 : "Unknown state in nsWebSocket::Send");
1331 :
1332 0 : if (msgStream) {
1333 0 : rv = mChannel->SendBinaryStream(msgStream, msgLen);
1334 : } else {
1335 0 : if (isBinary) {
1336 0 : rv = mChannel->SendBinaryMsg(msgString);
1337 : } else {
1338 0 : rv = mChannel->SendMsg(msgString);
1339 : }
1340 : }
1341 0 : NS_ENSURE_SUCCESS(rv, rv);
1342 :
1343 0 : UpdateMustKeepAlive();
1344 :
1345 0 : return NS_OK;
1346 : }
1347 :
1348 : nsresult
1349 0 : nsWebSocket::GetSendParams(nsIVariant *aData, nsCString &aStringOut,
1350 : nsCOMPtr<nsIInputStream> &aStreamOut,
1351 : bool &aIsBinary, PRUint32 &aOutgoingLength)
1352 : {
1353 : // Get type of data (arraybuffer, blob, or string)
1354 : PRUint16 dataType;
1355 0 : nsresult rv = aData->GetDataType(&dataType);
1356 0 : NS_ENSURE_SUCCESS(rv, rv);
1357 :
1358 0 : if (dataType == nsIDataType::VTYPE_INTERFACE ||
1359 : dataType == nsIDataType::VTYPE_INTERFACE_IS) {
1360 0 : nsCOMPtr<nsISupports> supports;
1361 : nsID *iid;
1362 0 : rv = aData->GetAsInterface(&iid, getter_AddRefs(supports));
1363 0 : NS_ENSURE_SUCCESS(rv, rv);
1364 :
1365 0 : nsMemory::Free(iid);
1366 :
1367 : // ArrayBuffer?
1368 : jsval realVal;
1369 : JSObject* obj;
1370 0 : nsresult rv = aData->GetAsJSVal(&realVal);
1371 0 : if (NS_SUCCEEDED(rv) && !JSVAL_IS_PRIMITIVE(realVal) &&
1372 : (obj = JSVAL_TO_OBJECT(realVal)) &&
1373 0 : (js_IsArrayBuffer(obj))) {
1374 0 : PRInt32 len = JS_GetArrayBufferByteLength(obj);
1375 0 : char* data = (char*)JS_GetArrayBufferData(obj);
1376 :
1377 0 : aStringOut.Assign(data, len);
1378 0 : aIsBinary = true;
1379 0 : aOutgoingLength = len;
1380 0 : return NS_OK;
1381 : }
1382 :
1383 : // Blob?
1384 0 : nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(supports);
1385 0 : if (blob) {
1386 0 : rv = blob->GetInternalStream(getter_AddRefs(aStreamOut));
1387 0 : NS_ENSURE_SUCCESS(rv, rv);
1388 :
1389 : // GetSize() should not perform blocking I/O (unlike Available())
1390 : PRUint64 blobLen;
1391 0 : rv = blob->GetSize(&blobLen);
1392 0 : NS_ENSURE_SUCCESS(rv, rv);
1393 0 : if (blobLen > PR_UINT32_MAX) {
1394 0 : return NS_ERROR_FILE_TOO_BIG;
1395 : }
1396 0 : aOutgoingLength = static_cast<PRUint32>(blobLen);
1397 :
1398 0 : aIsBinary = true;
1399 0 : return NS_OK;
1400 : }
1401 : }
1402 :
1403 : // Text message: if not already a string, turn it into one.
1404 : // TODO: bug 704444: Correctly coerce any JS type to string
1405 : //
1406 0 : PRUnichar* data = nsnull;
1407 0 : PRUint32 len = 0;
1408 0 : rv = aData->GetAsWStringWithSize(&len, &data);
1409 0 : NS_ENSURE_SUCCESS(rv, rv);
1410 :
1411 0 : nsString text;
1412 0 : text.Adopt(data, len);
1413 :
1414 0 : if (ContainsUnpairedSurrogates(text)) {
1415 0 : return NS_ERROR_DOM_SYNTAX_ERR;
1416 : }
1417 :
1418 0 : rv = ConvertTextToUTF8(text, aStringOut);
1419 0 : NS_ENSURE_SUCCESS(rv, rv);
1420 :
1421 0 : aIsBinary = false;
1422 0 : aOutgoingLength = aStringOut.Length();
1423 0 : return NS_OK;
1424 : }
1425 :
1426 : nsresult
1427 0 : nsWebSocket::ConvertTextToUTF8(const nsString& aMessage, nsCString& buf)
1428 : {
1429 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
1430 : nsresult rv;
1431 :
1432 : nsCOMPtr<nsICharsetConverterManager> ccm =
1433 0 : do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
1434 0 : SUCCESS_OR_FAIL_WEBSOCKET(rv, rv);
1435 :
1436 0 : nsCOMPtr<nsIUnicodeEncoder> converter;
1437 0 : rv = ccm->GetUnicodeEncoder("UTF-8", getter_AddRefs(converter));
1438 0 : SUCCESS_OR_FAIL_WEBSOCKET(rv, rv);
1439 :
1440 0 : rv = converter->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Replace,
1441 0 : nsnull, UTF_8_REPLACEMENT_CHAR);
1442 0 : SUCCESS_OR_FAIL_WEBSOCKET(rv, rv);
1443 :
1444 0 : PRInt32 inLen = aMessage.Length();
1445 : PRInt32 maxLen;
1446 0 : rv = converter->GetMaxLength(aMessage.BeginReading(), inLen, &maxLen);
1447 0 : SUCCESS_OR_FAIL_WEBSOCKET(rv, rv);
1448 :
1449 0 : buf.SetLength(maxLen);
1450 0 : TRUE_OR_FAIL_WEBSOCKET(buf.Length() == static_cast<PRUint32>(maxLen),
1451 : NS_ERROR_OUT_OF_MEMORY);
1452 :
1453 0 : char* start = buf.BeginWriting();
1454 :
1455 0 : PRInt32 outLen = maxLen;
1456 0 : rv = converter->Convert(aMessage.BeginReading(), &inLen, start, &outLen);
1457 0 : if (NS_SUCCEEDED(rv)) {
1458 0 : PRInt32 outLen2 = maxLen - outLen;
1459 0 : rv = converter->Finish(start + outLen, &outLen2);
1460 0 : outLen += outLen2;
1461 : }
1462 0 : if (NS_FAILED(rv) || rv == NS_ERROR_UENC_NOMAPPING) {
1463 : // Yes, NS_ERROR_UENC_NOMAPPING is a success code
1464 0 : return NS_ERROR_DOM_SYNTAX_ERR;
1465 : }
1466 :
1467 0 : buf.SetLength(outLen);
1468 0 : TRUE_OR_FAIL_WEBSOCKET(buf.Length() == static_cast<PRUint32>(outLen),
1469 : NS_ERROR_UNEXPECTED);
1470 :
1471 0 : return NS_OK;
1472 : }
1473 :
1474 : NS_IMETHODIMP
1475 0 : nsWebSocket::Close(PRUint16 code, const nsAString & reason, PRUint8 argc)
1476 : {
1477 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
1478 :
1479 : // the reason code is optional, but if provided it must be in a specific range
1480 0 : PRUint16 closeCode = 0;
1481 0 : if (argc >= 1) {
1482 0 : if (code != 1000 && (code < 3000 || code > 4999)) {
1483 0 : return NS_ERROR_DOM_INVALID_ACCESS_ERR;
1484 : }
1485 0 : closeCode = code;
1486 : }
1487 :
1488 0 : nsCAutoString closeReason;
1489 0 : if (argc >= 2) {
1490 0 : if (ContainsUnpairedSurrogates(reason)) {
1491 0 : return NS_ERROR_DOM_SYNTAX_ERR;
1492 : }
1493 0 : CopyUTF16toUTF8(reason, closeReason);
1494 :
1495 : // The API requires the UTF-8 string to be 123 or less bytes
1496 0 : if (closeReason.Length() > 123) {
1497 0 : return NS_ERROR_DOM_SYNTAX_ERR;
1498 : }
1499 : }
1500 :
1501 0 : if (mReadyState == nsIWebSocket::CLOSING ||
1502 : mReadyState == nsIWebSocket::CLOSED) {
1503 0 : return NS_OK;
1504 : }
1505 :
1506 0 : if (mReadyState == nsIWebSocket::CONNECTING) {
1507 : // FailConnection() can release the object, so we keep a reference
1508 : // before calling it
1509 0 : nsRefPtr<nsWebSocket> kungfuDeathGrip = this;
1510 :
1511 0 : FailConnection(closeCode, closeReason);
1512 0 : return NS_OK;
1513 : }
1514 :
1515 : // mReadyState == nsIWebSocket::OPEN
1516 0 : CloseConnection(closeCode, closeReason);
1517 :
1518 0 : return NS_OK;
1519 : }
1520 :
1521 : /**
1522 : * This Init method should only be called by C++ consumers.
1523 : */
1524 : NS_IMETHODIMP
1525 0 : nsWebSocket::Init(nsIPrincipal* aPrincipal,
1526 : nsIScriptContext* aScriptContext,
1527 : nsPIDOMWindow* aOwnerWindow,
1528 : const nsAString& aURL,
1529 : nsTArray<nsString> & protocolArray)
1530 : {
1531 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
1532 : nsresult rv;
1533 :
1534 0 : NS_ENSURE_ARG(aPrincipal);
1535 :
1536 0 : if (!PrefEnabled()) {
1537 0 : return NS_ERROR_DOM_SECURITY_ERR;
1538 : }
1539 :
1540 0 : mPrincipal = aPrincipal;
1541 0 : if (aOwnerWindow) {
1542 0 : BindToOwner(aOwnerWindow->IsOuterWindow() ?
1543 0 : aOwnerWindow->GetCurrentInnerWindow() : aOwnerWindow);
1544 : } else {
1545 0 : BindToOwner(aOwnerWindow);
1546 : }
1547 :
1548 : // Attempt to kill "ghost" websocket: but usually too early for check to fail
1549 0 : rv = CheckInnerWindowCorrectness();
1550 0 : NS_ENSURE_SUCCESS(rv, rv);
1551 :
1552 : // Shut down websocket if window is frozen or destroyed (only needed for
1553 : // "ghost" websockets--see bug 696085)
1554 0 : nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1555 0 : NS_ENSURE_STATE(os);
1556 0 : rv = os->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC, true);
1557 0 : NS_ENSURE_SUCCESS(rv, rv);
1558 0 : rv = os->AddObserver(this, DOM_WINDOW_FROZEN_TOPIC, true);
1559 0 : NS_ENSURE_SUCCESS(rv, rv);
1560 :
1561 : nsCOMPtr<nsIJSContextStack> stack =
1562 0 : do_GetService("@mozilla.org/js/xpc/ContextStack;1");
1563 0 : JSContext* cx = nsnull;
1564 0 : if (stack && NS_SUCCEEDED(stack->Peek(&cx)) && cx) {
1565 : unsigned lineno;
1566 : JSScript *script;
1567 :
1568 0 : if (JS_DescribeScriptedCaller(cx, &script, &lineno)) {
1569 0 : mScriptFile = JS_GetScriptFilename(cx, script);
1570 0 : mScriptLine = lineno;
1571 : }
1572 :
1573 0 : mInnerWindowID = nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx);
1574 : }
1575 :
1576 : // parses the url
1577 0 : rv = ParseURL(PromiseFlatString(aURL));
1578 0 : NS_ENSURE_SUCCESS(rv, rv);
1579 :
1580 0 : nsIScriptContext* sc = GetContextForEventHandlers(&rv);
1581 : nsCOMPtr<nsIDocument> originDoc =
1582 0 : nsContentUtils::GetDocumentFromScriptContext(sc);
1583 :
1584 : // Don't allow https:// to open ws://
1585 0 : if (!mSecure &&
1586 : !Preferences::GetBool("network.websocket.allowInsecureFromHTTPS",
1587 0 : false)) {
1588 : // Confirmed we are opening plain ws:// and want to prevent this from a
1589 : // secure context (e.g. https). Check the security context of the document
1590 : // associated with this script, which is the same as associated with mOwner.
1591 0 : if (originDoc && originDoc->GetSecurityInfo())
1592 0 : return NS_ERROR_DOM_SECURITY_ERR;
1593 : }
1594 :
1595 : // Assign the sub protocol list and scan it for illegal values
1596 0 : for (PRUint32 index = 0; index < protocolArray.Length(); ++index) {
1597 0 : for (PRUint32 i = 0; i < protocolArray[index].Length(); ++i) {
1598 0 : if (protocolArray[index][i] < static_cast<PRUnichar>(0x0021) ||
1599 0 : protocolArray[index][i] > static_cast<PRUnichar>(0x007E))
1600 0 : return NS_ERROR_DOM_SYNTAX_ERR;
1601 : }
1602 :
1603 0 : if (!mRequestedProtocolList.IsEmpty())
1604 0 : mRequestedProtocolList.Append(NS_LITERAL_CSTRING(", "));
1605 0 : AppendUTF16toUTF8(protocolArray[index], mRequestedProtocolList);
1606 : }
1607 :
1608 : // Check content policy.
1609 0 : PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
1610 : rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_WEBSOCKET,
1611 : mURI,
1612 : mPrincipal,
1613 : originDoc,
1614 0 : EmptyCString(),
1615 : nsnull,
1616 : &shouldLoad,
1617 : nsContentUtils::GetContentPolicy(),
1618 0 : nsContentUtils::GetSecurityManager());
1619 0 : NS_ENSURE_SUCCESS(rv, rv);
1620 0 : if (NS_CP_REJECTED(shouldLoad)) {
1621 : // Disallowed by content policy.
1622 0 : return NS_ERROR_CONTENT_BLOCKED;
1623 : }
1624 :
1625 : // the constructor should throw a SYNTAX_ERROR only if it fails to parse the
1626 : // url parameter, so we don't care about the EstablishConnection result.
1627 0 : EstablishConnection();
1628 :
1629 0 : return NS_OK;
1630 : }
1631 :
1632 : //-----------------------------------------------------------------------------
1633 : // nsWebSocket::nsIObserver
1634 : //-----------------------------------------------------------------------------
1635 :
1636 : NS_IMETHODIMP
1637 0 : nsWebSocket::Observe(nsISupports* aSubject,
1638 : const char* aTopic,
1639 : const PRUnichar* aData)
1640 : {
1641 0 : if ((mReadyState == nsIWebSocket::CLOSING) ||
1642 : (mReadyState == nsIWebSocket::CLOSED)) {
1643 0 : return NS_OK;
1644 : }
1645 :
1646 0 : nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aSubject);
1647 0 : if (!GetOwner() || window != GetOwner()) {
1648 0 : return NS_OK;
1649 : }
1650 :
1651 0 : if ((strcmp(aTopic, DOM_WINDOW_FROZEN_TOPIC) == 0) ||
1652 0 : (strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC) == 0))
1653 : {
1654 0 : FailConnectionQuietly();
1655 : }
1656 :
1657 0 : return NS_OK;
1658 : }
1659 :
1660 :
1661 : //-----------------------------------------------------------------------------
1662 : // nsWebSocket::nsIRequest
1663 : //-----------------------------------------------------------------------------
1664 :
1665 : NS_IMETHODIMP
1666 0 : nsWebSocket::GetName(nsACString &aName)
1667 : {
1668 0 : CopyUTF16toUTF8(mOriginalURL, aName);
1669 0 : return NS_OK;
1670 : }
1671 :
1672 : NS_IMETHODIMP
1673 0 : nsWebSocket::IsPending(bool *aValue)
1674 : {
1675 0 : *aValue = !mDisconnected;
1676 0 : return NS_OK;
1677 : }
1678 :
1679 : NS_IMETHODIMP
1680 0 : nsWebSocket::GetStatus(nsresult *aStatus)
1681 : {
1682 0 : *aStatus = NS_OK;
1683 0 : return NS_OK;
1684 : }
1685 :
1686 : // Window closed, stop/reload button pressed, user navigated away from page, etc.
1687 : NS_IMETHODIMP
1688 0 : nsWebSocket::Cancel(nsresult aStatus)
1689 : {
1690 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
1691 :
1692 0 : if (mDisconnected)
1693 0 : return NS_OK;
1694 :
1695 0 : ConsoleError();
1696 0 : return CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY);
1697 : }
1698 :
1699 : NS_IMETHODIMP
1700 0 : nsWebSocket::Suspend()
1701 : {
1702 0 : return NS_ERROR_NOT_IMPLEMENTED;
1703 : }
1704 :
1705 : NS_IMETHODIMP
1706 0 : nsWebSocket::Resume()
1707 : {
1708 0 : return NS_ERROR_NOT_IMPLEMENTED;
1709 : }
1710 :
1711 : NS_IMETHODIMP
1712 0 : nsWebSocket::GetLoadGroup(nsILoadGroup **aLoadGroup)
1713 : {
1714 0 : *aLoadGroup = nsnull;
1715 :
1716 : nsresult rv;
1717 0 : nsIScriptContext* sc = GetContextForEventHandlers(&rv);
1718 : nsCOMPtr<nsIDocument> doc =
1719 0 : nsContentUtils::GetDocumentFromScriptContext(sc);
1720 :
1721 0 : if (doc) {
1722 0 : *aLoadGroup = doc->GetDocumentLoadGroup().get(); // already_AddRefed
1723 : }
1724 :
1725 0 : return NS_OK;
1726 : }
1727 :
1728 : NS_IMETHODIMP
1729 0 : nsWebSocket::SetLoadGroup(nsILoadGroup *aLoadGroup)
1730 : {
1731 0 : return NS_ERROR_UNEXPECTED;
1732 : }
1733 :
1734 : NS_IMETHODIMP
1735 0 : nsWebSocket::GetLoadFlags(nsLoadFlags *aLoadFlags)
1736 : {
1737 0 : *aLoadFlags = nsIRequest::LOAD_BACKGROUND;
1738 0 : return NS_OK;
1739 : }
1740 :
1741 : NS_IMETHODIMP
1742 0 : nsWebSocket::SetLoadFlags(nsLoadFlags aLoadFlags)
1743 : {
1744 : // we won't change the load flags at all.
1745 0 : return NS_OK;
1746 4392 : }
|