NS_IMETHODIMP
PresentationService::Observe(nsISupports* aSubject, const char* aTopic,
                             const char16_t* aData) {
  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
    HandleShutdown();
    return NS_OK;
  } else if (!strcmp(aTopic, PRESENTATION_DEVICE_CHANGE_TOPIC)) {
    // Ignore the "update" case here, since we only care about the arrival and
    // removal of the device.
    if (!NS_strcmp(aData, u"add")) {
      nsCOMPtr<nsIPresentationDevice> device = do_QueryInterface(aSubject);
      if (NS_WARN_IF(!device)) {
        return NS_ERROR_FAILURE;
      }

      return HandleDeviceAdded(device);
    } else if (!NS_strcmp(aData, u"remove")) {
      return HandleDeviceRemoved();
    }

    return NS_OK;
  } else if (!strcmp(aTopic, PRESENTATION_SESSION_REQUEST_TOPIC)) {
    nsCOMPtr<nsIPresentationSessionRequest> request(
        do_QueryInterface(aSubject));
    if (NS_WARN_IF(!request)) {
      return NS_ERROR_FAILURE;
    }

    return HandleSessionRequest(request);
  } else if (!strcmp(aTopic, PRESENTATION_TERMINATE_REQUEST_TOPIC)) {
    nsCOMPtr<nsIPresentationTerminateRequest> request(
        do_QueryInterface(aSubject));
    if (NS_WARN_IF(!request)) {
      return NS_ERROR_FAILURE;
    }

    return HandleTerminateRequest(request);
  } else if (!strcmp(aTopic, PRESENTATION_RECONNECT_REQUEST_TOPIC)) {
    nsCOMPtr<nsIPresentationSessionRequest> request(
        do_QueryInterface(aSubject));
    if (NS_WARN_IF(!request)) {
      return NS_ERROR_FAILURE;
    }

    return HandleReconnectRequest(request);
  } else if (!strcmp(aTopic, "profile-after-change")) {
    // It's expected since we add and entry to |kLayoutCategories| in
    // |nsLayoutModule.cpp| to launch this service earlier.
    return NS_OK;
  }

  MOZ_ASSERT(false, "Unexpected topic for PresentationService");
  return NS_ERROR_UNEXPECTED;
}
nsresult
PresentationService::HandleReconnectRequest(nsIPresentationSessionRequest* aRequest)
{
  nsCOMPtr<nsIPresentationControlChannel> ctrlChannel;
  nsresult rv = aRequest->GetControlChannel(getter_AddRefs(ctrlChannel));
  if (NS_WARN_IF(NS_FAILED(rv) || !ctrlChannel)) {
    return rv;
  }

  nsAutoString sessionId;
  rv = aRequest->GetPresentationId(sessionId);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    ctrlChannel->Disconnect(rv);
    return rv;
  }

  uint64_t windowId;
  rv = GetWindowIdBySessionIdInternal(sessionId,
                                      nsIPresentationService::ROLE_RECEIVER,
                                      &windowId);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    ctrlChannel->Disconnect(rv);
    return rv;
  }

  RefPtr<PresentationSessionInfo> info =
    GetSessionInfo(sessionId, nsIPresentationService::ROLE_RECEIVER);
  if (NS_WARN_IF(!info)) {
    // Cannot reconnect non-existed session
    ctrlChannel->Disconnect(NS_ERROR_DOM_OPERATION_ERR);
    return NS_ERROR_DOM_ABORT_ERR;
  }

  nsAutoString url;
  rv = aRequest->GetUrl(url);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    ctrlChannel->Disconnect(rv);
    return rv;
  }

  // Make sure the url is the same as the previous one.
  if (NS_WARN_IF(!info->GetUrl().Equals(url))) {
    ctrlChannel->Disconnect(rv);
    return rv;
  }

  return HandleSessionRequest(aRequest);
}