dom/presentation/PresentationCallbacks.cpp
author Jan de Mooij <jdemooij@mozilla.com>
Tue, 05 Feb 2019 11:14:32 +0100
changeset 456689 4c6fca8134ce582dca4e5e63737a8ba47a66a2c1
parent 454652 f2b15591bb21fdf2eab19f347fe0fc1d135eab38
permissions -rw-r--r--
Bug 1525191 part 5 - Fix test_x509.js for updated certificates. a=bustage-fix

/* -*- 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<PresentationConnection> 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<nsIPresentationService> 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;
  }

  auto busyFlags = aDocShell->GetBusyFlags();

  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<nsPIDOMWindowOuter> window = do_GetInterface(mProgress);
  if (NS_WARN_IF(!window || !window->GetCurrentInnerWindow())) {
    return NS_ERROR_NOT_AVAILABLE;
  }
  uint64_t windowId = window->GetCurrentInnerWindow()->WindowID();

  nsCOMPtr<nsIPresentationService> service =
      do_GetService(PRESENTATION_SERVICE_CONTRACTID);
  if (NS_WARN_IF(!service)) {
    return NS_ERROR_NOT_AVAILABLE;
  }

  nsCOMPtr<nsIPresentationTransportBuilderConstructor> 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 aState) {
  // Do nothing.
  return NS_OK;
}

NS_IMETHODIMP
PresentationResponderLoadingCallback::OnContentBlockingEvent(
    nsIWebProgress* aWebProgress, nsIRequest* aRequest, uint32_t aEvent) {
  // Do nothing.
  return NS_OK;
}