1// Copyright 2013 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/serial/serial_event_dispatcher.h"
6
7#include "chrome/browser/browser_process.h"
8#include "chrome/browser/extensions/api/serial/serial_connection.h"
9#include "chrome/browser/extensions/extension_system.h"
10#include "chrome/browser/profiles/profile.h"
11#include "chrome/browser/profiles/profile_manager.h"
12#include "extensions/browser/event_router.h"
13
14namespace extensions {
15
16namespace api {
17
18namespace {
19
20bool ShouldPauseOnReceiveError(serial::ReceiveError error) {
21  return error == serial::RECEIVE_ERROR_DEVICE_LOST ||
22      error == serial::RECEIVE_ERROR_SYSTEM_ERROR ||
23      error == serial::RECEIVE_ERROR_DISCONNECTED;
24}
25
26}  // namespace
27
28static base::LazyInstance<ProfileKeyedAPIFactory<SerialEventDispatcher> >
29    g_factory = LAZY_INSTANCE_INITIALIZER;
30
31// static
32ProfileKeyedAPIFactory<SerialEventDispatcher>*
33    SerialEventDispatcher::GetFactoryInstance() {
34  return &g_factory.Get();
35}
36
37// static
38SerialEventDispatcher* SerialEventDispatcher::Get(Profile* profile) {
39  return ProfileKeyedAPIFactory<SerialEventDispatcher>::GetForProfile(profile);
40}
41
42SerialEventDispatcher::SerialEventDispatcher(Profile* profile)
43    : thread_id_(SerialConnection::kThreadId),
44      profile_(profile) {
45  ApiResourceManager<SerialConnection>* manager =
46      ApiResourceManager<SerialConnection>::Get(profile);
47  DCHECK(manager) << "No serial connection manager.";
48  connections_ = manager->data_;
49}
50
51SerialEventDispatcher::~SerialEventDispatcher() {}
52
53SerialEventDispatcher::ReceiveParams::ReceiveParams() {}
54
55SerialEventDispatcher::ReceiveParams::~ReceiveParams() {}
56
57void SerialEventDispatcher::PollConnection(const std::string& extension_id,
58                                           int connection_id) {
59  DCHECK(BrowserThread::CurrentlyOn(thread_id_));
60
61  ReceiveParams params;
62  params.thread_id = thread_id_;
63  params.profile_id = profile_;
64  params.extension_id = extension_id;
65  params.connections = connections_;
66  params.connection_id = connection_id;
67
68  StartReceive(params);
69}
70
71// static
72void SerialEventDispatcher::StartReceive(const ReceiveParams& params) {
73  DCHECK(BrowserThread::CurrentlyOn(params.thread_id));
74
75  SerialConnection* connection =
76      params.connections->Get(params.extension_id, params.connection_id);
77  if (!connection)
78    return;
79  DCHECK(params.extension_id == connection->owner_extension_id());
80
81  if (connection->paused())
82    return;
83
84  connection->Receive(base::Bind(&ReceiveCallback, params));
85}
86
87// static
88void SerialEventDispatcher::ReceiveCallback(const ReceiveParams& params,
89                                            const std::string& data,
90                                            serial::ReceiveError error) {
91  DCHECK(BrowserThread::CurrentlyOn(params.thread_id));
92
93  // Note that an error (e.g. timeout) does not necessarily mean that no data
94  // was read, so we may fire an onReceive regardless of any error code.
95  if (data.length() > 0) {
96    serial::ReceiveInfo receive_info;
97    receive_info.connection_id = params.connection_id;
98    receive_info.data = data;
99    scoped_ptr<base::ListValue> args = serial::OnReceive::Create(receive_info);
100    scoped_ptr<extensions::Event> event(
101        new extensions::Event(serial::OnReceive::kEventName, args.Pass()));
102    PostEvent(params, event.Pass());
103  }
104
105  if (error != serial::RECEIVE_ERROR_NONE) {
106    serial::ReceiveErrorInfo error_info;
107    error_info.connection_id = params.connection_id;
108    error_info.error = error;
109    scoped_ptr<base::ListValue> args =
110        serial::OnReceiveError::Create(error_info);
111    scoped_ptr<extensions::Event> event(
112        new extensions::Event(serial::OnReceiveError::kEventName, args.Pass()));
113    PostEvent(params, event.Pass());
114    if (ShouldPauseOnReceiveError(error)) {
115      SerialConnection* connection =
116          params.connections->Get(params.extension_id, params.connection_id);
117      if (connection)
118        connection->set_paused(true);
119    }
120  }
121
122  // Queue up the next read operation.
123  BrowserThread::PostTask(params.thread_id,
124                          FROM_HERE,
125                          base::Bind(&StartReceive, params));
126}
127
128// static
129void SerialEventDispatcher::PostEvent(const ReceiveParams& params,
130                                      scoped_ptr<extensions::Event> event) {
131  DCHECK(BrowserThread::CurrentlyOn(params.thread_id));
132
133  BrowserThread::PostTask(
134      BrowserThread::UI, FROM_HERE,
135      base::Bind(&DispatchEvent,
136                 params.profile_id,
137                 params.extension_id,
138                 base::Passed(event.Pass())));
139}
140
141// static
142void SerialEventDispatcher::DispatchEvent(void* profile_id,
143                                          const std::string& extension_id,
144                                          scoped_ptr<extensions::Event> event) {
145  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
146
147  Profile* profile = reinterpret_cast<Profile*>(profile_id);
148  if (!g_browser_process->profile_manager()->IsValidProfile(profile))
149    return;
150
151  EventRouter* router = ExtensionSystem::Get(profile)->event_router();
152  if (router)
153    router->DispatchEventToExtension(extension_id, event.Pass());
154}
155
156}  // namespace api
157
158}  // namespace extensions
159