From 43b0deedf05166f30d62bd666c040a3165a761a1 Mon Sep 17 00:00:00 2001 From: Roy Tam Date: Sun, 26 Aug 2018 00:35:59 +0800 Subject: [PATCH] ported mozilla patches: - Bug 848294 - Update MessageEvent to be compatible with the spec, r=bz - Bug 913761 - Something is wrong when transferring MessagePort object via MessageChannel. r=smaug - Bug 917254 - Only enable MessageEvent.ports when MessagePort is enabled; r=smaug --- content/events/src/nsDOMMessageEvent.cpp | 95 ++++++++++++++++++- content/events/src/nsDOMMessageEvent.h | 36 +++---- content/events/test/Makefile.in | 1 + content/events/test/test_messageEvent.html | 66 +++++++++++++ dom/base/MessagePort.cpp | 10 +- dom/base/MessagePort.h | 2 +- dom/base/MessagePortList.cpp | 29 ++++++ dom/base/MessagePortList.h | 76 +++++++++++++++ dom/base/moz.build | 2 + dom/base/nsGlobalWindow.cpp | 6 +- dom/base/test/Makefile.in | 1 + dom/base/test/test_bug913761.html | 43 +++++++++ .../mochitest/whatwg/test_MessageEvent.html | 19 +--- .../test_MessageEvent_dispatchToOther.html | 7 +- dom/webidl/MessageEvent.webidl | 29 +++--- dom/webidl/MessagePortList.webidl | 11 +++ dom/webidl/WebIDL.mk | 1 + .../chrome/test_weakmap_keys_preserved2.xul | 4 +- 18 files changed, 376 insertions(+), 62 deletions(-) create mode 100644 content/events/test/test_messageEvent.html create mode 100644 dom/base/MessagePortList.cpp create mode 100644 dom/base/MessagePortList.h create mode 100644 dom/base/test/test_bug913761.html create mode 100644 dom/webidl/MessagePortList.webidl diff --git a/content/events/src/nsDOMMessageEvent.cpp b/content/events/src/nsDOMMessageEvent.cpp index 381f5272e..1f3f2c0bb 100644 --- a/content/events/src/nsDOMMessageEvent.cpp +++ b/content/events/src/nsDOMMessageEvent.cpp @@ -4,6 +4,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsDOMMessageEvent.h" +#include "mozilla/dom/MessagePort.h" +#include "mozilla/dom/MessagePortBinding.h" +#include "mozilla/dom/MessagePortList.h" +#include "mozilla/dom/UnionTypes.h" + #include "nsContentUtils.h" #include "jsapi.h" @@ -12,11 +17,15 @@ using namespace mozilla::dom; NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMMessageEvent, nsDOMEvent) tmp->mData = JSVAL_VOID; - NS_IMPL_CYCLE_COLLECTION_UNLINK(mSource) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindowSource) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mPortSource) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mPorts) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMMessageEvent, nsDOMEvent) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSource) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindowSource) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPortSource) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPorts) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsDOMMessageEvent, nsDOMEvent) @@ -79,10 +88,88 @@ nsDOMMessageEvent::GetLastEventId(nsAString& aLastEventId) NS_IMETHODIMP nsDOMMessageEvent::GetSource(nsIDOMWindow** aSource) { - NS_IF_ADDREF(*aSource = mSource); + NS_IF_ADDREF(*aSource = mWindowSource); return NS_OK; } +void +nsDOMMessageEvent::GetSource(Nullable& aValue) const +{ + if (mWindowSource) { + aValue.SetValue().SetAsWindowProxy() = mWindowSource; + } else if (mPortSource) { + aValue.SetValue().SetAsMessagePort() = mPortSource; + } +} + +/* static */ already_AddRefed +nsDOMMessageEvent::Constructor(const mozilla::dom::GlobalObject& aGlobal, + JSContext* aCx, const nsAString& aType, + const mozilla::dom::MessageEventInit& aParam, + mozilla::ErrorResult& aRv) +{ + nsCOMPtr t = + do_QueryInterface(aGlobal.Get()); + nsRefPtr event = + new nsDOMMessageEvent(t, nullptr, nullptr); + + aRv = event->InitEvent(aType, aParam.mBubbles, aParam.mCancelable); + if (aRv.Failed()) { + return nullptr; + } + + bool trusted = event->Init(t); + event->SetTrusted(trusted); + + if (aParam.mData.WasPassed()) { + event->mData = aParam.mData.Value(); + } + + NS_HOLD_JS_OBJECTS(event.get(), nsDOMMessageEvent); + + if (aParam.mOrigin.WasPassed()) { + event->mOrigin = aParam.mOrigin.Value(); + } + + if (aParam.mLastEventId.WasPassed()) { + event->mLastEventId = aParam.mLastEventId.Value(); + } + + if (aParam.mSource) { + nsCOMPtr wrappedNative; + nsContentUtils::XPConnect()-> + GetWrappedNativeOfJSObject(aCx, aParam.mSource, + getter_AddRefs(wrappedNative)); + + if (wrappedNative) { + event->mWindowSource = do_QueryWrappedNative(wrappedNative); + } + + if (!event->mWindowSource) { + MessagePort* port = nullptr; + nsresult rv = mozilla::dom::UnwrapObject(aCx, aParam.mSource, port); + if (NS_FAILED(rv)) { + aRv.Throw(NS_ERROR_INVALID_ARG); + return nullptr; + } + + event->mPortSource = port; + } + } + + if (aParam.mPorts.WasPassed() && !aParam.mPorts.Value().IsNull()) { + nsTArray > ports; + for (uint32_t i = 0, len = aParam.mPorts.Value().Value().Length(); i < len; ++i) { + ports.AppendElement(aParam.mPorts.Value().Value()[i].get()); + } + + event->mPorts = new MessagePortList(static_cast(event), + ports); + } + + return event.forget(); +} + NS_IMETHODIMP nsDOMMessageEvent::InitMessageEvent(const nsAString& aType, bool aCanBubble, @@ -99,7 +186,7 @@ nsDOMMessageEvent::InitMessageEvent(const nsAString& aType, NS_HOLD_JS_OBJECTS(this, nsDOMMessageEvent); mOrigin = aOrigin; mLastEventId = aLastEventId; - mSource = aSource; + mWindowSource = aSource; return NS_OK; } diff --git a/content/events/src/nsDOMMessageEvent.h b/content/events/src/nsDOMMessageEvent.h index 1b540dde7..e0f655827 100644 --- a/content/events/src/nsDOMMessageEvent.h +++ b/content/events/src/nsDOMMessageEvent.h @@ -12,6 +12,13 @@ #include "jsapi.h" #include "mozilla/dom/MessageEventBinding.h" +namespace mozilla { +namespace dom { +class MessagePortList; +class WindowProxyOrMessagePortReturnValue; +} +} + /** * Implements the MessageEvent event, used for cross-document messaging and * server-sent events. @@ -44,31 +51,26 @@ public: JS::Value GetData(JSContext* aCx, mozilla::ErrorResult& aRv); - already_AddRefed GetSource() + void GetSource(Nullable& aValue) const; + + mozilla::dom::MessagePortList* GetPorts() { - nsCOMPtr ret = mSource; - return ret.forget(); + return mPorts; } - void InitMessageEvent(JSContext* aCx, - const nsAString& aType, - bool aCanBubble, - bool aCancelable, - JS::Handle aData, - const nsAString& aOrigin, - const nsAString& aLastEventId, - nsIDOMWindow* aSource, - mozilla::ErrorResult& aRv) - { - aRv = InitMessageEvent(aType, aCanBubble, aCancelable, aData, - aOrigin, aLastEventId, aSource); - } + static already_AddRefed + Constructor(const mozilla::dom::GlobalObject& aGlobal, JSContext* aCx, + const nsAString& aType, + const mozilla::dom::MessageEventInit& aEventInit, + mozilla::ErrorResult& aRv); private: JS::Heap mData; nsString mOrigin; nsString mLastEventId; - nsCOMPtr mSource; + nsCOMPtr mWindowSource; + nsCOMPtr mPortSource; + nsRefPtr mPorts; }; #endif // nsDOMMessageEvent_h__ diff --git a/content/events/test/Makefile.in b/content/events/test/Makefile.in index 9b851c94c..1e28599c8 100644 --- a/content/events/test/Makefile.in +++ b/content/events/test/Makefile.in @@ -102,6 +102,7 @@ MOCHITEST_FILES = \ test_focus_disabled.html \ test_bug847597.html \ test_bug855741.html \ + test_messageEvent.html \ $(NULL) # Disabled on Windows due to intermittent failures. diff --git a/content/events/test/test_messageEvent.html b/content/events/test/test_messageEvent.html new file mode 100644 index 000000000..ff992f3f3 --- /dev/null +++ b/content/events/test/test_messageEvent.html @@ -0,0 +1,66 @@ + + + + + + Test for Bug 848294 + + + + + + + + + diff --git a/dom/base/MessagePort.cpp b/dom/base/MessagePort.cpp index 364075495..770beeacf 100644 --- a/dom/base/MessagePort.cpp +++ b/dom/base/MessagePort.cpp @@ -110,7 +110,8 @@ PostMessageReadStructuredClone(JSContext* cx, uint32_t data, void* closure) { - NS_ASSERTION(closure, "Must have closure!"); + StructuredCloneInfo* scInfo = static_cast(closure); + NS_ASSERTION(scInfo, "Must have scInfo!"); if (tag == SCTAG_DOM_BLOB || tag == SCTAG_DOM_FILELIST) { NS_ASSERTION(!data, "Data should be empty"); @@ -139,6 +140,7 @@ PostMessageReadStructuredClone(JSContext* cx, if (global) { JS::Rooted obj(cx, port->WrapObject(cx, global)); if (JS_WrapObject(cx, obj.address())) { + port->BindToOwner(scInfo->mPort->GetOwner()); return obj; } } @@ -191,7 +193,7 @@ PostMessageWriteStructuredClone(JSContext* cx, MessagePort* port = nullptr; nsresult rv = mozilla::dom::UnwrapObject(cx, obj, port); if (NS_SUCCEEDED(rv)) { - nsRefPtr newPort = port->Clone(scInfo->mPort->GetOwner()); + nsRefPtr newPort = port->Clone(); return JS_WriteUint32Pair(writer, SCTAG_DOM_MESSAGEPORT, 0) && JS_WriteBytes(writer, &newPort, sizeof(newPort)) && @@ -455,9 +457,9 @@ MessagePort::Entangle(MessagePort* aMessagePort) } already_AddRefed -MessagePort::Clone(nsPIDOMWindow* aWindow) +MessagePort::Clone() { - nsRefPtr newPort = new MessagePort(aWindow->GetCurrentInnerWindow()); + nsRefPtr newPort = new MessagePort(nullptr); // Move all the events in the port message queue of original port. newPort->mMessageQueue.SwapElements(mMessageQueue); diff --git a/dom/base/MessagePort.h b/dom/base/MessagePort.h index 09afd1e30..31571a015 100644 --- a/dom/base/MessagePort.h +++ b/dom/base/MessagePort.h @@ -66,7 +66,7 @@ public: // Algorithm and makes the new MessagePort active with the entangled // MessagePort of this object. already_AddRefed - Clone(nsPIDOMWindow* aWindow); + Clone(); private: // Dispatch events from the Message Queue using a nsRunnable. diff --git a/dom/base/MessagePortList.cpp b/dom/base/MessagePortList.cpp new file mode 100644 index 000000000..02a530173 --- /dev/null +++ b/dom/base/MessagePortList.cpp @@ -0,0 +1,29 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* 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 "MessagePortList.h" +#include "mozilla/dom/MessagePortListBinding.h" +#include "mozilla/dom/MessagePort.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_2(MessagePortList, mOwner, mPorts) +NS_IMPL_CYCLE_COLLECTING_ADDREF(MessagePortList) +NS_IMPL_CYCLE_COLLECTING_RELEASE(MessagePortList) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MessagePortList) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +JSObject* +MessagePortList::WrapObject(JSContext* aCx, JS::Handle aScope) +{ + return MessagePortListBinding::Wrap(aCx, aScope, this); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/base/MessagePortList.h b/dom/base/MessagePortList.h new file mode 100644 index 000000000..515f777f6 --- /dev/null +++ b/dom/base/MessagePortList.h @@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* 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 mozilla_dom_MessagePortList_h +#define mozilla_dom_MessagePortList_h + +#include "mozilla/Attributes.h" +#include "mozilla/ErrorResult.h" +#include "nsCycleCollectionParticipant.h" +#include "mozilla/dom/MessagePort.h" +#include "nsWrapperCache.h" +#include "nsAutoPtr.h" +#include "nsTArray.h" + +namespace mozilla { +namespace dom { + +class MessagePortList MOZ_FINAL : public nsISupports + , public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MessagePortList) + +public: + MessagePortList(nsISupports* aOwner, nsTArray >& aPorts) + : mOwner(aOwner) + , mPorts(aPorts) + { + SetIsDOMBinding(); + } + + nsISupports* + GetParentObject() const + { + return mOwner; + } + + virtual JSObject* + WrapObject(JSContext* aCx, JS::Handle aScope) MOZ_OVERRIDE; + + uint32_t + Length() const + { + return mPorts.Length(); + } + + MessagePort* + Item(uint32_t aIndex) + { + return mPorts.SafeElementAt(aIndex); + } + + MessagePort* + IndexedGetter(uint32_t aIndex, bool &aFound) + { + aFound = aIndex < mPorts.Length(); + if (!aFound) { + return nullptr; + } + return mPorts[aIndex]; + } + +public: + nsCOMPtr mOwner; + nsTArray > mPorts; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_MessagePortList_h + diff --git a/dom/base/moz.build b/dom/base/moz.build index d2707d47e..4020ad44a 100644 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -62,6 +62,7 @@ EXPORTS.mozilla.dom += [ 'DOMRequest.h', 'MessageChannel.h', 'MessagePort.h', + 'MessagePortList.h', 'ScreenOrientation.h', 'StructuredCloneTags.h', 'URL.h', @@ -76,6 +77,7 @@ CPP_SOURCES += [ 'Navigator.cpp', 'MessageChannel.cpp', 'MessagePort.cpp', + 'MessagePortList.cpp', 'URL.cpp', 'nsContentPermissionHelper.cpp', 'nsDOMClassInfo.cpp', diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index fa9439485..3358ad59d 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -6680,7 +6680,8 @@ PostMessageReadStructuredClone(JSContext* cx, uint32_t data, void* closure) { - NS_ASSERTION(closure, "Must have closure!"); + StructuredCloneInfo* scInfo = static_cast(closure); + NS_ASSERTION(scInfo, "Must have scInfo!"); if (tag == SCTAG_DOM_BLOB || tag == SCTAG_DOM_FILELIST) { NS_ASSERTION(!data, "Data should be empty"); @@ -6709,6 +6710,7 @@ PostMessageReadStructuredClone(JSContext* cx, if (global) { JS::Rooted obj(cx, port->WrapObject(cx, global)); if (JS_WrapObject(cx, obj.address())) { + port->BindToOwner(scInfo->window); return obj; } } @@ -6759,7 +6761,7 @@ PostMessageWriteStructuredClone(JSContext* cx, MessagePort* port = nullptr; nsresult rv = mozilla::dom::UnwrapObject(cx, obj, port); if (NS_SUCCEEDED(rv) && scInfo->subsumes) { - nsRefPtr newPort = port->Clone(scInfo->window); + nsRefPtr newPort = port->Clone(); return JS_WriteUint32Pair(writer, SCTAG_DOM_MESSAGEPORT, 0) && JS_WriteBytes(writer, &newPort, sizeof(newPort)) && diff --git a/dom/base/test/Makefile.in b/dom/base/test/Makefile.in index 7eddd73c5..7be1f571e 100644 --- a/dom/base/test/Makefile.in +++ b/dom/base/test/Makefile.in @@ -43,6 +43,7 @@ MOCHITEST_FILES = \ test_messageChannel_unshipped.html \ test_messageChannel_pref.html \ test_url.html \ + test_bug913761.html \ $(NULL) MOCHITEST_CHROME_FILES = \ diff --git a/dom/base/test/test_bug913761.html b/dom/base/test/test_bug913761.html new file mode 100644 index 000000000..d8ec0cc9e --- /dev/null +++ b/dom/base/test/test_bug913761.html @@ -0,0 +1,43 @@ + + + + + + + Test for Bug 913761 - basic support + + + + +Mozilla Bug 913761 + + + diff --git a/dom/tests/mochitest/whatwg/test_MessageEvent.html b/dom/tests/mochitest/whatwg/test_MessageEvent.html index 4fb5f8d7d..ce30cd406 100644 --- a/dom/tests/mochitest/whatwg/test_MessageEvent.html +++ b/dom/tests/mochitest/whatwg/test_MessageEvent.html @@ -37,24 +37,11 @@ function sendMsg() { try { - var evt = document.createEvent("MessageEvent"); + var evt = new MessageEvent('message', { + bubbles: bubbles, cancelable: cancelable, data: data, + origin: origin, lastEventId: lastEventId, source: window}); ok(evt instanceof MessageEvent, "I ordered a MessageEvent!"); - if (isMozilla) - { - is(evt.source, null, - "not initialized yet, so null in our implementation"); - is(evt.lastEventId, "", - "not initialized yet, so empty string in our implementation"); - } - - evt.initMessageEvent("message", bubbles, cancelable, data, origin, - lastEventId, null); - ok(evt.source === null, "null source is fine for a MessageEvent"); - - evt.initMessageEvent("message", bubbles, cancelable, data, origin, - lastEventId, window); - is(evt.data, data, "unexpected data"); is(evt.origin, origin, "unexpected origin"); is(evt.lastEventId, lastEventId, "unexpected lastEventId"); diff --git a/dom/tests/mochitest/whatwg/test_MessageEvent_dispatchToOther.html b/dom/tests/mochitest/whatwg/test_MessageEvent_dispatchToOther.html index 2e71575e3..0b742e4f1 100644 --- a/dom/tests/mochitest/whatwg/test_MessageEvent_dispatchToOther.html +++ b/dom/tests/mochitest/whatwg/test_MessageEvent_dispatchToOther.html @@ -25,10 +25,9 @@ function run() { try { - var msg = document.createEvent("MessageEvent"); - msg.initMessageEvent("message", true, true, - "foo", "http://evil.com", "", - window); + var msg = new MessageEvent('message', { bubbles: true, cancelable: true, + data: "foo", origin: "http://evil.com", + source: window }); try { diff --git a/dom/webidl/MessageEvent.webidl b/dom/webidl/MessageEvent.webidl index 4a1e7dbc3..5c1af65f4 100644 --- a/dom/webidl/MessageEvent.webidl +++ b/dom/webidl/MessageEvent.webidl @@ -9,8 +9,8 @@ interface WindowProxy; -interface MessageEvent : Event -{ +[Constructor(DOMString type, optional MessageEventInit eventInitDict)] +interface MessageEvent : Event { /** * Custom data associated with this event. */ @@ -32,21 +32,26 @@ interface MessageEvent : Event readonly attribute DOMString lastEventId; /** - * The window which originated this event. + * The window or the port which originated this event. */ - readonly attribute WindowProxy? source; + readonly attribute (WindowProxy or MessagePort)? source; /** * Initializes this event with the given data, in a manner analogous to * the similarly-named method on the nsIDOMEvent interface, also setting the * data, origin, source, and lastEventId attributes of this appropriately. */ - [Throws] - void initMessageEvent(DOMString aType, - boolean aCanBubble, - boolean aCancelable, - any aData, - DOMString aOrigin, - DOMString aLastEventId, - WindowProxy? aSource); + [Pref="dom.messageChannel.enabled"] + readonly attribute MessagePortList? ports; +}; + +dictionary MessageEventInit : EventInit { + any data; + DOMString origin; + DOMString lastEventId; + + // TODO bug 767926 - This should be: (WindowProxy or MessagePort)? source; + object? source = null; + + sequence? ports; }; diff --git a/dom/webidl/MessagePortList.webidl b/dom/webidl/MessagePortList.webidl new file mode 100644 index 000000000..0483cb160 --- /dev/null +++ b/dom/webidl/MessagePortList.webidl @@ -0,0 +1,11 @@ +/* -*- 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/. + */ + +[NoInterfaceObject, ArrayClass] +interface MessagePortList { + readonly attribute unsigned long length; + getter MessagePort? item(unsigned long index); +}; diff --git a/dom/webidl/WebIDL.mk b/dom/webidl/WebIDL.mk index baf3f7543..c89a710a2 100644 --- a/dom/webidl/WebIDL.mk +++ b/dom/webidl/WebIDL.mk @@ -179,6 +179,7 @@ webidl_files = \ MessageChannel.webidl \ MessageEvent.webidl \ MessagePort.webidl \ + MessagePortList.webidl \ MobileMessageManager.webidl \ MouseEvent.webidl \ MouseScrollEvent.webidl \ diff --git a/js/xpconnect/tests/chrome/test_weakmap_keys_preserved2.xul b/js/xpconnect/tests/chrome/test_weakmap_keys_preserved2.xul index 349dd2d18..c9fb230a5 100644 --- a/js/xpconnect/tests/chrome/test_weakmap_keys_preserved2.xul +++ b/js/xpconnect/tests/chrome/test_weakmap_keys_preserved2.xul @@ -30,8 +30,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=673468 }; let wrappers_as_keys_test = function () { - let e = document.createEvent("MessageEvent"); - e.initMessageEvent("foo", false, false, { dummy: document.createElement("foo") }, null, null, null); + let e = new MessageEvent("foo", { bubbles: false, cancellable: false, + data: { dummy: document.createElement("foo") }}); window.eeeevent = e; let live_dom = e.data.dummy;