1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/extensions/api/copresence/copresence_api.h"
6
7#include "base/lazy_instance.h"
8#include "base/memory/linked_ptr.h"
9#include "chrome/browser/copresence/chrome_whispernet_client.h"
10#include "chrome/common/chrome_version_info.h"
11#include "chrome/common/extensions/api/copresence.h"
12#include "components/copresence/proto/data.pb.h"
13#include "components/copresence/proto/enums.pb.h"
14#include "components/copresence/proto/rpcs.pb.h"
15#include "components/copresence/public/copresence_manager.h"
16#include "components/copresence/public/whispernet_client.h"
17#include "content/public/browser/browser_context.h"
18#include "extensions/browser/event_router.h"
19
20namespace extensions {
21
22namespace {
23
24base::LazyInstance<BrowserContextKeyedAPIFactory<CopresenceService>>
25    g_factory = LAZY_INSTANCE_INITIALIZER;
26
27const char kInvalidOperationsMessage[] =
28    "Invalid operation in operations array.";
29const char kShuttingDownMessage[] = "Shutting down.";
30
31}  // namespace
32
33// CopresenceService implementation:
34
35CopresenceService::CopresenceService(content::BrowserContext* context)
36    : is_shutting_down_(false), browser_context_(context) {}
37
38CopresenceService::~CopresenceService() {}
39
40copresence::CopresenceManager* CopresenceService::manager() {
41  if (!manager_ && !is_shutting_down_)
42    manager_ = copresence::CopresenceManager::Create(this);
43  return manager_.get();
44}
45
46copresence::WhispernetClient* CopresenceService::whispernet_client() {
47  if (!whispernet_client_ && !is_shutting_down_)
48    whispernet_client_.reset(new ChromeWhispernetClient(browser_context_));
49  return whispernet_client_.get();
50}
51
52void CopresenceService::Shutdown() {
53  is_shutting_down_ = true;
54  manager_.reset();
55  whispernet_client_.reset();
56}
57
58void CopresenceService::set_manager_for_testing(
59    scoped_ptr<copresence::CopresenceManager> manager) {
60  manager_ = manager.Pass();
61}
62
63// static
64BrowserContextKeyedAPIFactory<CopresenceService>*
65CopresenceService::GetFactoryInstance() {
66  return g_factory.Pointer();
67}
68
69void CopresenceService::HandleMessages(
70    const std::string& /* app_id */,
71    const std::string& subscription_id,
72    const std::vector<copresence::Message>& messages) {
73  // TODO(ckehoe): Once the server starts sending back the app ids associated
74  // with subscriptions, use that instead of the apps_by_subs registry.
75  std::string app_id = apps_by_subscription_id_[subscription_id];
76
77  if (app_id.empty()) {
78    LOG(ERROR) << "Skipping message from unrecognized subscription "
79               << subscription_id;
80    return;
81  }
82
83  int message_count = messages.size();
84  std::vector<linked_ptr<api::copresence::Message>> api_messages(
85      message_count);
86
87  for (int m = 0; m < message_count; ++m) {
88    api_messages[m].reset(new api::copresence::Message);
89    api_messages[m]->type = messages[m].type().type();
90    api_messages[m]->payload = messages[m].payload();
91    DVLOG(2) << "Dispatching message of type " << api_messages[m]->type << ":\n"
92             << api_messages[m]->payload;
93  }
94
95  // Send the messages to the client app.
96  scoped_ptr<Event> event(
97      new Event(api::copresence::OnMessagesReceived::kEventName,
98                api::copresence::OnMessagesReceived::Create(subscription_id,
99                                                            api_messages),
100                browser_context_));
101  EventRouter::Get(browser_context_)
102      ->DispatchEventToExtension(app_id, event.Pass());
103  DVLOG(2) << "Passed " << api_messages.size() << " messages to app \""
104           << app_id << "\" for subscription \"" << subscription_id << "\"";
105}
106
107net::URLRequestContextGetter* CopresenceService::GetRequestContext() const {
108  return browser_context_->GetRequestContext();
109}
110
111const std::string CopresenceService::GetPlatformVersionString() const {
112  return chrome::VersionInfo().CreateVersionString();
113}
114
115const std::string CopresenceService::GetAPIKey() const {
116  return api_key_;
117}
118
119copresence::WhispernetClient* CopresenceService::GetWhispernetClient() {
120  return whispernet_client();
121}
122
123template <>
124void
125BrowserContextKeyedAPIFactory<CopresenceService>::DeclareFactoryDependencies() {
126  DependsOn(ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
127}
128
129// CopresenceExecuteFunction implementation:
130ExtensionFunction::ResponseAction CopresenceExecuteFunction::Run() {
131  scoped_ptr<api::copresence::Execute::Params> params(
132      api::copresence::Execute::Params::Create(*args_));
133  EXTENSION_FUNCTION_VALIDATE(params.get());
134
135  CopresenceService* service =
136      CopresenceService::GetFactoryInstance()->Get(browser_context());
137
138  // This can only happen if we're shutting down. In all other cases, if we
139  // don't have a manager, we'll create one.
140  if (!service->manager())
141    return RespondNow(Error(kShuttingDownMessage));
142
143  // Each execute will correspond to one ReportRequest protocol buffer.
144  copresence::ReportRequest request;
145  if (!PrepareReportRequestProto(params->operations,
146                                 extension_id(),
147                                 &service->apps_by_subscription_id(),
148                                 &request)) {
149    return RespondNow(Error(kInvalidOperationsMessage));
150  }
151
152  service->manager()->ExecuteReportRequest(
153      request,
154      extension_id(),
155      base::Bind(&CopresenceExecuteFunction::SendResult, this));
156  return RespondLater();
157}
158
159void CopresenceExecuteFunction::SendResult(
160    copresence::CopresenceStatus status) {
161  api::copresence::ExecuteStatus api_status =
162      (status == copresence::SUCCESS) ? api::copresence::EXECUTE_STATUS_SUCCESS
163                                      : api::copresence::EXECUTE_STATUS_FAILED;
164  Respond(ArgumentList(api::copresence::Execute::Results::Create(api_status)));
165}
166
167// CopresenceSetApiKeyFunction implementation:
168ExtensionFunction::ResponseAction CopresenceSetApiKeyFunction::Run() {
169  scoped_ptr<api::copresence::SetApiKey::Params> params(
170      api::copresence::SetApiKey::Params::Create(*args_));
171  EXTENSION_FUNCTION_VALIDATE(params.get());
172
173  // The api key may be set to empty, to clear it.
174  CopresenceService::GetFactoryInstance()->Get(browser_context())
175      ->set_api_key(params->api_key);
176  return RespondNow(NoArguments());
177}
178
179}  // namespace extensions
180