Bug 1258600 - Part2: Implement onconnect, onclose and onterminate event handlers, r=smaug

This commit is contained in:
Kershaw Chang 2016-05-30 08:48:00 +02:00
Родитель 280397cf5c
Коммит 7649e6aa87
15 изменённых файлов: 272 добавлений и 52 удалений

Просмотреть файл

@ -927,6 +927,7 @@ GK_ATOM(onstorageareachanged, "onstorageareachanged")
GK_ATOM(onsubmit, "onsubmit")
GK_ATOM(onsuccess, "onsuccess")
GK_ATOM(ontypechange, "ontypechange")
GK_ATOM(onterminate, "onterminate")
GK_ATOM(ontext, "ontext")
GK_ATOM(ontoggle, "ontoggle")
GK_ATOM(ontouchstart, "ontouchstart")

Просмотреть файл

@ -4,15 +4,19 @@
* 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 "PresentationConnection.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/dom/DOMException.h"
#include "mozilla/dom/MessageEvent.h"
#include "mozilla/dom/MessageEventBinding.h"
#include "mozilla/dom/PresentationConnectionClosedEvent.h"
#include "mozilla/ErrorNames.h"
#include "nsContentUtils.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIPresentationService.h"
#include "nsServiceManagerUtils.h"
#include "nsStringStream.h"
#include "PresentationConnection.h"
#include "PresentationConnectionList.h"
using namespace mozilla;
@ -34,6 +38,7 @@ NS_IMPL_RELEASE_INHERITED(PresentationConnection, DOMEventTargetHelper)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PresentationConnection)
NS_INTERFACE_MAP_ENTRY(nsIPresentationSessionListener)
NS_INTERFACE_MAP_ENTRY(nsIRequest)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
PresentationConnection::PresentationConnection(nsPIDOMWindowInner* aWindow,
@ -85,6 +90,18 @@ PresentationConnection::Init()
return false;
}
nsCOMPtr<nsILoadGroup> loadGroup;
GetLoadGroup(getter_AddRefs(loadGroup));
if(NS_WARN_IF(!loadGroup)) {
return false;
}
rv = loadGroup->AddRequest(this, nullptr);
if(NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
mWeakLoadGroup = do_GetWeakReference(loadGroup);
return true;
}
@ -99,12 +116,18 @@ PresentationConnection::Shutdown()
nsresult rv = service->UnregisterSessionListener(mId, mRole);
NS_WARN_IF(NS_FAILED(rv));
nsCOMPtr<nsILoadGroup> loadGroup = do_QueryReferent(mWeakLoadGroup);
if (loadGroup) {
loadGroup->RemoveRequest(this, nullptr, NS_OK);
mWeakLoadGroup = nullptr;
}
}
/* virtual */ void
PresentationConnection::DisconnectFromOwner()
{
Shutdown();
NS_WARN_IF(NS_FAILED(ProcessConnectionWentAway()));
DOMEventTargetHelper::DisconnectFromOwner();
}
@ -182,7 +205,8 @@ PresentationConnection::Terminate(ErrorResult& aRv)
NS_IMETHODIMP
PresentationConnection::NotifyStateChange(const nsAString& aSessionId,
uint16_t aState)
uint16_t aState,
nsresult aReason)
{
if (!aSessionId.Equals(mId)) {
return NS_ERROR_INVALID_ARG;
@ -210,24 +234,9 @@ PresentationConnection::NotifyStateChange(const nsAString& aSessionId,
if (mState == state) {
return NS_OK;
}
mState = state;
// Unregister session listener if the session is no longer connected.
if (mState == PresentationConnectionState::Terminated) {
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if (NS_WARN_IF(!service)) {
return NS_ERROR_NOT_AVAILABLE;
}
nsresult rv = service->UnregisterSessionListener(mId, mRole);
if(NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
nsresult rv = DispatchStateChangeEvent();
nsresult rv = ProcessStateChanged(aReason);
if(NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -239,6 +248,58 @@ PresentationConnection::NotifyStateChange(const nsAString& aSessionId,
return NS_OK;
}
nsresult
PresentationConnection::ProcessStateChanged(nsresult aReason)
{
switch (mState) {
case PresentationConnectionState::Connected: {
RefPtr<AsyncEventDispatcher> asyncDispatcher =
new AsyncEventDispatcher(this, NS_LITERAL_STRING("connect"), false);
return asyncDispatcher->PostDOMEvent();
}
case PresentationConnectionState::Closed: {
PresentationConnectionClosedReason reason =
PresentationConnectionClosedReason::Closed;
nsString errorMsg;
if (NS_FAILED(aReason)) {
reason = PresentationConnectionClosedReason::Error;
nsCString name, message;
// If aReason is not a DOM error, use error name as message.
if (NS_FAILED(NS_GetNameAndMessageForDOMNSResult(aReason,
name,
message))) {
mozilla::GetErrorName(aReason, message);
message.InsertLiteral("Internal error: ", 0);
}
CopyUTF8toUTF16(message, errorMsg);
}
return DispatchConnectionClosedEvent(reason, errorMsg);
}
case PresentationConnectionState::Terminated: {
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if (NS_WARN_IF(!service)) {
return NS_ERROR_NOT_AVAILABLE;
}
nsresult rv = service->UnregisterSessionListener(mId, mRole);
if(NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
RefPtr<AsyncEventDispatcher> asyncDispatcher =
new AsyncEventDispatcher(this, NS_LITERAL_STRING("terminate"), false);
return asyncDispatcher->PostDOMEvent();
}
default:
MOZ_CRASH("Unknown presentation session state.");
return NS_ERROR_INVALID_ARG;
}
}
NS_IMETHODIMP
PresentationConnection::NotifyMessage(const nsAString& aSessionId,
const nsACString& aData)
@ -268,10 +329,27 @@ PresentationConnection::NotifyMessage(const nsAString& aSessionId,
}
nsresult
PresentationConnection::DispatchStateChangeEvent()
PresentationConnection::DispatchConnectionClosedEvent(
PresentationConnectionClosedReason aReason,
const nsAString& aMessage)
{
if (mState != PresentationConnectionState::Closed) {
MOZ_ASSERT(false, "The connection state should be closed.");
return NS_ERROR_FAILURE;
}
PresentationConnectionClosedEventInit init;
init.mReason = aReason;
init.mMessage = aMessage;
RefPtr<PresentationConnectionClosedEvent> closedEvent =
PresentationConnectionClosedEvent::Constructor(this,
NS_LITERAL_STRING("close"),
init);
closedEvent->SetTrusted(true);
RefPtr<AsyncEventDispatcher> asyncDispatcher =
new AsyncEventDispatcher(this, NS_LITERAL_STRING("statechange"), false);
new AsyncEventDispatcher(this, static_cast<Event*>(closedEvent));
return asyncDispatcher->PostDOMEvent();
}
@ -303,3 +381,97 @@ PresentationConnection::DispatchMessageEvent(JS::Handle<JS::Value> aData)
new AsyncEventDispatcher(this, static_cast<Event*>(messageEvent));
return asyncDispatcher->PostDOMEvent();
}
nsresult
PresentationConnection::ProcessConnectionWentAway()
{
if (mState != PresentationConnectionState::Connected &&
mState != PresentationConnectionState::Connecting) {
// If the state is not connected or connecting, do not need to
// close the session.
return NS_OK;
}
mState = PresentationConnectionState::Terminated;
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if (NS_WARN_IF(!service)) {
return NS_ERROR_NOT_AVAILABLE;
}
return service->CloseSession(
mId, mRole, nsIPresentationService::CLOSED_REASON_WENTAWAY);
}
NS_IMETHODIMP
PresentationConnection::GetName(nsACString &aResult)
{
aResult.AssignLiteral("about:presentation-connection");
return NS_OK;
}
NS_IMETHODIMP
PresentationConnection::IsPending(bool* aRetval)
{
*aRetval = true;
return NS_OK;
}
NS_IMETHODIMP
PresentationConnection::GetStatus(nsresult* aStatus)
{
*aStatus = NS_OK;
return NS_OK;
}
NS_IMETHODIMP
PresentationConnection::Cancel(nsresult aStatus)
{
nsCOMPtr<nsIRunnable> event =
NewRunnableMethod(this, &PresentationConnection::ProcessConnectionWentAway);
return NS_DispatchToCurrentThread(event);
}
NS_IMETHODIMP
PresentationConnection::Suspend(void)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
PresentationConnection::Resume(void)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
PresentationConnection::GetLoadGroup(nsILoadGroup** aLoadGroup)
{
*aLoadGroup = nullptr;
nsCOMPtr<nsIDocument> doc = GetOwner() ? GetOwner()->GetExtantDoc() : nullptr;
if (!doc) {
return NS_ERROR_FAILURE;
}
*aLoadGroup = doc->GetDocumentLoadGroup().take();
return NS_OK;
}
NS_IMETHODIMP
PresentationConnection::SetLoadGroup(nsILoadGroup * aLoadGroup)
{
return NS_ERROR_UNEXPECTED;
}
NS_IMETHODIMP
PresentationConnection::GetLoadFlags(nsLoadFlags* aLoadFlags)
{
*aLoadFlags = nsIRequest::LOAD_BACKGROUND;
return NS_OK;
}
NS_IMETHODIMP
PresentationConnection::SetLoadFlags(nsLoadFlags aLoadFlags)
{
return NS_OK;
}

Просмотреть файл

@ -9,7 +9,10 @@
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/dom/PresentationConnectionBinding.h"
#include "mozilla/dom/PresentationConnectionClosedEventBinding.h"
#include "nsIPresentationListener.h"
#include "nsIRequest.h"
#include "nsWeakReference.h"
namespace mozilla {
namespace dom {
@ -18,12 +21,14 @@ class PresentationConnectionList;
class PresentationConnection final : public DOMEventTargetHelper
, public nsIPresentationSessionListener
, public nsIRequest
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PresentationConnection,
DOMEventTargetHelper)
NS_DECL_NSIPRESENTATIONSESSIONLISTENER
NS_DECL_NSIREQUEST
static already_AddRefed<PresentationConnection>
Create(nsPIDOMWindowInner* aWindow,
@ -48,7 +53,9 @@ public:
void Terminate(ErrorResult& aRv);
IMPL_EVENT_HANDLER(statechange);
IMPL_EVENT_HANDLER(connect);
IMPL_EVENT_HANDLER(close);
IMPL_EVENT_HANDLER(terminate);
IMPL_EVENT_HANDLER(message);
private:
@ -63,14 +70,20 @@ private:
void Shutdown();
nsresult DispatchStateChangeEvent();
nsresult ProcessStateChanged(nsresult aReason);
nsresult DispatchConnectionClosedEvent(PresentationConnectionClosedReason aReason,
const nsAString& aMessage);
nsresult DispatchMessageEvent(JS::Handle<JS::Value> aData);
nsresult ProcessConnectionWentAway();
nsString mId;
uint8_t mRole;
PresentationConnectionState mState;
RefPtr<PresentationConnectionList> mOwningConnectionList;
nsWeakPtr mWeakLoadGroup;;
};
} // namespace dom

Просмотреть файл

@ -511,7 +511,8 @@ PresentationService::SendSessionMessage(const nsAString& aSessionId,
NS_IMETHODIMP
PresentationService::CloseSession(const nsAString& aSessionId,
uint8_t aRole)
uint8_t aRole,
uint8_t aClosedReason)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!aSessionId.IsEmpty());
@ -523,6 +524,13 @@ PresentationService::CloseSession(const nsAString& aSessionId,
return NS_ERROR_NOT_AVAILABLE;
}
if (aClosedReason == nsIPresentationService::CLOSED_REASON_WENTAWAY) {
UntrackSessionInfo(aSessionId, aRole);
// Remove nsIPresentationSessionListener since we don't want to dispatch
// PresentationConnectionClosedEvent if the page is went away.
info->SetListener(nullptr);
}
return info->Close(NS_OK, nsIPresentationSessionListener::STATE_CLOSED);
}
@ -582,7 +590,8 @@ PresentationService::RegisterSessionListener(const nsAString& aSessionId,
// the receiver side, since a presentation session is created at beginning
// and here is the place to realize the underlying establishment fails.
nsresult rv = aListener->NotifyStateChange(aSessionId,
nsIPresentationSessionListener::STATE_TERMINATED);
nsIPresentationSessionListener::STATE_TERMINATED,
NS_ERROR_NOT_AVAILABLE);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}

Просмотреть файл

@ -256,7 +256,7 @@ PresentationSessionInfo::SetListener(nsIPresentationSessionListener* aListener)
// The transport might become ready, or might become un-ready again, before
// the listener has registered. So notify the listener of the state change.
return mListener->NotifyStateChange(mSessionId, mState);
return mListener->NotifyStateChange(mSessionId, mState, mReason);
}
return NS_OK;
@ -284,7 +284,7 @@ PresentationSessionInfo::Close(nsresult aReason,
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
SetState(aState);
SetStateWithReason(aState, aReason);
Shutdown(aReason);
return NS_OK;
@ -293,7 +293,7 @@ PresentationSessionInfo::Close(nsresult aReason,
nsresult
PresentationSessionInfo::ReplySuccess()
{
SetState(nsIPresentationSessionListener::STATE_CONNECTED);
SetStateWithReason(nsIPresentationSessionListener::STATE_CONNECTED, NS_OK);
return NS_OK;
}
@ -380,7 +380,7 @@ PresentationSessionInfo::NotifyTransportClosed(nsresult aReason)
if (mState == nsIPresentationSessionListener::STATE_CONNECTED) {
// The transport channel is closed unexpectedly (not caused by a |Close| call).
SetState(nsIPresentationSessionListener::STATE_CLOSED);
SetStateWithReason(nsIPresentationSessionListener::STATE_CLOSED, aReason);
}
Shutdown(aReason);
@ -668,7 +668,7 @@ PresentationControllingInfo::NotifyClosed(nsresult aReason)
if (NS_WARN_IF(NS_FAILED(aReason) || !mIsResponderReady)) {
// The presentation session instance may already exist.
// Change the state to TERMINATED since it never succeeds.
SetState(nsIPresentationSessionListener::STATE_TERMINATED);
SetStateWithReason(nsIPresentationSessionListener::STATE_TERMINATED, aReason);
// Reply error for an abnormal close.
return ReplyError(NS_ERROR_DOM_OPERATION_ERR);
@ -719,7 +719,7 @@ PresentationControllingInfo::OnStopListening(nsIServerSocket* aServerSocket,
}
// It happens after the session is ready. Change the state to CLOSED.
SetState(nsIPresentationSessionListener::STATE_CLOSED);
SetStateWithReason(nsIPresentationSessionListener::STATE_CLOSED, aStatus);
return NS_OK;
}
@ -994,7 +994,7 @@ PresentationPresentingInfo::NotifyClosed(nsresult aReason)
if (NS_WARN_IF(NS_FAILED(aReason))) {
// The presentation session instance may already exist.
// Change the state to TERMINATED since it never succeeds.
SetState(nsIPresentationSessionListener::STATE_TERMINATED);
SetStateWithReason(nsIPresentationSessionListener::STATE_TERMINATED, aReason);
// Reply error for an abnormal close.
return ReplyError(NS_ERROR_DOM_OPERATION_ERR);

Просмотреть файл

@ -44,6 +44,7 @@ public:
, mIsResponderReady(false)
, mIsTransportReady(false)
, mState(nsIPresentationSessionListener::STATE_CONNECTING)
, mReason(NS_OK)
{
MOZ_ASSERT(!mUrl.IsEmpty());
MOZ_ASSERT(!mSessionId.IsEmpty());
@ -117,17 +118,18 @@ protected:
virtual nsresult UntrackFromService();
void SetState(uint32_t aState)
void SetStateWithReason(uint32_t aState, nsresult aReason)
{
if (mState == aState) {
return;
}
mState = aState;
mReason = aReason;
// Notify session state change.
if (mListener) {
nsresult rv = mListener->NotifyStateChange(mSessionId, mState);
nsresult rv = mListener->NotifyStateChange(mSessionId, mState, aReason);
NS_WARN_IF(NS_FAILED(rv));
}
}
@ -145,6 +147,7 @@ protected:
bool mIsResponderReady;
bool mIsTransportReady;
uint32_t mState; // CONNECTED, CLOSED, TERMINATED
nsresult mReason;
nsCOMPtr<nsIPresentationSessionListener> mListener;
nsCOMPtr<nsIPresentationDevice> mDevice;
nsCOMPtr<nsIPresentationSessionTransport> mTransport;

Просмотреть файл

@ -25,7 +25,8 @@ interface nsIPresentationSessionListener : nsISupports
* Called when session state changes.
*/
void notifyStateChange(in DOMString sessionId,
in unsigned short state);
in unsigned short state,
in nsresult reason);
/*
* Called when receive messages.

Просмотреть файл

@ -39,6 +39,10 @@ interface nsIPresentationService : nsISupports
const unsigned short ROLE_CONTROLLER = 0x1;
const unsigned short ROLE_RECEIVER = 0x2;
const unsigned short CLOSED_REASON_ERROR = 0x1;
const unsigned short CLOSED_REASON_CLOSED = 0x2;
const unsigned short CLOSED_REASON_WENTAWAY = 0x3;
/*
* Start a new presentation session and display a prompt box which asks users
* to select a device.
@ -82,7 +86,8 @@ interface nsIPresentationService : nsISupports
* @param role: Identify the function called by controller or receiver.
*/
void closeSession(in DOMString sessionId,
in uint8_t role);
in uint8_t role,
in uint8_t closedReason);
/*
* Terminate the session.

Просмотреть файл

@ -32,6 +32,7 @@ struct CloseSessionRequest
{
nsString sessionId;
uint8_t role;
uint8_t closedReason;
};
struct TerminateSessionRequest
@ -55,7 +56,9 @@ sync protocol PPresentation
child:
async NotifyAvailableChange(bool aAvailable);
async NotifySessionStateChange(nsString aSessionId, uint16_t aState);
async NotifySessionStateChange(nsString aSessionId,
uint16_t aState,
nsresult aReason);
async NotifyMessage(nsString aSessionId, nsCString aData);
async NotifySessionConnect(uint64_t aWindowId, nsString aSessionId);

Просмотреть файл

@ -68,10 +68,13 @@ PresentationChild::RecvNotifyAvailableChange(const bool& aAvailable)
bool
PresentationChild::RecvNotifySessionStateChange(const nsString& aSessionId,
const uint16_t& aState)
const uint16_t& aState,
const nsresult& aReason)
{
if (mService) {
NS_WARN_IF(NS_FAILED(mService->NotifySessionStateChange(aSessionId, aState)));
NS_WARN_IF(NS_FAILED(mService->NotifySessionStateChange(aSessionId,
aState,
aReason)));
}
return true;
}

Просмотреть файл

@ -36,7 +36,8 @@ public:
virtual bool
RecvNotifySessionStateChange(const nsString& aSessionId,
const uint16_t& aState) override;
const uint16_t& aState,
const nsresult& aReason) override;
virtual bool
RecvNotifyMessage(const nsString& aSessionId,

Просмотреть файл

@ -81,11 +81,14 @@ PresentationIPCService::SendSessionMessage(const nsAString& aSessionId,
NS_IMETHODIMP
PresentationIPCService::CloseSession(const nsAString& aSessionId,
uint8_t aRole)
uint8_t aRole,
uint8_t aClosedReason)
{
MOZ_ASSERT(!aSessionId.IsEmpty());
return SendRequest(nullptr, CloseSessionRequest(nsString(aSessionId), aRole));
return SendRequest(nullptr, CloseSessionRequest(nsString(aSessionId),
aRole,
aClosedReason));
}
NS_IMETHODIMP
@ -198,14 +201,15 @@ PresentationIPCService::GetWindowIdBySessionId(const nsAString& aSessionId,
nsresult
PresentationIPCService::NotifySessionStateChange(const nsAString& aSessionId,
uint16_t aState)
uint16_t aState,
nsresult aReason)
{
nsCOMPtr<nsIPresentationSessionListener> listener;
if (NS_WARN_IF(!mSessionListeners.Get(aSessionId, getter_AddRefs(listener)))) {
return NS_OK;
}
return listener->NotifyStateChange(aSessionId, aState);
return listener->NotifyStateChange(aSessionId, aState, aReason);
}
nsresult

Просмотреть файл

@ -31,7 +31,8 @@ public:
nsresult NotifyAvailableChange(bool aAvailable);
nsresult NotifySessionStateChange(const nsAString& aSessionId,
uint16_t aState);
uint16_t aState,
nsresult aReason);
nsresult NotifyMessage(const nsAString& aSessionId,
const nsACString& aData);

Просмотреть файл

@ -200,10 +200,13 @@ PresentationParent::NotifyAvailableChange(bool aAvailable)
NS_IMETHODIMP
PresentationParent::NotifyStateChange(const nsAString& aSessionId,
uint16_t aState)
uint16_t aState,
nsresult aReason)
{
if (NS_WARN_IF(mActorDestroyed ||
!SendNotifySessionStateChange(nsString(aSessionId), aState))) {
!SendNotifySessionStateChange(nsString(aSessionId),
aState,
aReason))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
@ -311,7 +314,9 @@ PresentationRequestParent::DoRequest(const CloseSessionRequest& aRequest)
return NotifyError(NS_ERROR_DOM_SECURITY_ERR);
}
nsresult rv = mService->CloseSession(aRequest.sessionId(), aRequest.role());
nsresult rv = mService->CloseSession(aRequest.sessionId(),
aRequest.role(),
aRequest.closedReason());
if (NS_WARN_IF(NS_FAILED(rv))) {
return NotifyError(rv);
}

Просмотреть файл

@ -34,10 +34,9 @@ interface PresentationConnection : EventTarget {
*/
readonly attribute PresentationConnectionState state;
/*
* It is called when connection state changes.
*/
attribute EventHandler onstatechange;
attribute EventHandler onconnect;
attribute EventHandler onclose;
attribute EventHandler onterminate;
/*
* After a communication channel has been established between the controlling