Merge remote-tracking branch 'origin/tracking' into custom

This commit is contained in:
roytam1 2023-04-10 07:27:58 +08:00
commit 20b787a4df
37 changed files with 523 additions and 153 deletions

View file

@ -824,6 +824,10 @@ nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor)
aVisitor.mCanHandle = true;
aVisitor.mMayHaveListenerManager = HasListenerManager();
if (IsInShadowTree()) {
aVisitor.mItemInShadowTree = true;
}
// Don't propagate mouseover and mouseout events when mouse is moving
// inside chrome access only content.
bool isAnonForEvents = IsRootOfChromeAccessOnlySubtree();

View file

@ -47,6 +47,7 @@
#include "mozilla/dom/HTMLTemplateElement.h"
#include "mozilla/dom/ipc/BlobChild.h"
#include "mozilla/dom/ipc/BlobParent.h"
#include "mozilla/dom/MessagePort.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/TabParent.h"
@ -9993,3 +9994,71 @@ nsContentUtils::GetClosestNonNativeAnonymousAncestor(Element* aElement)
}
return e;
}
/* static */ nsresult
nsContentUtils::CreateJSValueFromSequenceOfObject(JSContext* aCx,
const Sequence<JSObject*>& aTransfer,
JS::MutableHandle<JS::Value> aValue)
{
if (aTransfer.IsEmpty()) {
return NS_OK;
}
JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, aTransfer.Length()));
if (!array) {
return NS_ERROR_OUT_OF_MEMORY;
}
for (uint32_t i = 0; i < aTransfer.Length(); ++i) {
JS::Rooted<JSObject*> object(aCx, aTransfer[i]);
if (!object) {
continue;
}
if (NS_WARN_IF(!JS_DefineElement(aCx, array, i, object,
JSPROP_ENUMERATE))) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
aValue.setObject(*array);
return NS_OK;
}
/* static */
void nsContentUtils::StructuredClone(JSContext* aCx,
nsIGlobalObject* aGlobal,
JS::Handle<JS::Value> aValue,
const StructuredSerializeOptions& aOptions,
JS::MutableHandle<JS::Value> aRv,
ErrorResult& aError) {
JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
aError = nsContentUtils::CreateJSValueFromSequenceOfObject(
aCx, aOptions.mTransfer, &transferArray);
if (NS_WARN_IF(aError.Failed())) {
return;
}
JS::CloneDataPolicy clonePolicy;
// FIXME: Uncomment once bug 1609990 and bug 1611855 lands.
//clonePolicy.allowIntraClusterClonableSharedObjects();
//clonePolicy.allowSharedMemoryObjects();
StructuredCloneHolder holder(StructuredCloneHolder::CloningSupported,
StructuredCloneHolder::TransferringSupported,
JS::StructuredCloneScope::SameProcessDifferentThread);
holder.Write(aCx, aValue, transferArray, clonePolicy, aError);
if (NS_WARN_IF(aError.Failed())) {
return;
}
// TODO: Pass clonePolicy.
//holder.Read(this, aCx, aRv, clonePolicy, aError);
holder.Read(aGlobal, aCx, aRv, aError);
if (NS_WARN_IF(aError.Failed())) {
return;
}
nsTArray<RefPtr<MessagePort>> ports = holder.TakeTransferredPorts();
Unused << ports;
}

View file

@ -132,6 +132,7 @@ class nsIContentParent;
class TabChild;
class Selection;
class TabParent;
struct StructuredSerializeOptions;
} // namespace dom
namespace ipc {
@ -2812,6 +2813,23 @@ public:
static bool
IsCustomElementsEnabled() { return sIsCustomElementsEnabled; }
static nsresult
CreateJSValueFromSequenceOfObject(JSContext* aCx,
const mozilla::dom::Sequence<JSObject*>& aTransfer,
JS::MutableHandle<JS::Value> aValue);
/**
* This implements the structured cloning algorithm as described by
* https://html.spec.whatwg.org/#structured-cloning.
*/
static void
StructuredClone(JSContext* aCx,
nsIGlobalObject* aGlobal,
JS::Handle<JS::Value> aValue,
const mozilla::dom::StructuredSerializeOptions& aOptions,
JS::MutableHandle<JS::Value> aRv,
mozilla::ErrorResult& aError);
private:
static bool InitializeEventTable();

View file

@ -909,7 +909,8 @@ nsPIDOMWindow<T>::nsPIDOMWindow(nsPIDOMWindowOuter *aOuterWindow)
mOuterWindow(aOuterWindow),
// Make sure no actual window ends up with mWindowID == 0
mWindowID(NextWindowID()), mHasNotifiedGlobalCreated(false),
mMarkedCCGeneration(0), mServiceWorkersTestingEnabled(false)
mMarkedCCGeneration(0), mServiceWorkersTestingEnabled(false),
mEvent(nullptr)
{}
template<class T>
@ -5245,6 +5246,16 @@ nsGlobalWindow::SetOpener(JSContext* aCx, JS::Handle<JS::Value> aOpener,
SetOpenerWindow(outer, false);
}
void
nsGlobalWindow::GetEvent(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval)
{
if (mEvent) {
Unused << nsContentUtils::WrapNative(aCx, mEvent, aRetval);
} else {
aRetval.setUndefined();
}
}
void
nsGlobalWindow::GetStatusOuter(nsAString& aStatus)
{
@ -8886,30 +8897,33 @@ nsGlobalWindow::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
}
void
nsGlobalWindow::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
nsGlobalWindow::PostMessageMoz(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const nsAString& aTargetOrigin,
const Optional<Sequence<JS::Value>>& aTransfer,
const Sequence<JSObject*>& aTransfer,
nsIPrincipal& aSubjectPrincipal,
ErrorResult& aError)
ErrorResult& aRv)
{
JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
if (aTransfer.WasPassed()) {
const Sequence<JS::Value >& values = aTransfer.Value();
// The input sequence only comes from the generated bindings code, which
// ensures it is rooted.
JS::HandleValueArray elements =
JS::HandleValueArray::fromMarkedLocation(values.Length(), values.Elements());
transferArray = JS::ObjectOrNullValue(JS_NewArrayObject(aCx, elements));
if (transferArray.isNull()) {
aError.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransfer,
&transferArray);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
PostMessageMoz(aCx, aMessage, aTargetOrigin, transferArray,
aSubjectPrincipal, aError);
aSubjectPrincipal, aRv);
}
void
nsGlobalWindow::PostMessageMoz(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const WindowPostMessageOptions& aOptions,
nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv)
{
PostMessageMoz(aCx, aMessage, aOptions.mTargetOrigin, aOptions.mTransfer,
aSubjectPrincipal, aRv);
}
class nsCloseEvent : public Runnable {
@ -14641,6 +14655,17 @@ nsGlobalWindow::CreateImageBitmap(const ImageBitmapSource& aImage,
}
}
// https://html.spec.whatwg.org/#structured-cloning
void
nsGlobalWindow::StructuredClone(JSContext* aCx,
JS::Handle<JS::Value> aValue,
const StructuredSerializeOptions& aOptions,
JS::MutableHandle<JS::Value> aRv,
ErrorResult& aError)
{
nsContentUtils::StructuredClone(aCx, this, aValue, aOptions, aRv, aError);
}
// Helper called by methods that move/resize the window,
// to ensure the presContext (if any) is aware of resolution
// change that may happen in multi-monitor configuration.

View file

@ -135,6 +135,7 @@ class TabGroup;
class Timeout;
class U2F;
class WakeLock;
struct WindowPostMessageOptions;
class Worklet;
namespace cache {
class CacheStorage;
@ -845,6 +846,7 @@ public:
already_AddRefed<nsPIDOMWindowOuter> GetOpener() override;
void SetOpener(JSContext* aCx, JS::Handle<JS::Value> aOpener,
mozilla::ErrorResult& aError);
void GetEvent(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval);
already_AddRefed<nsPIDOMWindowOuter> GetParentOuter();
already_AddRefed<nsPIDOMWindowOuter> GetParent(mozilla::ErrorResult& aError);
already_AddRefed<nsPIDOMWindowOuter> GetParent() override;
@ -933,7 +935,12 @@ public:
void Print(mozilla::ErrorResult& aError);
void PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
const nsAString& aTargetOrigin,
const mozilla::dom::Optional<mozilla::dom::Sequence<JS::Value > >& aTransfer,
const mozilla::dom::Sequence<JSObject*>& aTransfer,
nsIPrincipal& aSubjectPrincipal,
mozilla::ErrorResult& aRv);
void PostMessageMoz(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const mozilla::dom::WindowPostMessageOptions& aOptions,
nsIPrincipal& aSubjectPrincipal,
mozilla::ErrorResult& aError);
int32_t SetTimeout(JSContext* aCx, mozilla::dom::Function& aFunction,
@ -1177,6 +1184,10 @@ public:
const mozilla::dom::Sequence<mozilla::dom::ChannelPixelLayout>& aLayout,
mozilla::ErrorResult& aRv);
void StructuredClone(JSContext* aCx, JS::Handle<JS::Value> aValue,
const mozilla::dom::StructuredSerializeOptions& aOptions,
JS::MutableHandle<JS::Value> aRv,
mozilla::ErrorResult& aError);
// ChromeWindow bits. Do NOT call these unless your window is in
// fact an nsGlobalChromeWindow.

View file

@ -194,6 +194,10 @@ protected:
// we have what it takes to do so.
void MaybeCreateDoc();
// The event dispatch code sets and unsets this while keeping
// the event object alive.
nsIDOMEvent* mEvent;
public:
inline bool IsLoadingOrRunningTimeout() const;
@ -789,6 +793,17 @@ public:
return mInnerObjectsFreed;
}
// Sets the event for window.event. Does NOT take ownership, so
// the caller is responsible for clearing the event before the
// event gets deallocated. Pass nullptr to set window.event to
// undefined. Returns the previous value.
nsIDOMEvent* SetEvent(nsIDOMEvent* aEvent)
{
nsIDOMEvent* old = mEvent;
mEvent = aEvent;
return old;
}
/**
* Check whether this window is a secure context.
*/

View file

@ -264,6 +264,16 @@ public:
return mFlags.mRootOfClosedTree;
}
void SetItemInShadowTree(bool aSet)
{
mFlags.mItemInShadowTree = aSet;
}
bool IsItemInShadowTree()
{
return mFlags.mItemInShadowTree;
}
void SetIsSlotInClosedTree(bool aSet)
{
mFlags.mIsSlotInClosedTree = aSet;
@ -351,7 +361,8 @@ public:
mManager->HandleEvent(aVisitor.mPresContext, aVisitor.mEvent,
&aVisitor.mDOMEvent,
CurrentTarget(),
&aVisitor.mEventStatus);
&aVisitor.mEventStatus,
IsItemInShadowTree());
NS_ASSERTION(aVisitor.mEvent->mCurrentTarget == nullptr,
"CurrentTarget should be null!");
}
@ -384,6 +395,7 @@ private:
bool mWantsPreHandleEvent : 1;
bool mPreHandleEventOnly : 1;
bool mRootOfClosedTree : 1;
bool mItemInShadowTree : 1;
bool mIsSlotInClosedTree : 1;
bool mIsChromeHandler : 1;
private:
@ -433,6 +445,7 @@ EventTargetChainItem::GetEventTargetParent(EventChainPreVisitor& aVisitor)
SetWantsPreHandleEvent(aVisitor.mWantsPreHandleEvent);
SetPreHandleEventOnly(aVisitor.mWantsPreHandleEvent && !aVisitor.mCanHandle);
SetRootOfClosedTree(aVisitor.mRootOfClosedTree);
SetItemInShadowTree(aVisitor.mItemInShadowTree);
SetRetargetedRelatedTarget(aVisitor.mRetargetedRelatedTarget);
mItemFlags = aVisitor.mItemFlags;
mItemData = aVisitor.mItemData;

View file

@ -125,6 +125,7 @@ public:
, mMayHaveListenerManager(true)
, mWantsPreHandleEvent(false)
, mRootOfClosedTree(false)
, mItemInShadowTree(false)
, mParentIsSlotInClosedTree(false)
, mParentIsChromeHandler(false)
, mRelatedTargetRetargetedInCurrentScope(false)
@ -147,6 +148,7 @@ public:
mMayHaveListenerManager = true;
mWantsPreHandleEvent = false;
mRootOfClosedTree = false;
mItemInShadowTree = false;
mParentIsSlotInClosedTree = false;
mParentIsChromeHandler = false;
// Note, we don't clear mRelatedTargetRetargetedInCurrentScope explicitly,
@ -236,6 +238,12 @@ public:
*/
bool mRootOfClosedTree;
/**
* If target is node and its root is a shadow root.
* https://dom.spec.whatwg.org/#event-path-item-in-shadow-tree
*/
bool mItemInShadowTree;
/**
* True if mParentTarget is HTMLSlotElement in a closed shadow tree and the
* current target is assigned to that slot.

View file

@ -1121,6 +1121,38 @@ EventListenerManager::GetLegacyEventMessage(EventMessage aEventMessage) const
}
}
already_AddRefed<nsPIDOMWindowInner>
EventListenerManager::WindowFromListener(Listener* aListener,
bool aItemInShadowTree)
{
nsCOMPtr<nsPIDOMWindowInner> innerWindow;
if (!aItemInShadowTree) {
if (aListener->mListener.HasWebIDLCallback()) {
CallbackObject* callback = aListener->mListener.GetWebIDLCallback();
nsGlobalWindow* win;
if (callback) {
// Find the real underlying callback.
JSObject* realCallback =
js::UncheckedUnwrap(callback->CallbackPreserveColor());
// Get the global for this callback.
win = mIsMainThreadELM ?
xpc::WindowGlobalOrNull(realCallback) :
nullptr;
}
if (win && win->IsInnerWindow()) {
innerWindow = win->AsInner(); // Can be nullptr
}
} else {
// Can't get the global from
// listener->mListener.GetXPCOMCallback().
// In most cases, it would be the same as for
// the target, so let's do that.
innerWindow = GetInnerWindowForTarget(); // Can be nullptr
}
}
return innerWindow.forget();
}
/**
* Causes a check for event listeners and processing by them if they exist.
* @param an event listener
@ -1131,7 +1163,8 @@ EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
WidgetEvent* aEvent,
nsIDOMEvent** aDOMEvent,
EventTarget* aCurrentTarget,
nsEventStatus* aEventStatus)
nsEventStatus* aEventStatus,
bool aItemInShadowTree)
{
//Set the value of the internal PreventDefault flag properly based on aEventStatus
if (!aEvent->DefaultPrevented() &&
@ -1223,9 +1256,18 @@ EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
listener = listenerHolder.ptr();
hasRemovedListener = true;
}
nsCOMPtr<nsPIDOMWindowInner> innerWindow =
WindowFromListener(listener, aItemInShadowTree);
nsIDOMEvent* oldWindowEvent = nullptr;
if (innerWindow) {
oldWindowEvent = innerWindow->SetEvent(*aDOMEvent);
}
if (NS_FAILED(HandleEventSubType(listener, *aDOMEvent, aCurrentTarget))) {
aEvent->mFlags.mExceptionWasRaised = true;
}
if (innerWindow) {
Unused << innerWindow->SetEvent(oldWindowEvent);
}
aEvent->mFlags.mInPassiveListener = false;
if (needsEndEventMarker) {

View file

@ -350,7 +350,8 @@ public:
WidgetEvent* aEvent,
nsIDOMEvent** aDOMEvent,
dom::EventTarget* aCurrentTarget,
nsEventStatus* aEventStatus)
nsEventStatus* aEventStatus,
bool aItemInShadowTree)
{
if (mListeners.IsEmpty() || aEvent->PropagationStopped()) {
return;
@ -371,7 +372,7 @@ public:
return;
}
HandleEventInternal(aPresContext, aEvent, aDOMEvent, aCurrentTarget,
aEventStatus);
aEventStatus, aItemInShadowTree);
}
/**
@ -482,7 +483,8 @@ protected:
WidgetEvent* aEvent,
nsIDOMEvent** aDOMEvent,
dom::EventTarget* aCurrentTarget,
nsEventStatus* aEventStatus);
nsEventStatus* aEventStatus,
bool aItemInShadowTree);
nsresult HandleEventSubType(Listener* aListener,
nsIDOMEvent* aDOMEvent,
@ -573,6 +575,10 @@ public:
return typedHandler ? typedHandler->OnBeforeUnloadEventHandler() : nullptr;
}
private:
already_AddRefed<nsPIDOMWindowInner> WindowFromListener(Listener* aListener,
bool aItemInShadowTree);
protected:
/**
* Helper method for implementing the various Get*EventHandler above. Will

View file

@ -394,52 +394,39 @@ MessagePort::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
}
void
MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
MessagePort::PostMessage(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const Sequence<JSObject*>& aTransferable,
ErrorResult& aRv)
{
// We *must* clone the data here, or the JS::Value could be modified
// by script
JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
if (aTransferable.WasPassed()) {
const Sequence<JS::Value>& realTransferable = aTransferable.Value();
// Here we want to check if the transerable object list contains
// this port. No other checks are done.
for (const JS::Value& value : realTransferable) {
if (!value.isObject()) {
continue;
}
JS::Rooted<JSObject*> object(aCx, &value.toObject());
MessagePort* port = nullptr;
nsresult rv = UNWRAP_OBJECT(MessagePort, &object, port);
if (NS_FAILED(rv)) {
continue;
}
if (port == this) {
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
return;
}
// Here we want to check if the transferable object list contains
// this port.
for (uint32_t i = 0; i < aTransferable.Length(); ++i) {
JSObject* rawObject = aTransferable[i];
if (!rawObject) {
continue;
}
// The input sequence only comes from the generated bindings code, which
// ensures it is rooted.
JS::HandleValueArray elements =
JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(),
realTransferable.Elements());
JS::Rooted<JSObject*> object(aCx, rawObject);
JSObject* array =
JS_NewArrayObject(aCx, elements);
if (!array) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
MessagePort* port = nullptr;
nsresult rv = UNWRAP_OBJECT(MessagePort, &object, port);
if (NS_SUCCEEDED(rv) && port == this) {
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
return;
}
}
transferable.setObject(*array);
JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx,
aTransferable,
&transferable);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
RefPtr<SharedMessagePortMessage> data = new SharedMessagePortMessage();
@ -508,6 +495,14 @@ MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
mActor->SendPostMessages(messages);
}
void
MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
const StructuredSerializeOptions& aOptions,
ErrorResult& aRv)
{
PostMessage(aCx, aMessage, aOptions.mTransfer, aRv);
}
void
MessagePort::Start()
{

View file

@ -26,6 +26,7 @@ class MessagePortIdentifier;
class MessagePortMessage;
class PostMessageRunnable;
class SharedMessagePortMessage;
struct StructuredSerializeOptions;
namespace workers {
class WorkerHolder;
@ -62,7 +63,12 @@ public:
void
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
const Sequence<JSObject*>& aTransferable,
ErrorResult& aRv);
void
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
const StructuredSerializeOptions& aOptions,
ErrorResult& aRv);
void Start();

View file

@ -383,13 +383,6 @@ var tests =
hasWrongReturnOriginBug: true
},
// 55
{
args: ["NOT-RECEIVED", undefined],
source: "sameDomain",
name: "SyntaxError",
code: DOMException.SYNTAX_ERR
},
];
function allTests(callback)

View file

@ -15,7 +15,9 @@ interface Client {
readonly attribute DOMString id;
[Throws]
void postMessage(any message, optional sequence<Transferable> transfer);
void postMessage(any message, sequence<object> transferable);
[Throws]
void postMessage(any message, optional StructuredSerializeOptions options);
};
[Exposed=ServiceWorker]

View file

@ -16,7 +16,9 @@
Exposed=DedicatedWorker]
interface DedicatedWorkerGlobalScope : WorkerGlobalScope {
[Throws]
void postMessage(any message, optional sequence<any> transfer);
void postMessage(any message, sequence<object> transfer);
[Throws]
void postMessage(any message, optional StructuredSerializeOptions options);
attribute EventHandler onmessage;
};

View file

@ -10,7 +10,9 @@
[Exposed=(Window,Worker,System)]
interface MessagePort : EventTarget {
[Throws]
void postMessage(any message, optional sequence<Transferable> transferable);
void postMessage(any message, sequence<object> transferable);
[Throws]
void postMessage(any message, optional StructuredSerializeOptions options);
void start();
void close();
@ -19,3 +21,8 @@ interface MessagePort : EventTarget {
attribute EventHandler onmessage;
};
// MessagePort implements Transferable;
// Used to declare which objects should be transferred.
dictionary StructuredSerializeOptions {
sequence<object> transfer = [];
};

View file

@ -19,9 +19,10 @@ interface ServiceWorker : EventTarget {
attribute EventHandler onstatechange;
// FIXME(catalinb): Should inherit this from Worker.
[Throws]
void postMessage(any message, optional sequence<Transferable> transferable);
void postMessage(any message, sequence<object> transferable);
[Throws]
void postMessage(any message, optional StructuredSerializeOptions options);
};
ServiceWorker implements AbstractWorker;

View file

@ -22,7 +22,6 @@ interface IID;
interface nsIBrowserDOMWindow;
interface nsIMessageBroadcaster;
interface nsIDOMCrypto;
typedef any Transferable;
// http://www.whatwg.org/specs/web-apps/current-work/
[PrimaryGlobal, LegacyUnenumerableNamedProperties, NeedResolve]
@ -51,6 +50,7 @@ typedef any Transferable;
[Throws] void stop();
[Throws, CrossOriginCallable, UnsafeInPrerendering] void focus();
[Throws, CrossOriginCallable] void blur();
[Replaceable, Pref="dom.window.event.enabled"] readonly attribute any event;
// other browsing contexts
[Replaceable, Throws, CrossOriginReadable] readonly attribute WindowProxy frames;
@ -80,7 +80,9 @@ typedef any Transferable;
[Throws, UnsafeInPrerendering] void print();
[Throws, CrossOriginCallable, NeedsSubjectPrincipal]
void postMessage(any message, DOMString targetOrigin, optional sequence<Transferable> transfer);
void postMessage(any message, DOMString targetOrigin, optional sequence<object> transfer = []);
[Throws, CrossOriginCallable, NeedsSubjectPrincipal]
void postMessage(any message, optional WindowPostMessageOptions options);
// also has obsolete members
};
@ -486,3 +488,7 @@ callback IdleRequestCallback = void (IdleDeadline deadline);
partial interface Window {
[ChromeOnly] readonly attribute boolean isSecureContextIfOpenerIgnored;
};
dictionary WindowPostMessageOptions : StructuredSerializeOptions {
USVString targetOrigin = "/";
};

View file

@ -44,6 +44,10 @@ interface WindowOrWorkerGlobalScope {
Promise<ImageBitmap> createImageBitmap(ImageBitmapSource aImage);
[Throws]
Promise<ImageBitmap> createImageBitmap(ImageBitmapSource aImage, long aSx, long aSy, long aSw, long aSh);
// structured cloning
[Throws]
any structuredClone(any value, optional StructuredSerializeOptions options);
};
// https://fetch.spec.whatwg.org/#fetch-method

View file

@ -19,7 +19,9 @@ interface Worker : EventTarget {
void terminate();
[Throws]
void postMessage(any message, optional sequence<any> transfer);
void postMessage(any message, sequence<object> transfer);
[Throws]
void postMessage(any message, optional StructuredSerializeOptions options);
attribute EventHandler onmessage;
};

View file

@ -14,6 +14,7 @@
#include "mozilla/Preferences.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/MessagePortBinding.h"
#include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
#ifdef XP_WIN
@ -77,7 +78,7 @@ ServiceWorker::GetScriptURL(nsString& aURL) const
void
ServiceWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
const Sequence<JSObject*>& aTransferable,
ErrorResult& aRv)
{
if (State() == ServiceWorkerState::Redundant) {
@ -97,6 +98,15 @@ ServiceWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
aRv = workerPrivate->SendMessageEvent(aCx, aMessage, aTransferable, Move(clientInfo));
}
void
ServiceWorker::PostMessage(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const StructuredSerializeOptions& aOptions,
ErrorResult& aRv)
{
PostMessage(aCx, aMessage, aOptions.mTransfer, aRv);
}
} // namespace workers
} // namespace dom
} // namespace mozilla

View file

@ -15,6 +15,8 @@ class nsPIDOMWindowInner;
namespace mozilla {
namespace dom {
struct StructuredSerializeOptions;
namespace workers {
class ServiceWorkerInfo;
@ -63,7 +65,13 @@ public:
void
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
const Sequence<JSObject*>& aTransferable,
ErrorResult& aRv);
void
PostMessage(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const StructuredSerializeOptions& aOptions,
ErrorResult& aRv);
private:

View file

@ -11,6 +11,7 @@
#include "mozilla/dom/Navigator.h"
#include "mozilla/dom/ServiceWorkerMessageEvent.h"
#include "mozilla/dom/ServiceWorkerMessageEventBinding.h"
#include "mozilla/dom/MessagePortBinding.h"
#include "nsGlobalWindow.h"
#include "nsIBrowserDOMWindow.h"
#include "nsIDocument.h"
@ -190,7 +191,7 @@ private:
void
ServiceWorkerClient::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
const Sequence<JSObject*>& aTransferable,
ErrorResult& aRv)
{
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
@ -198,20 +199,11 @@ ServiceWorkerClient::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
workerPrivate->AssertIsOnWorkerThread();
JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
if (aTransferable.WasPassed()) {
const Sequence<JS::Value>& realTransferable = aTransferable.Value();
JS::HandleValueArray elements =
JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(),
realTransferable.Elements());
JSObject* array = JS_NewArrayObject(aCx, elements);
if (!array) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
transferable.setObject(*array);
aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx,
aTransferable,
&transferable);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
RefPtr<ServiceWorkerClientPostMessageRunnable> runnable =
@ -229,3 +221,11 @@ ServiceWorkerClient::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
}
}
void
ServiceWorkerClient::PostMessage(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const StructuredSerializeOptions& aOptions,
ErrorResult& aRv)
{
PostMessage(aCx, aMessage, aOptions.mTransfer, aRv);
}

View file

@ -91,7 +91,13 @@ public:
void
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
const Sequence<JSObject*>& aTransferable,
ErrorResult& aRv);
void
PostMessage(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const StructuredSerializeOptions& aOptions,
ErrorResult& aRv);
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;

View file

@ -141,7 +141,7 @@ NS_IMPL_ISUPPORTS0(MessageWaitUntilHandler)
nsresult
ServiceWorkerPrivate::SendMessageEvent(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
const Sequence<JSObject*>& aTransferable,
UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
{
ErrorResult rv(SpawnWorkerIfNeeded(MessageEvent, nullptr));

View file

@ -74,7 +74,7 @@ public:
nsresult
SendMessageEvent(JSContext* aCx, JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
const Sequence<JSObject*>& aTransferable,
UniquePtr<ServiceWorkerClientInfo>&& aClientInfo);
// This is used to validate the worker script and continue the installation

View file

@ -22,6 +22,7 @@
using mozilla::dom::Optional;
using mozilla::dom::Sequence;
using mozilla::dom::MessagePort;
using mozilla::dom::StructuredSerializeOptions;
using namespace mozilla;
USING_WORKERS_NAMESPACE
@ -142,7 +143,7 @@ SharedWorker::Close()
void
SharedWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
const Sequence<JSObject*>& aTransferable,
ErrorResult& aRv)
{
AssertIsOnMainThread();
@ -152,6 +153,15 @@ SharedWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
mMessagePort->PostMessage(aCx, aMessage, aTransferable, aRv);
}
void
SharedWorker::PostMessage(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const StructuredSerializeOptions& aOptions,
ErrorResult& aRv)
{
PostMessage(aCx, aMessage, aOptions.mTransfer, aRv);
}
NS_IMPL_ADDREF_INHERITED(SharedWorker, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(SharedWorker, DOMEventTargetHelper)

View file

@ -94,8 +94,15 @@ private:
// Only called by MessagePort.
void
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
PostMessage(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const Sequence<JSObject*>& aTransferable,
ErrorResult& aRv);
void
PostMessage(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const StructuredSerializeOptions& aOptions,
ErrorResult& aRv);
};

View file

@ -3006,13 +3006,12 @@ WorkerPrivateParent<Derived>::ForgetMainThreadObjects(
template <class Derived>
void
WorkerPrivateParent<Derived>::PostMessageInternal(
JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
PromiseNativeHandler* aHandler,
ErrorResult& aRv)
WorkerPrivateParent<Derived>::PostMessageInternal(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const Sequence<JSObject*>& aTransferable,
UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
PromiseNativeHandler* aHandler,
ErrorResult& aRv)
{
AssertIsOnParentThread();
@ -3024,22 +3023,11 @@ WorkerPrivateParent<Derived>::PostMessageInternal(
}
JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
if (aTransferable.WasPassed()) {
const Sequence<JS::Value>& realTransferable = aTransferable.Value();
// The input sequence only comes from the generated bindings code, which
// ensures it is rooted.
JS::HandleValueArray elements =
JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(),
realTransferable.Elements());
JSObject* array =
JS_NewArrayObject(aCx, elements);
if (!array) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
transferable.setObject(*array);
aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx,
aTransferable,
&transferable);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
RefPtr<MessageEventRunnable> runnable =
@ -3083,18 +3071,31 @@ WorkerPrivateParent<Derived>::PostMessageInternal(
template <class Derived>
void
WorkerPrivateParent<Derived>::PostMessage(
JSContext* aCx, JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const Sequence<JSObject*>& aTransferable,
ErrorResult& aRv)
{
PostMessageInternal(aCx, aMessage, aTransferable, nullptr, nullptr, aRv);
}
template <class Derived>
void
WorkerPrivateParent<Derived>::PostMessage(
JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const StructuredSerializeOptions& aOptions,
ErrorResult& aRv)
{
PostMessageInternal(aCx, aMessage, aOptions.mTransfer, nullptr, nullptr, aRv);
}
template <class Derived>
void
WorkerPrivateParent<Derived>::PostMessageToServiceWorker(
JSContext* aCx, JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const Sequence<JSObject*>& aTransferable,
UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
PromiseNativeHandler* aHandler,
ErrorResult& aRv)
@ -5677,27 +5678,17 @@ void
WorkerPrivate::PostMessageToParentInternal(
JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
const Sequence<JSObject*>& aTransferable,
ErrorResult& aRv)
{
AssertIsOnWorkerThread();
JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
if (aTransferable.WasPassed()) {
const Sequence<JS::Value>& realTransferable = aTransferable.Value();
// The input sequence only comes from the generated bindings code, which
// ensures it is rooted.
JS::HandleValueArray elements =
JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(),
realTransferable.Elements());
JSObject* array = JS_NewArrayObject(aCx, elements);
if (!array) {
aRv = NS_ERROR_OUT_OF_MEMORY;
return;
}
transferable.setObject(*array);
aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx,
aTransferable,
&transferable);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
RefPtr<MessageEventRunnable> runnable =

View file

@ -68,6 +68,7 @@ class PromiseNativeHandler;
class StructuredCloneHolder;
class WorkerDebuggerGlobalScope;
class WorkerGlobalScope;
struct StructuredSerializeOptions;
} // namespace dom
namespace ipc {
class PrincipalInfo;
@ -287,7 +288,7 @@ private:
void
PostMessageInternal(JSContext* aCx, JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
const Sequence<JSObject*>& aTransferable,
UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
PromiseNativeHandler* aHandler,
ErrorResult& aRv);
@ -400,12 +401,18 @@ public:
void
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
const Sequence<JSObject*>& aTransferable,
ErrorResult& aRv);
void
PostMessage(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const StructuredSerializeOptions& aOptions,
ErrorResult& aRv);
void
PostMessageToServiceWorker(JSContext* aCx, JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
const Sequence<JSObject*>& aTransferable,
UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
PromiseNativeHandler* aHandler,
ErrorResult& aRv);
@ -1161,18 +1168,17 @@ public:
void
PostMessageToParent(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
const Sequence<JSObject*>& aTransferable,
ErrorResult& aRv)
{
PostMessageToParentInternal(aCx, aMessage, aTransferable, aRv);
}
void
PostMessageToParentMessagePort(
JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
ErrorResult& aRv);
PostMessageToParentMessagePort(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const Sequence<JSObject*>& aTransferable,
ErrorResult& aRv);
void
EnterDebuggerEventLoop();
@ -1471,7 +1477,7 @@ private:
void
PostMessageToParentInternal(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
const Sequence<JSObject*>& aTransferable,
ErrorResult& aRv);
void

View file

@ -7,6 +7,7 @@
#include "jsapi.h"
#include "mozilla/EventListenerManager.h"
#include "mozilla/Unused.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/Console.h"
#include "mozilla/dom/DedicatedWorkerGlobalScopeBinding.h"
@ -492,6 +493,15 @@ WorkerGlobalScope::CreateImageBitmap(const ImageBitmapSource& aImage,
}
}
// https://html.spec.whatwg.org/#structured-cloning
void WorkerGlobalScope::StructuredClone(JSContext* aCx,
JS::Handle<JS::Value> aValue,
const StructuredSerializeOptions& aOptions,
JS::MutableHandle<JS::Value> aRv,
ErrorResult& aError) {
nsContentUtils::StructuredClone(aCx, this, aValue, aOptions, aRv, aError);
}
DedicatedWorkerGlobalScope::DedicatedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate)
: WorkerGlobalScope(aWorkerPrivate)
{
@ -535,13 +545,22 @@ DedicatedWorkerGlobalScope::WrapGlobalObject(JSContext* aCx,
void
DedicatedWorkerGlobalScope::PostMessage(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
const Sequence<JSObject*>& aTransferable,
ErrorResult& aRv)
{
mWorkerPrivate->AssertIsOnWorkerThread();
mWorkerPrivate->PostMessageToParent(aCx, aMessage, aTransferable, aRv);
}
void
DedicatedWorkerGlobalScope::PostMessage(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const StructuredSerializeOptions& aOptions,
ErrorResult& aRv)
{
PostMessage(aCx, aMessage, aOptions.mTransfer, aRv);
}
SharedWorkerGlobalScope::SharedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate,
const nsCString& aName)
: WorkerGlobalScope(aWorkerPrivate), mName(aName)

View file

@ -24,6 +24,7 @@ class Function;
class IDBFactory;
enum class ImageBitmapFormat : uint32_t;
class Performance;
struct StructuredSerializeOptions;
class Promise;
class RequestOrUSVString;
class ServiceWorkerRegistration;
@ -182,6 +183,11 @@ public:
const mozilla::dom::Sequence<mozilla::dom::ChannelPixelLayout>& aLayout,
mozilla::ErrorResult& aRv);
void StructuredClone(JSContext* aCx, JS::Handle<JS::Value> aValue,
const StructuredSerializeOptions& aOptions,
JS::MutableHandle<JS::Value> aRv,
ErrorResult& aError);
bool
WindowInteractionAllowed() const
{
@ -215,7 +221,13 @@ public:
void
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
const Sequence<JSObject*>& aTransferable,
ErrorResult& aRv);
void
PostMessage(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const StructuredSerializeOptions& aOptions,
ErrorResult& aRv);
IMPL_EVENT_HANDLER(message)

View file

@ -326,6 +326,43 @@ SandboxCreateFetch(JSContext* cx, HandleObject obj)
dom::HeadersBinding::GetConstructorObject(cx);
}
static bool SandboxStructuredClone(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
if (!args.requireAtLeast(cx, "structuredClone", 1)) {
return false;
}
RootedDictionary<dom::StructuredSerializeOptions> options(cx);
if (!options.Init(cx, args.hasDefined(1) ? args[1] : JS::NullHandleValue,
"Argument 2", false)) {
return false;
}
nsIGlobalObject* global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(cx));
if (!global) {
JS_ReportErrorASCII(cx, "structuredClone: Missing global");
return false;
}
JS::Rooted<JS::Value> result(cx);
ErrorResult rv;
nsContentUtils::StructuredClone(cx, global, args[0], options, &result, rv);
if (rv.MaybeSetPendingException(cx)) {
return false;
}
args.rval().set(result);
return true;
}
static bool SandboxCreateStructuredClone(JSContext* cx, HandleObject obj) {
MOZ_ASSERT(JS_IsGlobalObject(obj));
return JS_DefineFunction(cx, obj, "structuredClone", SandboxStructuredClone,
1, 0);
}
static bool
SandboxIsProxy(JSContext* cx, unsigned argc, Value* vp)
{
@ -931,6 +968,8 @@ xpc::GlobalProperties::Parse(JSContext* cx, JS::HandleObject obj)
#endif
} else if (!strcmp(name.ptr(), "fetch")) {
fetch = true;
} else if (!strcmp(name.ptr(), "structuredClone")) {
structuredClone = true;
} else if (!strcmp(name.ptr(), "caches")) {
caches = true;
} else if (!strcmp(name.ptr(), "FileReader")) {
@ -1006,6 +1045,9 @@ xpc::GlobalProperties::Define(JSContext* cx, JS::HandleObject obj)
if (fetch && !SandboxCreateFetch(cx, obj))
return false;
if (structuredClone && !SandboxCreateStructuredClone(cx, obj))
return false;
if (caches && !dom::cache::CacheStorage::DefineCaches(cx, obj))
return false;

View file

@ -2879,6 +2879,7 @@ struct GlobalProperties {
bool crypto : 1;
bool rtcIdentityProvider : 1;
bool fetch : 1;
bool structuredClone : 1;
bool caches : 1;
bool fileReader: 1;
private:

View file

@ -0,0 +1,25 @@
function run_test() {
var sb = new Cu.Sandbox('http://www.example.com',
{ wantGlobalProperties: ["structuredClone"] });
sb.equal = equal;
sb.testing = Components.utils.cloneInto({xyz: 123}, sb);
Cu.evalInSandbox(`
equal(structuredClone("abc"), "abc");
var obj = {a: 1};
obj.self = obj;
var clone = structuredClone(obj);
equal(clone.a, 1);
equal(clone.self, clone);
var ab = new ArrayBuffer(1);
clone = structuredClone(ab, {transfer: [ab]});
equal(clone.byteLength, 1);
equal(ab.byteLength, 0);
clone = structuredClone(testing);
equal(clone.xyz, 123);
`, sb);
}

View file

@ -114,6 +114,7 @@ skip-if = os == "android" # native test components aren't available on Android
[test_css.js]
[test_rtcIdentityProvider.js]
[test_sandbox_atob.js]
[test_structuredClone.js]
[test_isProxy.js]
[test_getObjectPrincipal.js]
[test_sandbox_name.js]

View file

@ -5252,6 +5252,9 @@ pref("prompts.content_handling_dialog_modal.enabled", false);
// Whether module scripts (<script type="module">) are enabled for content.
pref("dom.moduleScripts.enabled", true);
// Whether read-only Window property event is enabled for content.
pref("dom.window.event.enabled", false);
// Report details when a media source error occurs?
// Enabled by default in debug builds, otherwise should be explicitly enabled
// by the user to prevent XO leaking of the response status (CVE-2020-15666)