/* -*- 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/. */ #include "mozilla/dom/Promise.h" #include "nsIDocShell.h" #include "nsIInterfaceRequestorUtils.h" #include "nsIPresentationService.h" #include "nsIWebProgress.h" #include "nsServiceManagerUtils.h" #include "nsThreadUtils.h" #include "PresentationCallbacks.h" #include "PresentationRequest.h" #include "PresentationConnection.h" #include "PresentationTransportBuilderConstructor.h" using namespace mozilla; using namespace mozilla::dom; /* * Implementation of PresentationRequesterCallback */ NS_IMPL_ISUPPORTS(PresentationRequesterCallback, nsIPresentationServiceCallback) PresentationRequesterCallback::PresentationRequesterCallback(PresentationRequest* aRequest, const nsAString& aSessionId, Promise* aPromise) : mRequest(aRequest) , mSessionId(aSessionId) , mPromise(aPromise) { MOZ_ASSERT(mRequest); MOZ_ASSERT(mPromise); MOZ_ASSERT(!mSessionId.IsEmpty()); } PresentationRequesterCallback::~PresentationRequesterCallback() { } // nsIPresentationServiceCallback NS_IMETHODIMP PresentationRequesterCallback::NotifySuccess(const nsAString& aUrl) { MOZ_ASSERT(NS_IsMainThread()); if (aUrl.IsEmpty()) { return NotifyError(NS_ERROR_DOM_OPERATION_ERR); } RefPtr connection = PresentationConnection::Create(mRequest->GetOwner(), mSessionId, aUrl, nsIPresentationService::ROLE_CONTROLLER); if (NS_WARN_IF(!connection)) { return NotifyError(NS_ERROR_DOM_OPERATION_ERR); } mRequest->NotifyPromiseSettled(); mPromise->MaybeResolve(connection); return mRequest->DispatchConnectionAvailableEvent(connection); } NS_IMETHODIMP PresentationRequesterCallback::NotifyError(nsresult aError) { MOZ_ASSERT(NS_IsMainThread()); mRequest->NotifyPromiseSettled(); mPromise->MaybeReject(aError); return NS_OK; } /* * Implementation of PresentationRequesterCallback */ PresentationReconnectCallback::PresentationReconnectCallback( PresentationRequest* aRequest, const nsAString& aSessionId, Promise* aPromise, PresentationConnection* aConnection) : PresentationRequesterCallback(aRequest, aSessionId, aPromise) , mConnection(aConnection) { } PresentationReconnectCallback::~PresentationReconnectCallback() { } NS_IMETHODIMP PresentationReconnectCallback::NotifySuccess(const nsAString& aUrl) { MOZ_ASSERT(NS_IsMainThread()); nsCOMPtr service = do_GetService(PRESENTATION_SERVICE_CONTRACTID); if (NS_WARN_IF(!service)) { return NS_ERROR_NOT_AVAILABLE; } nsresult rv = NS_OK; // We found a matched connection with the same window ID, URL, and // the session ID. Resolve the promise with this connection and dispatch // the event. if (mConnection) { mConnection->NotifyStateChange( mSessionId, nsIPresentationSessionListener::STATE_CONNECTING, NS_OK); mPromise->MaybeResolve(mConnection); rv = mRequest->DispatchConnectionAvailableEvent(mConnection); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } else { // Use |PresentationRequesterCallback::NotifySuccess| to create a new // connection since we don't find one that can be reused. rv = PresentationRequesterCallback::NotifySuccess(aUrl); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } rv = service->UpdateWindowIdBySessionId(mSessionId, nsIPresentationService::ROLE_CONTROLLER, mRequest->GetOwner()->WindowID()); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } nsString sessionId = nsString(mSessionId); return NS_DispatchToMainThread(NS_NewRunnableFunction( "dom::PresentationReconnectCallback::NotifySuccess", [sessionId, service]() -> void { service->BuildTransport(sessionId, nsIPresentationService::ROLE_CONTROLLER); })); } NS_IMETHODIMP PresentationReconnectCallback::NotifyError(nsresult aError) { if (mConnection) { mConnection->NotifyStateChange( mSessionId, nsIPresentationSessionListener::STATE_CLOSED, aError); } return PresentationRequesterCallback::NotifyError(aError); } NS_IMPL_ISUPPORTS(PresentationResponderLoadingCallback, nsIWebProgressListener, nsISupportsWeakReference) PresentationResponderLoadingCallback::PresentationResponderLoadingCallback(const nsAString& aSessionId) : mSessionId(aSessionId) { } PresentationResponderLoadingCallback::~PresentationResponderLoadingCallback() { if (mProgress) { mProgress->RemoveProgressListener(this); mProgress = nullptr; } } nsresult PresentationResponderLoadingCallback::Init(nsIDocShell* aDocShell) { mProgress = do_GetInterface(aDocShell); if (NS_WARN_IF(!mProgress)) { return NS_ERROR_NOT_AVAILABLE; } uint32_t busyFlags = nsIDocShell::BUSY_FLAGS_NONE; nsresult rv = aDocShell->GetBusyFlags(&busyFlags); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } if ((busyFlags == nsIDocShell::BUSY_FLAGS_NONE) || (busyFlags & nsIDocShell::BUSY_FLAGS_PAGE_LOADING)) { // The docshell has finished loading or is receiving data (|STATE_TRANSFERRING| // has already been fired), so the page is ready for presentation use. return NotifyReceiverReady(/* isLoading = */ true); } // Start to listen to document state change event |STATE_TRANSFERRING|. return mProgress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT); } nsresult PresentationResponderLoadingCallback::NotifyReceiverReady(bool aIsLoading) { nsCOMPtr window = do_GetInterface(mProgress); if (NS_WARN_IF(!window || !window->GetCurrentInnerWindow())) { return NS_ERROR_NOT_AVAILABLE; } uint64_t windowId = window->GetCurrentInnerWindow()->WindowID(); nsCOMPtr service = do_GetService(PRESENTATION_SERVICE_CONTRACTID); if (NS_WARN_IF(!service)) { return NS_ERROR_NOT_AVAILABLE; } nsCOMPtr constructor = PresentationTransportBuilderConstructor::Create(); return service->NotifyReceiverReady(mSessionId, windowId,aIsLoading, constructor); } // nsIWebProgressListener NS_IMETHODIMP PresentationResponderLoadingCallback::OnStateChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, uint32_t aStateFlags, nsresult aStatus) { MOZ_ASSERT(NS_IsMainThread()); if (aStateFlags & (nsIWebProgressListener::STATE_TRANSFERRING | nsIWebProgressListener::STATE_STOP)) { mProgress->RemoveProgressListener(this); bool isLoading = aStateFlags & nsIWebProgressListener::STATE_TRANSFERRING; return NotifyReceiverReady(isLoading); } return NS_OK; } NS_IMETHODIMP PresentationResponderLoadingCallback::OnProgressChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, int32_t aCurSelfProgress, int32_t aMaxSelfProgress, int32_t aCurTotalProgress, int32_t aMaxTotalProgress) { // Do nothing. return NS_OK; } NS_IMETHODIMP PresentationResponderLoadingCallback::OnLocationChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, nsIURI* aURI, uint32_t aFlags) { // Do nothing. return NS_OK; } NS_IMETHODIMP PresentationResponderLoadingCallback::OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, nsresult aStatus, const char16_t* aMessage) { // Do nothing. return NS_OK; } NS_IMETHODIMP PresentationResponderLoadingCallback::OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, uint32_t aOldState, uint32_t aState, const nsAString& aContentBlockingLogJSON) { // Do nothing. return NS_OK; }