/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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 mozilla_dom_PostMessageEvent_h
#define mozilla_dom_PostMessageEvent_h

#include "js/StructuredClone.h"
#include "js/TypeDecls.h"
#include "mozilla/Maybe.h"
#include "mozilla/MaybeOneOf.h"
#include "mozilla/RefPtr.h"
#include "mozilla/dom/DOMTypes.h"
#include "mozilla/dom/StructuredCloneHolder.h"
#include "mozilla/dom/ipc/StructuredCloneData.h"
#include "nsCOMPtr.h"
#include "nsThreadUtils.h"

class nsGlobalWindowOuter;
class nsGlobalWindowInner;
class nsIPrincipal;
class nsIURI;

namespace mozilla {
class ErrorResult;

namespace dom {

class BrowsingContext;
class Event;
class EventTarget;

/**
 * Class used to represent events generated by calls to Window.postMessage,
 * which asynchronously creates and dispatches events.
 */
class PostMessageEvent final : public Runnable {
 public:
  NS_DECL_NSIRUNNABLE

  // aCallerWindowID should not be 0.
  PostMessageEvent(BrowsingContext* aSource, const nsAString& aCallerOrigin,
                   nsGlobalWindowOuter* aTargetWindow,
                   nsIPrincipal* aProvidedPrincipal, uint64_t aCallerWindowID,
                   nsIURI* aCallerURI, const nsCString& aScriptLocation,
                   const Maybe<nsID>& aCallerAgentClusterId)
      : PostMessageEvent(aSource, aCallerOrigin, aTargetWindow,
                         aProvidedPrincipal, aCallerWindowID, aCallerURI,
                         aScriptLocation, false, aCallerAgentClusterId) {}

  // To be used when the caller's window lives in a different process.
  PostMessageEvent(BrowsingContext* aSource, const nsAString& aCallerOrigin,
                   nsGlobalWindowOuter* aTargetWindow,
                   nsIPrincipal* aProvidedPrincipal, uint64_t aCallerWindowID,
                   nsIURI* aCallerURI, const nsCString& aScriptLocation,
                   bool aIsFromPrivateWindow)
      : PostMessageEvent(aSource, aCallerOrigin, aTargetWindow,
                         aProvidedPrincipal, aCallerWindowID, aCallerURI,
                         aScriptLocation, aIsFromPrivateWindow, Nothing()) {}

  void Write(JSContext* aCx, JS::Handle<JS::Value> aMessage,
             JS::Handle<JS::Value> aTransfer,
             const JS::CloneDataPolicy& aClonePolicy, ErrorResult& aError) {
    mHolder.construct<StructuredCloneHolder>(
        StructuredCloneHolder::CloningSupported,
        StructuredCloneHolder::TransferringSupported,
        JS::StructuredCloneScope::SameProcess);
    mHolder.ref<StructuredCloneHolder>().Write(aCx, aMessage, aTransfer,
                                               aClonePolicy, aError);
  }
  void UnpackFrom(const ClonedOrErrorMessageData& aMessageData) {
    if (aMessageData.type() != ClonedOrErrorMessageData::TClonedMessageData) {
      return;
    }

    mHolder.construct<ipc::StructuredCloneData>();
    // FIXME Want to steal!
    //       See https://bugzilla.mozilla.org/show_bug.cgi?id=1516349.
    mHolder.ref<ipc::StructuredCloneData>().CopyFromClonedMessageData(
        aMessageData);
  }

  void DispatchToTargetThread(ErrorResult& aError);

 private:
  PostMessageEvent(BrowsingContext* aSource, const nsAString& aCallerOrigin,
                   nsGlobalWindowOuter* aTargetWindow,
                   nsIPrincipal* aProvidedPrincipal, uint64_t aCallerWindowID,
                   nsIURI* aCallerURI, const nsCString& aScriptLocation,
                   bool aIsFromPrivateWindow,
                   const Maybe<nsID>& aCallerAgentClusterId);
  ~PostMessageEvent();

  MOZ_CAN_RUN_SCRIPT void Dispatch(nsGlobalWindowInner* aTargetWindow,
                                   Event* aEvent);

  // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
  MOZ_CAN_RUN_SCRIPT_BOUNDARY void DispatchError(
      JSContext* aCx, nsGlobalWindowInner* aTargetWindow,
      mozilla::dom::EventTarget* aEventTarget);

  RefPtr<BrowsingContext> mSource;
  nsString mCallerOrigin;
  RefPtr<nsGlobalWindowOuter> mTargetWindow;
  nsCOMPtr<nsIPrincipal> mProvidedPrincipal;
  // If the postMessage call was made on a WindowProxy whose Window lives in a
  // separate process then mHolder will contain a StructuredCloneData, else
  // it'll contain a StructuredCloneHolder.
  MaybeOneOf<StructuredCloneHolder, ipc::StructuredCloneData> mHolder;
  uint64_t mCallerWindowID;
  const Maybe<nsID> mCallerAgentClusterId;
  nsCOMPtr<nsIURI> mCallerURI;
  // if callerURI is null, then we can use script location for reporting errors
  // to console
  const Maybe<nsCString> mScriptLocation;
  // This is only set to a relevant value if mCallerWindowID doesn't contain a
  // value.
  bool mIsFromPrivateWindow;
};

}  // namespace dom
}  // namespace mozilla

#endif  // mozilla_dom_PostMessageEvent_h
