From 79d74b910c23ab4ff93d3fa2715cfe4db8983c38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Mon, 10 Sep 2012 12:24:29 +0200 Subject: [PATCH 01/78] Bug 789079 - In the tab container's dragover handler, calculate the drop indicator's minimum and maximum margins only when scrolling. r=jaws --- browser/base/content/tabbrowser.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index 879ba7202392..79e448148fae 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -3647,18 +3647,18 @@ } } - var scrollRect = tabStrip.scrollClientRect; var rect = tabStrip.getBoundingClientRect(); - var minMargin = scrollRect.left - rect.left; - var maxMargin = Math.min(minMargin + scrollRect.width, - scrollRect.right); - if (!ltr) - [minMargin, maxMargin] = [this.clientWidth - maxMargin, - this.clientWidth - minMargin]; var newMargin; if (pixelsToScroll) { // if we are scrolling, put the drop indicator at the edge // so that it doesn't jump while scrolling + let scrollRect = tabStrip.scrollClientRect; + let minMargin = scrollRect.left - rect.left; + let maxMargin = Math.min(minMargin + scrollRect.width, + scrollRect.right); + if (!ltr) + [minMargin, maxMargin] = [this.clientWidth - maxMargin, + this.clientWidth - minMargin]; newMargin = (pixelsToScroll > 0) ? maxMargin : minMargin; } else { From 78172ca06a1a6c4f2ccae500a68b91328f7d94b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Mon, 10 Sep 2012 12:25:00 +0200 Subject: [PATCH 02/78] Bug 788418 - Image error is unreadable against dark background. r=jaws --- toolkit/themes/pinstripe/global/TopLevelImageDocument.css | 3 ++- toolkit/themes/winstripe/global/TopLevelImageDocument.css | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/toolkit/themes/pinstripe/global/TopLevelImageDocument.css b/toolkit/themes/pinstripe/global/TopLevelImageDocument.css index 57b250192971..86db6472c29e 100644 --- a/toolkit/themes/pinstripe/global/TopLevelImageDocument.css +++ b/toolkit/themes/pinstripe/global/TopLevelImageDocument.css @@ -4,10 +4,11 @@ @media not print { body { + color: #eee; background: #222 url(data:image/png;base64,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); } - .decoded { + img.decoded { background-color: #fff; color: #222; } diff --git a/toolkit/themes/winstripe/global/TopLevelImageDocument.css b/toolkit/themes/winstripe/global/TopLevelImageDocument.css index 57b250192971..86db6472c29e 100644 --- a/toolkit/themes/winstripe/global/TopLevelImageDocument.css +++ b/toolkit/themes/winstripe/global/TopLevelImageDocument.css @@ -4,10 +4,11 @@ @media not print { body { + color: #eee; background: #222 url(data:image/png;base64,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); } - .decoded { + img.decoded { background-color: #fff; color: #222; } From 34ff20808bcb351b9eb6b15c9a8077af52bdada5 Mon Sep 17 00:00:00 2001 From: Andres Hernandez Date: Thu, 6 Sep 2012 17:31:49 -0600 Subject: [PATCH 03/78] Bug 787745 - try to fix intermittent browser_DeferredTask.js failure by having DeferredTask completely re-initialize its timer, rather than just re-setting its delay, r=gavin --- toolkit/content/DeferredTask.jsm | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/toolkit/content/DeferredTask.jsm b/toolkit/content/DeferredTask.jsm index c9d35a93ec7c..99ea301194ad 100644 --- a/toolkit/content/DeferredTask.jsm +++ b/toolkit/content/DeferredTask.jsm @@ -56,12 +56,11 @@ DeferredTask.prototype = { */ start: function DeferredTask_start() { if (this._timer) { - this._timer.delay = this._delay; - } else { - this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - this._timer.initWithCallback( - this._callback, this._delay, Ci.nsITimer.TYPE_ONE_SHOT); + this._timer.cancel(); } + this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + this._timer.initWithCallback( + this._callback, this._delay, Ci.nsITimer.TYPE_ONE_SHOT); }, /** From df9a39c5cdcca4597bbe584416f9965056c8cccd Mon Sep 17 00:00:00 2001 From: Jacek Caban Date: Mon, 10 Sep 2012 14:57:17 +0200 Subject: [PATCH 04/78] Bug 789904 - QuerySelector doesn't set result to null on successful call with no matches r=bz --- content/base/src/nsINode.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/content/base/src/nsINode.cpp b/content/base/src/nsINode.cpp index beb6ac36104b..c5a433e30637 100644 --- a/content/base/src/nsINode.cpp +++ b/content/base/src/nsINode.cpp @@ -2071,7 +2071,12 @@ nsNodeSelectorTearoff::QuerySelector(const nsAString& aSelector, { nsresult rv; nsIContent* result = mNode->QuerySelector(aSelector, &rv); - return result ? CallQueryInterface(result, aReturn) : rv; + if (!result) { + *aReturn = nullptr; + return rv; + } + + return CallQueryInterface(result, aReturn); } NS_IMETHODIMP From a5a56227be1622df42bf5c64446e2b8143cd27bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20=C3=81vila=20de=20Esp=C3=ADndola?= Date: Mon, 10 Sep 2012 09:19:46 -0400 Subject: [PATCH 05/78] Bug 786019 - gcc 4.2 is dead, simplify code that was complicated because of gcc 4.2 support. --- xpcom/build/mozPoisonWriteMac.cpp | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/xpcom/build/mozPoisonWriteMac.cpp b/xpcom/build/mozPoisonWriteMac.cpp index f2ce2923c0c9..8ec28b9c2abe 100644 --- a/xpcom/build/mozPoisonWriteMac.cpp +++ b/xpcom/build/mozPoisonWriteMac.cpp @@ -130,14 +130,8 @@ ssize_t wrap_pwrite_temp(int fd, const void *buf, size_t nbyte, off_t offset) { } // Define a FuncData for a pwrite-like functions. -// FIXME: clang accepts usinging wrap_pwrite_temp in the struct -// initialization. Is this a gcc 4.2 bug? #define DEFINE_PWRITE_DATA(X, NAME) \ -ssize_t wrap_ ## X (int fd, const void *buf, size_t nbyte, off_t offset); \ -FuncData X ## _data = { NAME, (void*) wrap_ ## X }; \ -ssize_t wrap_ ## X (int fd, const void *buf, size_t nbyte, off_t offset) { \ - return wrap_pwrite_temp(fd, buf, nbyte, offset); \ -} +FuncData X ## _data = { NAME, (void*) wrap_pwrite_temp }; \ // This exists everywhere. DEFINE_PWRITE_DATA(pwrite, "pwrite") @@ -158,12 +152,8 @@ ssize_t wrap_writev_temp(int fd, const struct iovec *iov, int iovcnt) { } // Define a FuncData for a writev-like functions. -#define DEFINE_WRITEV_DATA(X, NAME) \ -ssize_t wrap_ ## X (int fd, const struct iovec *iov, int iovcnt); \ -FuncData X ## _data = { NAME, (void*) wrap_ ## X }; \ -ssize_t wrap_ ## X (int fd, const struct iovec *iov, int iovcnt) { \ - return wrap_writev_temp(fd, iov, iovcnt); \ -} +#define DEFINE_WRITEV_DATA(X, NAME) \ +FuncData X ## _data = { NAME, (void*) wrap_writev_temp }; \ // This exists everywhere. DEFINE_WRITEV_DATA(writev, "writev"); @@ -183,11 +173,7 @@ ssize_t wrap_write_temp(int fd, const void *buf, size_t count) { // Define a FuncData for a write-like functions. #define DEFINE_WRITE_DATA(X, NAME) \ -ssize_t wrap_ ## X (int fd, const void *buf, size_t count); \ -FuncData X ## _data = { NAME, (void*) wrap_ ## X }; \ -ssize_t wrap_ ## X (int fd, const void *buf, size_t count) { \ - return wrap_write_temp(fd, buf, count); \ -} +FuncData X ## _data = { NAME, (void*) wrap_write_temp }; \ // This exists everywhere. DEFINE_WRITE_DATA(write, "write"); From 26d50534e07a69ad356dad695210a18b5988ca35 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Mon, 10 Sep 2012 09:48:14 -0400 Subject: [PATCH 06/78] Bug 775368 - Porting Websockets to WebIDL, r=bz --HG-- rename : content/base/src/nsWebSocket.cpp => content/base/src/WebSocket.cpp rename : content/base/src/nsWebSocket.h => content/base/src/WebSocket.h --- content/base/public/Makefile.in | 1 - content/base/public/nsIWebSocket.idl | 95 -- content/base/src/Makefile.in | 2 +- .../src/{nsWebSocket.cpp => WebSocket.cpp} | 1013 ++++++++--------- content/base/src/WebSocket.h | 302 +++++ content/base/src/nsWebSocket.h | 193 ---- dom/base/nsDOMClassInfo.cpp | 18 - dom/base/nsDOMClassInfoClasses.h | 3 - dom/bindings/Bindings.conf | 6 + dom/webidl/WebIDL.mk | 1 + dom/webidl/WebSocket.webidl | 69 ++ js/xpconnect/src/dom_quickstubs.qsconf | 3 - layout/build/nsLayoutModule.cpp | 5 - 13 files changed, 838 insertions(+), 873 deletions(-) delete mode 100644 content/base/public/nsIWebSocket.idl rename content/base/src/{nsWebSocket.cpp => WebSocket.cpp} (62%) create mode 100644 content/base/src/WebSocket.h delete mode 100644 content/base/src/nsWebSocket.h create mode 100644 dom/webidl/WebSocket.webidl diff --git a/content/base/public/Makefile.in b/content/base/public/Makefile.in index 109e02fae92c..b15106a11b2b 100644 --- a/content/base/public/Makefile.in +++ b/content/base/public/Makefile.in @@ -83,7 +83,6 @@ XPIDLSRCS = \ nsIXMLHttpRequest.idl \ nsIContentSecurityPolicy.idl \ nsIMessageManager.idl \ - nsIWebSocket.idl \ nsIEventSource.idl \ $(NULL) diff --git a/content/base/public/nsIWebSocket.idl b/content/base/public/nsIWebSocket.idl deleted file mode 100644 index a063b47df83c..000000000000 --- a/content/base/public/nsIWebSocket.idl +++ /dev/null @@ -1,95 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set sw=2 ts=8 et tw=80 : */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsISupports.idl" - -interface nsIDOMEventListener; -interface nsIPrincipal; -interface nsIScriptContext; -interface nsPIDOMWindow; -interface nsIDOMDOMStringList; -interface nsIVariant; - -%{C++ -#include "nsTArray.h" -class nsString; -%} -[ref] native nsStringTArrayRef(nsTArray); - -/** - * The nsIWebSocket interface enables Web applications to maintain - * bidirectional communications with server-side processes as described in: - * - * http://dev.w3.org/html5/websockets/ - * - */ -[scriptable, uuid(5224dbe7-58ac-43e6-93ba-f288a1421dff)] -interface nsIWebSocket : nsISupports -{ - readonly attribute DOMString url; - readonly attribute DOMString extensions; - readonly attribute DOMString protocol; - - //ready state - const unsigned short CONNECTING = 0; - const unsigned short OPEN = 1; - const unsigned short CLOSING = 2; - const unsigned short CLOSED = 3; - readonly attribute unsigned short readyState; - - readonly attribute unsigned long bufferedAmount; - - // "blob" by default: can set to "blob" or "arraybuffer": setting to other - // values will throw SYNTAX_ERR exception. - attribute DOMString binaryType; - - // event handler attributes - [implicit_jscontext] attribute jsval onopen; - [implicit_jscontext] attribute jsval onmessage; - [implicit_jscontext] attribute jsval onerror; - [implicit_jscontext] attribute jsval onclose; - - /** - * Transmits data to other end of the connection. - * @param data The data to be transmitted. Arraybuffers and Blobs are sent as - * binary data. Strings are sent as UTF-8 text data. Other types are - * converted to a String and sent as a String. - * @return if the connection is still established and the data was queued or - * sent successfully. - */ - [implicit_jscontext] void send(in nsIVariant data); - - /** - * Closes the Web Socket connection or connection attempt, if any. - * If the connection is already closed, it does nothing. - */ - [optional_argc] void close([optional] in unsigned short code, - [optional] in DOMString reason); - - /** - * Initialize the object for use from C++ code with the principal, script - * context, and owner window that should be used. - * - * @param principal The principal to use for the request. This must not be - * null. - * @param scriptContext The script context to use for the request. May be - * null. - * @param ownerWindow The associated window for the request. May be null. - * @param url The url for opening the socket. This must not be empty, and - * must have an absolute url, using either the ws or wss schemes. - * @param protocol Specifies array of sub-protocols acceptable to the client. - * If the length of the array is at least one, the server - * must select one of the listed sub-protocols for the - * connection to be successful. If empty, no sub-protocol is - * specified. The server selected sub-protocol can be read - * from the protocol attribute after connection. - */ - [noscript] void init(in nsIPrincipal principal, - in nsIScriptContext scriptContext, - in nsPIDOMWindow ownerWindow, - in DOMString url, - in nsStringTArrayRef protocol); -}; diff --git a/content/base/src/Makefile.in b/content/base/src/Makefile.in index 74e1c7ecc03f..21087e346982 100644 --- a/content/base/src/Makefile.in +++ b/content/base/src/Makefile.in @@ -122,7 +122,7 @@ CPPSRCS = \ nsTraversal.cpp \ nsTreeSanitizer.cpp \ nsTreeWalker.cpp \ - nsWebSocket.cpp \ + WebSocket.cpp \ nsXHTMLContentSerializer.cpp \ nsXMLContentSerializer.cpp \ nsXMLHttpRequest.cpp \ diff --git a/content/base/src/nsWebSocket.cpp b/content/base/src/WebSocket.cpp similarity index 62% rename from content/base/src/nsWebSocket.cpp rename to content/base/src/WebSocket.cpp index 526c9432f42a..aca1ed2f0c20 100644 --- a/content/base/src/nsWebSocket.cpp +++ b/content/base/src/WebSocket.cpp @@ -4,9 +4,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "mozilla/Util.h" - -#include "nsWebSocket.h" +#include "WebSocket.h" +#include "mozilla/dom/WebSocketBinding.h" #include "nsIScriptGlobalObject.h" #include "nsIDOMWindow.h" @@ -50,33 +49,15 @@ #include "nsIObserverService.h" #include "GeneratedEvents.h" -using namespace mozilla; +namespace mozilla { +namespace dom { #define UTF_8_REPLACEMENT_CHAR static_cast(0xFFFD) -#define TRUE_OR_FAIL_WEBSOCKET(x, ret) \ - PR_BEGIN_MACRO \ - if (NS_UNLIKELY(!(x))) { \ - NS_WARNING("TRUE_OR_FAIL_WEBSOCKET(" #x ") failed"); \ - FailConnection(nsIWebSocketChannel::CLOSE_INTERNAL_ERROR); \ - return ret; \ - } \ - PR_END_MACRO - -#define SUCCESS_OR_FAIL_WEBSOCKET(res, ret) \ - PR_BEGIN_MACRO \ - nsresult __rv = res; \ - if (NS_FAILED(__rv)) { \ - NS_ENSURE_SUCCESS_BODY(res, ret) \ - FailConnection(nsIWebSocketChannel::CLOSE_INTERNAL_ERROR); \ - return ret; \ - } \ - PR_END_MACRO - class CallDispatchConnectionCloseEvents: public nsRunnable { public: -CallDispatchConnectionCloseEvents(nsWebSocket *aWebSocket) +CallDispatchConnectionCloseEvents(WebSocket* aWebSocket) : mWebSocket(aWebSocket) {} @@ -87,22 +68,22 @@ CallDispatchConnectionCloseEvents(nsWebSocket *aWebSocket) } private: - nsRefPtr mWebSocket; + nsRefPtr mWebSocket; }; //----------------------------------------------------------------------------- -// nsWebSocket +// WebSocket //----------------------------------------------------------------------------- nsresult -nsWebSocket::PrintErrorOnConsole(const char *aBundleURI, - const PRUnichar *aError, - const PRUnichar **aFormatStrings, - uint32_t aFormatStringsLen) +WebSocket::PrintErrorOnConsole(const char *aBundleURI, + const PRUnichar *aError, + const PRUnichar **aFormatStrings, + uint32_t aFormatStringsLen) { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); - nsresult rv; + nsresult rv; nsCOMPtr bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); @@ -145,31 +126,31 @@ nsWebSocket::PrintErrorOnConsole(const char *aBundleURI, } nsresult -nsWebSocket::CloseConnection(uint16_t aReasonCode, +WebSocket::CloseConnection(uint16_t aReasonCode, const nsACString& aReasonString) { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); - if (mReadyState == nsIWebSocket::CLOSING || - mReadyState == nsIWebSocket::CLOSED) { + if (mReadyState == WebSocket::CLOSING || + mReadyState == WebSocket::CLOSED) { return NS_OK; } // The common case... if (mChannel) { - mReadyState = nsIWebSocket::CLOSING; + mReadyState = WebSocket::CLOSING; return mChannel->Close(aReasonCode, aReasonString); } // No channel, but not disconnected: canceled or failed early // - MOZ_ASSERT(mReadyState == nsIWebSocket::CONNECTING, + MOZ_ASSERT(mReadyState == WebSocket::CONNECTING, "Should only get here for early websocket cancel/error"); // Server won't be sending us a close code, so use what's passed in here. mCloseEventCode = aReasonCode; CopyUTF8toUTF16(aReasonString, mCloseEventReason); - mReadyState = nsIWebSocket::CLOSING; + mReadyState = WebSocket::CLOSING; // Can be called from Cancel() or Init() codepaths, so need to dispatch // onerror/onclose asynchronously @@ -184,20 +165,19 @@ nsWebSocket::CloseConnection(uint16_t aReasonCode, } nsresult -nsWebSocket::ConsoleError() +WebSocket::ConsoleError() { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); - nsresult rv; nsAutoCString targetSpec; - rv = mURI->GetSpec(targetSpec); + nsresult rv = mURI->GetSpec(targetSpec); if (NS_FAILED(rv)) { NS_WARNING("Failed to get targetSpec"); } else { NS_ConvertUTF8toUTF16 specUTF16(targetSpec); - const PRUnichar *formatStrings[] = { specUTF16.get() }; + const PRUnichar* formatStrings[] = { specUTF16.get() }; - if (mReadyState < nsIWebSocket::OPEN) { + if (mReadyState < WebSocket::OPEN) { PrintErrorOnConsole("chrome://global/locale/appstrings.properties", NS_LITERAL_STRING("connectionFailure").get(), formatStrings, ArrayLength(formatStrings)); @@ -213,8 +193,8 @@ nsWebSocket::ConsoleError() nsresult -nsWebSocket::FailConnection(uint16_t aReasonCode, - const nsACString& aReasonString) +WebSocket::FailConnection(uint16_t aReasonCode, + const nsACString& aReasonString) { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); @@ -226,7 +206,7 @@ nsWebSocket::FailConnection(uint16_t aReasonCode, } nsresult -nsWebSocket::Disconnect() +WebSocket::Disconnect() { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); @@ -246,7 +226,7 @@ nsWebSocket::Disconnect() // DontKeepAliveAnyMore() can release the object. So hold a reference to this // until the end of the method. - nsRefPtr kungfuDeathGrip = this; + nsRefPtr kungfuDeathGrip = this; DontKeepAliveAnyMore(); mChannel = nullptr; @@ -256,20 +236,20 @@ nsWebSocket::Disconnect() } //----------------------------------------------------------------------------- -// nsWebSocket::nsIWebSocketListener methods: +// WebSocket::nsIWebSocketListener methods: //----------------------------------------------------------------------------- nsresult -nsWebSocket::DoOnMessageAvailable(const nsACString & aMsg, bool isBinary) +WebSocket::DoOnMessageAvailable(const nsACString& aMsg, bool isBinary) { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); - if (mReadyState == nsIWebSocket::CLOSED) { + if (mReadyState == WebSocket::CLOSED) { NS_ERROR("Received message after CLOSED"); return NS_ERROR_UNEXPECTED; } - if (mReadyState == nsIWebSocket::OPEN) { + if (mReadyState == WebSocket::OPEN) { // Dispatch New Message nsresult rv = CreateAndDispatchMessageEvent(aMsg, isBinary); if (NS_FAILED(rv)) { @@ -278,7 +258,7 @@ nsWebSocket::DoOnMessageAvailable(const nsACString & aMsg, bool isBinary) } else { // CLOSING should be the only other state where it's possible to get msgs // from channel: Spec says to drop them. - MOZ_ASSERT(mReadyState == nsIWebSocket::CLOSING, + MOZ_ASSERT(mReadyState == WebSocket::CLOSING, "Received message while CONNECTING or CLOSED"); } @@ -286,29 +266,29 @@ nsWebSocket::DoOnMessageAvailable(const nsACString & aMsg, bool isBinary) } NS_IMETHODIMP -nsWebSocket::OnMessageAvailable(nsISupports *aContext, const nsACString & aMsg) +WebSocket::OnMessageAvailable(nsISupports* aContext, const nsACString& aMsg) { return DoOnMessageAvailable(aMsg, false); } NS_IMETHODIMP -nsWebSocket::OnBinaryMessageAvailable(nsISupports *aContext, - const nsACString & aMsg) +WebSocket::OnBinaryMessageAvailable(nsISupports* aContext, + const nsACString& aMsg) { return DoOnMessageAvailable(aMsg, true); } NS_IMETHODIMP -nsWebSocket::OnStart(nsISupports *aContext) +WebSocket::OnStart(nsISupports* aContext) { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); // This is the only function that sets OPEN, and should be called only once - MOZ_ASSERT(mReadyState != nsIWebSocket::OPEN, + MOZ_ASSERT(mReadyState != WebSocket::OPEN, "readyState already OPEN! OnStart called twice?"); // Nothing to do if we've already closed/closing - if (mReadyState != nsIWebSocket::CONNECTING) { + if (mReadyState != WebSocket::CONNECTING) { return NS_OK; } @@ -326,7 +306,7 @@ nsWebSocket::OnStart(nsISupports *aContext) mChannel->GetExtensions(mEstablishedExtensions); UpdateURI(); - mReadyState = nsIWebSocket::OPEN; + mReadyState = WebSocket::OPEN; // Call 'onopen' rv = CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("open")); @@ -340,12 +320,12 @@ nsWebSocket::OnStart(nsISupports *aContext) } NS_IMETHODIMP -nsWebSocket::OnStop(nsISupports *aContext, nsresult aStatusCode) +WebSocket::OnStop(nsISupports* aContext, nsresult aStatusCode) { // We can be CONNECTING here if connection failed. // We can be OPEN if we have encountered a fatal protocol error // We can be CLOSING if close() was called and/or server initiated close. - MOZ_ASSERT(mReadyState != nsIWebSocket::CLOSED, + MOZ_ASSERT(mReadyState != WebSocket::CLOSED, "Shouldn't already be CLOSED when OnStop called"); // called by network stack, not JS, so can dispatch JS events synchronously @@ -353,9 +333,9 @@ nsWebSocket::OnStop(nsISupports *aContext, nsresult aStatusCode) } nsresult -nsWebSocket::ScheduleConnectionCloseEvents(nsISupports *aContext, - nsresult aStatusCode, - bool sync) +WebSocket::ScheduleConnectionCloseEvents(nsISupports* aContext, + nsresult aStatusCode, + bool sync) { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); @@ -387,7 +367,7 @@ nsWebSocket::ScheduleConnectionCloseEvents(nsISupports *aContext, } NS_IMETHODIMP -nsWebSocket::OnAcknowledge(nsISupports *aContext, uint32_t aSize) +WebSocket::OnAcknowledge(nsISupports *aContext, uint32_t aSize) { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); @@ -399,21 +379,21 @@ nsWebSocket::OnAcknowledge(nsISupports *aContext, uint32_t aSize) } NS_IMETHODIMP -nsWebSocket::OnServerClose(nsISupports *aContext, uint16_t aCode, +WebSocket::OnServerClose(nsISupports *aContext, uint16_t aCode, const nsACString &aReason) { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); - MOZ_ASSERT(mReadyState != nsIWebSocket::CONNECTING, + MOZ_ASSERT(mReadyState != WebSocket::CONNECTING, "Received server close before connected?"); - MOZ_ASSERT(mReadyState != nsIWebSocket::CLOSED, + MOZ_ASSERT(mReadyState != WebSocket::CLOSED, "Received server close after already closed!"); // store code/string for onclose DOM event mCloseEventCode = aCode; CopyUTF8toUTF16(aReason, mCloseEventReason); - if (mReadyState == nsIWebSocket::OPEN) { + if (mReadyState == WebSocket::OPEN) { // Server initiating close. // RFC 6455, 5.5.1: "When sending a Close frame in response, the endpoint // typically echos the status code it received". @@ -425,28 +405,27 @@ nsWebSocket::OnServerClose(nsISupports *aContext, uint16_t aCode, } } else { // We initiated close, and server has replied: OnStop does rest of the work. - MOZ_ASSERT(mReadyState == nsIWebSocket::CLOSING, "unknown state"); + MOZ_ASSERT(mReadyState == WebSocket::CLOSING, "unknown state"); } return NS_OK; } //----------------------------------------------------------------------------- -// nsWebSocket::nsIInterfaceRequestor +// WebSocket::nsIInterfaceRequestor //----------------------------------------------------------------------------- NS_IMETHODIMP -nsWebSocket::GetInterface(const nsIID &aIID, void **aResult) +WebSocket::GetInterface(const nsIID& aIID, void** aResult) { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); - if (mReadyState == nsIWebSocket::CLOSED) + if (mReadyState == WebSocket::CLOSED) return NS_ERROR_FAILURE; if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) || aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) { nsresult rv; - nsIScriptContext* sc = GetContextForEventHandlers(&rv); nsCOMPtr doc = nsContentUtils::GetDocumentFromScriptContext(sc); @@ -465,27 +444,30 @@ nsWebSocket::GetInterface(const nsIID &aIID, void **aResult) } //////////////////////////////////////////////////////////////////////////////// -// nsWebSocket +// WebSocket //////////////////////////////////////////////////////////////////////////////// -nsWebSocket::nsWebSocket() : mKeepingAlive(false), - mCheckMustKeepAlive(true), - mOnCloseScheduled(false), - mFailed(false), - mDisconnected(false), - mCloseEventWasClean(false), - mCloseEventCode(nsIWebSocketChannel::CLOSE_ABNORMAL), - mReadyState(nsIWebSocket::CONNECTING), - mOutgoingBufferedAmount(0), - mBinaryType(WS_BINARY_TYPE_BLOB), - mScriptLine(0), - mInnerWindowID(0) +WebSocket::WebSocket() +: mKeepingAlive(false), + mCheckMustKeepAlive(true), + mOnCloseScheduled(false), + mFailed(false), + mDisconnected(false), + mCloseEventWasClean(false), + mCloseEventCode(nsIWebSocketChannel::CLOSE_ABNORMAL), + mReadyState(WebSocket::CONNECTING), + mOutgoingBufferedAmount(0), + mBinaryType(BinaryTypeValues::Blob), + mScriptLine(0), + mInnerWindowID(0) { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); nsLayoutStatics::AddRef(); + + SetIsDOMBinding(); } -nsWebSocket::~nsWebSocket() +WebSocket::~WebSocket() { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); @@ -496,9 +478,110 @@ nsWebSocket::~nsWebSocket() nsLayoutStatics::Release(); } -NS_IMPL_CYCLE_COLLECTION_CLASS(nsWebSocket) +JSObject* +WebSocket::WrapObject(JSContext* cx, JSObject* scope, bool* triedToWrap) +{ + return WebSocketBinding::Wrap(cx, scope, this, triedToWrap); +} -NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsWebSocket) +//--------------------------------------------------------------------------- +// WebIDL +//--------------------------------------------------------------------------- + +// Constructor: +already_AddRefed +WebSocket::Constructor(JSContext* aCx, + nsISupports* aGlobal, + const nsAString& aUrl, + ErrorResult& aRv) +{ + Sequence protocols; + return WebSocket::Constructor(aCx, aGlobal, aUrl, protocols, aRv); +} + +already_AddRefed +WebSocket::Constructor(JSContext* aCx, + nsISupports* aGlobal, + const nsAString& aUrl, + const nsAString& aProtocol, + ErrorResult& aRv) +{ + Sequence protocols; + protocols.AppendElement(aProtocol); + return WebSocket::Constructor(aCx, aGlobal, aUrl, protocols, aRv); +} + +already_AddRefed +WebSocket::Constructor(JSContext* aCx, + nsISupports* aGlobal, + const nsAString& aUrl, + const Sequence& aProtocols, + ErrorResult& aRv) +{ + if (!PrefEnabled()) { + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + return nullptr; + } + + nsCOMPtr scriptPrincipal = do_QueryInterface(aGlobal); + if (!scriptPrincipal) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsCOMPtr principal = scriptPrincipal->GetPrincipal(); + if (!principal) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsCOMPtr sgo = do_QueryInterface(aGlobal); + if (!sgo) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsCOMPtr ownerWindow = do_QueryInterface(aGlobal); + if (!ownerWindow) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsTArray protocolArray; + + for (uint32_t index = 0, len = aProtocols.Length(); index < len; ++index) { + + const nsString& protocolElement = aProtocols[index]; + + if (protocolElement.IsEmpty()) { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return nullptr; + } + if (protocolArray.Contains(protocolElement)) { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return nullptr; + } + if (protocolElement.FindChar(',') != -1) /* interferes w/list */ { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return nullptr; + } + + protocolArray.AppendElement(protocolElement); + } + + nsRefPtr webSocket = new WebSocket(); + nsresult rv = webSocket->Init(aCx, principal, ownerWindow, aUrl, protocolArray); + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return nullptr; + } + + return webSocket.forget(); +} + +NS_IMPL_CYCLE_COLLECTION_CLASS(WebSocket) + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(WebSocket) bool isBlack = tmp->IsBlack(); if (isBlack|| tmp->mKeepingAlive) { if (tmp->mListenerManager) { @@ -511,26 +594,26 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsWebSocket) } NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END -NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsWebSocket) +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(WebSocket) return tmp->IsBlack(); NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END -NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsWebSocket) +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(WebSocket) return tmp->IsBlack(); NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END -NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsWebSocket, +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WebSocket, nsDOMEventTargetHelper) NS_IMPL_CYCLE_COLLECTION_TRACE_END -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsWebSocket, +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WebSocket, nsDOMEventTargetHelper) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mPrincipal) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mURI) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChannel) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsWebSocket, +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WebSocket, nsDOMEventTargetHelper) tmp->Disconnect(); NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mPrincipal) @@ -538,29 +621,24 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsWebSocket, NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mChannel) NS_IMPL_CYCLE_COLLECTION_UNLINK_END -DOMCI_DATA(WebSocket, nsWebSocket) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsWebSocket) - NS_INTERFACE_MAP_ENTRY(nsIWebSocket) - NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WebSocket) NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) NS_INTERFACE_MAP_ENTRY(nsIWebSocketListener) NS_INTERFACE_MAP_ENTRY(nsIObserver) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_ENTRY(nsIRequest) - NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(WebSocket) NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper) -NS_IMPL_ADDREF_INHERITED(nsWebSocket, nsDOMEventTargetHelper) -NS_IMPL_RELEASE_INHERITED(nsWebSocket, nsDOMEventTargetHelper) +NS_IMPL_ADDREF_INHERITED(WebSocket, nsDOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(WebSocket, nsDOMEventTargetHelper) -NS_IMPL_EVENT_HANDLER(nsWebSocket, open) -NS_IMPL_EVENT_HANDLER(nsWebSocket, error) -NS_IMPL_EVENT_HANDLER(nsWebSocket, message) -NS_IMPL_EVENT_HANDLER(nsWebSocket, close) +NS_IMPL_EVENT_HANDLER(WebSocket, open) +NS_IMPL_EVENT_HANDLER(WebSocket, error) +NS_IMPL_EVENT_HANDLER(WebSocket, message) +NS_IMPL_EVENT_HANDLER(WebSocket, close) void -nsWebSocket::DisconnectFromOwner() +WebSocket::DisconnectFromOwner() { nsDOMEventTargetHelper::DisconnectFromOwner(); CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY); @@ -568,129 +646,122 @@ nsWebSocket::DisconnectFromOwner() } //----------------------------------------------------------------------------- -// nsWebSocket::nsIJSNativeInitializer methods: +// WebSocket:: initialization //----------------------------------------------------------------------------- -/** - * This Initialize method is called from XPConnect via nsIJSNativeInitializer. - * It is used for constructing our nsWebSocket from JavaScript. It expects a URL - * string parameter and an optional protocol parameter which may be a string or - * an array of strings. It also initializes the principal, the script context and - * the window owner. - */ -NS_IMETHODIMP -nsWebSocket::Initialize(nsISupports* aOwner, - JSContext* aContext, - JSObject* aObject, - uint32_t aArgc, - JS::Value* aArgv) +nsresult +WebSocket::Init(JSContext* aCx, + nsIPrincipal* aPrincipal, + nsPIDOMWindow* aOwnerWindow, + const nsAString& aURL, + nsTArray& aProtocolArray) { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); - nsAutoString urlParam; + MOZ_ASSERT(aPrincipal); + MOZ_ASSERT(aOwnerWindow); + MOZ_ASSERT(aOwnerWindow->IsInnerWindow()); if (!PrefEnabled()) { return NS_ERROR_DOM_SECURITY_ERR; } - if (aArgc != 1 && aArgc != 2) { - return NS_ERROR_DOM_SYNTAX_ERR; + mPrincipal = aPrincipal; + BindToOwner(aOwnerWindow); + + // Attempt to kill "ghost" websocket: but usually too early for check to fail + nsresult rv = CheckInnerWindowCorrectness(); + NS_ENSURE_SUCCESS(rv, rv); + + // Shut down websocket if window is frozen or destroyed (only needed for + // "ghost" websockets--see bug 696085) + nsCOMPtr os = mozilla::services::GetObserverService(); + NS_ENSURE_STATE(os); + rv = os->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC, true); + NS_ENSURE_SUCCESS(rv, rv); + rv = os->AddObserver(this, DOM_WINDOW_FROZEN_TOPIC, true); + NS_ENSURE_SUCCESS(rv, rv); + + unsigned lineno; + JSScript* script; + if (JS_DescribeScriptedCaller(aCx, &script, &lineno)) { + mScriptFile = JS_GetScriptFilename(aCx, script); + mScriptLine = lineno; } - JSAutoRequest ar(aContext); + // Get WindowID + mInnerWindowID = nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(aCx); - JSString* jsstr = JS_ValueToString(aContext, aArgv[0]); - if (!jsstr) { - return NS_ERROR_DOM_SYNTAX_ERR; - } + // parses the url + rv = ParseURL(PromiseFlatString(aURL)); + NS_ENSURE_SUCCESS(rv, rv); - JS::Anchor deleteProtector(jsstr); - size_t length; - const jschar *chars = JS_GetStringCharsAndLength(aContext, jsstr, &length); - if (!chars) { - return NS_ERROR_OUT_OF_MEMORY; - } + nsIScriptContext* sc = GetContextForEventHandlers(&rv); + NS_ENSURE_SUCCESS(rv, rv); - urlParam.Assign(chars, length); - deleteProtector.clear(); + nsCOMPtr originDoc = nsContentUtils::GetDocumentFromScriptContext(sc); - nsCOMPtr ownerWindow = do_QueryInterface(aOwner); - NS_ENSURE_STATE(ownerWindow); - - nsCOMPtr sgo = do_QueryInterface(aOwner); - NS_ENSURE_STATE(sgo); - nsCOMPtr scriptContext = sgo->GetContext(); - NS_ENSURE_STATE(scriptContext); - - nsCOMPtr scriptPrincipal(do_QueryInterface(aOwner)); - NS_ENSURE_STATE(scriptPrincipal); - nsCOMPtr principal = scriptPrincipal->GetPrincipal(); - NS_ENSURE_STATE(principal); - - nsTArray protocolArray; - - if (aArgc == 2) { - if (aArgv[1].isObject() && - JS_IsArrayObject(aContext, &aArgv[1].toObject())) { - JSObject* jsobj = &aArgv[1].toObject(); - - uint32_t len; - JS_GetArrayLength(aContext, jsobj, &len); - - for (uint32_t index = 0; index < len; ++index) { - jsval value; - - if (!JS_GetElement(aContext, jsobj, index, &value)) - return NS_ERROR_DOM_SYNTAX_ERR; - - jsstr = JS_ValueToString(aContext, value); - if (!jsstr) - return NS_ERROR_DOM_SYNTAX_ERR; - - deleteProtector.set(jsstr); - chars = JS_GetStringCharsAndLength(aContext, jsstr, &length); - if (!chars) - return NS_ERROR_OUT_OF_MEMORY; - - nsDependentString protocolElement(chars, length); - if (protocolElement.IsEmpty()) - return NS_ERROR_DOM_SYNTAX_ERR; - if (protocolArray.Contains(protocolElement)) - return NS_ERROR_DOM_SYNTAX_ERR; - if (protocolElement.FindChar(',') != -1) /* interferes w/list */ - return NS_ERROR_DOM_SYNTAX_ERR; - protocolArray.AppendElement(protocolElement); - deleteProtector.clear(); - } - } else { - jsstr = JS_ValueToString(aContext, aArgv[1]); - if (!jsstr) - return NS_ERROR_DOM_SYNTAX_ERR; - - deleteProtector.set(jsstr); - chars = JS_GetStringCharsAndLength(aContext, jsstr, &length); - if (!chars) - return NS_ERROR_OUT_OF_MEMORY; - - nsDependentString protocolElement(chars, length); - if (protocolElement.IsEmpty()) - return NS_ERROR_DOM_SYNTAX_ERR; - if (protocolElement.FindChar(',') != -1) /* interferes w/list */ - return NS_ERROR_DOM_SYNTAX_ERR; - protocolArray.AppendElement(protocolElement); + // Don't allow https:// to open ws:// + if (!mSecure && + !Preferences::GetBool("network.websocket.allowInsecureFromHTTPS", + false)) { + // Confirmed we are opening plain ws:// and want to prevent this from a + // secure context (e.g. https). Check the security context of the document + // associated with this script, which is the same as associated with mOwner. + if (originDoc && originDoc->GetSecurityInfo()) { + return NS_ERROR_DOM_SECURITY_ERR; } } - return Init(principal, scriptContext, ownerWindow, urlParam, protocolArray); + // Assign the sub protocol list and scan it for illegal values + for (uint32_t index = 0; index < aProtocolArray.Length(); ++index) { + for (uint32_t i = 0; i < aProtocolArray[index].Length(); ++i) { + if (aProtocolArray[index][i] < static_cast(0x0021) || + aProtocolArray[index][i] > static_cast(0x007E)) + return NS_ERROR_DOM_SYNTAX_ERR; + } + + if (!mRequestedProtocolList.IsEmpty()) { + mRequestedProtocolList.Append(NS_LITERAL_CSTRING(", ")); + } + + AppendUTF16toUTF8(aProtocolArray[index], mRequestedProtocolList); + } + + // Check content policy. + int16_t shouldLoad = nsIContentPolicy::ACCEPT; + rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_WEBSOCKET, + mURI, + mPrincipal, + originDoc, + EmptyCString(), + nullptr, + &shouldLoad, + nsContentUtils::GetContentPolicy(), + nsContentUtils::GetSecurityManager()); + NS_ENSURE_SUCCESS(rv, rv); + if (NS_CP_REJECTED(shouldLoad)) { + // Disallowed by content policy. + return NS_ERROR_CONTENT_BLOCKED; + } + + // the constructor should throw a SYNTAX_ERROR only if it fails to parse the + // url parameter, so don't throw if EstablishConnection fails, and call + // onerror/onclose asynchronously + if (NS_FAILED(EstablishConnection())) { + FailConnection(nsIWebSocketChannel::CLOSE_ABNORMAL); + } + + return NS_OK; } //----------------------------------------------------------------------------- -// nsWebSocket methods: +// WebSocket methods: //----------------------------------------------------------------------------- class nsAutoCloseWS { public: - nsAutoCloseWS(nsWebSocket *aWebSocket) + nsAutoCloseWS(WebSocket* aWebSocket) : mWebSocket(aWebSocket) {} @@ -701,19 +772,18 @@ public: } } private: - nsRefPtr mWebSocket; + nsRefPtr mWebSocket; }; nsresult -nsWebSocket::EstablishConnection() +WebSocket::EstablishConnection() { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); NS_ABORT_IF_FALSE(!mChannel, "mChannel should be null"); - nsresult rv; - nsCOMPtr wsChannel; nsAutoCloseWS autoClose(this); + nsresult rv; if (mSecure) { wsChannel = @@ -758,11 +828,9 @@ nsWebSocket::EstablishConnection() } void -nsWebSocket::DispatchConnectionCloseEvents() +WebSocket::DispatchConnectionCloseEvents() { - nsresult rv; - - mReadyState = nsIWebSocket::CLOSED; + mReadyState = WebSocket::CLOSED; // Call 'onerror' if needed if (mFailed) { @@ -772,8 +840,8 @@ nsWebSocket::DispatchConnectionCloseEvents() } } - rv = CreateAndDispatchCloseEvent(mCloseEventWasClean, mCloseEventCode, - mCloseEventReason); + nsresult rv = CreateAndDispatchCloseEvent(mCloseEventWasClean, mCloseEventCode, + mCloseEventReason); if (NS_FAILED(rv)) { NS_WARNING("Failed to dispatch the close event"); } @@ -783,12 +851,11 @@ nsWebSocket::DispatchConnectionCloseEvents() } nsresult -nsWebSocket::CreateAndDispatchSimpleEvent(const nsString& aName) +WebSocket::CreateAndDispatchSimpleEvent(const nsString& aName) { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); - nsresult rv; - rv = CheckInnerWindowCorrectness(); + nsresult rv = CheckInnerWindowCorrectness(); if (NS_FAILED(rv)) { return NS_OK; } @@ -808,13 +875,12 @@ nsWebSocket::CreateAndDispatchSimpleEvent(const nsString& aName) } nsresult -nsWebSocket::CreateAndDispatchMessageEvent(const nsACString& aData, - bool isBinary) +WebSocket::CreateAndDispatchMessageEvent(const nsACString& aData, + bool isBinary) { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); - nsresult rv; - rv = CheckInnerWindowCorrectness(); + nsresult rv = CheckInnerWindowCorrectness(); if (NS_FAILED(rv)) return NS_OK; @@ -833,11 +899,11 @@ nsWebSocket::CreateAndDispatchMessageEvent(const nsACString& aData, { JSAutoRequest ar(cx); if (isBinary) { - if (mBinaryType == WS_BINARY_TYPE_BLOB) { + if (mBinaryType == BinaryTypeValues::Blob) { rv = CreateResponseBlob(aData, cx, jsData); NS_ENSURE_SUCCESS(rv, rv); - } else if (mBinaryType == WS_BINARY_TYPE_ARRAYBUFFER) { - JSObject *arrayBuf; + } else if (mBinaryType == BinaryTypeValues::Arraybuffer) { + JSObject* arrayBuf; rv = nsContentUtils::CreateArrayBuffer(cx, aData, &arrayBuf); NS_ENSURE_SUCCESS(rv, rv); jsData = OBJECT_TO_JSVAL(arrayBuf); @@ -880,11 +946,12 @@ nsWebSocket::CreateAndDispatchMessageEvent(const nsACString& aData, // Initial implementation: only stores to RAM, not file // TODO: bug 704447: large file support nsresult -nsWebSocket::CreateResponseBlob(const nsACString& aData, JSContext *aCx, - jsval &jsData) +WebSocket::CreateResponseBlob(const nsACString& aData, + JSContext *aCx, + jsval &jsData) { uint32_t blobLen = aData.Length(); - void *blobData = PR_Malloc(blobLen); + void* blobData = PR_Malloc(blobLen); nsCOMPtr blob; if (blobData) { memcpy(blobData, aData.BeginReading(), blobLen); @@ -897,14 +964,13 @@ nsWebSocket::CreateResponseBlob(const nsACString& aData, JSContext *aCx, } nsresult -nsWebSocket::CreateAndDispatchCloseEvent(bool aWasClean, - uint16_t aCode, - const nsString &aReason) +WebSocket::CreateAndDispatchCloseEvent(bool aWasClean, + uint16_t aCode, + const nsString &aReason) { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); - nsresult rv; - rv = CheckInnerWindowCorrectness(); + nsresult rv = CheckInnerWindowCorrectness(); if (NS_FAILED(rv)) { return NS_OK; } @@ -929,23 +995,21 @@ nsWebSocket::CreateAndDispatchCloseEvent(bool aWasClean, } bool -nsWebSocket::PrefEnabled() +WebSocket::PrefEnabled() { return Preferences::GetBool("network.websocket.enabled", true); } nsresult -nsWebSocket::ParseURL(const nsString& aURL) +WebSocket::ParseURL(const nsString& aURL) { - nsresult rv; - NS_ENSURE_TRUE(!aURL.IsEmpty(), NS_ERROR_DOM_SYNTAX_ERR); nsCOMPtr uri; - rv = NS_NewURI(getter_AddRefs(uri), aURL); + nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR); - nsCOMPtr parsedURL(do_QueryInterface(uri, &rv)); + nsCOMPtr parsedURL = do_QueryInterface(uri, &rv); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR); nsAutoCString fragment; @@ -1023,7 +1087,7 @@ nsWebSocket::ParseURL(const nsString& aURL) //----------------------------------------------------------------------------- void -nsWebSocket::UpdateMustKeepAlive() +WebSocket::UpdateMustKeepAlive() { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); if (!mCheckMustKeepAlive) { @@ -1035,7 +1099,7 @@ nsWebSocket::UpdateMustKeepAlive() if (mListenerManager) { switch (mReadyState) { - case nsIWebSocket::CONNECTING: + case WebSocket::CONNECTING: { if (mListenerManager->HasListenersFor(NS_LITERAL_STRING("open")) || mListenerManager->HasListenersFor(NS_LITERAL_STRING("message")) || @@ -1046,8 +1110,8 @@ nsWebSocket::UpdateMustKeepAlive() } break; - case nsIWebSocket::OPEN: - case nsIWebSocket::CLOSING: + case WebSocket::OPEN: + case WebSocket::CLOSING: { if (mListenerManager->HasListenersFor(NS_LITERAL_STRING("message")) || mListenerManager->HasListenersFor(NS_LITERAL_STRING("error")) || @@ -1058,7 +1122,7 @@ nsWebSocket::UpdateMustKeepAlive() } break; - case nsIWebSocket::CLOSED: + case WebSocket::CLOSED: { shouldKeepAlive = false; } @@ -1075,7 +1139,7 @@ nsWebSocket::UpdateMustKeepAlive() } void -nsWebSocket::DontKeepAliveAnyMore() +WebSocket::DontKeepAliveAnyMore() { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); if (mKeepingAlive) { @@ -1086,7 +1150,7 @@ nsWebSocket::DontKeepAliveAnyMore() } nsresult -nsWebSocket::UpdateURI() +WebSocket::UpdateURI() { // Check for Redirections nsCOMPtr uri; @@ -1107,9 +1171,9 @@ nsWebSocket::UpdateURI() } NS_IMETHODIMP -nsWebSocket::RemoveEventListener(const nsAString& aType, - nsIDOMEventListener* aListener, - bool aUseCapture) +WebSocket::RemoveEventListener(const nsAString& aType, + nsIDOMEventListener* aListener, + bool aUseCapture) { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); nsresult rv = nsDOMEventTargetHelper::RemoveEventListener(aType, @@ -1122,11 +1186,11 @@ nsWebSocket::RemoveEventListener(const nsAString& aType, } NS_IMETHODIMP -nsWebSocket::AddEventListener(const nsAString& aType, - nsIDOMEventListener *aListener, - bool aUseCapture, - bool aWantsUntrusted, - uint8_t optional_argc) +WebSocket::AddEventListener(const nsAString& aType, + nsIDOMEventListener *aListener, + bool aUseCapture, + bool aWantsUntrusted, + uint8_t optional_argc) { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); nsresult rv = nsDOMEventTargetHelper::AddEventListener(aType, @@ -1141,358 +1205,197 @@ nsWebSocket::AddEventListener(const nsAString& aType, } //----------------------------------------------------------------------------- -// nsWebSocket::nsIWebSocket methods: +// WebSocket - methods //----------------------------------------------------------------------------- -NS_IMETHODIMP -nsWebSocket::GetUrl(nsAString& aURL) +// webIDL: readonly attribute DOMString url +void +WebSocket::GetUrl(nsAString& aURL) { if (mEffectiveURL.IsEmpty()) { aURL = mOriginalURL; } else { aURL = mEffectiveURL; } - return NS_OK; } -NS_IMETHODIMP -nsWebSocket::GetExtensions(nsAString& aExtensions) +// webIDL: readonly attribute DOMString extensions; +void +WebSocket::GetExtensions(nsAString& aExtensions) { CopyUTF8toUTF16(mEstablishedExtensions, aExtensions); - return NS_OK; } -NS_IMETHODIMP -nsWebSocket::GetProtocol(nsAString& aProtocol) +// webIDL: readonly attribute DOMString protocol; +void +WebSocket::GetProtocol(nsAString& aProtocol) { CopyUTF8toUTF16(mEstablishedProtocol, aProtocol); - return NS_OK; } -NS_IMETHODIMP -nsWebSocket::GetReadyState(uint16_t *aReadyState) -{ - *aReadyState = mReadyState; - return NS_OK; -} - -NS_IMETHODIMP -nsWebSocket::GetBufferedAmount(uint32_t *aBufferedAmount) -{ - *aBufferedAmount = mOutgoingBufferedAmount; - return NS_OK; -} - -NS_IMETHODIMP -nsWebSocket::GetBinaryType(nsAString& aBinaryType) -{ - switch (mBinaryType) { - case WS_BINARY_TYPE_ARRAYBUFFER: - aBinaryType.AssignLiteral("arraybuffer"); - break; - case WS_BINARY_TYPE_BLOB: - aBinaryType.AssignLiteral("blob"); - break; - default: - NS_ERROR("Should not happen"); - } - return NS_OK; -} - -NS_IMETHODIMP -nsWebSocket::SetBinaryType(const nsAString& aBinaryType) -{ - if (aBinaryType.EqualsLiteral("arraybuffer")) { - mBinaryType = WS_BINARY_TYPE_ARRAYBUFFER; - } else if (aBinaryType.EqualsLiteral("blob")) { - mBinaryType = WS_BINARY_TYPE_BLOB; - } - - return NS_OK; -} - -NS_IMETHODIMP -nsWebSocket::Send(nsIVariant *aData, JSContext *aCx) +// webIDL: void send(DOMString data); +void +WebSocket::Send(const nsAString& aData, + ErrorResult& aRv) { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); - if (mReadyState == nsIWebSocket::CONNECTING) { - return NS_ERROR_DOM_INVALID_STATE_ERR; + NS_ConvertUTF16toUTF8 msgString(aData); + Send(nullptr, msgString, msgString.Length(), false, aRv); +} + +void +WebSocket::Send(nsIDOMBlob* aData, + ErrorResult& aRv) +{ + NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); + + nsCOMPtr msgStream; + nsresult rv = aData->GetInternalStream(getter_AddRefs(msgStream)); + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return; } - nsCString msgString; - nsCOMPtr msgStream; - bool isBinary; - uint32_t msgLen; - nsresult rv = GetSendParams(aData, msgString, msgStream, isBinary, msgLen, aCx); - NS_ENSURE_SUCCESS(rv, rv); + uint64_t msgLength; + rv = aData->GetSize(&msgLength); + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return; + } + + if (msgLength > PR_UINT32_MAX) { + aRv.Throw(NS_ERROR_FILE_TOO_BIG); + return; + } + + Send(msgStream, EmptyCString(), msgLength, true, aRv); +} + +void +WebSocket::Send(ArrayBuffer& aData, + ErrorResult& aRv) +{ + NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); + + MOZ_ASSERT(sizeof(*aData.Data()) == 1); + uint32_t len = aData.Length(); + char* data = reinterpret_cast(aData.Data()); + + nsDependentCSubstring msgString(data, len); + Send(nullptr, msgString, len, true, aRv); +} + +void +WebSocket::Send(ArrayBufferView& aData, + ErrorResult& aRv) +{ + NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); + + MOZ_ASSERT(sizeof(*aData.Data()) == 1); + uint32_t len = aData.Length(); + char* data = reinterpret_cast(aData.Data()); + + nsDependentCSubstring msgString(data, len); + Send(nullptr, msgString, len, true, aRv); +} + +void +WebSocket::Send(nsIInputStream* aMsgStream, + const nsACString& aMsgString, + uint32_t aMsgLength, + bool aIsBinary, + ErrorResult& aRv) +{ + if (mReadyState == WebSocket::CONNECTING) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return; + } // Always increment outgoing buffer len, even if closed - mOutgoingBufferedAmount += msgLen; + mOutgoingBufferedAmount += aMsgLength; - if (mReadyState == nsIWebSocket::CLOSING || - mReadyState == nsIWebSocket::CLOSED) { - return NS_OK; + if (mReadyState == WebSocket::CLOSING || + mReadyState == WebSocket::CLOSED) { + return; } - MOZ_ASSERT(mReadyState == nsIWebSocket::OPEN, - "Unknown state in nsWebSocket::Send"); + MOZ_ASSERT(mReadyState == WebSocket::OPEN, + "Unknown state in WebSocket::Send"); - if (msgStream) { - rv = mChannel->SendBinaryStream(msgStream, msgLen); + nsresult rv; + if (aMsgStream) { + rv = mChannel->SendBinaryStream(aMsgStream, aMsgLength); } else { - if (isBinary) { - rv = mChannel->SendBinaryMsg(msgString); + if (aIsBinary) { + rv = mChannel->SendBinaryMsg(aMsgString); } else { - rv = mChannel->SendMsg(msgString); + rv = mChannel->SendMsg(aMsgString); } } - NS_ENSURE_SUCCESS(rv, rv); + + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return; + } UpdateMustKeepAlive(); - - return NS_OK; } -nsresult -nsWebSocket::GetSendParams(nsIVariant *aData, nsCString &aStringOut, - nsCOMPtr &aStreamOut, - bool &aIsBinary, uint32_t &aOutgoingLength, - JSContext *aCx) -{ - // Get type of data (arraybuffer, blob, or string) - uint16_t dataType; - nsresult rv = aData->GetDataType(&dataType); - NS_ENSURE_SUCCESS(rv, rv); - - if (dataType == nsIDataType::VTYPE_INTERFACE || - dataType == nsIDataType::VTYPE_INTERFACE_IS) { - nsCOMPtr supports; - nsID *iid; - rv = aData->GetAsInterface(&iid, getter_AddRefs(supports)); - NS_ENSURE_SUCCESS(rv, rv); - - nsMemory::Free(iid); - - // ArrayBuffer? - jsval realVal; - JSObject* obj; - nsresult rv = aData->GetAsJSVal(&realVal); - if (NS_SUCCEEDED(rv) && !JSVAL_IS_PRIMITIVE(realVal) && - (obj = JSVAL_TO_OBJECT(realVal)) && - (JS_IsArrayBufferObject(obj, aCx))) { - int32_t len = JS_GetArrayBufferByteLength(obj, aCx); - char* data = reinterpret_cast(JS_GetArrayBufferData(obj, aCx)); - - aStringOut.Assign(data, len); - aIsBinary = true; - aOutgoingLength = len; - return NS_OK; - } - - // Blob? - nsCOMPtr blob = do_QueryInterface(supports); - if (blob) { - rv = blob->GetInternalStream(getter_AddRefs(aStreamOut)); - NS_ENSURE_SUCCESS(rv, rv); - - // GetSize() should not perform blocking I/O (unlike Available()) - uint64_t blobLen; - rv = blob->GetSize(&blobLen); - NS_ENSURE_SUCCESS(rv, rv); - if (blobLen > PR_UINT32_MAX) { - return NS_ERROR_FILE_TOO_BIG; - } - aOutgoingLength = static_cast(blobLen); - - aIsBinary = true; - return NS_OK; - } - } - - // Text message: if not already a string, turn it into one. - // TODO: bug 704444: Correctly coerce any JS type to string - // - PRUnichar* data = nullptr; - uint32_t len = 0; - rv = aData->GetAsWStringWithSize(&len, &data); - NS_ENSURE_SUCCESS(rv, rv); - - nsString text; - text.Adopt(data, len); - - CopyUTF16toUTF8(text, aStringOut); - - aIsBinary = false; - aOutgoingLength = aStringOut.Length(); - return NS_OK; -} - -NS_IMETHODIMP -nsWebSocket::Close(uint16_t code, const nsAString & reason, uint8_t argc) +// webIDL: void close(optional unsigned short code, optional DOMString reason): +void +WebSocket::Close(const Optional& aCode, + const Optional& aReason, + ErrorResult& aRv) { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); // the reason code is optional, but if provided it must be in a specific range uint16_t closeCode = 0; - if (argc >= 1) { - if (code != 1000 && (code < 3000 || code > 4999)) { - return NS_ERROR_DOM_INVALID_ACCESS_ERR; + if (aCode.WasPassed()) { + if (aCode.Value() != 1000 && (aCode.Value() < 3000 || aCode.Value() > 4999)) { + aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); + return; } - closeCode = code; + closeCode = aCode.Value(); } - nsAutoCString closeReason; - if (argc >= 2) { - CopyUTF16toUTF8(reason, closeReason); + nsCString closeReason; + if (aReason.WasPassed()) { + CopyUTF16toUTF8(aReason.Value(), closeReason); // The API requires the UTF-8 string to be 123 or less bytes if (closeReason.Length() > 123) { - return NS_ERROR_DOM_SYNTAX_ERR; + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return; } } - if (mReadyState == nsIWebSocket::CLOSING || - mReadyState == nsIWebSocket::CLOSED) { - return NS_OK; + if (mReadyState == WebSocket::CLOSING || + mReadyState == WebSocket::CLOSED) { + return; } - if (mReadyState == nsIWebSocket::CONNECTING) { + if (mReadyState == WebSocket::CONNECTING) { FailConnection(closeCode, closeReason); - return NS_OK; + return; } - // mReadyState == nsIWebSocket::OPEN + MOZ_ASSERT(mReadyState == WebSocket::OPEN); CloseConnection(closeCode, closeReason); - - return NS_OK; -} - -/** - * This Init method should only be called by C++ consumers. - */ -NS_IMETHODIMP -nsWebSocket::Init(nsIPrincipal* aPrincipal, - nsIScriptContext* aScriptContext, - nsPIDOMWindow* aOwnerWindow, - const nsAString& aURL, - nsTArray & protocolArray) -{ - NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); - nsresult rv; - - NS_ENSURE_ARG(aPrincipal); - - if (!PrefEnabled()) { - return NS_ERROR_DOM_SECURITY_ERR; - } - - mPrincipal = aPrincipal; - if (aOwnerWindow) { - BindToOwner(aOwnerWindow->IsOuterWindow() ? - aOwnerWindow->GetCurrentInnerWindow() : aOwnerWindow); - } else { - BindToOwner(aOwnerWindow); - } - - // Attempt to kill "ghost" websocket: but usually too early for check to fail - rv = CheckInnerWindowCorrectness(); - NS_ENSURE_SUCCESS(rv, rv); - - // Shut down websocket if window is frozen or destroyed (only needed for - // "ghost" websockets--see bug 696085) - nsCOMPtr os = mozilla::services::GetObserverService(); - NS_ENSURE_STATE(os); - rv = os->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC, true); - NS_ENSURE_SUCCESS(rv, rv); - rv = os->AddObserver(this, DOM_WINDOW_FROZEN_TOPIC, true); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr stack = - do_GetService("@mozilla.org/js/xpc/ContextStack;1"); - JSContext* cx = nullptr; - if (stack && NS_SUCCEEDED(stack->Peek(&cx)) && cx) { - unsigned lineno; - JSScript *script; - - if (JS_DescribeScriptedCaller(cx, &script, &lineno)) { - mScriptFile = JS_GetScriptFilename(cx, script); - mScriptLine = lineno; - } - - mInnerWindowID = nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx); - } - - // parses the url - rv = ParseURL(PromiseFlatString(aURL)); - NS_ENSURE_SUCCESS(rv, rv); - - nsIScriptContext* sc = GetContextForEventHandlers(&rv); - nsCOMPtr originDoc = - nsContentUtils::GetDocumentFromScriptContext(sc); - - // Don't allow https:// to open ws:// - if (!mSecure && - !Preferences::GetBool("network.websocket.allowInsecureFromHTTPS", - false)) { - // Confirmed we are opening plain ws:// and want to prevent this from a - // secure context (e.g. https). Check the security context of the document - // associated with this script, which is the same as associated with mOwner. - if (originDoc && originDoc->GetSecurityInfo()) - return NS_ERROR_DOM_SECURITY_ERR; - } - - // Assign the sub protocol list and scan it for illegal values - for (uint32_t index = 0; index < protocolArray.Length(); ++index) { - for (uint32_t i = 0; i < protocolArray[index].Length(); ++i) { - if (protocolArray[index][i] < static_cast(0x0021) || - protocolArray[index][i] > static_cast(0x007E)) - return NS_ERROR_DOM_SYNTAX_ERR; - } - - if (!mRequestedProtocolList.IsEmpty()) - mRequestedProtocolList.Append(NS_LITERAL_CSTRING(", ")); - AppendUTF16toUTF8(protocolArray[index], mRequestedProtocolList); - } - - // Check content policy. - int16_t shouldLoad = nsIContentPolicy::ACCEPT; - rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_WEBSOCKET, - mURI, - mPrincipal, - originDoc, - EmptyCString(), - nullptr, - &shouldLoad, - nsContentUtils::GetContentPolicy(), - nsContentUtils::GetSecurityManager()); - NS_ENSURE_SUCCESS(rv, rv); - if (NS_CP_REJECTED(shouldLoad)) { - // Disallowed by content policy. - return NS_ERROR_CONTENT_BLOCKED; - } - - // the constructor should throw a SYNTAX_ERROR only if it fails to parse the - // url parameter, so don't throw if EstablishConnection fails, and call - // onerror/onclose asynchronously - if (NS_FAILED(EstablishConnection())) { - FailConnection(nsIWebSocketChannel::CLOSE_ABNORMAL); - } - - return NS_OK; } //----------------------------------------------------------------------------- -// nsWebSocket::nsIObserver +// WebSocket::nsIObserver //----------------------------------------------------------------------------- NS_IMETHODIMP -nsWebSocket::Observe(nsISupports* aSubject, - const char* aTopic, - const PRUnichar* aData) +WebSocket::Observe(nsISupports* aSubject, + const char* aTopic, + const PRUnichar* aData) { - if ((mReadyState == nsIWebSocket::CLOSING) || - (mReadyState == nsIWebSocket::CLOSED)) { + if ((mReadyState == WebSocket::CLOSING) || + (mReadyState == WebSocket::CLOSED)) { return NS_OK; } @@ -1510,27 +1413,26 @@ nsWebSocket::Observe(nsISupports* aSubject, return NS_OK; } - //----------------------------------------------------------------------------- -// nsWebSocket::nsIRequest +// WebSocket::nsIRequest //----------------------------------------------------------------------------- NS_IMETHODIMP -nsWebSocket::GetName(nsACString &aName) +WebSocket::GetName(nsACString& aName) { CopyUTF16toUTF8(mOriginalURL, aName); return NS_OK; } NS_IMETHODIMP -nsWebSocket::IsPending(bool *aValue) +WebSocket::IsPending(bool* aValue) { - *aValue = (mReadyState != nsIWebSocket::CLOSED); + *aValue = (mReadyState != WebSocket::CLOSED); return NS_OK; } NS_IMETHODIMP -nsWebSocket::GetStatus(nsresult *aStatus) +WebSocket::GetStatus(nsresult* aStatus) { *aStatus = NS_OK; return NS_OK; @@ -1538,7 +1440,7 @@ nsWebSocket::GetStatus(nsresult *aStatus) // Window closed, stop/reload button pressed, user navigated away from page, etc. NS_IMETHODIMP -nsWebSocket::Cancel(nsresult aStatus) +WebSocket::Cancel(nsresult aStatus) { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); @@ -1552,19 +1454,19 @@ nsWebSocket::Cancel(nsresult aStatus) } NS_IMETHODIMP -nsWebSocket::Suspend() +WebSocket::Suspend() { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP -nsWebSocket::Resume() +WebSocket::Resume() { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP -nsWebSocket::GetLoadGroup(nsILoadGroup **aLoadGroup) +WebSocket::GetLoadGroup(nsILoadGroup** aLoadGroup) { *aLoadGroup = nullptr; @@ -1581,21 +1483,24 @@ nsWebSocket::GetLoadGroup(nsILoadGroup **aLoadGroup) } NS_IMETHODIMP -nsWebSocket::SetLoadGroup(nsILoadGroup *aLoadGroup) +WebSocket::SetLoadGroup(nsILoadGroup* aLoadGroup) { return NS_ERROR_UNEXPECTED; } NS_IMETHODIMP -nsWebSocket::GetLoadFlags(nsLoadFlags *aLoadFlags) +WebSocket::GetLoadFlags(nsLoadFlags* aLoadFlags) { *aLoadFlags = nsIRequest::LOAD_BACKGROUND; return NS_OK; } NS_IMETHODIMP -nsWebSocket::SetLoadFlags(nsLoadFlags aLoadFlags) +WebSocket::SetLoadFlags(nsLoadFlags aLoadFlags) { // we won't change the load flags at all. return NS_OK; } + +} // dom namespace +} // mozilla namespace diff --git a/content/base/src/WebSocket.h b/content/base/src/WebSocket.h new file mode 100644 index 000000000000..5bf7e8dbae29 --- /dev/null +++ b/content/base/src/WebSocket.h @@ -0,0 +1,302 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef WebSocket_h__ +#define WebSocket_h__ + +#include "mozilla/Util.h" + +#include "nsWrapperCache.h" +#include "nsIWebSocketListener.h" +#include "nsISupports.h" + +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/TypedArray.h" +#include "mozilla/dom/BindingUtils.h" + +// Need this for BinaryType. +#include "mozilla/dom/WebSocketBinding.h" + +#include "jsfriendapi.h" +#include "nsISupportsUtils.h" +#include "nsCOMPtr.h" +#include "nsString.h" +#include "nsIPrincipal.h" +#include "nsCycleCollectionParticipant.h" +#include "nsIDOMEventListener.h" +#include "nsDOMEventTargetHelper.h" +#include "nsAutoPtr.h" +#include "nsIDOMDOMStringList.h" +#include "nsIInterfaceRequestor.h" +#include "nsIWebSocketChannel.h" +#include "nsIWebSocketListener.h" +#include "nsIObserver.h" +#include "nsIRequest.h" +#include "nsWeakReference.h" + +#define DEFAULT_WS_SCHEME_PORT 80 +#define DEFAULT_WSS_SCHEME_PORT 443 + +namespace mozilla { +namespace dom { + +#define IMPL_EVENT_HANDLER(_lowercase) \ + inline JSObject* GetOn##_lowercase(JSContext* aCx) \ + { \ + JS::Value val; \ + nsresult rv = GetOn##_lowercase(aCx, &val); \ + return NS_SUCCEEDED(rv) ? JSVAL_TO_OBJECT(val) : nullptr; \ + } \ + void SetOn##_lowercase(JSContext* aCx, JSObject* aCallback, \ + ErrorResult& aRv) \ + { \ + aRv = SetOn##_lowercase(aCx, OBJECT_TO_JSVAL(aCallback)); \ + } \ + NS_IMETHOD GetOn##_lowercase(JSContext* cx, JS::Value* aVal); \ + NS_IMETHOD SetOn##_lowercase(JSContext* cx, const JS::Value& aVal); + +class WebSocket : public nsDOMEventTargetHelper, + public nsIInterfaceRequestor, + public nsIWebSocketListener, + public nsIObserver, + public nsSupportsWeakReference, + public nsIRequest +{ +friend class CallDispatchConnectionCloseEvents; +friend class nsAutoCloseWS; + +public: + enum { + CONNECTING = 0, + OPEN = 1, + CLOSING = 2, + CLOSED = 3 + }; + +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_INHERITED(WebSocket, + nsDOMEventTargetHelper) + NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSIWEBSOCKETLISTENER + NS_DECL_NSIOBSERVER + NS_DECL_NSIREQUEST + + // nsIDOMEventTarget + NS_IMETHOD AddEventListener(const nsAString& aType, + nsIDOMEventListener *aListener, + bool aUseCapture, + bool aWantsUntrusted, + uint8_t optional_argc); + NS_IMETHOD RemoveEventListener(const nsAString& aType, + nsIDOMEventListener* aListener, + bool aUseCapture); + + virtual void DisconnectFromOwner(); + + // nsWrapperCache + nsPIDOMWindow* GetParentObject() { return GetOwner(); } + + JSObject* WrapObject(JSContext *cx, + JSObject *scope, + bool *triedToWrap); + +public: // static helpers: + + // Determine if preferences allow WebSocket + static bool PrefEnabled(); + +public: // WebIDL interface: + + // Constructor: + static already_AddRefed Constructor(JSContext *aCx, + nsISupports* aGlobal, + const nsAString& aUrl, + ErrorResult& rv); + + static already_AddRefed Constructor(JSContext *aCx, + nsISupports* aGlobal, + const nsAString& aUrl, + const nsAString& aProtocol, + ErrorResult& rv); + + static already_AddRefed Constructor(JSContext *aCx, + nsISupports* aGlobal, + const nsAString& aUrl, + const Sequence& aProtocols, + ErrorResult& rv); + + // webIDL: readonly attribute DOMString url + void GetUrl(nsAString& aResult); + + // webIDL: readonly attribute unsigned short readyState; + uint16_t GetReadyState() { return (uint16_t)mReadyState; } + + // webIDL: readonly attribute unsigned long bufferedAmount; + uint32_t GetBufferedAmount() { return (uint32_t) mOutgoingBufferedAmount; } + + // webIDL: attribute Function? onopen; + IMPL_EVENT_HANDLER(open) + + // webIDL: attribute Function? onerror; + IMPL_EVENT_HANDLER(error) + + // webIDL: attribute Function? onclose; + IMPL_EVENT_HANDLER(close) + + // webIDL: readonly attribute DOMString extensions; + void GetExtensions(nsAString& aResult); + + // webIDL: readonly attribute DOMString protocol; + void GetProtocol(nsAString& aResult); + + // webIDL: void close(optional unsigned short code, optional DOMString reason): + void Close(const Optional& aCode, + const Optional& aReason, + ErrorResult& aRv); + + // webIDL: attribute Function? onmessage; + IMPL_EVENT_HANDLER(message) + + // webIDL: attribute DOMString binaryType; + BinaryType GetBinaryType() { return mBinaryType; } + void SetBinaryType(BinaryType aData) { mBinaryType = aData; } + + // webIDL: void send(DOMString|Blob|ArrayBufferView data); + void Send(const nsAString& aData, + ErrorResult& aRv); + void Send(nsIDOMBlob* aData, + ErrorResult& aRv); + void Send(ArrayBuffer& aData, + ErrorResult& aRv); + void Send(ArrayBufferView& aData, + ErrorResult& aRv); + +private: // constructor && distructor + WebSocket(); + virtual ~WebSocket(); + +protected: + nsresult Init(JSContext* aCx, + nsIPrincipal* aPrincipal, + nsPIDOMWindow* aOwnerWindow, + const nsAString& aURL, + nsTArray& aProtocolArray); + + void Send(nsIInputStream* aMsgStream, + const nsACString& aMsgString, + PRUint32 aMsgLength, + bool aIsBinary, + ErrorResult& aRv); + + nsresult ParseURL(const nsString& aURL); + nsresult EstablishConnection(); + + // These methods when called can release the WebSocket object + nsresult FailConnection(uint16_t reasonCode, + const nsACString& aReasonString = EmptyCString()); + nsresult CloseConnection(uint16_t reasonCode, + const nsACString& aReasonString = EmptyCString()); + nsresult Disconnect(); + + nsresult ConsoleError(); + nsresult PrintErrorOnConsole(const char* aBundleURI, + const PRUnichar* aError, + const PRUnichar** aFormatStrings, + PRUint32 aFormatStringsLen); + + nsresult DoOnMessageAvailable(const nsACString& aMsg, + bool isBinary); + + // ConnectionCloseEvents: 'error' event if needed, then 'close' event. + // - These must not be dispatched while we are still within an incoming call + // from JS (ex: close()). Set 'sync' to false in that case to dispatch in a + // separate new event. + nsresult ScheduleConnectionCloseEvents(nsISupports* aContext, + nsresult aStatusCode, + bool sync); + // 2nd half of ScheduleConnectionCloseEvents, sometimes run in its own event. + void DispatchConnectionCloseEvents(); + + // These methods actually do the dispatch for various events. + nsresult CreateAndDispatchSimpleEvent(const nsString& aName); + nsresult CreateAndDispatchMessageEvent(const nsACString& aData, + bool isBinary); + nsresult CreateAndDispatchCloseEvent(bool aWasClean, + PRUint16 aCode, + const nsString& aReason); + nsresult CreateResponseBlob(const nsACString& aData, + JSContext* aCx, + jsval& jsData); + + // if there are "strong event listeners" (see comment in WebSocket.cpp) or + // outgoing not sent messages then this method keeps the object alive + // when js doesn't have strong references to it. + void UpdateMustKeepAlive(); + // ATTENTION, when calling this method the object can be released + // (and possibly collected). + void DontKeepAliveAnyMore(); + + nsresult UpdateURI(); + +protected: //data + + nsCOMPtr mChannel; + + // related to the WebSocket constructor steps + nsString mOriginalURL; + nsString mEffectiveURL; // after redirects + bool mSecure; // if true it is using SSL and the wss scheme, + // otherwise it is using the ws scheme with no SSL + + bool mKeepingAlive; + bool mCheckMustKeepAlive; + bool mOnCloseScheduled; + bool mFailed; + bool mDisconnected; + + // Set attributes of DOM 'onclose' message + bool mCloseEventWasClean; + nsString mCloseEventReason; + uint16_t mCloseEventCode; + + nsCString mAsciiHost; // hostname + uint32_t mPort; + nsCString mResource; // [filepath[?query]] + nsString mUTF16Origin; + + nsCOMPtr mURI; + nsCString mRequestedProtocolList; + nsCString mEstablishedProtocol; + nsCString mEstablishedExtensions; + + uint16_t mReadyState; + + nsCOMPtr mPrincipal; + + uint32_t mOutgoingBufferedAmount; + + BinaryType mBinaryType; + + // Web Socket owner information: + // - the script file name, UTF8 encoded. + // - source code line number where the Web Socket object was constructed. + // - the ID of the inner window where the script lives. Note that this may not + // be the same as the Web Socket owner window. + // These attributes are used for error reporting. + nsCString mScriptFile; + uint32_t mScriptLine; + uint64_t mInnerWindowID; + +private: + WebSocket(const WebSocket& x) MOZ_DELETE; // prevent bad usage + WebSocket& operator=(const WebSocket& x) MOZ_DELETE; +}; + +} //namespace dom +} //namespace mozilla + +#endif diff --git a/content/base/src/nsWebSocket.h b/content/base/src/nsWebSocket.h deleted file mode 100644 index f2717f492be0..000000000000 --- a/content/base/src/nsWebSocket.h +++ /dev/null @@ -1,193 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set sw=2 ts=8 et tw=80 : */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef nsWebSocket_h__ -#define nsWebSocket_h__ - -#include "nsISupportsUtils.h" -#include "nsIWebSocket.h" -#include "nsCOMPtr.h" -#include "nsString.h" -#include "nsIJSNativeInitializer.h" -#include "nsIPrincipal.h" -#include "nsCycleCollectionParticipant.h" -#include "nsIDOMEventListener.h" -#include "nsDOMEventTargetHelper.h" -#include "nsAutoPtr.h" -#include "nsIDOMDOMStringList.h" -#include "nsIInterfaceRequestor.h" -#include "nsIWebSocketChannel.h" -#include "nsIWebSocketListener.h" -#include "nsIObserver.h" -#include "nsIRequest.h" -#include "nsWeakReference.h" - -#define DEFAULT_WS_SCHEME_PORT 80 -#define DEFAULT_WSS_SCHEME_PORT 443 - -#define NS_WEBSOCKET_CID \ - { /* 7ca25214-98dc-40a6-bc1f-41ddbe41f46c */ \ - 0x7ca25214, 0x98dc, 0x40a6, \ - {0xbc, 0x1f, 0x41, 0xdd, 0xbe, 0x41, 0xf4, 0x6c} } - -#define NS_WEBSOCKET_CONTRACTID "@mozilla.org/websocket;1" - -class CallDispatchConnectionCloseEvents; -class nsAutoCloseWS; - -class nsWebSocket: public nsDOMEventTargetHelper, - public nsIWebSocket, - public nsIJSNativeInitializer, - public nsIInterfaceRequestor, - public nsIWebSocketListener, - public nsIObserver, - public nsSupportsWeakReference, - public nsIRequest -{ -friend class CallDispatchConnectionCloseEvents; -friend class nsAutoCloseWS; - -public: - nsWebSocket(); - virtual ~nsWebSocket(); - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_INHERITED(nsWebSocket, - nsDOMEventTargetHelper) - NS_DECL_NSIWEBSOCKET - NS_DECL_NSIINTERFACEREQUESTOR - NS_DECL_NSIWEBSOCKETLISTENER - NS_DECL_NSIOBSERVER - NS_DECL_NSIREQUEST - - // nsIJSNativeInitializer - NS_IMETHOD Initialize(nsISupports* aOwner, JSContext* aContext, - JSObject* aObject, uint32_t aArgc, jsval* aArgv); - - // nsIDOMEventTarget - NS_IMETHOD AddEventListener(const nsAString& aType, - nsIDOMEventListener *aListener, - bool aUseCapture, - bool aWantsUntrusted, - uint8_t optional_argc); - NS_IMETHOD RemoveEventListener(const nsAString& aType, - nsIDOMEventListener* aListener, - bool aUseCapture); - - // Determine if preferences allow WebSocket - static bool PrefEnabled(); - - virtual void DisconnectFromOwner(); -protected: - nsresult ParseURL(const nsString& aURL); - nsresult EstablishConnection(); - - // These methods when called can release the WebSocket object - nsresult FailConnection(uint16_t reasonCode, - const nsACString& aReasonString = EmptyCString()); - nsresult CloseConnection(uint16_t reasonCode, - const nsACString& aReasonString = EmptyCString()); - nsresult Disconnect(); - - nsresult ConsoleError(); - nsresult PrintErrorOnConsole(const char *aBundleURI, - const PRUnichar *aError, - const PRUnichar **aFormatStrings, - uint32_t aFormatStringsLen); - - // Get msg info out of JS variable being sent (string, arraybuffer, blob) - nsresult GetSendParams(nsIVariant *aData, nsCString &aStringOut, - nsCOMPtr &aStreamOut, - bool &aIsBinary, uint32_t &aOutgoingLength, - JSContext *aCx); - - nsresult DoOnMessageAvailable(const nsACString & aMsg, bool isBinary); - - // ConnectionCloseEvents: 'error' event if needed, then 'close' event. - // - These must not be dispatched while we are still within an incoming call - // from JS (ex: close()). Set 'sync' to false in that case to dispatch in a - // separate new event. - nsresult ScheduleConnectionCloseEvents(nsISupports *aContext, - nsresult aStatusCode, - bool sync); - // 2nd half of ScheduleConnectionCloseEvents, sometimes run in its own event. - void DispatchConnectionCloseEvents(); - - // These methods actually do the dispatch for various events. - nsresult CreateAndDispatchSimpleEvent(const nsString& aName); - nsresult CreateAndDispatchMessageEvent(const nsACString& aData, - bool isBinary); - nsresult CreateAndDispatchCloseEvent(bool aWasClean, uint16_t aCode, - const nsString &aReason); - nsresult CreateResponseBlob(const nsACString& aData, JSContext *aCx, - jsval &jsData); - - // if there are "strong event listeners" (see comment in nsWebSocket.cpp) or - // outgoing not sent messages then this method keeps the object alive - // when js doesn't have strong references to it. - void UpdateMustKeepAlive(); - // ATTENTION, when calling this method the object can be released - // (and possibly collected). - void DontKeepAliveAnyMore(); - - nsresult UpdateURI(); - - nsCOMPtr mChannel; - - // related to the WebSocket constructor steps - nsString mOriginalURL; - nsString mEffectiveURL; // after redirects - bool mSecure; // if true it is using SSL and the wss scheme, - // otherwise it is using the ws scheme with no SSL - - bool mKeepingAlive; - bool mCheckMustKeepAlive; - bool mOnCloseScheduled; - bool mFailed; - bool mDisconnected; - - // Set attributes of DOM 'onclose' message - bool mCloseEventWasClean; - nsString mCloseEventReason; - uint16_t mCloseEventCode; - - nsCString mAsciiHost; // hostname - uint32_t mPort; - nsCString mResource; // [filepath[?query]] - nsString mUTF16Origin; - - nsCOMPtr mURI; - nsCString mRequestedProtocolList; - nsCString mEstablishedProtocol; - nsCString mEstablishedExtensions; - - uint16_t mReadyState; - - nsCOMPtr mPrincipal; - - uint32_t mOutgoingBufferedAmount; - - enum - { - WS_BINARY_TYPE_ARRAYBUFFER, - WS_BINARY_TYPE_BLOB, - } mBinaryType; - - // Web Socket owner information: - // - the script file name, UTF8 encoded. - // - source code line number where the Web Socket object was constructed. - // - the ID of the inner window where the script lives. Note that this may not - // be the same as the Web Socket owner window. - // These attributes are used for error reporting. - nsCString mScriptFile; - uint32_t mScriptLine; - uint64_t mInnerWindowID; - -private: - nsWebSocket(const nsWebSocket& x); // prevent bad usage - nsWebSocket& operator=(const nsWebSocket& x); -}; - -#endif diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index a07ec7049115..1268e751d81c 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -171,7 +171,6 @@ #include "nsIDOMParser.h" #include "nsIDOMSerializer.h" #include "nsXMLHttpRequest.h" -#include "nsWebSocket.h" #include "nsEventSource.h" #include "nsIDOMSettingsManager.h" #include "nsIDOMContactManager.h" @@ -1618,9 +1617,6 @@ static nsDOMClassInfoData sClassInfoData[] = { NS_DEFINE_CLASSINFO_DATA(DesktopNotificationCenter, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(WebSocket, nsEventTargetSH, - EVENTTARGET_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(IDBFactory, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA_WITH_NAME(IDBFileHandle, FileHandle, nsEventTargetSH, @@ -1745,7 +1741,6 @@ NS_DEFINE_CONTRACT_CTOR(FileReader, NS_FILEREADER_CONTRACTID) NS_DEFINE_CONTRACT_CTOR(ArchiveReader, NS_ARCHIVEREADER_CONTRACTID) NS_DEFINE_CONTRACT_CTOR(FormData, NS_FORMDATA_CONTRACTID) NS_DEFINE_CONTRACT_CTOR(XMLSerializer, NS_XMLSERIALIZER_CONTRACTID) -NS_DEFINE_CONTRACT_CTOR(WebSocket, NS_WEBSOCKET_CONTRACTID) NS_DEFINE_CONTRACT_CTOR(XPathEvaluator, NS_XPATH_EVALUATOR_CONTRACTID) NS_DEFINE_CONTRACT_CTOR(XSLTProcessor, "@mozilla.org/document-transformer;1?type=xslt") @@ -1823,7 +1818,6 @@ static const nsConstructorFuncMapData kConstructorFuncMap[] = NS_DEFINE_CONSTRUCTOR_FUNC_DATA(ArchiveReader, ArchiveReaderCtor) NS_DEFINE_CONSTRUCTOR_FUNC_DATA(FormData, FormDataCtor) NS_DEFINE_CONSTRUCTOR_FUNC_DATA(XMLSerializer, XMLSerializerCtor) - NS_DEFINE_CONSTRUCTOR_FUNC_DATA(WebSocket, WebSocketCtor) NS_DEFINE_CONSTRUCTOR_FUNC_DATA(XPathEvaluator, XPathEvaluatorCtor) NS_DEFINE_CONSTRUCTOR_FUNC_DATA(XSLTProcessor, XSLTProcessorCtor) NS_DEFINE_CONSTRUCTOR_FUNC_DATA(EventSource, EventSourceCtor) @@ -4358,11 +4352,6 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_MAP_ENTRY(nsIDOMDesktopNotificationCenter) DOM_CLASSINFO_MAP_END - DOM_CLASSINFO_MAP_BEGIN(WebSocket, nsIWebSocket) - DOM_CLASSINFO_MAP_ENTRY(nsIWebSocket) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget) - DOM_CLASSINFO_MAP_END - DOM_CLASSINFO_MAP_BEGIN(IDBFactory, nsIIDBFactory) DOM_CLASSINFO_MAP_ENTRY(nsIIDBFactory) DOM_CLASSINFO_MAP_END @@ -6713,13 +6702,6 @@ ConstructorEnabled(const nsGlobalNameStruct *aStruct, nsGlobalWindow *aWin) return false; } - // For now don't expose web sockets unless user has explicitly enabled them - if (aStruct->mDOMClassInfoID == eDOMClassInfo_WebSocket_id) { - if (!nsWebSocket::PrefEnabled()) { - return false; - } - } - // For now don't expose server events unless user has explicitly enabled them if (aStruct->mDOMClassInfoID == eDOMClassInfo_EventSource_id) { if (!nsEventSource::PrefEnabled()) { diff --git a/dom/base/nsDOMClassInfoClasses.h b/dom/base/nsDOMClassInfoClasses.h index 53687a2b274d..e32fb4a088ba 100644 --- a/dom/base/nsDOMClassInfoClasses.h +++ b/dom/base/nsDOMClassInfoClasses.h @@ -484,9 +484,6 @@ DOMCI_CLASS(FormData) DOMCI_CLASS(DesktopNotification) DOMCI_CLASS(DesktopNotificationCenter) -// WebSocket -DOMCI_CLASS(WebSocket) - DOMCI_CLASS(IDBFactory) DOMCI_CLASS(IDBFileHandle) DOMCI_CLASS(IDBRequest) diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index e61517114335..1d26bdfae75c 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -236,6 +236,12 @@ DOMInterfaces = { 'headerFile': 'mozilla/dom/workers/bindings/XMLHttpRequestUpload.h' }], +'WebSocket': [ +{ + 'headerFile': 'WebSocket.h', + 'implicitJSContext': [ 'constructor' ] +}], + #################################### # Test Interfaces of various sorts # #################################### diff --git a/dom/webidl/WebIDL.mk b/dom/webidl/WebIDL.mk index c6c081470f64..a5216eff477c 100644 --- a/dom/webidl/WebIDL.mk +++ b/dom/webidl/WebIDL.mk @@ -18,6 +18,7 @@ webidl_files = \ Performance.webidl \ PerformanceNavigation.webidl \ PerformanceTiming.webidl \ + WebSocket.webidl \ XMLHttpRequest.webidl \ XMLHttpRequestEventTarget.webidl \ XMLHttpRequestUpload.webidl \ diff --git a/dom/webidl/WebSocket.webidl b/dom/webidl/WebSocket.webidl new file mode 100644 index 000000000000..10238bef5312 --- /dev/null +++ b/dom/webidl/WebSocket.webidl @@ -0,0 +1,69 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * The origin of this IDL file is + * http://www.whatwg.org/html/#network + * + * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and Opera Software ASA. + * You are granted a license to use, reproduce and create derivative works of this document. + */ + +enum BinaryType { "blob", "arraybuffer" }; + +[PrefControlled, + Constructor(DOMString url), + Constructor(DOMString url, DOMString protocols), + Constructor(DOMString url, sequence protocols)] +interface WebSocket : EventTarget { + + readonly attribute DOMString url; + + // ready state + const unsigned short CONNECTING = 0; + const unsigned short OPEN = 1; + const unsigned short CLOSING = 2; + const unsigned short CLOSED = 3; + + readonly attribute unsigned short readyState; + + readonly attribute unsigned long bufferedAmount; + + // networking + + [TreatNonCallableAsNull, SetterThrows] + attribute Function? onopen; + + [TreatNonCallableAsNull, SetterThrows] + attribute Function? onerror; + + [TreatNonCallableAsNull, SetterThrows] + attribute Function? onclose; + + readonly attribute DOMString extensions; + + readonly attribute DOMString protocol; + + [Throws] + void close([Clamp] optional unsigned short code, optional DOMString reason); + + // messaging + + [TreatNonCallableAsNull, SetterThrows] + attribute Function? onmessage; + + attribute BinaryType binaryType; + + [Throws] + void send(DOMString data); + + [Throws] + void send(Blob data); + + [Throws] + void send(ArrayBuffer data); + + [Throws] + void send(ArrayBufferView data); +}; diff --git a/js/xpconnect/src/dom_quickstubs.qsconf b/js/xpconnect/src/dom_quickstubs.qsconf index 06797dd14927..bb9fd95485fc 100644 --- a/js/xpconnect/src/dom_quickstubs.qsconf +++ b/js/xpconnect/src/dom_quickstubs.qsconf @@ -429,9 +429,6 @@ members = [ # can't be quickstubbed '-nsIXMLHttpRequest.upload', - # WebSocket - 'nsIWebSocket.*', - # webgl 'nsIDOMWebGLRenderingContext.*', # getContextAttributes is directly manipulating its return value diff --git a/layout/build/nsLayoutModule.cpp b/layout/build/nsLayoutModule.cpp index 83ff81f37fb6..c915e2fd4fc0 100644 --- a/layout/build/nsLayoutModule.cpp +++ b/layout/build/nsLayoutModule.cpp @@ -65,7 +65,6 @@ #include "nsDOMSerializer.h" #include "nsXMLHttpRequest.h" #include "nsChannelPolicy.h" -#include "nsWebSocket.h" #include "nsEventSource.h" // view stuff @@ -263,7 +262,6 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(txNodeSetAdaptor, Init) NS_GENERIC_FACTORY_CONSTRUCTOR(nsDOMSerializer) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsXMLHttpRequest, Init) NS_GENERIC_FACTORY_CONSTRUCTOR(nsEventSource) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsWebSocket) NS_GENERIC_FACTORY_CONSTRUCTOR(Activity) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsDOMFileReader, Init) NS_GENERIC_FACTORY_CONSTRUCTOR(ArchiveReader) @@ -753,7 +751,6 @@ NS_DEFINE_NAMED_CID(NS_BLOBPROTOCOLHANDLER_CID); NS_DEFINE_NAMED_CID(NS_BLOBURI_CID); NS_DEFINE_NAMED_CID(NS_XMLHTTPREQUEST_CID); NS_DEFINE_NAMED_CID(NS_EVENTSOURCE_CID); -NS_DEFINE_NAMED_CID(NS_WEBSOCKET_CID); NS_DEFINE_NAMED_CID(NS_DOMACTIVITY_CID); NS_DEFINE_NAMED_CID(NS_DOMPARSER_CID); NS_DEFINE_NAMED_CID(NS_DOMSTORAGE2_CID); @@ -1029,7 +1026,6 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = { { &kNS_BLOBURI_CID, false, NULL, nsBlobURIConstructor }, { &kNS_XMLHTTPREQUEST_CID, false, NULL, nsXMLHttpRequestConstructor }, { &kNS_EVENTSOURCE_CID, false, NULL, nsEventSourceConstructor }, - { &kNS_WEBSOCKET_CID, false, NULL, nsWebSocketConstructor }, { &kNS_DOMACTIVITY_CID, false, NULL, ActivityConstructor }, { &kNS_DOMPARSER_CID, false, NULL, nsDOMParserConstructor }, { &kNS_DOMSTORAGE2_CID, false, NULL, NS_NewDOMStorage2 }, @@ -1170,7 +1166,6 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = { { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX BLOBURI_SCHEME, &kNS_BLOBPROTOCOLHANDLER_CID }, { NS_XMLHTTPREQUEST_CONTRACTID, &kNS_XMLHTTPREQUEST_CID }, { NS_EVENTSOURCE_CONTRACTID, &kNS_EVENTSOURCE_CID }, - { NS_WEBSOCKET_CONTRACTID, &kNS_WEBSOCKET_CID }, { NS_DOMACTIVITY_CONTRACTID, &kNS_DOMACTIVITY_CID }, { NS_DOMPARSER_CONTRACTID, &kNS_DOMPARSER_CID }, { "@mozilla.org/dom/storage;2", &kNS_DOMSTORAGE2_CID }, From 2ba921a9a1a9e5b689e207d90e68728bf2688722 Mon Sep 17 00:00:00 2001 From: Masatoshi Kimura Date: Mon, 10 Sep 2012 09:57:15 -0400 Subject: [PATCH 07/78] Bug 789691 - Migrate BlobPropertyBag to WebIDL binding dictionary, r=bzbarsky --- content/base/public/nsIDOMFile.idl | 6 ---- content/base/src/nsDOMBlobBuilder.cpp | 25 +++++++++----- dom/webidl/Blob.webidl | 37 +++++++++++++++++++++ dom/webidl/WebIDL.mk | 1 + dom/workers/test/test_blobConstructor.html | 7 ++-- js/xpconnect/src/dictionary_helper_gen.conf | 1 - js/xpconnect/src/dictionary_helper_gen.py | 8 ++--- 7 files changed, 61 insertions(+), 24 deletions(-) create mode 100644 dom/webidl/Blob.webidl diff --git a/content/base/public/nsIDOMFile.idl b/content/base/public/nsIDOMFile.idl index b950456724e1..f7097cf7e0f6 100644 --- a/content/base/public/nsIDOMFile.idl +++ b/content/base/public/nsIDOMFile.idl @@ -83,9 +83,3 @@ interface nsIDOMMozBlobBuilder : nsISupports [implicit_jscontext] void append(in jsval data, [optional] in DOMString endings); }; - -dictionary BlobPropertyBag -{ - DOMString type; - DOMString endings = "transparent"; -}; diff --git a/content/base/src/nsDOMBlobBuilder.cpp b/content/base/src/nsDOMBlobBuilder.cpp index 4eb4e32a9fd9..f29880fa8772 100644 --- a/content/base/src/nsDOMBlobBuilder.cpp +++ b/content/base/src/nsDOMBlobBuilder.cpp @@ -5,6 +5,7 @@ #include "nsDOMBlobBuilder.h" #include "jsfriendapi.h" +#include "mozilla/dom/BlobBinding.h" #include "nsAutoPtr.h" #include "nsDOMClassInfoID.h" #include "nsIMultiplexInputStream.h" @@ -12,10 +13,10 @@ #include "nsTArray.h" #include "nsJSUtils.h" #include "nsContentUtils.h" -#include "DictionaryHelpers.h" #include "nsIScriptError.h" using namespace mozilla; +using namespace mozilla::dom; NS_IMPL_ISUPPORTS_INHERITED1(nsDOMMultipartFile, nsDOMFile, nsIJSNativeInitializer) @@ -181,14 +182,20 @@ nsDOMMultipartFile::InitInternal(JSContext* aCx, { bool nativeEOL = false; if (aArgc > 1) { - mozilla::dom::BlobPropertyBag d; - nsresult rv = d.Init(aCx, &aArgv[1]); - NS_ENSURE_SUCCESS(rv, rv); - mContentType = d.type; - if (d.endings.EqualsLiteral("native")) { - nativeEOL = true; - } else if (!d.endings.EqualsLiteral("transparent")) { - return NS_ERROR_TYPE_ERR; + if (NS_IsMainThread()) { + BlobPropertyBag d; + if (!d.Init(aCx, aArgv[1])) { + return NS_ERROR_TYPE_ERR; + } + mContentType = d.type; + nativeEOL = d.endings == EndingTypesValues::Native; + } else { + BlobPropertyBagWorkers d; + if (!d.Init(aCx, aArgv[1])) { + return NS_ERROR_TYPE_ERR; + } + mContentType = d.type; + nativeEOL = d.endings == EndingTypesValues::Native; } } diff --git a/dom/webidl/Blob.webidl b/dom/webidl/Blob.webidl new file mode 100644 index 000000000000..d64c24096dc1 --- /dev/null +++ b/dom/webidl/Blob.webidl @@ -0,0 +1,37 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * The origin of this IDL file is + * http://dev.w3.org/2006/webapi/FileAPI/#blob + * + * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C + * liability, trademark and document use rules apply. + */ + +/* +[Constructor, Constructor((ArrayBuffer or ArrayBufferView or Blob or DOMString)[] blobParts, optional BlobPropertyBag options)] +interface Blob { + + readonly attribute unsigned long long size; + readonly attribute DOMString type; + + //slice Blob into byte-ranged chunks + + Blob slice(optional long long start, + optional long long end, + optional DOMString contentType); + void close(); + +}; +*/ + +enum EndingTypes{"transparent", "native"}; + +dictionary BlobPropertyBag { + + DOMString type = ""; + EndingTypes endings = "transparent"; + +}; diff --git a/dom/webidl/WebIDL.mk b/dom/webidl/WebIDL.mk index a5216eff477c..0ecdb253116a 100644 --- a/dom/webidl/WebIDL.mk +++ b/dom/webidl/WebIDL.mk @@ -10,6 +10,7 @@ generated_webidl_files = \ webidl_files = \ AudioContext.webidl \ + Blob.webidl \ CanvasRenderingContext2D.webidl \ CSSStyleDeclaration.webidl \ Function.webidl \ diff --git a/dom/workers/test/test_blobConstructor.html b/dom/workers/test/test_blobConstructor.html index f009930a2862..cd2f5d263245 100644 --- a/dom/workers/test/test_blobConstructor.html +++ b/dom/workers/test/test_blobConstructor.html @@ -26,9 +26,9 @@ Tests of DOM Worker Blob constructor }; function f() { onmessage = function(e) { - var b = new Blob([e.data, "World"],{}); + var b = new Blob([e.data, "World"],{type: "text/plain"}); var fr = new FileReaderSync(); - postMessage(fr.readAsText(b)); + postMessage({text: fr.readAsText(b), type: b.type}); }; } var b = new Blob([f,"f();"]); @@ -36,7 +36,8 @@ Tests of DOM Worker Blob constructor var w = new Worker(u); w.onmessage = function(e) { URL.revokeObjectURL(u); - is(e.data, fr.result); + is(e.data.text, fr.result); + is(e.data.type, "text/plain"); SimpleTest.finish(); }; w.onerror = function(e) { diff --git a/js/xpconnect/src/dictionary_helper_gen.conf b/js/xpconnect/src/dictionary_helper_gen.conf index 2558054c5e1b..e3fdfa1df19b 100644 --- a/js/xpconnect/src/dictionary_helper_gen.conf +++ b/js/xpconnect/src/dictionary_helper_gen.conf @@ -10,7 +10,6 @@ dictionaries = [ [ 'WheelEventInit', 'nsIDOMWheelEvent.idl' ], [ 'IDBObjectStoreParameters', 'nsIIDBDatabase.idl' ], [ 'IDBIndexParameters', 'nsIIDBObjectStore.idl' ], - [ 'BlobPropertyBag', 'nsIDOMFile.idl' ], [ 'MutationObserverInit', 'nsIDOMMutationObserver.idl' ], [ 'WifiConnectionInfoEventInit', 'nsIWifiEventInits.idl' ], [ 'WifiStatusChangeEventInit', 'nsIWifiEventInits.idl' ], diff --git a/js/xpconnect/src/dictionary_helper_gen.py b/js/xpconnect/src/dictionary_helper_gen.py index 7097e7409152..5c4db7ce294f 100644 --- a/js/xpconnect/src/dictionary_helper_gen.py +++ b/js/xpconnect/src/dictionary_helper_gen.py @@ -412,6 +412,7 @@ def write_cpp(iface, fd): fd.write("nsresult\n%s::Init(JSContext* aCx, const jsval* aVal)\n" % iface.name) fd.write("{\n" + " MOZ_ASSERT(NS_IsMainThread());\n" " if (!aCx || !aVal) {\n" " return NS_OK;\n" " }\n" @@ -419,11 +420,8 @@ def write_cpp(iface, fd): " return aVal->isNullOrUndefined() ? NS_OK : NS_ERROR_TYPE_ERR;\n" " }\n\n" " JSObject* obj = &aVal->toObject();\n" - " Maybe pusher;\n" - " if (NS_IsMainThread()) {\n" - " pusher.construct();\n" - " NS_ENSURE_STATE(pusher.ref().Push(aCx, false));\n" - " }\n" + " nsCxPusher pusher;\n" + " NS_ENSURE_STATE(pusher.Push(aCx, false));\n" " JSAutoRequest ar(aCx);\n" " JSAutoCompartment ac(aCx, obj);\n") From 7683743c7546419a843c4b9c4c9eb1addf7cfe94 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Mon, 10 Sep 2012 09:57:17 -0400 Subject: [PATCH 08/78] Bug 789741 - Fix a crash in nsMediaSniffer::GetMIMETypeFromContent. r=bz --- toolkit/components/mediasniffer/nsMediaSniffer.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/toolkit/components/mediasniffer/nsMediaSniffer.cpp b/toolkit/components/mediasniffer/nsMediaSniffer.cpp index c632b2268138..5e1e09a6cfaf 100644 --- a/toolkit/components/mediasniffer/nsMediaSniffer.cpp +++ b/toolkit/components/mediasniffer/nsMediaSniffer.cpp @@ -75,6 +75,9 @@ nsMediaSniffer::GetMIMETypeFromContent(nsIRequest* aRequest, // For media, we want to sniff only if the Content-Type is unknown, or if it // is application/octet-stream. nsCOMPtr channel = do_QueryInterface(aRequest); + if (!channel) { + return NS_ERROR_NOT_AVAILABLE; + } nsAutoCString contentType; nsresult rv = channel->GetContentType(contentType); NS_ENSURE_SUCCESS(rv, rv); From f4f949ab6d45565af01c840d90a9ff6d6a856863 Mon Sep 17 00:00:00 2001 From: Jeff Hammel Date: Mon, 10 Sep 2012 11:13:04 -0400 Subject: [PATCH 09/78] Bug 789328 - bump mozprocess and mozrunner (again) and release to pypi --- testing/mozbase/mozdevice/setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testing/mozbase/mozdevice/setup.py b/testing/mozbase/mozdevice/setup.py index cc698533662d..bf93d32d30c8 100644 --- a/testing/mozbase/mozdevice/setup.py +++ b/testing/mozbase/mozdevice/setup.py @@ -5,7 +5,7 @@ import os from setuptools import setup -PACKAGE_VERSION = '0.5' +PACKAGE_VERSION = '0.6' # take description from README here = os.path.dirname(os.path.abspath(__file__)) @@ -14,7 +14,7 @@ try: except (OSError, IOError): description = '' -deps = ['mozprocess == 0.5'] +deps = ['mozprocess == 0.7'] setup(name='mozdevice', version=PACKAGE_VERSION, From da52969f34e57c79aec78be19f0f83f3297a72aa Mon Sep 17 00:00:00 2001 From: Mihnea Dobrescu-Balaur Date: Mon, 10 Sep 2012 11:23:45 -0400 Subject: [PATCH 10/78] Bug 785797 - adapt test_info to the current implemented functionality --- testing/mozbase/mozdevice/sut_tests/test_info.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/testing/mozbase/mozdevice/sut_tests/test_info.py b/testing/mozbase/mozdevice/sut_tests/test_info.py index 02170d8c887c..fa8f8785eaea 100644 --- a/testing/mozbase/mozdevice/sut_tests/test_info.py +++ b/testing/mozbase/mozdevice/sut_tests/test_info.py @@ -13,13 +13,13 @@ class InfoTestCase(DeviceManagerTestCase): """ This tests the "info" command """ cmds = ('os', 'id', 'systime', 'uptime', 'screen', - 'memory', 'power', 'process') + 'memory', 'power') for c in cmds: data = self.dm.getInfo(c) print c + str(data) print " ==== Now we call them all ====" - data = self.dm.getInfo('all') - print str(data) + #data = self.dm.getInfo('all') + #print str(data) # No real good way to verify this. If it doesn't throw, we're ok. From de0a808cbe84bbae35b5079bc0363032c6db2883 Mon Sep 17 00:00:00 2001 From: Mark Cote Date: Mon, 10 Sep 2012 11:33:11 -0400 Subject: [PATCH 11/78] Bug 789496 - DeviceManagerSUT: always consume prompt after receiving agent warning. r=wlach --- .../mozdevice/mozdevice/devicemanagerSUT.py | 15 ++++++--- testing/mozbase/mozdevice/tests/sut.py | 32 +++++++++++++++---- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/testing/mozbase/mozdevice/mozdevice/devicemanagerSUT.py b/testing/mozbase/mozdevice/mozdevice/devicemanagerSUT.py index ee13dfa896f8..fc6678ca6f1b 100644 --- a/testing/mozbase/mozdevice/mozdevice/devicemanagerSUT.py +++ b/testing/mozbase/mozdevice/mozdevice/devicemanagerSUT.py @@ -209,6 +209,7 @@ class DeviceManagerSUT(DeviceManager): found = False loopguard = 0 data = "" + commandFailed = False while (found == False and (loopguard < recvGuard)): temp = '' @@ -232,10 +233,12 @@ class DeviceManagerSUT(DeviceManager): # If something goes wrong in the agent it will send back a string that # starts with '##AGENT-WARNING##' - errorMatch = self.agentErrorRE.match(data) - if errorMatch: - raise AgentError("Agent Error processing command '%s'; err='%s'" % - (cmd['cmd'], errorMatch.group(1)), fatal=True) + if not commandFailed: + errorMatch = self.agentErrorRE.match(data) + if errorMatch: + # We still need to consume the prompt, so raise an error after + # draining the rest of the buffer. + commandFailed = True for line in data.splitlines(): if promptre.match(line): @@ -254,6 +257,10 @@ class DeviceManagerSUT(DeviceManager): if (temp == ''): loopguard += 1 + if commandFailed: + raise AgentError("Agent Error processing command '%s'; err='%s'" % + (cmd['cmd'], errorMatch.group(1)), fatal=True) + # Write any remaining data to outputfile outputfile.write(data) diff --git a/testing/mozbase/mozdevice/tests/sut.py b/testing/mozbase/mozdevice/tests/sut.py index 9056ee27ace0..0fca9d0aea0b 100644 --- a/testing/mozbase/mozdevice/tests/sut.py +++ b/testing/mozbase/mozdevice/tests/sut.py @@ -11,15 +11,13 @@ import unittest class BasicTest(unittest.TestCase): def _serve_thread(self): - need_connection = True + conn, addr = self._sock.accept() + conn.send("$>\x00") while self.commands: (command, response) = self.commands.pop(0) - if need_connection: - conn, addr = self._sock.accept() - need_connection = False - conn.send("$>\x00") data = conn.recv(1024).strip() self.assertEqual(data, command) + # send response and prompt separately to test for bug 789496 conn.send("%s\n" % response) conn.send("$>\x00") @@ -30,16 +28,36 @@ class BasicTest(unittest.TestCase): return thread def test_init(self): + """Tests DeviceManager initialization.""" self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._sock.bind(("127.0.0.1", 0)) self._sock.listen(1) thread = self._serve([("testroot", "/mnt/sdcard"), ("cd /mnt/sdcard/tests", ""), - ("cwd", "/mnt/sdcard/tests")]) + ("cwd", "/mnt/sdcard/tests"), + ("ver", "SUTAgentAndroid Version XX")]) + + port = self._sock.getsockname()[1] + mozdevice.DroidSUT.debug = 4 + d = mozdevice.DroidSUT("127.0.0.1", port=port) + thread.join() + + def test_err(self): + """Tests error handling during initialization.""" + self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self._sock.bind(("127.0.0.1", 0)) + self._sock.listen(1) + + thread = self._serve([("testroot", "/mnt/sdcard"), + ("cd /mnt/sdcard/tests", "##AGENT-WARNING## no such file or directory"), + ("cd /mnt/sdcard/tests", "##AGENT-WARNING## no such file or directory"), + ("mkdr /mnt/sdcard/tests", "/mnt/sdcard/tests successfully created"), + ("ver", "SUTAgentAndroid Version XX")]) port = self._sock.getsockname()[1] - d = mozdevice.DroidSUT("127.0.0.1", port=port) + mozdevice.DroidSUT.debug = 4 + dm = mozdevice.DroidSUT("127.0.0.1", port=port) thread.join() if __name__ == '__main__': From f0c3fcd491ec633dde58b3f50c3b8dbd7e3565be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Mon, 10 Sep 2012 17:43:52 +0200 Subject: [PATCH 12/78] Bug 789007 - Unwanted border between pinned tabs and navigation toolbar when tabstrip overflows. r=ttaubert --- browser/themes/gnomestripe/browser.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/themes/gnomestripe/browser.css b/browser/themes/gnomestripe/browser.css index 7e6a637266f5..d3696345bf4f 100644 --- a/browser/themes/gnomestripe/browser.css +++ b/browser/themes/gnomestripe/browser.css @@ -1587,7 +1587,7 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action- -moz-radial-gradient(center 3px, circle cover, rgba(233,242,252,1) 3%, rgba(172,206,255,.75) 40%, rgba(87,151,201,.5) 80%, rgba(87,151,201,0)); } -#tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-tab[pinned] { +#tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-tab > .tab-stack > .tab-content[pinned] { min-height: 18px; /* corresponds to the max. height of non-textual tab contents, i.e. the tab close button */ } From da63f78ed61c4d84fea4b46833f31905f420cd0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Mon, 10 Sep 2012 17:44:20 +0200 Subject: [PATCH 13/78] Bug 789833 - Don't give pinned tabs a min-height on Windows. r=ttaubert --- browser/themes/winstripe/browser.css | 4 ---- 1 file changed, 4 deletions(-) diff --git a/browser/themes/winstripe/browser.css b/browser/themes/winstripe/browser.css index 5224a7f6bf03..ca2dc688479d 100644 --- a/browser/themes/winstripe/browser.css +++ b/browser/themes/winstripe/browser.css @@ -1923,10 +1923,6 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action- list-style-image: url("chrome://browser/skin/tabbrowser/loading.png"); } -#tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-tab[pinned] { - min-height: 16px; /* corresponds to the max. height of non-textual tab contents, i.e. the favicon */ -} - .tab-throbber[pinned], .tab-icon-image[pinned] { -moz-margin-start: 5px; From 80a1c43a2c0f961a4f6dedce0e1fc98d0ca91029 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 5 Sep 2012 16:52:54 -0700 Subject: [PATCH 14/78] Bug 788884 - Fix compiler warning and potential integer underflow [r=bsmedberg] --- b2g/app/nsBrowserApp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/app/nsBrowserApp.cpp b/b2g/app/nsBrowserApp.cpp index 1758e379b046..a0bb7bf1c0fa 100644 --- a/b2g/app/nsBrowserApp.cpp +++ b/b2g/app/nsBrowserApp.cpp @@ -164,7 +164,7 @@ int main(int argc, char* argv[]) } char *lastSlash = strrchr(exePath, XPCOM_FILE_PATH_SEPARATOR[0]); - if (!lastSlash || (lastSlash - exePath > MAXPATHLEN - sizeof(XPCOM_DLL) - 1)) + if (!lastSlash || ((lastSlash - exePath) + sizeof(XPCOM_DLL) + 1 > MAXPATHLEN)) return 255; strcpy(++lastSlash, XPCOM_DLL); From f01720d02abf505c4d2d8cb1edfbdc10c8b60b1c Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Mon, 10 Sep 2012 18:24:25 +0100 Subject: [PATCH 15/78] Bug 768268 - Never show duplicate URLs in awesomebar results (r=margaret) --- .../android/base/db/BrowserProvider.java.in | 91 ++++++++++++++++++- .../base/tests/testBrowserProvider.java.in | 78 ++++++++++++++-- 2 files changed, 156 insertions(+), 13 deletions(-) diff --git a/mobile/android/base/db/BrowserProvider.java.in b/mobile/android/base/db/BrowserProvider.java.in index 8bdf8e976c0a..175e7474230a 100644 --- a/mobile/android/base/db/BrowserProvider.java.in +++ b/mobile/android/base/db/BrowserProvider.java.in @@ -67,7 +67,7 @@ public class BrowserProvider extends ContentProvider { static final String DATABASE_NAME = "browser.db"; - static final int DATABASE_VERSION = 10; + static final int DATABASE_VERSION = 11; // Maximum age of deleted records to be cleaned up (20 days in ms) static final long MAX_AGE_OF_DELETED_RECORDS = 86400000 * 20; @@ -209,7 +209,7 @@ public class BrowserProvider extends ContentProvider { map.put(Combined._ID, Combined._ID); map.put(Combined.BOOKMARK_ID, Combined.BOOKMARK_ID); map.put(Combined.HISTORY_ID, Combined.HISTORY_ID); - map.put(Combined.DISPLAY, Combined.DISPLAY); + map.put(Combined.DISPLAY, "MAX(" + Combined.DISPLAY + ") AS " + Combined.DISPLAY); map.put(Combined.URL, Combined.URL); map.put(Combined.TITLE, Combined.TITLE); map.put(Combined.VISITS, Combined.VISITS); @@ -315,6 +315,8 @@ public class BrowserProvider extends ContentProvider { db.execSQL("CREATE INDEX bookmarks_url_index ON " + TABLE_BOOKMARKS + "(" + Bookmarks.URL + ")"); + db.execSQL("CREATE INDEX bookmarks_type_deleted_index ON " + TABLE_BOOKMARKS + "(" + + Bookmarks.TYPE + ", " + Bookmarks.IS_DELETED + ")"); db.execSQL("CREATE UNIQUE INDEX bookmarks_guid_index ON " + TABLE_BOOKMARKS + "(" + Bookmarks.GUID + ")"); db.execSQL("CREATE INDEX bookmarks_modified_index ON " + TABLE_BOOKMARKS + "(" @@ -553,6 +555,68 @@ public class BrowserProvider extends ContentProvider { " ON " + Combined.URL + " = " + qualifyColumn(TABLE_IMAGES, Images.URL)); } + private void createCombinedWithImagesViewOn11(SQLiteDatabase db) { + debug("Creating " + VIEW_COMBINED_WITH_IMAGES + " view"); + + db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_COMBINED_WITH_IMAGES + " AS" + + " SELECT " + Combined.BOOKMARK_ID + ", " + + Combined.HISTORY_ID + ", " + + // We need to return an _id column because CursorAdapter requires it for its + // default implementation for the getItemId() method. However, since + // we're not using this feature in the parts of the UI using this view, + // we can just use 0 for all rows. + "0 AS " + Combined._ID + ", " + + Combined.URL + ", " + + Combined.TITLE + ", " + + Combined.VISITS + ", " + + Combined.DISPLAY + ", " + + Combined.DATE_LAST_VISITED + ", " + + qualifyColumn(TABLE_IMAGES, Images.FAVICON) + " AS " + Combined.FAVICON + ", " + + qualifyColumn(TABLE_IMAGES, Images.THUMBNAIL) + " AS " + Combined.THUMBNAIL + + " FROM (" + + // Bookmarks without history. + " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " + + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " + + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " + + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + + Bookmarks.FIXED_READING_LIST_ID + " THEN " + Combined.DISPLAY_READER + " ELSE " + + Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " + + "-1 AS " + Combined.HISTORY_ID + ", " + + "-1 AS " + Combined.VISITS + ", " + + "-1 AS " + Combined.DATE_LAST_VISITED + + " FROM " + TABLE_BOOKMARKS + + " WHERE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + " AND " + + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0 AND " + + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + + " NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY + ")" + + " UNION ALL" + + // History with and without bookmark. + " SELECT " + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN " + + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " ELSE NULL END AS " + Combined.BOOKMARK_ID + ", " + + qualifyColumn(TABLE_HISTORY, History.URL) + " AS " + Combined.URL + ", " + + // Prioritze bookmark titles over history titles, since the user may have + // customized the title for a bookmark. + "COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " + + qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " + + // Only use DISPLAY_READER if the matching bookmark entry inside reading + // list folder is not marked as deleted. + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN CASE " + + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + Bookmarks.FIXED_READING_LIST_ID + + " THEN " + Combined.DISPLAY_READER + " ELSE " + Combined.DISPLAY_NORMAL + " END ELSE " + + Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " + + qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " + + qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " + + qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED + + " FROM " + TABLE_HISTORY + " LEFT OUTER JOIN " + TABLE_BOOKMARKS + + " ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " + qualifyColumn(TABLE_HISTORY, History.URL) + + " WHERE " + qualifyColumn(TABLE_HISTORY, History.URL) + " IS NOT NULL AND " + + qualifyColumn(TABLE_HISTORY, History.IS_DELETED) + " = 0 AND (" + + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " IS NULL OR " + + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + ") " + + ") LEFT OUTER JOIN " + TABLE_IMAGES + + " ON " + Combined.URL + " = " + qualifyColumn(TABLE_IMAGES, Images.URL)); + } + @Override public void onCreate(SQLiteDatabase db) { debug("Creating browser.db: " + db.getPath()); @@ -563,7 +627,7 @@ public class BrowserProvider extends ContentProvider { createBookmarksWithImagesView(db); createHistoryWithImagesView(db); - createCombinedWithImagesViewOn10(db); + createCombinedWithImagesViewOn11(db); createOrUpdateSpecialFolder(db, Bookmarks.PLACES_FOLDER_GUID, R.string.bookmarks_folder_places, 0); @@ -990,6 +1054,16 @@ public class BrowserProvider extends ContentProvider { createCombinedWithImagesViewOn10(db); } + private void upgradeDatabaseFrom10to11(SQLiteDatabase db) { + debug("Dropping view: " + VIEW_COMBINED_WITH_IMAGES); + db.execSQL("DROP VIEW IF EXISTS " + VIEW_COMBINED_WITH_IMAGES); + + db.execSQL("CREATE INDEX bookmarks_type_deleted_index ON " + TABLE_BOOKMARKS + "(" + + Bookmarks.TYPE + ", " + Bookmarks.IS_DELETED + ")"); + + createCombinedWithImagesViewOn11(db); + } + @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { debug("Upgrading browser.db: " + db.getPath() + " from " + @@ -1036,6 +1110,10 @@ public class BrowserProvider extends ContentProvider { case 10: upgradeDatabaseFrom9to10(db); break; + + case 11: + upgradeDatabaseFrom10to11(db); + break; } } @@ -1668,6 +1746,7 @@ public class BrowserProvider extends ContentProvider { SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); String limit = uri.getQueryParameter(BrowserContract.PARAM_LIMIT); + String groupBy = null; switch (match) { case BOOKMARKS_FOLDER_ID: @@ -1763,6 +1842,10 @@ public class BrowserProvider extends ContentProvider { if (TextUtils.isEmpty(sortOrder)) sortOrder = DEFAULT_HISTORY_SORT_ORDER; + // This will avoid duplicate entries in the awesomebar + // results when a history entry has multiple bookmarks. + groupBy = Combined.URL; + qb.setProjectionMap(COMBINED_PROJECTION_MAP); qb.setTables(VIEW_COMBINED_WITH_IMAGES); @@ -1783,7 +1866,7 @@ public class BrowserProvider extends ContentProvider { } trace("Running built query."); - Cursor cursor = qb.query(db, projection, selection, selectionArgs, null, + Cursor cursor = qb.query(db, projection, selection, selectionArgs, groupBy, null, sortOrder, limit); cursor.setNotificationUri(getContext().getContentResolver(), BrowserContract.AUTHORITY_URI); diff --git a/mobile/android/base/tests/testBrowserProvider.java.in b/mobile/android/base/tests/testBrowserProvider.java.in index 27e12346fa0a..6442fd7b4608 100644 --- a/mobile/android/base/tests/testBrowserProvider.java.in +++ b/mobile/android/base/tests/testBrowserProvider.java.in @@ -322,6 +322,7 @@ public class testBrowserProvider extends ContentProviderTest { mTests.add(new TestCombinedView()); mTests.add(new TestCombinedViewDisplay()); mTests.add(new TestCombinedViewWithDeletedBookmark()); + mTests.add(new TestCombinedViewWithDeletedReadingListItem()); } public void testBrowserProvider() throws Exception { @@ -1319,10 +1320,11 @@ public class testBrowserProvider extends ContentProviderTest { final String TITLE_2 = "Test Page 2"; final String TITLE_3_HISTORY = "Test Page 3 (History Entry)"; final String TITLE_3_BOOKMARK = "Test Page 3 (Bookmark Entry)"; + final String TITLE_3_BOOKMARK2 = "Test Page 3 (Bookmark Entry 2)"; - final String URL_1 = "http://example.com"; - final String URL_2 = "http://example.org"; - final String URL_3 = "http://examples2.com"; + final String URL_1 = "http://example1.com"; + final String URL_2 = "http://example2.com"; + final String URL_3 = "http://example3.com"; final String HISTORY_FAVICON = "HISTORY_FAVICON"; final String HISTORY_THUMBNAIL = "HISTORY_THUMBNAIL"; @@ -1352,12 +1354,19 @@ public class testBrowserProvider extends ContentProviderTest { combinedHistory.put(mHistoryThumbnailCol, HISTORY_THUMBNAIL.getBytes("UTF8")); long combinedHistoryId = ContentUris.parseId(mProvider.insert(mHistoryUri, combinedHistory)); + ContentValues combinedBookmark = createBookmark(TITLE_3_BOOKMARK, URL_3, mMobileFolderId, mBookmarksTypeBookmark, 0, "tags", "description", "keyword"); combinedBookmark.put(mBookmarksFaviconCol, BOOKMARK_FAVICON.getBytes("UTF8")); combinedBookmark.put(mBookmarksThumbnailCol, BOOKMARK_THUMBNAIL.getBytes("UTF8")); long combinedBookmarkId = ContentUris.parseId(mProvider.insert(mBookmarksUri, combinedBookmark)); + ContentValues combinedBookmark2 = createBookmark(TITLE_3_BOOKMARK2, URL_3, mMobileFolderId, + mBookmarksTypeBookmark, 0, "tags", "description", "keyword"); + combinedBookmark2.put(mBookmarksFaviconCol, BOOKMARK_FAVICON.getBytes("UTF8")); + combinedBookmark2.put(mBookmarksThumbnailCol, BOOKMARK_THUMBNAIL.getBytes("UTF8")); + long combinedBookmarkId2 = ContentUris.parseId(mProvider.insert(mBookmarksUri, combinedBookmark2)); + // Create a bookmark folder to make sure it _doesn't_ show up in the results ContentValues folderBookmark = createBookmark("", "", mMobileFolderId, mBookmarksTypeFolder, 0, "tags", "description", "keyword"); @@ -1415,12 +1424,16 @@ public class testBrowserProvider extends ContentProviderTest { mAsserter.is(c.moveToNext(), true, "Found third combined entry"); mAsserter.is(new Long(c.getLong(c.getColumnIndex(mCombinedIdCol))), new Long(0), "Combined _id column should always be 0"); - mAsserter.is(new Long(c.getLong(c.getColumnIndex(mCombinedBookmarkIdCol))), new Long(combinedBookmarkId), + // The bookmark data (bookmark_id and title) associated with the combined entry is non-deterministic, + // it might end up with data coming from any of the matching bookmark entries. + mAsserter.is(c.getLong(c.getColumnIndex(mCombinedBookmarkIdCol)) == combinedBookmarkId || + c.getLong(c.getColumnIndex(mCombinedBookmarkIdCol)) == combinedBookmarkId2, true, "Combined entry has correct bookmark id"); + mAsserter.is(c.getString(c.getColumnIndex(mCombinedTitleCol)).equals(TITLE_3_BOOKMARK) || + c.getString(c.getColumnIndex(mCombinedTitleCol)).equals(TITLE_3_BOOKMARK2), true, + "Combined entry has title corresponding to bookmark entry"); mAsserter.is(new Long(c.getLong(c.getColumnIndex(mCombinedHistoryIdCol))), new Long(combinedHistoryId), "Combined entry has correct history id"); - mAsserter.is(c.getString(c.getColumnIndex(mCombinedTitleCol)), TITLE_3_BOOKMARK, - "Combined entry has title corresponding to bookmark entry"); mAsserter.is(c.getString(c.getColumnIndex(mCombinedUrlCol)), URL_3, "Combined entry has correct url"); mAsserter.is(c.getInt(c.getColumnIndex(mCombinedVisitsCol)), VISITS, @@ -1468,12 +1481,16 @@ public class testBrowserProvider extends ContentProviderTest { mBookmarksTypeBookmark, 0, "tags", "description", "keyword"); mProvider.insert(mBookmarksUri, combinedBookmark); - // Create a reading list entry + // Create a reading list entries int readingListId = getIntColumn("Bookmarks", "FIXED_READING_LIST_ID"); - ContentValues readingListItem = createBookmark(TITLE_4, URL_4, readingListId, + ContentValues readingListItem = createBookmark(TITLE_3_BOOKMARK, URL_3, readingListId, mBookmarksTypeBookmark, 0, "tags", "description", "keyword"); long readingListItemId = ContentUris.parseId(mProvider.insert(mBookmarksUri, readingListItem)); + ContentValues readingListItem2 = createBookmark(TITLE_4, URL_4, readingListId, + mBookmarksTypeBookmark, 0, "tags", "description", "keyword"); + long readingListItemId2 = ContentUris.parseId(mProvider.insert(mBookmarksUri, readingListItem2)); + Cursor c = mProvider.query(mCombinedUri, null, "", null, null); mAsserter.is(c.getCount(), 4, "4 combined entries found"); @@ -1481,7 +1498,7 @@ public class testBrowserProvider extends ContentProviderTest { long id = c.getLong(c.getColumnIndex(mCombinedBookmarkIdCol)); int display = c.getInt(c.getColumnIndex(mCombinedDisplayCol)); - int expectedDisplay = (id == readingListItemId ? mCombinedDisplayReader : mCombinedDisplayNormal); + int expectedDisplay = (id == readingListItemId || id == readingListItemId2 ? mCombinedDisplayReader : mCombinedDisplayNormal); mAsserter.is(new Integer(display), new Integer(expectedDisplay), "Combined display column should always be DISPLAY_READER for the reading list item"); @@ -1526,4 +1543,47 @@ public class testBrowserProvider extends ContentProviderTest { "Bookmark id should not be set to removed bookmark id"); } } + + class TestCombinedViewWithDeletedReadingListItem extends Test { + public void test() throws Exception { + final String TITLE = "Test Page 1"; + final String URL = "http://example.com"; + final int VISITS = 10; + final long LAST_VISITED = System.currentTimeMillis(); + + // Create a combined history entry + ContentValues combinedHistory = createHistoryEntry(TITLE, URL, VISITS, LAST_VISITED); + mProvider.insert(mHistoryUri, combinedHistory); + + // Create a combined bookmark entry + int readingListId = getIntColumn("Bookmarks", "FIXED_READING_LIST_ID"); + ContentValues combinedReadingListItem = createBookmark(TITLE, URL, readingListId, + mBookmarksTypeBookmark, 0, "tags", "description", "keyword"); + long combinedReadingListItemId = ContentUris.parseId(mProvider.insert(mBookmarksUri, combinedReadingListItem)); + + Cursor c = mProvider.query(mCombinedUri, null, "", null, null); + mAsserter.is(c.getCount(), 1, "1 combined entry found"); + + mAsserter.is(c.moveToFirst(), true, "Found combined entry with bookmark id"); + mAsserter.is(new Long(c.getLong(c.getColumnIndex(mCombinedBookmarkIdCol))), new Long(combinedReadingListItemId), + "Bookmark id should be set correctly on combined entry"); + mAsserter.is(new Long(c.getLong(c.getColumnIndex(mCombinedDisplayCol))), new Long(mCombinedDisplayReader), + "Combined entry should have reader display type"); + + int deleted = mProvider.delete(mBookmarksUri, + mBookmarksIdCol + " = ?", + new String[] { String.valueOf(combinedReadingListItemId) }); + + mAsserter.is((deleted == 1), true, "Inserted combined reading list item was deleted"); + + c = mProvider.query(mCombinedUri, null, "", null, null); + mAsserter.is(c.getCount(), 1, "1 combined entry found"); + + mAsserter.is(c.moveToFirst(), true, "Found combined entry without bookmark id"); + mAsserter.is(new Long(c.getLong(c.getColumnIndex(mCombinedBookmarkIdCol))), new Long(0), + "Bookmark id should not be set to removed bookmark id"); + mAsserter.is(new Long(c.getLong(c.getColumnIndex(mCombinedDisplayCol))), new Long(mCombinedDisplayNormal), + "Combined entry should have reader display type"); + } + } } From eed355c9b5c806abd4a1224312196cc7f5e23419 Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Mon, 10 Sep 2012 18:24:26 +0100 Subject: [PATCH 16/78] Bug 768268 - Always open original page in awesomebar search (r=mfinkle) --- mobile/android/base/awesomebar/AllPagesTab.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/mobile/android/base/awesomebar/AllPagesTab.java b/mobile/android/base/awesomebar/AllPagesTab.java index 3871e3e6c44f..e0021f8a4b10 100644 --- a/mobile/android/base/awesomebar/AllPagesTab.java +++ b/mobile/android/base/awesomebar/AllPagesTab.java @@ -184,11 +184,6 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener { return; String url = mCursor.getString(mCursor.getColumnIndexOrThrow(URLColumns.URL)); - - int display = mCursor.getInt(mCursor.getColumnIndexOrThrow(Combined.DISPLAY)); - if (display == Combined.DISPLAY_READER) { - url = getReaderForUrl(url); - } listener.onUrlOpen(url); } From c6e6862f16d6c4e8bf05078bc05adee6b16e9498 Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Mon, 10 Sep 2012 18:24:26 +0100 Subject: [PATCH 17/78] Bug 768268 - Move getReaderMode() method to AwesomeBar (r=mfinkle) --- mobile/android/base/AwesomeBar.java | 7 +++++++ mobile/android/base/AwesomeBarTabs.java | 6 ------ mobile/android/base/awesomebar/AwesomeBarTab.java | 7 ------- mobile/android/base/awesomebar/BookmarksTab.java | 2 +- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/mobile/android/base/AwesomeBar.java b/mobile/android/base/AwesomeBar.java index 3cc5ba9107f4..c3a3323efb23 100644 --- a/mobile/android/base/AwesomeBar.java +++ b/mobile/android/base/AwesomeBar.java @@ -17,6 +17,7 @@ import android.content.Intent; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.net.Uri; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; @@ -651,6 +652,12 @@ public class AwesomeBar extends GeckoActivity { return true; } + public static String getReaderForUrl(String url) { + // FIXME: still need to define the final way to open items from + // reading list. For now, we're using an about:reader page. + return "about:reader?url=" + Uri.encode(url) + "&readingList=1"; + } + private static boolean hasCompositionString(Editable content) { Object[] spans = content.getSpans(0, content.length(), Object.class); if (spans != null) { diff --git a/mobile/android/base/AwesomeBarTabs.java b/mobile/android/base/AwesomeBarTabs.java index 59c61d2affa4..b09d90b562a8 100644 --- a/mobile/android/base/AwesomeBarTabs.java +++ b/mobile/android/base/AwesomeBarTabs.java @@ -174,12 +174,6 @@ public class AwesomeBarTabs extends TabHost { return imm.hideSoftInputFromWindow(view.getWindowToken(), 0); } - private String getReaderForUrl(String url) { - // FIXME: still need to define the final way to open items from - // reading list. For now, we're using an about:reader page. - return "about:reader?url=" + Uri.encode(url); - } - public void setOnUrlOpenListener(OnUrlOpenListener listener) { mUrlOpenListener = listener; for (AwesomeBarTab tab : mTabs) { diff --git a/mobile/android/base/awesomebar/AwesomeBarTab.java b/mobile/android/base/awesomebar/AwesomeBarTab.java index f38c54d4f6b9..75d0492a1857 100644 --- a/mobile/android/base/awesomebar/AwesomeBarTab.java +++ b/mobile/android/base/awesomebar/AwesomeBarTab.java @@ -14,7 +14,6 @@ import android.content.res.Resources; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.net.Uri; import android.text.TextUtils; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; @@ -89,12 +88,6 @@ abstract public class AwesomeBarTab { return mResources; } - protected String getReaderForUrl(String url) { - // FIXME: still need to define the final way to open items from - // reading list. For now, we're using an about:reader page. - return "about:reader?url=" + Uri.encode(url) + "&readingList=1"; - } - protected void updateFavicon(ImageView faviconView, Cursor cursor) { byte[] b = cursor.getBlob(cursor.getColumnIndexOrThrow(URLColumns.FAVICON)); if (b == null) { diff --git a/mobile/android/base/awesomebar/BookmarksTab.java b/mobile/android/base/awesomebar/BookmarksTab.java index 9813ce40b93e..cd74973ccde8 100644 --- a/mobile/android/base/awesomebar/BookmarksTab.java +++ b/mobile/android/base/awesomebar/BookmarksTab.java @@ -203,7 +203,7 @@ public class BookmarksTab extends AwesomeBarTab { String url = cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.URL)); long parentId = cursor.getLong(cursor.getColumnIndexOrThrow(Bookmarks.PARENT)); if (parentId == Bookmarks.FIXED_READING_LIST_ID) { - url = getReaderForUrl(url); + url = AwesomeBar.getReaderForUrl(url); } listener.onUrlOpen(url); } From 19f023059597de908991872dca86b50cb4d95037 Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Mon, 10 Sep 2012 18:24:38 +0100 Subject: [PATCH 18/78] Bug 768268 - Add "Open in Reader" menu item to be shown on reading list items (r=mfinkle) --- mobile/android/base/AwesomeBar.java | 16 ++++++++++++++++ mobile/android/base/awesomebar/AllPagesTab.java | 4 +++- mobile/android/base/awesomebar/BookmarksTab.java | 1 + mobile/android/base/awesomebar/HistoryTab.java | 1 + .../base/locales/en-US/android_strings.dtd | 3 ++- .../layout-land-v14/browser_toolbar.xml.in | 2 +- .../layout-land-v14/browser_toolbar_menu.xml.in | 2 +- .../layout-large-v11/browser_toolbar_menu.xml.in | 2 +- .../browser_toolbar_menu.xml.in | 2 +- .../base/resources/layout/browser_toolbar.xml.in | 2 +- .../resources/layout/browser_toolbar_menu.xml.in | 2 +- .../resources/menu/awesomebar_contextmenu.xml | 3 +++ mobile/android/base/strings.xml.in | 3 ++- 13 files changed, 34 insertions(+), 9 deletions(-) diff --git a/mobile/android/base/AwesomeBar.java b/mobile/android/base/AwesomeBar.java index c3a3323efb23..6132effabeba 100644 --- a/mobile/android/base/AwesomeBar.java +++ b/mobile/android/base/AwesomeBar.java @@ -6,6 +6,7 @@ package org.mozilla.gecko; import org.mozilla.gecko.db.BrowserDB; +import org.mozilla.gecko.db.BrowserContract.Combined; import org.mozilla.gecko.util.GeckoAsyncTask; import android.app.Activity; @@ -476,13 +477,19 @@ public class AwesomeBar extends GeckoActivity { public byte[] favicon; public String title; public String keyword; + public int display; public ContextMenuSubject(int id, String url, byte[] favicon, String title, String keyword) { + this(id, url, favicon, title, keyword, Combined.DISPLAY_NORMAL); + } + + public ContextMenuSubject(int id, String url, byte[] favicon, String title, String keyword, int display) { this.id = id; this.url = url; this.favicon = favicon; this.title = title; this.keyword = keyword; + this.display = display; } }; @@ -516,6 +523,15 @@ public class AwesomeBar extends GeckoActivity { Toast.makeText(this, R.string.new_tab_opened, Toast.LENGTH_SHORT).show(); break; } + case R.id.open_in_reader: { + if (url == null) { + Log.e(LOGTAG, "Can't open in reader mode because URL is null"); + break; + } + + openUrlAndFinish(getReaderForUrl(url)); + break; + } case R.id.edit_bookmark: { AlertDialog.Builder editPrompt = new AlertDialog.Builder(this); View editView = getLayoutInflater().inflate(R.layout.bookmark_edit, null); diff --git a/mobile/android/base/awesomebar/AllPagesTab.java b/mobile/android/base/awesomebar/AllPagesTab.java index e0021f8a4b10..84755676653d 100644 --- a/mobile/android/base/awesomebar/AllPagesTab.java +++ b/mobile/android/base/awesomebar/AllPagesTab.java @@ -200,7 +200,8 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener { mCursor.getString(mCursor.getColumnIndexOrThrow(URLColumns.URL)), mCursor.getBlob(mCursor.getColumnIndexOrThrow(URLColumns.FAVICON)), mCursor.getString(mCursor.getColumnIndexOrThrow(URLColumns.TITLE)), - keyword); + keyword, + mCursor.getInt(mCursor.getColumnIndexOrThrow(Combined.DISPLAY))); } } @@ -550,6 +551,7 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener { inflater.inflate(R.menu.awesomebar_contextmenu, menu); menu.findItem(R.id.remove_bookmark).setVisible(false); menu.findItem(R.id.edit_bookmark).setVisible(false); + menu.findItem(R.id.open_in_reader).setVisible(subject.display == Combined.DISPLAY_READER); // Hide "Remove" item if there isn't a valid history ID if (subject.id < 0) diff --git a/mobile/android/base/awesomebar/BookmarksTab.java b/mobile/android/base/awesomebar/BookmarksTab.java index cd74973ccde8..b7f2fdf4996e 100644 --- a/mobile/android/base/awesomebar/BookmarksTab.java +++ b/mobile/android/base/awesomebar/BookmarksTab.java @@ -446,6 +446,7 @@ public class BookmarksTab extends AwesomeBarTab { inflater.inflate(R.menu.awesomebar_contextmenu, menu); menu.findItem(R.id.remove_history).setVisible(false); + menu.findItem(R.id.open_in_reader).setVisible(false); menu.setHeaderTitle(subject.title); return subject; diff --git a/mobile/android/base/awesomebar/HistoryTab.java b/mobile/android/base/awesomebar/HistoryTab.java index 908f2356b04f..f628ddadc432 100644 --- a/mobile/android/base/awesomebar/HistoryTab.java +++ b/mobile/android/base/awesomebar/HistoryTab.java @@ -436,6 +436,7 @@ public class HistoryTab extends AwesomeBarTab { menu.findItem(R.id.remove_bookmark).setVisible(false); menu.findItem(R.id.edit_bookmark).setVisible(false); + menu.findItem(R.id.open_in_reader).setVisible(false); // Hide "Remove" item if there isn't a valid history ID if (subject.id < 0) diff --git a/mobile/android/base/locales/en-US/android_strings.dtd b/mobile/android/base/locales/en-US/android_strings.dtd index c2a46cb4ee2f..e1515218025b 100644 --- a/mobile/android/base/locales/en-US/android_strings.dtd +++ b/mobile/android/base/locales/en-US/android_strings.dtd @@ -133,6 +133,7 @@ size. --> + @@ -160,7 +161,7 @@ size. --> - + diff --git a/mobile/android/base/resources/layout-land-v14/browser_toolbar.xml.in b/mobile/android/base/resources/layout-land-v14/browser_toolbar.xml.in index 29b789b6a1bc..6951d5fec9eb 100644 --- a/mobile/android/base/resources/layout-land-v14/browser_toolbar.xml.in +++ b/mobile/android/base/resources/layout-land-v14/browser_toolbar.xml.in @@ -107,7 +107,7 @@ + + diff --git a/mobile/android/base/strings.xml.in b/mobile/android/base/strings.xml.in index 54f038b0dd5a..b1af21db88a5 100644 --- a/mobile/android/base/strings.xml.in +++ b/mobile/android/base/strings.xml.in @@ -135,9 +135,10 @@ &reading_list_added; &reading_list_removed; &reading_list_failed; - &reader_mode; + &reader; &contextmenu_open_new_tab; + &contextmenu_open_in_reader; &contextmenu_remove_history; &contextmenu_remove_bookmark; &contextmenu_add_to_launcher; From 492ab13e09ef56f89b5767fe6fc0f72c53a04143 Mon Sep 17 00:00:00 2001 From: James Willcox Date: Mon, 10 Sep 2012 13:51:18 -0400 Subject: [PATCH 19/78] Bug 789791 - Use contents of update.locale file in update URL on Android r=mfinkle --- mobile/android/base/GeckoJarReader.java | 37 +++++++++++++++++++ mobile/android/base/UpdateService.java | 2 +- .../android/base/UpdateServiceHelper.java.in | 32 +++++++++++++--- 3 files changed, 65 insertions(+), 6 deletions(-) diff --git a/mobile/android/base/GeckoJarReader.java b/mobile/android/base/GeckoJarReader.java index 545e40ccbf5a..a160c66a3193 100644 --- a/mobile/android/base/GeckoJarReader.java +++ b/mobile/android/base/GeckoJarReader.java @@ -8,9 +8,11 @@ import android.content.res.Resources; import android.graphics.drawable.BitmapDrawable; import android.util.Log; +import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.net.URL; import java.util.EmptyStackException; import java.util.Stack; @@ -61,6 +63,41 @@ public final class GeckoJarReader { return bitmap; } + public static String getText(String url) { + Stack jarUrls = parseUrl(url); + + ZipFile zip = null; + BufferedReader reader = null; + String text = null; + try { + zip = getZipFile(jarUrls.pop()); + InputStream input = getStream(zip, jarUrls); + if (input != null) { + reader = new BufferedReader(new InputStreamReader(input)); + text = reader.readLine(); + } + } catch (IOException ex) { + Log.e(LOGTAG, "Exception ", ex); + } finally { + if (reader != null) { + try { + reader.close(); + } catch(IOException ex) { + Log.e(LOGTAG, "Error closing reader", ex); + } + } + if (zip != null) { + try { + zip.close(); + } catch(IOException ex) { + Log.e(LOGTAG, "Error closing zip", ex); + } + } + } + + return text; + } + private static ZipFile getZipFile(String url) throws IOException { URL fileUrl = new URL(url); File file = new File(fileUrl.getPath()); diff --git a/mobile/android/base/UpdateService.java b/mobile/android/base/UpdateService.java index 66d226b39890..c4033a7a5ba1 100644 --- a/mobile/android/base/UpdateService.java +++ b/mobile/android/base/UpdateService.java @@ -258,7 +258,7 @@ public class UpdateService extends IntentService { private UpdateInfo findUpdate(boolean force) { try { - URL url = UpdateServiceHelper.getUpdateUrl(force); + URL url = UpdateServiceHelper.getUpdateUrl(this, force); DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Document dom = builder.parse(url.openConnection().getInputStream()); diff --git a/mobile/android/base/UpdateServiceHelper.java.in b/mobile/android/base/UpdateServiceHelper.java.in index 01018126bef7..b213f2505c30 100644 --- a/mobile/android/base/UpdateServiceHelper.java.in +++ b/mobile/android/base/UpdateServiceHelper.java.in @@ -7,17 +7,20 @@ package org.mozilla.gecko.updater; +import org.mozilla.gecko.util.GeckoJarReader; + import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ApplicationInfo; + import android.os.Build; import android.util.Log; import java.net.URL; -import java.util.Locale; - public class UpdateServiceHelper { public static final String ACTION_REGISTER_FOR_UPDATES = "@ANDROID_PACKAGE_NAME@.REGISTER_FOR_UPDATES"; public static final String ACTION_UNREGISTER_FOR_UPDATES = "@ANDROID_PACKAGE_NAME@.UNREGISTER_FOR_UPDATES"; @@ -41,6 +44,7 @@ public class UpdateServiceHelper { private static final String LOGTAG = "UpdateServiceHelper"; private static final String BUILDID = "@MOZ_APP_BUILDID@"; + private static final String DEFAULT_UPDATE_LOCALE = "en-US"; #ifdef MOZ_PKG_SPECIAL private static final String UPDATE_URL = "https://aus2.mozilla.org/update/4/@MOZ_APP_BASENAME@/@MOZ_APP_VERSION@/%BUILDID%/Android_@MOZ_APP_ABI@-@MOZ_PKG_SPECIAL@/%LOCALE%/@MOZ_UPDATE_CHANNEL@/%OS_VERSION%/default/default/@MOZ_APP_VERSION@/update.xml"; @@ -48,9 +52,27 @@ public class UpdateServiceHelper { private static final String UPDATE_URL = "https://aus2.mozilla.org/update/4/@MOZ_APP_BASENAME@/@MOZ_APP_VERSION@/%BUILDID%/Android_@MOZ_APP_ABI@/%LOCALE%/@MOZ_UPDATE_CHANNEL@/%OS_VERSION%/default/default/@MOZ_APP_VERSION@/update.xml"; #endif - public static URL getUpdateUrl(boolean force) { - Locale locale = Locale.getDefault(); - String url = UPDATE_URL.replace("%LOCALE%", locale.getLanguage() + "-" + locale.getCountry()). + public static URL getUpdateUrl(Context context, boolean force) { + PackageManager pm = context.getPackageManager(); + + String locale = null; + try { + ApplicationInfo info = pm.getApplicationInfo("@ANDROID_PACKAGE_NAME@", 0); + String updateLocaleUrl = "jar:jar:file://" + info.sourceDir + "!/omni.ja!/update.locale"; + + locale = GeckoJarReader.getText(updateLocaleUrl); + + if (locale != null) + locale = locale.trim(); + else + locale = DEFAULT_UPDATE_LOCALE; + } catch (android.content.pm.PackageManager.NameNotFoundException e) { + // Shouldn't really be possible, but fallback to default locale + Log.i(LOGTAG, "Failed to read update locale file, falling back to " + DEFAULT_UPDATE_LOCALE); + locale = DEFAULT_UPDATE_LOCALE; + } + + String url = UPDATE_URL.replace("%LOCALE%", locale). replace("%OS_VERSION%", Build.VERSION.RELEASE). replace("%BUILDID%", force ? "0" : BUILDID); From 69aacaaaa40e198cee6bf9568c803b468904f5df Mon Sep 17 00:00:00 2001 From: James Willcox Date: Mon, 10 Sep 2012 13:51:35 -0400 Subject: [PATCH 20/78] Bug 789964 - Always use Android install wizard for updates r=mfinkle --- mobile/android/base/UpdateService.java | 85 +------------------ .../base/locales/en-US/android_strings.dtd | 8 -- mobile/android/base/strings.xml.in | 8 -- 3 files changed, 1 insertion(+), 100 deletions(-) diff --git a/mobile/android/base/UpdateService.java b/mobile/android/base/UpdateService.java index c4033a7a5ba1..878cdccb47e9 100644 --- a/mobile/android/base/UpdateService.java +++ b/mobile/android/base/UpdateService.java @@ -234,8 +234,7 @@ public class UpdateService extends IntentService { saveUpdateInfo(info); - // If we have root, we always apply the update immediately because it happens in the background - if (mApplyImmediately || checkRoot()) { + if (mApplyImmediately) { applyUpdate(pkg); } else { // Prompt to apply the update @@ -490,65 +489,6 @@ public class UpdateService extends IntentService { return; } - if (checkRoot()) - applyUpdateWithRoot(updateFile); - else - applyUpdateWithActivity(updateFile); - } - - private void applyUpdateWithRoot(File updateFile) { - mNotificationManager.cancel(NOTIFICATION_ID); - - Notification notification = new Notification(R.drawable.icon, getResources().getString(R.string.updater_installing_ticker), System.currentTimeMillis()); - notification.flags = Notification.FLAG_NO_CLEAR; - - Intent notificationIntent = new Intent("org.mozilla.gecko.ACTION_NOOP"); - notificationIntent.setClass(this, UpdateService.class); - - PendingIntent contentIntent = PendingIntent.getService(this, 0, notificationIntent, 0); - notification.flags = Notification.FLAG_NO_CLEAR; - - notification.setLatestEventInfo(this, getResources().getString(R.string.updater_installing_title), - getResources().getString(R.string.updater_installing_text), - contentIntent); - - mNotificationManager.notify(NOTIFICATION_ID, notification); - - int result = runAsRoot("pm install " + updateFile.getAbsolutePath()); - Log.i(LOGTAG, "install result = " + result); - - updateFile.delete(); - - int tickerText = result == 0 ? R.string.updater_installing_ticker_success : R.string.updater_installing_ticker_fail; - int contentText = result == 0 ? R.string.updater_installing_text_success : R.string.updater_installing_text_fail; - - mNotificationManager.cancel(NOTIFICATION_ID); - - notification = new Notification(R.drawable.icon, getResources().getString(tickerText), System.currentTimeMillis()); - notification.flags = Notification.FLAG_NO_CLEAR; - - notificationIntent = new Intent(Intent.ACTION_MAIN); - notificationIntent.setClassName(getPackageName(), getPackageName() + ".App"); - notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - - contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); - notification.flags = Notification.FLAG_NO_CLEAR; - - notification.setLatestEventInfo(this, getResources().getString(R.string.updater_installing_title), - getResources().getString(result == 0 ? R.string.updater_installing_text_success : R.string.updater_installing_text_fail), - contentIntent); - - mNotificationManager.notify(NOTIFICATION_ID, notification); - - - notification.setLatestEventInfo(this, getResources().getString(R.string.updater_installing_title), - getResources().getString(result == 0 ? R.string.updater_installing_text_success : R.string.updater_installing_text_fail), - contentIntent); - notification.flags = Notification.FLAG_AUTO_CANCEL; - mNotificationManager.notify(NOTIFICATION_ID, notification); - } - - private void applyUpdateWithActivity(File updateFile) { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.fromFile(updateFile), "application/vnd.android.package-archive"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); @@ -591,29 +531,6 @@ public class UpdateService extends IntentService { editor.commit(); } - private int runAsRoot(String command) { - Process p = null; - try { - p = Runtime.getRuntime().exec("su"); - OutputStream output = p.getOutputStream(); - output.write(command.getBytes()); - output.write(new String("; exit\n").getBytes()); - output.flush(); - p.waitFor(); - - return p.exitValue(); - } catch (Exception e) { - return -1; - } finally { - if (p != null) - p.destroy(); - } - } - - private boolean checkRoot() { - return runAsRoot("echo woooooo") == 0; - } - private class UpdateInfo { public URL url; public String buildID; diff --git a/mobile/android/base/locales/en-US/android_strings.dtd b/mobile/android/base/locales/en-US/android_strings.dtd index e1515218025b..002b3e92958d 100644 --- a/mobile/android/base/locales/en-US/android_strings.dtd +++ b/mobile/android/base/locales/en-US/android_strings.dtd @@ -241,11 +241,3 @@ just addresses the organization to follow, e.g. "This site is run by " --> - - - - - - - - diff --git a/mobile/android/base/strings.xml.in b/mobile/android/base/strings.xml.in index b1af21db88a5..986c0b317ad7 100644 --- a/mobile/android/base/strings.xml.in +++ b/mobile/android/base/strings.xml.in @@ -228,12 +228,4 @@ &updater_apply_ticker; &updater_apply_select; - &updater_installing_title; - &updater_installing_ticker; - &updater_installing_ticker_success; - &updater_installing_ticker_fail; - &updater_installing_text; - &updater_installing_text_success; - &updater_installing_text_fail; - From 61816d0a79a7d636b24e27ce15790a89fa84e51e Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Mon, 10 Sep 2012 14:11:19 -0400 Subject: [PATCH 21/78] Bug 579517 follow-up: Remove NSPR types that crept in --- content/base/src/WebSocket.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/content/base/src/WebSocket.h b/content/base/src/WebSocket.h index 5bf7e8dbae29..fb0788471dc9 100644 --- a/content/base/src/WebSocket.h +++ b/content/base/src/WebSocket.h @@ -188,7 +188,7 @@ protected: void Send(nsIInputStream* aMsgStream, const nsACString& aMsgString, - PRUint32 aMsgLength, + uint32_t aMsgLength, bool aIsBinary, ErrorResult& aRv); @@ -206,7 +206,7 @@ protected: nsresult PrintErrorOnConsole(const char* aBundleURI, const PRUnichar* aError, const PRUnichar** aFormatStrings, - PRUint32 aFormatStringsLen); + uint32_t aFormatStringsLen); nsresult DoOnMessageAvailable(const nsACString& aMsg, bool isBinary); @@ -226,7 +226,7 @@ protected: nsresult CreateAndDispatchMessageEvent(const nsACString& aData, bool isBinary); nsresult CreateAndDispatchCloseEvent(bool aWasClean, - PRUint16 aCode, + uint16_t aCode, const nsString& aReason); nsresult CreateResponseBlob(const nsACString& aData, JSContext* aCx, From 4f7f0fe23f700bd49db185ad4d51cdd6e9dccc65 Mon Sep 17 00:00:00 2001 From: Mihnea Dobrescu-Balaur Date: Mon, 10 Sep 2012 11:15:00 -0700 Subject: [PATCH 22/78] Bug 786848 - Add ability to post xpcshell test results to autolog, r=jgriffin --- testing/xpcshell/runxpcshelltests.py | 75 +++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 12 deletions(-) diff --git a/testing/xpcshell/runxpcshelltests.py b/testing/xpcshell/runxpcshelltests.py index 077a73f76211..9560afc81b0f 100644 --- a/testing/xpcshell/runxpcshelltests.py +++ b/testing/xpcshell/runxpcshelltests.py @@ -13,6 +13,8 @@ from tempfile import mkdtemp, gettempdir import manifestparser import mozinfo import random +import socket +import time from automationutils import * @@ -22,11 +24,11 @@ def parse_json(j): Awful hack to parse a restricted subset of JSON strings into Python dicts. """ return eval(j, {'true':True,'false':False,'null':None}) - + """ Control-C handling """ gotSIGINT = False def markGotSIGINT(signum, stackFrame): - global gotSIGINT + global gotSIGINT gotSIGINT = True class XPCShellTests(object): @@ -43,7 +45,7 @@ class XPCShellTests(object): def buildTestList(self): """ - read the xpcshell.ini manifest and set self.alltests to be + read the xpcshell.ini manifest and set self.alltests to be an array of test objects. if we are chunking tests, it will be done here as well @@ -82,7 +84,7 @@ class XPCShellTests(object): dirCount = len(self.alltests[dir]) endPosition = dirCount if currentCount < start and currentCount + dirCount >= start: - startPosition = int(start - currentCount) + startPosition = int(start - currentCount) if currentCount + dirCount > end: endPosition = int(end - currentCount) if end - currentCount < 0 or (currentCount + dirCount < start): @@ -304,7 +306,7 @@ class XPCShellTests(object): On a remote system, this is more complex and we need to overload this function. """ cmd = wrapCommand(cmd) - proc = Popen(cmd, stdout=stdout, stderr=stderr, + proc = Popen(cmd, stdout=stdout, stderr=stderr, env=env, cwd=cwd) return proc @@ -314,7 +316,7 @@ class XPCShellTests(object): On a remote system, this is overloaded to handle remote process communication. """ return proc.communicate() - + def removeDir(self, dirname): """ Simple wrapper to remove (recursively) a given directory. @@ -328,7 +330,7 @@ class XPCShellTests(object): On a remote system, we need to overload this to work on the remote filesystem. """ return os.path.abspath(dirname) - + def getReturnCode(self, proc): """ Simple wrapper to get the return code for a given process. @@ -345,7 +347,7 @@ class XPCShellTests(object): f = open(test + ".log", "w") f.write(stdout) - for leakLog in leakLogs: + for leakLog in leakLogs: if os.path.exists(leakLog): leaks = open(leakLog, "r") f.write(leaks.read()) @@ -530,6 +532,48 @@ class XPCShellTests(object): doc.writexml(fh, addindent=" ", newl="\n", encoding="utf-8") + def post_to_autolog(self, results, name): + from moztest.results import TestContext, TestResult, TestResultCollection + from moztest.output.autolog import AutologOutput + + context = TestContext( + testgroup='b2g xpcshell testsuite', + operating_system='android', + arch='emulator', + harness='xpcshell', + hostname=socket.gethostname(), + tree='b2g', + buildtype='opt', + ) + + collection = TestResultCollection('b2g emulator testsuite') + + for result in results: + duration = result.get('time', 0) + + if 'skipped' in result: + outcome = 'SKIPPED' + elif 'todo' in result: + outcome = 'KNOWN-FAIL' + elif result['passed']: + outcome = 'PASS' + else: + outcome = 'UNEXPECTED-FAIL' + + output = None + if 'failure' in result: + output = result['failure']['text'] + + t = TestResult(name=result['name'], test_class=name, + time_start=0, context=context) + t.finish(result=outcome, time_end=duration, output=output) + + collection.append(t) + collection.time_taken += duration + + out = AutologOutput() + out.post(out.make_testgroups(collection)) + def runTests(self, xpcshell, xrePath=None, appPath=None, symbolsPath=None, manifest=None, testdirs=None, testPath=None, interactive=False, verbose=False, keepGoing=False, logfiles=True, @@ -537,7 +581,7 @@ class XPCShellTests(object): debuggerArgs=None, debuggerInteractive=False, profileName=None, mozInfo=None, shuffle=False, testsRootDir=None, xunitFilename=None, xunitName=None, - testingModulesDir=None, **otherOptions): + testingModulesDir=None, autolog=False, **otherOptions): """Run xpcshell tests. |xpcshell|, is the xpcshell executable to use to run the tests. @@ -661,7 +705,7 @@ class XPCShellTests(object): # We have to do this before we build the test list so we know whether or # not to run tests that depend on having the node spdy server self.trySetupNode() - + pStdout, pStderr = self.getPipes() self.buildTestList() @@ -768,8 +812,8 @@ class XPCShellTests(object): (stdout and re.search(": SyntaxError:", stdout, re.MULTILINE)) or # if e10s test started but never finished (child process crash) - (stdout and re.search("^child: CHILD-TEST-STARTED", - stdout, re.MULTILINE) + (stdout and re.search("^child: CHILD-TEST-STARTED", + stdout, re.MULTILINE) and not re.search("^child: CHILD-TEST-COMPLETED", stdout, re.MULTILINE))) @@ -801,6 +845,7 @@ class XPCShellTests(object): self.passCount += 1 else: self.todoCount += 1 + xunitResult["todo"] = True checkForCrashes(testdir, self.symbolsPath, testName=name) # Find child process(es) leak log(s), if any: See InitLog() in @@ -848,6 +893,9 @@ INFO | Passed: %d INFO | Failed: %d INFO | Todo: %d""" % (self.passCount, self.failCount, self.todoCount)) + if autolog: + self.post_to_autolog(xunitResults, xunitName) + if xunitFilename is not None: self.writeXunitResults(filename=xunitFilename, results=xunitResults, name=xunitName) @@ -868,6 +916,9 @@ class XPCShellOptions(OptionParser): self.add_option("--app-path", type="string", dest="appPath", default=None, help="application directory (as opposed to XRE directory)") + self.add_option("--autolog", + action="store_true", dest="autolog", default=False, + help="post to autolog") self.add_option("--interactive", action="store_true", dest="interactive", default=False, help="don't automatically run tests, drop to an xpcshell prompt") From 2e0a2ad958f7eeb22bdd4f59ddf2a597f71722f4 Mon Sep 17 00:00:00 2001 From: Terrence Cole Date: Mon, 10 Sep 2012 10:31:58 -0700 Subject: [PATCH 23/78] Bug 789633 - Ergonomics improvements for stack rooting assertions; r=sfink There are a handful of classes that want to mix in this assertion, but it is somewhat annoying to do this with our current tools. The current names are also quite long and annoying to type. --- js/src/gc/Root.h | 48 +++++++++++++++------------------ js/src/jsapi.cpp | 43 ++++++++++++++++++++++++++--- js/src/jscntxt.cpp | 27 ------------------- js/src/jscntxt.h | 11 +++----- js/src/jsgc.cpp | 18 ++++++------- js/src/jsinfer.cpp | 2 +- js/src/jspropertycacheinlines.h | 2 +- js/src/shell/js.cpp | 2 +- 8 files changed, 77 insertions(+), 76 deletions(-) diff --git a/js/src/gc/Root.h b/js/src/gc/Root.h index 058b690700df..8c709909a1e0 100644 --- a/js/src/gc/Root.h +++ b/js/src/gc/Root.h @@ -11,9 +11,11 @@ #ifdef __cplusplus #include "mozilla/TypeTraits.h" +#include "mozilla/GuardObjects.h" #include "js/TemplateLib.h" -#include "js/Utility.h" + +#include "jspubtd.h" namespace JS { @@ -440,38 +442,30 @@ class SkipRoot JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; +JS_FRIEND_API(void) EnterAssertNoGCScope(); +JS_FRIEND_API(void) LeaveAssertNoGCScope(); +JS_FRIEND_API(bool) InNoGCScope(); + /* - * This typedef is to annotate parameters that we have manually verified do not - * need rooting, as opposed to parameters that have not yet been considered. + * The scoped guard object AutoAssertNoGC forces the GC to assert if a GC is + * attempted while the guard object is live. If you have a GC-unsafe operation + * to perform, use this guard object to protect your opertion. */ -typedef JSObject *RawObject; +class AutoAssertNoGC +{ + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER -#ifdef DEBUG -JS_FRIEND_API(bool) IsRootingUnnecessaryForContext(JSContext *cx); -JS_FRIEND_API(void) SetRootingUnnecessaryForContext(JSContext *cx, bool value); -JS_FRIEND_API(bool) RelaxRootChecksForContext(JSContext *cx); -#endif - -class AssertRootingUnnecessary { - JS_DECL_USE_GUARD_OBJECT_NOTIFIER -#ifdef DEBUG - JSContext *cx; - bool prev; -#endif public: - AssertRootingUnnecessary(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; + AutoAssertNoGC(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; #ifdef DEBUG - this->cx = cx; - prev = IsRootingUnnecessaryForContext(cx); - SetRootingUnnecessaryForContext(cx, true); + EnterAssertNoGCScope(); #endif } - ~AssertRootingUnnecessary() { + ~AutoAssertNoGC() { #ifdef DEBUG - SetRootingUnnecessaryForContext(cx, prev); + LeaveAssertNoGCScope(); #endif } }; @@ -481,6 +475,8 @@ extern void CheckStackRoots(JSContext *cx); #endif +JS_FRIEND_API(bool) NeedRelaxedRootChecks(); + /* * Hook for dynamic root analysis. Checks the native stack and poisons * references to GC things which have not been rooted. @@ -488,9 +484,9 @@ CheckStackRoots(JSContext *cx); inline void MaybeCheckStackRoots(JSContext *cx, bool relax = true) { #ifdef DEBUG - JS_ASSERT(!IsRootingUnnecessaryForContext(cx)); + JS_ASSERT(!InNoGCScope()); # if defined(JS_GC_ZEAL) && defined(JSGC_ROOT_ANALYSIS) && !defined(JS_THREADSAFE) - if (relax && RelaxRootChecksForContext(cx)) + if (relax && NeedRelaxedRootChecks()) return; CheckStackRoots(cx); # endif diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index c1dc69649c5e..6ead3638bfbf 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -740,8 +740,41 @@ static JSBool js_NewRuntimeWasCalled = JS_FALSE; */ namespace JS { mozilla::ThreadLocal TlsRuntime; + +#ifdef DEBUG +JS_FRIEND_API(void) +EnterAssertNoGCScope() +{ + ++TlsRuntime.get()->gcAssertNoGCDepth; } +JS_FRIEND_API(void) +LeaveAssertNoGCScope() +{ + --TlsRuntime.get()->gcAssertNoGCDepth; + JS_ASSERT(TlsRuntime.get()->gcAssertNoGCDepth >= 0); +} + +JS_FRIEND_API(bool) +InNoGCScope() +{ + return TlsRuntime.get()->gcAssertNoGCDepth > 0; +} + +JS_FRIEND_API(bool) +NeedRelaxedRootChecks() +{ + return TlsRuntime.get()->gcRelaxRootChecks; +} +#else +JS_FRIEND_API(void) EnterAssertNoGCScope() {} +JS_FRIEND_API(void) LeaveAssertNoGCScope() {} +JS_FRIEND_API(bool) InNoGCScope() { return false; } +JS_FRIEND_API(bool) NeedRelaxedRootChecks() { return false; } +#endif + +} /* namespace JS */ + static const JSSecurityCallbacks NullSecurityCallbacks = { }; JSRuntime::JSRuntime() @@ -817,6 +850,10 @@ JSRuntime::JSRuntime() gcSliceBudget(SliceBudget::Unlimited), gcIncrementalEnabled(true), gcExactScanningEnabled(true), +#ifdef DEBUG + gcRelaxRootChecks(false), + gcAssertNoGCDepth(0), +#endif gcPoke(false), heapState(Idle), #ifdef JS_GC_ZEAL @@ -3451,7 +3488,7 @@ JS_NewObject(JSContext *cx, JSClass *jsclasp, JSObject *protoArg, JSObject *pare JS_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL)); JSObject *obj = NewObjectWithClassProto(cx, clasp, proto, parent); - AssertRootingUnnecessary safe(cx); + AutoAssertNoGC nogc; if (obj) { if (clasp->ext.equality) MarkTypeObjectFlags(cx, obj, OBJECT_FLAG_SPECIAL_EQUALITY); @@ -3479,7 +3516,7 @@ JS_NewObjectWithGivenProto(JSContext *cx, JSClass *jsclasp, JSObject *protoArg, JS_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL)); JSObject *obj = NewObjectWithGivenProto(cx, clasp, proto, parent); - AssertRootingUnnecessary safe(cx); + AutoAssertNoGC nogc; if (obj) MarkTypeObjectUnknownProperties(cx, obj->type()); return obj; @@ -4679,7 +4716,7 @@ JS_PUBLIC_API(JSBool) JS_NextProperty(JSContext *cx, JSObject *iterobjArg, jsid *idp) { RootedObject iterobj(cx, iterobjArg); - AssertRootingUnnecessary safe(cx); + AutoAssertNoGC nogc; int32_t i; Shape *shape; JSIdArray *ida; diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index d0d14ed8494f..e4fb84b8fc5f 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -1174,9 +1174,6 @@ JSContext::JSContext(JSRuntime *rt) localeCallbacks(NULL), resolvingList(NULL), generatingError(false), -#ifdef DEBUG - rootingUnnecessary(false), -#endif compartment(NULL), enterCompartmentDepth_(0), savedFrameChains_(), @@ -1238,30 +1235,6 @@ JSContext::~JSContext() JS_ASSERT(!resolvingList); } -#ifdef DEBUG -namespace JS { - -JS_FRIEND_API(void) -SetRootingUnnecessaryForContext(JSContext *cx, bool value) -{ - cx->rootingUnnecessary = value; -} - -JS_FRIEND_API(bool) -IsRootingUnnecessaryForContext(JSContext *cx) -{ - return cx->rootingUnnecessary; -} - -JS_FRIEND_API(bool) -RelaxRootChecksForContext(JSContext *cx) -{ - return cx->runtime->relaxRootChecks; -} - -} /* namespace JS */ -#endif - /* * Since this function is only called in the context of a pending exception, * the caller must subsequently take an error path. If wrapping fails, it will diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 75403422260a..e450cdf07b0a 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -649,14 +649,13 @@ struct JSRuntime : js::RuntimeFriendFields SavedGCRoot(void *thing, JSGCTraceKind kind) : thing(thing), kind(kind) {} }; js::Vector gcSavedRoots; + + bool gcRelaxRootChecks; + int gcAssertNoGCDepth; #endif bool gcPoke; -#ifdef DEBUG - bool relaxRootChecks; -#endif - enum HeapState { Idle, // doing nothing with the GC heap Tracing, // tracing the GC heap without collecting, e.g. IterateCompartments() @@ -1190,10 +1189,6 @@ struct JSContext : js::ContextFriendFields /* True if generating an error, to prevent runaway recursion. */ bool generatingError; -#ifdef DEBUG - bool rootingUnnecessary; -#endif - /* The current compartment. */ JSCompartment *compartment; diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 285b740ed9e1..28b305ab092c 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -4884,16 +4884,16 @@ JS::CheckStackRoots(JSContext *cx) if (rt->gcZeal_ == ZealStackRootingSafeValue && !rt->gcExactScanningEnabled) return; - // If this assertion fails, it means that an AssertRootingUnnecessary was - // placed around code that could trigger GC, and is therefore wrong. The - // AssertRootingUnnecessary should be removed and the code it was guarding - // should be modified to properly root any gcthings, and very possibly any - // code calling that function should also be modified if it was improperly + // If this assertion fails, it means that an AutoAssertNoGC was placed + // around code that could trigger GC, and is therefore wrong. The + // AutoAssertNoGC should be removed and the code it was guarding should be + // modified to properly root any gcthings, and very possibly any code + // calling that function should also be modified if it was improperly // assuming that GC could not happen at all within the called function. - // (The latter may not apply if the AssertRootingUnnecessary only protected - // a portion of a function, so the callers were already assuming that GC - // could happen.) - JS_ASSERT(!cx->rootingUnnecessary); + // (The latter may not apply if the AutoAssertNoGC only protected a portion + // of a function, so the callers were already assuming that GC could + // happen.) + JS_ASSERT(!InNoGCScope()); // GCs can't happen when analysis/inference/compilation are active. if (cx->compartment->activeAnalysis) diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index e95870428cfd..25eb7088713e 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -5481,7 +5481,7 @@ JSObject::makeLazyType(JSContext *cx) RootedObject self(cx, this); JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(getClass()); TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL, key, getProto()); - AssertRootingUnnecessary safe(cx); + AutoAssertNoGC nogc; if (!type) { if (cx->typeInferenceEnabled()) cx->compartment->types.setPendingNukeTypes(cx); diff --git a/js/src/jspropertycacheinlines.h b/js/src/jspropertycacheinlines.h index 5ab24fbfdf13..fafe7360814a 100644 --- a/js/src/jspropertycacheinlines.h +++ b/js/src/jspropertycacheinlines.h @@ -31,7 +31,7 @@ JS_ALWAYS_INLINE void js::PropertyCache::test(JSContext *cx, jsbytecode *pc, JSObject *&obj, JSObject *&pobj, PropertyCacheEntry *&entry, PropertyName *&name) { - AssertRootingUnnecessary assert(cx); + AutoAssertNoGC nogc; JS_ASSERT(this == &cx->propertyCache()); diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index e511e74be4c5..f46578bf7c1c 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -3534,7 +3534,7 @@ RelaxRootChecks(JSContext *cx, unsigned argc, jsval *vp) } #ifdef DEBUG - cx->runtime->relaxRootChecks = true; + cx->runtime->gcRelaxRootChecks = true; #endif return true; From e65c049a3b77334f351272859e87cbe01a31fe00 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Wed, 5 Sep 2012 14:01:29 -0700 Subject: [PATCH 24/78] Bug 778460 - Copy the various GC-related --enable-* configure options into toplevel configure.in to get correct defines. r=ted --HG-- extra : rebase_source : ab778875ce9dfcf3ab115a8be7e81061a2b293d6 --- configure.in | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/configure.in b/configure.in index 6af5bb25320a..2043ea4d9d46 100644 --- a/configure.in +++ b/configure.in @@ -1616,6 +1616,50 @@ if test "$MOZ_PROFILING" -a -z "$STRIP_FLAGS"; then STRIP_FLAGS="--strip-debug" fi +dnl ======================================================== +dnl = Use incremental GC +dnl ======================================================== +JSGC_INCREMENTAL=1 +MOZ_ARG_DISABLE_BOOL(gcincremental, +[ --disable-gcincremental Disable incremental GC], + JSGC_INCREMENTAL= ) +if test -n "$JSGC_INCREMENTAL"; then + AC_DEFINE(JSGC_INCREMENTAL) +fi + +dnl ======================================================== +dnl = Use generational GC +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(gcgenerational, +[ --enable-gcgenerational Enable generational GC], + JSGC_GENERATIONAL=1, + JSGC_GENERATIONAL= ) +if test -n "$JSGC_GENERATIONAL"; then + AC_DEFINE(JSGC_GENERATIONAL) +fi + +dnl ======================================================== +dnl = Perform moving GC stack rooting analysis +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(root-analysis, +[ --enable-root-analysis Enable moving GC stack root analysis], + JSGC_ROOT_ANALYSIS=1, + JSGC_ROOT_ANALYSIS= ) +if test -n "$JSGC_ROOT_ANALYSIS"; then + AC_DEFINE(JSGC_ROOT_ANALYSIS) +fi + +dnl ======================================================== +dnl = Use exact stack rooting for GC +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(exact-rooting, +[ --enable-exact-rooting Enable use of exact stack roots for GC], + JSGC_USE_EXACT_ROOTING=1, + JSGC_USE_EXACT_ROOTING= ) +if test -n "$JSGC_USE_EXACT_ROOTING"; then + AC_DEFINE(JSGC_USE_EXACT_ROOTING) +fi + dnl ======================================================== dnl = Use Valgrind dnl ======================================================== From 5de9a5598944ff18fbae8a16a1fb86259c91fc32 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Mon, 2 Jul 2012 09:07:57 -0700 Subject: [PATCH 25/78] Bug 770253 - Various compilation fixes for different --enable options. r=terrence --HG-- extra : rebase_source : f13fdd9acc72910350dda650acdc66d0d80f7aed --- js/src/gc/Root.h | 2 +- js/src/jscntxt.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/gc/Root.h b/js/src/gc/Root.h index 8c709909a1e0..a22dfdd2a637 100644 --- a/js/src/gc/Root.h +++ b/js/src/gc/Root.h @@ -390,7 +390,7 @@ typedef Rooted RootedValue; */ class SkipRoot { -#if defined(DEBUG) && defined(JSGC_ROOT_ANALYSIS) +#if defined(DEBUG) && defined(JS_GC_ZEAL) && defined(JSGC_ROOT_ANALYSIS) && !defined(JS_THREADSAFE) SkipRoot **stack, *prev; const uint8_t *start; diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index e4fb84b8fc5f..daaecbe75e99 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -1209,7 +1209,7 @@ JSContext::JSContext(JSRuntime *rt) PodZero(&link); #ifdef JSGC_ROOT_ANALYSIS PodArrayZero(thingGCRooters); -#ifdef DEBUG +#if defined(JS_GC_ZEAL) && defined(DEBUG) && !defined(JS_THREADSAFE) skipGCRooters = NULL; #endif #endif From 2f044a0ac06ffb7d3b34e777252431103d686470 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Mon, 10 Sep 2012 11:26:46 -0700 Subject: [PATCH 26/78] Bug 780309 - Implement InternalHandle and use it for Bindings. r=terrence --- js/src/frontend/BytecodeCompiler.cpp | 6 ++- js/src/frontend/Parser.cpp | 11 +++-- js/src/frontend/Parser.h | 2 +- js/src/gc/Root.h | 67 ++++++++++++++++++++++++++++ js/src/jsapi.h | 1 - js/src/jsgc.cpp | 14 +----- js/src/jsscript.cpp | 45 +++++++++++-------- js/src/jsscript.h | 27 +++-------- 8 files changed, 113 insertions(+), 60 deletions(-) diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index 93ebaf035d15..a369c36a84c0 100644 --- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -114,7 +114,8 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *call // Global/eval script bindings are always empty (all names are added to the // scope dynamically via JSOP_DEFFUN/VAR). - if (!script->bindings.initWithTemporaryStorage(cx, 0, 0, NULL)) + InternalHandle bindings(script, &script->bindings); + if (!Bindings::initWithTemporaryStorage(cx, bindings, 0, 0, NULL)) return NULL; // We can specialize a bit for the given scope chain if that scope chain is the global object. @@ -327,7 +328,8 @@ frontend::CompileFunctionBody(JSContext *cx, HandleFunction fun, CompileOptions if (!script) return false; - if (!funpc.generateFunctionBindings(cx, &script->bindings)) + InternalHandle bindings(script, &script->bindings); + if (!funpc.generateFunctionBindings(cx, bindings)) return false; BytecodeEmitter funbce(/* parent = */ NULL, &parser, &funsc, script, /* callerFrame = */ NULL, diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index d7d563849432..1fba743eb760 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -296,7 +296,7 @@ AppendPackedBindings(const ParseContext *pc, const DeclVector &vec, Binding *dst } bool -ParseContext::generateFunctionBindings(JSContext *cx, Bindings *bindings) const +ParseContext::generateFunctionBindings(JSContext *cx, InternalHandle bindings) const { JS_ASSERT(sc->inFunction()); @@ -310,8 +310,11 @@ ParseContext::generateFunctionBindings(JSContext *cx, Bindings *bindings) const AppendPackedBindings(this, args_, packedBindings); AppendPackedBindings(this, vars_, packedBindings + args_.length()); - if (!bindings->initWithTemporaryStorage(cx, args_.length(), vars_.length(), packedBindings)) + if (!Bindings::initWithTemporaryStorage(cx, bindings, args_.length(), vars_.length(), + packedBindings)) + { return false; + } if (bindings->hasAnyAliasedBindings() || sc->funHasExtensibleScope()) sc->funbox()->fun()->flags |= JSFUN_HEAVYWEIGHT; @@ -1257,7 +1260,9 @@ LeaveFunction(ParseNode *fn, Parser *parser, PropertyName *funName = NULL, } } - if (!funpc->generateFunctionBindings(cx, &funbox->bindings)) + InternalHandle bindings = + InternalHandle::fromMarkedLocation(&funbox->bindings); + if (!funpc->generateFunctionBindings(cx, bindings)) return false; funpc->lexdeps.releaseMap(cx); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index c0886b07e426..e1c81fc4d7c3 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -151,7 +151,7 @@ struct ParseContext /* tree context for semantic checks */ * - Sometimes a script's bindings are accessed at runtime to retrieve the * contents of the lexical scope (e.g., from the debugger). */ - bool generateFunctionBindings(JSContext *cx, Bindings *bindings) const; + bool generateFunctionBindings(JSContext *cx, InternalHandle bindings) const; public: ParseNode *yieldNode; /* parse node for a yield expression that might diff --git a/js/src/gc/Root.h b/js/src/gc/Root.h index a22dfdd2a637..28667d076086 100644 --- a/js/src/gc/Root.h +++ b/js/src/gc/Root.h @@ -240,6 +240,73 @@ typedef JSObject * RawObject; typedef JSString * RawString; typedef Value RawValue; +/* + * InternalHandle is a handle to an internal pointer into a gcthing. Use + * InternalHandle when you have a pointer to a direct field of a gcthing, or + * when you need a parameter type for something that *may* be a pointer to a + * direct field of a gcthing. + */ +class InternalHandleBase +{ + protected: + static void * const zeroPointer; +}; + +template +class InternalHandle { }; + +template +class InternalHandle : public InternalHandleBase +{ + void * const *holder; + size_t offset; + + public: + /* + * Create an InternalHandle using a Handle to the gcthing containing the + * field in question, and a pointer to the field. + */ + template + InternalHandle(const Handle &handle, T *field) + : holder((void**)handle.address()), offset(uintptr_t(field) - uintptr_t(handle.get())) + { + } + + /* + * Create an InternalHandle to a field within a Rooted<>. + */ + template + InternalHandle(const Rooted &root, T *field) + : holder((void**)root.address()), offset(uintptr_t(field) - uintptr_t(root.get())) + { + } + + T *get() const { return reinterpret_cast(uintptr_t(*holder) + offset); } + + const T& operator *() const { return *get(); } + T* operator ->() const { return get(); } + + static InternalHandle fromMarkedLocation(T *fieldPtr) { + return InternalHandle(fieldPtr); + } + + private: + /* + * Create an InternalHandle to something that is not a pointer to a + * gcthing, and so does not need to be rooted in the first place. Use these + * InternalHandles to pass pointers into functions that also need to accept + * regular InternalHandles to gcthing fields. + * + * Make this private to prevent accidental misuse; this is only for + * fromMarkedLocation(). + */ + InternalHandle(T *field) + : holder(reinterpret_cast(&zeroPointer)), + offset(uintptr_t(field)) + { + } +}; + extern mozilla::ThreadLocal TlsRuntime; /* diff --git a/js/src/jsapi.h b/js/src/jsapi.h index f8d7b3910a31..cf37eab45ffa 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1061,7 +1061,6 @@ class JS_PUBLIC_API(AutoGCRooter) { SHAPERANGE = -20, /* js::Shape::Range::AutoRooter */ STACKSHAPE = -21, /* js::StackShape::AutoRooter */ STACKBASESHAPE=-22,/* js::StackBaseShape::AutoRooter */ - BINDINGS = -23, /* js::Bindings::AutoRooter */ GETTERSETTER =-24, /* js::AutoRooterGetterSetter */ REGEXPSTATICS=-25, /* js::RegExpStatics::AutoRooter */ NAMEVECTOR = -26, /* js::AutoNameVector */ diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 28b305ab092c..3dabcf4eb3d4 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -103,6 +103,8 @@ using namespace mozilla; using namespace js; using namespace js::gc; +void * const JS::InternalHandleBase::zeroPointer = NULL; + namespace js { namespace gc { @@ -2402,12 +2404,6 @@ AutoGCRooter::trace(JSTracer *trc) return; } - case BINDINGS: { - Bindings::AutoRooter *rooter = static_cast(this); - rooter->trace(trc); - return; - } - case GETTERSETTER: { AutoRooterGetterSetter::Inner *rooter = static_cast(this); if ((rooter->attrs & JSPROP_GETTER) && *rooter->pgetter) @@ -2453,12 +2449,6 @@ Shape::Range::AutoRooter::trace(JSTracer *trc) MarkShapeRoot(trc, const_cast(&r->cursor), "Shape::Range::AutoRooter"); } -void -Bindings::AutoRooter::trace(JSTracer *trc) -{ - bindings->trace(trc); -} - void RegExpStatics::AutoRooter::trace(JSTracer *trc) { diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index c63bffaafc14..b67072d44ed8 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -60,23 +60,25 @@ Bindings::argumentsVarIndex(JSContext *cx) const } bool -Bindings::initWithTemporaryStorage(JSContext *cx, unsigned numArgs, unsigned numVars, Binding *bindingArray) +Bindings::initWithTemporaryStorage(JSContext *cx, InternalHandle self, + unsigned numArgs, unsigned numVars, + Binding *bindingArray) { - JS_ASSERT(!callObjShape_); - JS_ASSERT(bindingArrayAndFlag_ == TEMPORARY_STORAGE_BIT); + JS_ASSERT(!self->callObjShape_); + JS_ASSERT(self->bindingArrayAndFlag_ == TEMPORARY_STORAGE_BIT); if (numArgs > UINT16_MAX || numVars > UINT16_MAX) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - numArgs_ > numVars_ ? + self->numArgs_ > self->numVars_ ? JSMSG_TOO_MANY_FUN_ARGS : JSMSG_TOO_MANY_LOCALS); return false; } JS_ASSERT(!(uintptr_t(bindingArray) & TEMPORARY_STORAGE_BIT)); - bindingArrayAndFlag_ = uintptr_t(bindingArray) | TEMPORARY_STORAGE_BIT; - numArgs_ = numArgs; - numVars_ = numVars; + self->bindingArrayAndFlag_ = uintptr_t(bindingArray) | TEMPORARY_STORAGE_BIT; + self->numArgs_ = numArgs; + self->numVars_ = numVars; /* * Get the initial shape to use when creating CallObjects for this script. @@ -90,8 +92,8 @@ Bindings::initWithTemporaryStorage(JSContext *cx, unsigned numArgs, unsigned num JS_STATIC_ASSERT(CallObject::RESERVED_SLOTS == 2); gc::AllocKind allocKind = gc::FINALIZE_OBJECT2_BACKGROUND; JS_ASSERT(gc::GetGCKindSlots(allocKind) == CallObject::RESERVED_SLOTS); - callObjShape_ = EmptyShape::getInitialShape(cx, &CallClass, NULL, cx->global(), - allocKind, BaseShape::VAROBJ); + self->callObjShape_ = EmptyShape::getInitialShape(cx, &CallClass, NULL, cx->global(), + allocKind, BaseShape::VAROBJ); #ifdef DEBUG HashSet added(cx); @@ -99,9 +101,9 @@ Bindings::initWithTemporaryStorage(JSContext *cx, unsigned numArgs, unsigned num return false; #endif - BindingIter bi(*this); + BindingIter bi(*self); unsigned slot = CallObject::RESERVED_SLOTS; - for (unsigned i = 0, n = count(); i < n; i++, bi++) { + for (unsigned i = 0, n = self->count(); i < n; i++, bi++) { if (!bi->aliased()) continue; @@ -123,8 +125,8 @@ Bindings::initWithTemporaryStorage(JSContext *cx, unsigned numArgs, unsigned num unsigned frameIndex = bi.frameIndex(); StackShape child(nbase, id, slot++, 0, attrs, Shape::HAS_SHORTID, frameIndex); - callObjShape_ = callObjShape_->getChildBinding(cx, child); - if (!callObjShape_) + self->callObjShape_ = self->callObjShape_->getChildBinding(cx, child); + if (!self->callObjShape_) return false; } JS_ASSERT(!bi); @@ -144,7 +146,9 @@ Bindings::switchToScriptStorage(Binding *newBindingArray) } bool -Bindings::clone(JSContext *cx, uint8_t *dstScriptData, HandleScript srcScript) +Bindings::clone(JSContext *cx, InternalHandle self, + uint8_t *dstScriptData, + HandleScript srcScript) { /* The clone has the same bindingArray_ offset as 'src'. */ Bindings &src = srcScript->bindings; @@ -157,9 +161,9 @@ Bindings::clone(JSContext *cx, uint8_t *dstScriptData, HandleScript srcScript) * Since atoms are shareable throughout the runtime, we can simply copy * the source's bindingArray directly. */ - if (!initWithTemporaryStorage(cx, src.numArgs(), src.numVars(), src.bindingArray())) + if (!initWithTemporaryStorage(cx, self, src.numArgs(), src.numVars(), src.bindingArray())) return false; - switchToScriptStorage(dstPackedBindings); + self->switchToScriptStorage(dstPackedBindings); return true; } @@ -210,7 +214,8 @@ XDRScriptBindings(XDRState *xdr, LifoAllocScope &las, unsigned numArgs, un bindingArray[i] = Binding(name, kind, aliased); } - if (!script->bindings.initWithTemporaryStorage(cx, numArgs, numVars, bindingArray)) + InternalHandle bindings(script, &script->bindings); + if (!Bindings::initWithTemporaryStorage(cx, bindings, numArgs, numVars, bindingArray)) return false; } @@ -2064,8 +2069,9 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, /* Bindings */ Bindings bindings; - Bindings::AutoRooter bindingRooter(cx, &bindings); - if (!bindings.clone(cx, data, src)) + InternalHandle bindingsHandle = + InternalHandle::fromMarkedLocation(&bindings); + if (!Bindings::clone(cx, bindingsHandle, data, src)) return NULL; /* Objects */ @@ -2130,6 +2136,7 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, js_free(data); return NULL; } + AutoAssertNoGC nogc; dst->bindings = bindings; diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 7b6face72a22..d66d16ec4b72 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -160,14 +160,17 @@ class Bindings * storage is release, switchToScriptStorage must be called, providing a * pointer into the Binding array stored in script->data. */ - bool initWithTemporaryStorage(JSContext *cx, unsigned numArgs, unsigned numVars, Binding *bindingArray); + static bool initWithTemporaryStorage(JSContext *cx, InternalHandle self, + unsigned numArgs, unsigned numVars, + Binding *bindingArray); + uint8_t *switchToScriptStorage(Binding *newStorage); /* * Clone srcScript's bindings (as part of js::CloneScript). dstScriptData * is the pointer to what will eventually be dstScript->data. */ - bool clone(JSContext *cx, uint8_t *dstScriptData, HandleScript srcScript); + static bool clone(JSContext *cx, InternalHandle self, uint8_t *dstScriptData, HandleScript srcScript); unsigned numArgs() const { return numArgs_; } unsigned numVars() const { return numVars_; } @@ -186,26 +189,6 @@ class Bindings bool hasAnyAliasedBindings() const { return !callObjShape_->isEmptyShape(); } void trace(JSTracer *trc); - class AutoRooter; -}; - -class Bindings::AutoRooter : private AutoGCRooter -{ - public: - explicit AutoRooter(JSContext *cx, Bindings *bindings_ - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoGCRooter(cx, BINDINGS), bindings(bindings_), skip(cx, bindings_) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; - } - - friend void AutoGCRooter::trace(JSTracer *trc); - void trace(JSTracer *trc); - - private: - Bindings *bindings; - SkipRoot skip; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; class ScriptCounts From cf3261c6831a7a3db07ef9ae516080b8ee3ec355 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Mon, 10 Sep 2012 11:46:03 -0700 Subject: [PATCH 27/78] Bug 781498 - Conditionally install TESTING_JS_MODULES; r=glandium Previously, installation was unconditional. It should be conditional on ENABLE_TESTS. --HG-- extra : rebase_source : 8fb11f96f4c776ee13ede2320ecc6b67d7e68971 --- config/rules.mk | 2 ++ js/src/config/rules.mk | 2 ++ 2 files changed, 4 insertions(+) diff --git a/config/rules.mk b/config/rules.mk index 1554498153e6..43b6633d318a 100644 --- a/config/rules.mk +++ b/config/rules.mk @@ -1356,6 +1356,7 @@ endif # objdir/_tests/modules/. If TESTING_JS_MODULE_DIR is defined, that path # wlll be appended to the output directory. +ifdef ENABLE_TESTS ifdef TESTING_JS_MODULES testmodulesdir = $(DEPTH)/_tests/modules/$(TESTING_JS_MODULE_DIR) @@ -1367,6 +1368,7 @@ TESTING_JS_MODULES_DEST := $(testmodulesdir) INSTALL_TARGETS += TESTING_JS_MODULES endif +endif endif ################################################################################ diff --git a/js/src/config/rules.mk b/js/src/config/rules.mk index 1554498153e6..43b6633d318a 100644 --- a/js/src/config/rules.mk +++ b/js/src/config/rules.mk @@ -1356,6 +1356,7 @@ endif # objdir/_tests/modules/. If TESTING_JS_MODULE_DIR is defined, that path # wlll be appended to the output directory. +ifdef ENABLE_TESTS ifdef TESTING_JS_MODULES testmodulesdir = $(DEPTH)/_tests/modules/$(TESTING_JS_MODULE_DIR) @@ -1367,6 +1368,7 @@ TESTING_JS_MODULES_DEST := $(testmodulesdir) INSTALL_TARGETS += TESTING_JS_MODULES endif +endif endif ################################################################################ From 63490c62d3e66f428524b04133c335668d44972e Mon Sep 17 00:00:00 2001 From: Andrew McCreight Date: Mon, 10 Sep 2012 12:43:45 -0700 Subject: [PATCH 28/78] Bug 775868, part 1: make tests access DomWinUtils via SpecialPowers. r=smaug --- dom/identity/tests/head_identity.js | 3 +-- dom/tests/mochitest/bugs/test_bug61098.html | 4 +--- dom/tests/mochitest/general/test_domWindowUtils.html | 5 +---- .../mochitest/general/test_domWindowUtils_scrollXY.html | 8 ++------ layout/forms/test/test_bug348236.html | 7 +------ toolkit/components/prompts/test/test_bug625187.html | 3 +-- toolkit/content/tests/widgets/popup_shared.js | 7 +------ toolkit/identity/tests/mochitest/head_identity.js | 3 +-- toolkit/identity/tests/mochitest/test_authentication.html | 3 +-- toolkit/identity/tests/mochitest/test_provisioning.html | 3 +-- toolkit/identity/tests/mochitest/test_relying_party.html | 3 +-- 11 files changed, 12 insertions(+), 37 deletions(-) diff --git a/dom/identity/tests/head_identity.js b/dom/identity/tests/head_identity.js index 8852e9a74873..c9004f616e74 100644 --- a/dom/identity/tests/head_identity.js +++ b/dom/identity/tests/head_identity.js @@ -13,8 +13,7 @@ const Services = Cu.import("resource://gre/modules/Services.jsm").Services; const DOMIdentity = Cu.import("resource://gre/modules/DOMIdentity.jsm") .DOMIdentity; -let util = window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); +let util = SpecialPowers.getDOMWindowUtils(window); let outerWinId = util.outerWindowID; const identity = navigator.id || navigator.mozId; diff --git a/dom/tests/mochitest/bugs/test_bug61098.html b/dom/tests/mochitest/bugs/test_bug61098.html index 72c14656f484..33c455244d20 100644 --- a/dom/tests/mochitest/bugs/test_bug61098.html +++ b/dom/tests/mochitest/bugs/test_bug61098.html @@ -51,9 +51,7 @@ function registerMockPromptService() // leave the modal state -- this is done to trigger the necessary // accounting for triggering the "stop showing more prompts" code for // abusive pages. - var winUtils = this.domWindow - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); + var winUtils = SpecialPowers.getDOMWindowUtils(this.domWindow); var w = winUtils.enterModalStateWithWindow(); winUtils.leaveModalStateWithWindow(w); }, diff --git a/dom/tests/mochitest/general/test_domWindowUtils.html b/dom/tests/mochitest/general/test_domWindowUtils.html index 44bdf97939a1..70d90b0e8e2f 100644 --- a/dom/tests/mochitest/general/test_domWindowUtils.html +++ b/dom/tests/mochitest/general/test_domWindowUtils.html @@ -25,10 +25,7 @@ + + + + + + + + +
ttestselection1 Lorem ipsum dolor sit amet, at duo debet graeci, vivendum vulputate per ut. Ne labore incorrupte vix. Cu copiosae postulant tincidunt ius, in illud appetere contentiones eos. Ei munere officiis assentior pro, nibh decore ius at.
+ +
+ + + +
+ +
Lorem ipsum dolor sit amet, at duo debet graeci, vivendum vulputate per ut. Ne labore incorrupte vix. Cu copiosae postulant tincidunt ius, in illud appetere contentiones eos.
+ +
+ +t + + + diff --git a/dom/tests/mochitest/chrome/test_selectAtPoint.html b/dom/tests/mochitest/chrome/test_selectAtPoint.html new file mode 100644 index 000000000000..c96c6607b426 --- /dev/null +++ b/dom/tests/mochitest/chrome/test_selectAtPoint.html @@ -0,0 +1,21 @@ + + + + nsIDOMWindowUtils::selectAtPoint test + + + + + +

+