16e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
26e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
36e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// found in the LICENSE file.
46e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
56e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "chrome/browser/extensions/api/copresence/copresence_api.h"
66e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
76e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "base/lazy_instance.h"
86e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "base/memory/linked_ptr.h"
96e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "chrome/browser/copresence/chrome_whispernet_client.h"
106e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "chrome/common/chrome_version_info.h"
116e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "chrome/common/extensions/api/copresence.h"
126e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "components/copresence/proto/data.pb.h"
136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "components/copresence/proto/enums.pb.h"
146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "components/copresence/proto/rpcs.pb.h"
1503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include "components/copresence/public/copresence_manager.h"
166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "components/copresence/public/whispernet_client.h"
176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "content/public/browser/browser_context.h"
186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "extensions/browser/event_router.h"
196e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
206e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)namespace extensions {
216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
226e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)namespace {
236e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccibase::LazyInstance<BrowserContextKeyedAPIFactory<CopresenceService>>
256e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    g_factory = LAZY_INSTANCE_INITIALIZER;
266e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
276e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)const char kInvalidOperationsMessage[] =
286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    "Invalid operation in operations array.";
296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)const char kShuttingDownMessage[] = "Shutting down.";
306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}  // namespace
326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// CopresenceService implementation:
346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)CopresenceService::CopresenceService(content::BrowserContext* context)
366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    : is_shutting_down_(false), browser_context_(context) {}
376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)CopresenceService::~CopresenceService() {}
396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
4003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)copresence::CopresenceManager* CopresenceService::manager() {
4103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if (!manager_ && !is_shutting_down_)
4203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    manager_ = copresence::CopresenceManager::Create(this);
4303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  return manager_.get();
446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)copresence::WhispernetClient* CopresenceService::whispernet_client() {
476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (!whispernet_client_ && !is_shutting_down_)
486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    whispernet_client_.reset(new ChromeWhispernetClient(browser_context_));
496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return whispernet_client_.get();
506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
526e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void CopresenceService::Shutdown() {
536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  is_shutting_down_ = true;
5403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  manager_.reset();
556e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  whispernet_client_.reset();
566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
576e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
5803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)void CopresenceService::set_manager_for_testing(
5903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    scoped_ptr<copresence::CopresenceManager> manager) {
6003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  manager_ = manager.Pass();
6103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}
6203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
636e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// static
646e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)BrowserContextKeyedAPIFactory<CopresenceService>*
656e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)CopresenceService::GetFactoryInstance() {
666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return g_factory.Pointer();
676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void CopresenceService::HandleMessages(
706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    const std::string& /* app_id */,
716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    const std::string& subscription_id,
726e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    const std::vector<copresence::Message>& messages) {
736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  // TODO(ckehoe): Once the server starts sending back the app ids associated
746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  // with subscriptions, use that instead of the apps_by_subs registry.
756e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  std::string app_id = apps_by_subscription_id_[subscription_id];
766e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
776e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (app_id.empty()) {
786e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    LOG(ERROR) << "Skipping message from unrecognized subscription "
796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)               << subscription_id;
806e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return;
816e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
826e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
836e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  int message_count = messages.size();
841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  std::vector<linked_ptr<api::copresence::Message>> api_messages(
856e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      message_count);
866e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
876e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  for (int m = 0; m < message_count; ++m) {
886e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    api_messages[m].reset(new api::copresence::Message);
896e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    api_messages[m]->type = messages[m].type().type();
906e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    api_messages[m]->payload = messages[m].payload();
916e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    DVLOG(2) << "Dispatching message of type " << api_messages[m]->type << ":\n"
926e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)             << api_messages[m]->payload;
936e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
946e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
956e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  // Send the messages to the client app.
966e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  scoped_ptr<Event> event(
976e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      new Event(api::copresence::OnMessagesReceived::kEventName,
986e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                api::copresence::OnMessagesReceived::Create(subscription_id,
996e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                                                            api_messages),
1006e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                browser_context_));
1016e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  EventRouter::Get(browser_context_)
1026e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      ->DispatchEventToExtension(app_id, event.Pass());
1036e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  DVLOG(2) << "Passed " << api_messages.size() << " messages to app \""
1046e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)           << app_id << "\" for subscription \"" << subscription_id << "\"";
1056e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
1066e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1076e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)net::URLRequestContextGetter* CopresenceService::GetRequestContext() const {
1086e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return browser_context_->GetRequestContext();
1096e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
1106e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1116e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)const std::string CopresenceService::GetPlatformVersionString() const {
1126e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return chrome::VersionInfo().CreateVersionString();
1136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
1146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)const std::string CopresenceService::GetAPIKey() const {
1166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return api_key_;
1176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
1186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1196e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)copresence::WhispernetClient* CopresenceService::GetWhispernetClient() {
1206e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return whispernet_client();
1216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
1226e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1236e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)template <>
1246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void
1256e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)BrowserContextKeyedAPIFactory<CopresenceService>::DeclareFactoryDependencies() {
1266e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  DependsOn(ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
1276e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
1286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// CopresenceExecuteFunction implementation:
1306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)ExtensionFunction::ResponseAction CopresenceExecuteFunction::Run() {
1316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  scoped_ptr<api::copresence::Execute::Params> params(
1326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      api::copresence::Execute::Params::Create(*args_));
1336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  EXTENSION_FUNCTION_VALIDATE(params.get());
1346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  CopresenceService* service =
1366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      CopresenceService::GetFactoryInstance()->Get(browser_context());
1376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  // This can only happen if we're shutting down. In all other cases, if we
13903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  // don't have a manager, we'll create one.
14003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if (!service->manager())
1416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return RespondNow(Error(kShuttingDownMessage));
1426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  // Each execute will correspond to one ReportRequest protocol buffer.
1446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  copresence::ReportRequest request;
1456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (!PrepareReportRequestProto(params->operations,
1466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                                 extension_id(),
1476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                                 &service->apps_by_subscription_id(),
1486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                                 &request)) {
1496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return RespondNow(Error(kInvalidOperationsMessage));
1506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
1516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
15203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  service->manager()->ExecuteReportRequest(
1536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      request,
1546e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      extension_id(),
1556e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      base::Bind(&CopresenceExecuteFunction::SendResult, this));
1566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return RespondLater();
1576e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
1586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void CopresenceExecuteFunction::SendResult(
1606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    copresence::CopresenceStatus status) {
1616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  api::copresence::ExecuteStatus api_status =
1626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      (status == copresence::SUCCESS) ? api::copresence::EXECUTE_STATUS_SUCCESS
1636e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                                      : api::copresence::EXECUTE_STATUS_FAILED;
1646e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  Respond(ArgumentList(api::copresence::Execute::Results::Create(api_status)));
1656e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
1666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// CopresenceSetApiKeyFunction implementation:
1686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)ExtensionFunction::ResponseAction CopresenceSetApiKeyFunction::Run() {
1696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  scoped_ptr<api::copresence::SetApiKey::Params> params(
1706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      api::copresence::SetApiKey::Params::Create(*args_));
1716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  EXTENSION_FUNCTION_VALIDATE(params.get());
1726e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  // The api key may be set to empty, to clear it.
1746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  CopresenceService::GetFactoryInstance()->Get(browser_context())
1756e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      ->set_api_key(params->api_key);
1766e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return RespondNow(NoArguments());
1776e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
1786e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}  // namespace extensions
180