summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authormiket@chromium.org <miket@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-16 21:51:38 +0000
committermiket@chromium.org <miket@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-16 21:51:38 +0000
commit58edca5827ed7b6f0af126fa2456f5cec0ff6ae3 (patch)
tree54eba06a541e5e9bf7493929e2cc93527095de72 /chrome/browser
parent4a3f5de339c9048ba220145a111886443d29bc1b (diff)
downloadchromium_src-58edca5827ed7b6f0af126fa2456f5cec0ff6ae3.zip
chromium_src-58edca5827ed7b6f0af126fa2456f5cec0ff6ae3.tar.gz
chromium_src-58edca5827ed7b6f0af126fa2456f5cec0ff6ae3.tar.bz2
- Simple layer over POSIX open/read/write.
- Very limited one-byte read. - Large refactoring of socket's controller and event notifier functionality into common classes. BUG=110241 TEST=Added, to the extent possible (it's hardware, no meaningful way to do loopback) Review URL: http://codereview.chromium.org/9317065 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@122363 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/extensions/api/api_function.cc71
-rw-r--r--chrome/browser/extensions/api/api_function.h50
-rw-r--r--chrome/browser/extensions/api/api_resource.cc26
-rw-r--r--chrome/browser/extensions/api/api_resource.h44
-rw-r--r--chrome/browser/extensions/api/api_resource_controller.cc82
-rw-r--r--chrome/browser/extensions/api/api_resource_controller.h62
-rw-r--r--chrome/browser/extensions/api/api_resource_event_notifier.cc (renamed from chrome/browser/extensions/api/socket/socket_event_notifier.cc)66
-rw-r--r--chrome/browser/extensions/api/api_resource_event_notifier.h72
-rw-r--r--chrome/browser/extensions/api/serial/serial_api.cc129
-rw-r--r--chrome/browser/extensions/api/serial/serial_api.h49
-rw-r--r--chrome/browser/extensions/api/serial/serial_apitest.cc46
-rw-r--r--chrome/browser/extensions/api/serial/serial_connection.h36
-rw-r--r--chrome/browser/extensions/api/serial/serial_connection_posix.cc45
-rw-r--r--chrome/browser/extensions/api/serial/serial_connection_win.cc39
-rw-r--r--chrome/browser/extensions/api/socket/socket.cc19
-rw-r--r--chrome/browser/extensions/api/socket/socket.h13
-rw-r--r--chrome/browser/extensions/api/socket/socket_api.cc109
-rw-r--r--chrome/browser/extensions/api/socket/socket_api.h36
-rw-r--r--chrome/browser/extensions/api/socket/socket_api_controller.cc114
-rw-r--r--chrome/browser/extensions/api/socket/socket_api_controller.h75
-rw-r--r--chrome/browser/extensions/api/socket/socket_api_controller_unittest.cc57
-rw-r--r--chrome/browser/extensions/api/socket/socket_event_notifier.h64
-rw-r--r--chrome/browser/extensions/api/socket/socket_event_notifier_unittest.cc173
-rw-r--r--chrome/browser/extensions/api/socket/tcp_socket.cc39
-rw-r--r--chrome/browser/extensions/api/socket/tcp_socket.h18
-rw-r--r--chrome/browser/extensions/api/socket/tcp_socket_unittest.cc99
-rw-r--r--chrome/browser/extensions/api/socket/udp_socket.cc34
-rw-r--r--chrome/browser/extensions/api/socket/udp_socket.h25
-rw-r--r--chrome/browser/extensions/api/socket/udp_socket_unittest.cc (renamed from chrome/browser/extensions/api/socket/socket_unittest.cc)44
-rw-r--r--chrome/browser/extensions/extension_function_dispatcher.cc2
-rw-r--r--chrome/browser/extensions/extension_service.cc34
-rw-r--r--chrome/browser/extensions/extension_service.h6
32 files changed, 1039 insertions, 739 deletions
diff --git a/chrome/browser/extensions/api/api_function.cc b/chrome/browser/extensions/api/api_function.cc
new file mode 100644
index 0000000..8e6278d
--- /dev/null
+++ b/chrome/browser/extensions/api/api_function.cc
@@ -0,0 +1,71 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/api/api_function.h"
+
+#include "base/bind.h"
+#include "chrome/browser/extensions/api/api_resource_controller.h"
+#include "chrome/browser/extensions/api/api_resource_event_notifier.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/browser_thread.h"
+
+using content::BrowserThread;
+
+namespace extensions {
+
+bool AsyncIOAPIFunction::RunImpl() {
+ if (!Prepare()) {
+ return false;
+ }
+ bool rv = BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&AsyncIOAPIFunction::WorkOnIOThread, this));
+ DCHECK(rv);
+ return true;
+}
+
+void AsyncIOAPIFunction::WorkOnIOThread() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ Work();
+ bool rv = BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&AsyncIOAPIFunction::RespondOnUIThread, this));
+ DCHECK(rv);
+}
+
+void AsyncIOAPIFunction::RespondOnUIThread() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ SendResponse(Respond());
+}
+
+int AsyncIOAPIFunction::ExtractSrcId(size_t argument_position) {
+ scoped_ptr<DictionaryValue> options(new DictionaryValue());
+ if (args_->GetSize() > argument_position) {
+ DictionaryValue* temp_options = NULL;
+ if (args_->GetDictionary(argument_position, &temp_options))
+ options.reset(temp_options->DeepCopy());
+ }
+
+ // If we tacked on a srcId to the options object, pull it out here to provide
+ // to the Socket.
+ int src_id = -1;
+ if (options->HasKey(kSrcIdKey)) {
+ EXTENSION_FUNCTION_VALIDATE(options->GetInteger(kSrcIdKey, &src_id));
+ }
+
+ return src_id;
+}
+
+APIResourceEventNotifier* AsyncIOAPIFunction::CreateEventNotifier(int src_id) {
+ return new APIResourceEventNotifier(
+ profile()->GetExtensionEventRouter(), profile(), extension_id(),
+ src_id, source_url());
+}
+
+APIResourceController* AsyncIOAPIFunction::controller() {
+ return profile()->GetExtensionService()->api_resource_controller();
+}
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/api_function.h b/chrome/browser/extensions/api/api_function.h
new file mode 100644
index 0000000..09f8e59
--- /dev/null
+++ b/chrome/browser/extensions/api/api_function.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_API_API_FUNCTION_H_
+#define CHROME_BROWSER_EXTENSIONS_API_API_FUNCTION_H_
+#pragma once
+
+#include "chrome/browser/extensions/extension_function.h"
+#include "chrome/browser/extensions/api/api_resource.h"
+
+namespace extensions {
+
+class APIResourceController;
+class APIResourceEventNotifier;
+
+// AsyncIOAPIFunction provides convenient thread management for APIs that
+// need to do essentially all their work on the IO thread.
+class AsyncIOAPIFunction : public AsyncExtensionFunction {
+ protected:
+ // Set up for work (e.g., validate arguments). Guaranteed to happen on UI
+ // thread.
+ virtual bool Prepare() = 0;
+
+ // Do actual work. Guaranteed to happen on IO thread.
+ virtual void Work() = 0;
+
+ // Respond. Guaranteed to happen on UI thread.
+ virtual bool Respond() = 0;
+
+ // Looks for a kSrcId key that might have been added to a create method's
+ // options object.
+ int ExtractSrcId(size_t argument_position);
+
+ // Utility.
+ APIResourceEventNotifier* CreateEventNotifier(int src_id);
+
+ // Access to the controller singleton.
+ APIResourceController* controller();
+
+ virtual bool RunImpl() OVERRIDE;
+
+ private:
+ void WorkOnIOThread();
+ void RespondOnUIThread();
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_API_API_FUNCTION_H_
diff --git a/chrome/browser/extensions/api/api_resource.cc b/chrome/browser/extensions/api/api_resource.cc
new file mode 100644
index 0000000..482aeec
--- /dev/null
+++ b/chrome/browser/extensions/api/api_resource.cc
@@ -0,0 +1,26 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/api/api_resource.h"
+#include "chrome/browser/extensions/api/api_resource_event_notifier.h"
+
+namespace extensions {
+
+APIResource::APIResource(APIResource::APIResourceType api_resource_type,
+ APIResourceEventNotifier* event_notifier)
+ : api_resource_type_(api_resource_type), event_notifier_(event_notifier) {
+}
+
+APIResource::~APIResource() {
+}
+
+APIResource::APIResourceType APIResource::api_resource_type() const {
+ return api_resource_type_;
+}
+
+APIResourceEventNotifier* APIResource::event_notifier() const {
+ return event_notifier_.get();
+}
+
+}
diff --git a/chrome/browser/extensions/api/api_resource.h b/chrome/browser/extensions/api/api_resource.h
new file mode 100644
index 0000000..26489d3
--- /dev/null
+++ b/chrome/browser/extensions/api/api_resource.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_API_API_RESOURCE_H_
+#define CHROME_BROWSER_EXTENSIONS_API_API_RESOURCE_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace extensions {
+
+class APIResourceController;
+class APIResourceEventNotifier;
+
+// An APIResource represents something that an extension API manages, such as a
+// socket or a serial-port connection.
+class APIResource {
+ public:
+ virtual ~APIResource();
+
+ protected:
+ enum APIResourceType {
+ SocketResource,
+ SerialConnectionResource
+ };
+ APIResource(APIResourceType api_resource_type,
+ APIResourceEventNotifier* event_notifier);
+
+ APIResourceType api_resource_type() const;
+ APIResourceEventNotifier* event_notifier() const;
+
+ private:
+ APIResourceType api_resource_type_;
+ scoped_ptr<APIResourceEventNotifier> event_notifier_;
+
+ friend class APIResourceController;
+ DISALLOW_COPY_AND_ASSIGN(APIResource);
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_API_API_RESOURCE_H_
diff --git a/chrome/browser/extensions/api/api_resource_controller.cc b/chrome/browser/extensions/api/api_resource_controller.cc
new file mode 100644
index 0000000..b101429
--- /dev/null
+++ b/chrome/browser/extensions/api/api_resource_controller.cc
@@ -0,0 +1,82 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/api/api_resource_controller.h"
+#include "chrome/browser/extensions/api/serial/serial_connection.h"
+#include "chrome/browser/extensions/api/socket/socket.h"
+
+namespace extensions {
+
+APIResourceController::APIResourceController() : next_api_resource_id_(1) {}
+
+APIResourceController::~APIResourceController() {}
+
+APIResource* APIResourceController::GetAPIResource(int api_resource_id) const {
+ // TODO(miket): verify that the extension asking for the APIResource is the
+ // same one that created it.
+ APIResourceMap::const_iterator i = api_resource_map_.find(api_resource_id);
+ if (i != api_resource_map_.end())
+ return i->second.get();
+ return NULL;
+}
+
+APIResource* APIResourceController::GetAPIResource(
+ APIResource::APIResourceType api_resource_type,
+ int api_resource_id) const {
+ APIResource* resource = GetAPIResource(api_resource_id);
+
+ // This DCHECK is going to catch some legitimate application-developer
+ // errors, where someone asks for resource of Type A with the wrong ID that
+ // happens to belong to a resource of Type B. But in debug mode, it's more
+ // likely to catch a silly copy/paste coding error while developing a new
+ // resource type, and unfortunately we can't really tell the difference.
+ // We'll choose the more informative outcome (loud explosion during debug)
+ // rather than a mysterious empty resource returned to JavaScript.
+ DCHECK(!resource || resource->api_resource_type() == api_resource_type);
+
+ if (resource && resource->api_resource_type() != api_resource_type)
+ resource = NULL;
+
+ return resource;
+}
+
+int APIResourceController::AddAPIResource(APIResource* api_resource) {
+ int id = GenerateAPIResourceId();
+ if (id > 0) {
+ linked_ptr<APIResource> resource_ptr(api_resource);
+ api_resource_map_[id] = resource_ptr;
+ return id;
+ }
+ return 0;
+}
+
+bool APIResourceController::RemoveAPIResource(int api_resource_id) {
+ APIResource* api_resource = GetAPIResource(api_resource_id);
+ if (!api_resource)
+ return false;
+ api_resource_map_.erase(api_resource_id);
+ return true;
+}
+
+// TODO(miket): consider partitioning the ID space by extension ID
+// to make it harder for extensions to peek into each others' resources.
+int APIResourceController::GenerateAPIResourceId() {
+ while (next_api_resource_id_ > 0 &&
+ api_resource_map_.count(next_api_resource_id_) > 0)
+ ++next_api_resource_id_;
+ return next_api_resource_id_++;
+}
+
+Socket* APIResourceController::GetSocket(int api_resource_id) const {
+ return static_cast<Socket*>(GetAPIResource(APIResource::SocketResource,
+ api_resource_id));
+}
+
+SerialConnection* APIResourceController::GetSerialConnection(
+ int api_resource_id) const {
+ return static_cast<SerialConnection*>(
+ GetAPIResource(APIResource::SerialConnectionResource, api_resource_id));
+}
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/api_resource_controller.h b/chrome/browser/extensions/api/api_resource_controller.h
new file mode 100644
index 0000000..3e79182
--- /dev/null
+++ b/chrome/browser/extensions/api/api_resource_controller.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_API_API_RESOURCE_CONTROLLER_H_
+#define CHROME_BROWSER_EXTENSIONS_API_API_RESOURCE_CONTROLLER_H_
+#pragma once
+
+#include <string>
+#include <map>
+
+#include "base/memory/linked_ptr.h"
+#include "chrome/browser/extensions/api/api_resource.h"
+
+namespace extensions {
+
+class SerialConnection;
+class Socket;
+
+// kSrcIdKey, or "srcId," binds an APIResource to the onEvent closure that was
+// optionally passed to the APIResource's create() method. We generated it in
+// schema_generated_bindings.js; the application code is unaware of it.
+extern const char kSrcIdKey[];
+
+// APIController keeps track of a collection of APIResources and provides a
+// convenient set of methods to work with them.
+class APIResourceController {
+ public:
+ APIResourceController();
+ virtual ~APIResourceController();
+
+ // Takes ownership of api_resource.
+ int AddAPIResource(APIResource* api_resource);
+ bool RemoveAPIResource(int api_resource_id);
+
+ // APIResourceController knows about all classes derived from APIResource.
+ // This is intentional to avoid scattering potentially unsafe static_cast<>
+ // operations throughout the codebase.
+ //
+ // Another alternative we considered and rejected was templatizing
+ // APIResourceController and scattering specialized versions throughout the
+ // codebase.
+ Socket* GetSocket(int api_resource_id) const;
+ SerialConnection* GetSerialConnection(int api_resource_id) const;
+
+ private:
+ int next_api_resource_id_;
+ typedef std::map<int, linked_ptr<APIResource> > APIResourceMap;
+ APIResourceMap api_resource_map_;
+
+ APIResource* GetAPIResource(APIResource::APIResourceType api_resource_type,
+ int api_resource_id) const;
+ APIResource* GetAPIResource(int api_resource_id) const;
+
+ int GenerateAPIResourceId();
+
+ DISALLOW_COPY_AND_ASSIGN(APIResourceController);
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_API_API_RESOURCE_CONTROLLER_H_
diff --git a/chrome/browser/extensions/api/socket/socket_event_notifier.cc b/chrome/browser/extensions/api/api_resource_event_notifier.cc
index dde760a..1c510cf 100644
--- a/chrome/browser/extensions/api/socket/socket_event_notifier.cc
+++ b/chrome/browser/extensions/api/api_resource_event_notifier.cc
@@ -2,14 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/extensions/api/socket/socket_event_notifier.h"
+#include "chrome/browser/extensions/api/api_resource_event_notifier.h"
#include "base/json/json_writer.h"
#include "chrome/browser/extensions/extension_event_router.h"
#include "chrome/browser/profiles/profile.h"
namespace events {
-const char kOnSocketEvent[] = "experimental.socket.onEvent";
+// TODO(miket): This should be generic, but at the moment only socket sends
+// onEvent events. We'll fix this when serial becomes nonblocking.
+const char kOnAPIResourceEvent[] = "experimental.socket.onEvent";
};
namespace extensions {
@@ -25,66 +27,70 @@ const char kIsFinalEventKey[] = "isFinalEvent";
const char kResultCodeKey[] = "resultCode";
const char kDataKey[] = "data";
-SocketEventNotifier::SocketEventNotifier(ExtensionEventRouter* router,
- Profile* profile,
- const std::string& src_extension_id,
- int src_id,
- const GURL& src_url)
+APIResourceEventNotifier::APIResourceEventNotifier(
+ ExtensionEventRouter* router,
+ Profile* profile,
+ const std::string& src_extension_id,
+ int src_id,
+ const GURL& src_url)
: router_(router),
profile_(profile),
src_extension_id_(src_extension_id),
src_id_(src_id),
src_url_(src_url) {}
-SocketEventNotifier::~SocketEventNotifier() {}
+APIResourceEventNotifier::~APIResourceEventNotifier() {}
-void SocketEventNotifier::OnConnectComplete(int result_code) {
- SendEventWithResultCode(SOCKET_EVENT_CONNECT_COMPLETE, result_code);
+void APIResourceEventNotifier::OnConnectComplete(int result_code) {
+ SendEventWithResultCode(API_RESOURCE_EVENT_CONNECT_COMPLETE, result_code);
}
-void SocketEventNotifier::OnDataRead(int result_code,
- const std::string& data) {
+void APIResourceEventNotifier::OnDataRead(int result_code,
+ const std::string& data) {
// Do we have a destination for this event? There will be one if a source id
- // was injected by the request handler for socket.create in
+ // was injected by the request handler for the resource's create method in
// schema_generated_bindings.js, which will in turn be the case if the caller
- // of socket.create provided an onEvent closure.
+ // of the create method provided an onEvent closure.
if (src_id_ < 0)
return;
- DictionaryValue* event = CreateSocketEvent(SOCKET_EVENT_DATA_READ);
+ DictionaryValue* event = CreateAPIResourceEvent(
+ API_RESOURCE_EVENT_DATA_READ);
event->SetInteger(kResultCodeKey, result_code);
event->SetString(kDataKey, data);
DispatchEvent(event);
}
-void SocketEventNotifier::OnWriteComplete(int result_code) {
- SendEventWithResultCode(SOCKET_EVENT_WRITE_COMPLETE, result_code);
+void APIResourceEventNotifier::OnWriteComplete(int result_code) {
+ SendEventWithResultCode(API_RESOURCE_EVENT_WRITE_COMPLETE, result_code);
}
-void SocketEventNotifier::SendEventWithResultCode(SocketEventType event_type,
- int result_code) {
+void APIResourceEventNotifier::SendEventWithResultCode(
+ APIResourceEventType event_type,
+ int result_code) {
if (src_id_ < 0)
return;
- DictionaryValue* event = CreateSocketEvent(event_type);
+ DictionaryValue* event = CreateAPIResourceEvent(event_type);
event->SetInteger(kResultCodeKey, result_code);
DispatchEvent(event);
}
-void SocketEventNotifier::DispatchEvent(DictionaryValue* event) {
+void APIResourceEventNotifier::DispatchEvent(DictionaryValue* event) {
ListValue args;
args.Set(0, event);
std::string json_args;
base::JSONWriter::Write(&args, false, &json_args);
- router_->DispatchEventToExtension(src_extension_id_, events::kOnSocketEvent,
+ router_->DispatchEventToExtension(src_extension_id_,
+ events::kOnAPIResourceEvent,
json_args, profile_, src_url_);
}
-DictionaryValue* SocketEventNotifier::CreateSocketEvent(
- SocketEventType event_type) {
+DictionaryValue* APIResourceEventNotifier::CreateAPIResourceEvent(
+ APIResourceEventType event_type) {
DictionaryValue* event = new DictionaryValue();
- event->SetString(kEventTypeKey, SocketEventTypeToString(event_type));
+ event->SetString(kEventTypeKey, APIResourceEventTypeToString(event_type));
event->SetInteger(kSrcIdKey, src_id_);
// TODO(miket): Signal that it's OK to clean up onEvent listeners. This is
@@ -97,14 +103,14 @@ DictionaryValue* SocketEventNotifier::CreateSocketEvent(
}
// static
-std::string SocketEventNotifier::SocketEventTypeToString(
- SocketEventType event_type) {
+std::string APIResourceEventNotifier::APIResourceEventTypeToString(
+ APIResourceEventType event_type) {
switch (event_type) {
- case SOCKET_EVENT_CONNECT_COMPLETE:
+ case API_RESOURCE_EVENT_CONNECT_COMPLETE:
return kEventTypeConnectComplete;
- case SOCKET_EVENT_DATA_READ:
+ case API_RESOURCE_EVENT_DATA_READ:
return kEventTypeDataRead;
- case SOCKET_EVENT_WRITE_COMPLETE:
+ case API_RESOURCE_EVENT_WRITE_COMPLETE:
return kEventTypeWriteComplete;
}
diff --git a/chrome/browser/extensions/api/api_resource_event_notifier.h b/chrome/browser/extensions/api/api_resource_event_notifier.h
new file mode 100644
index 0000000..28abfd0
--- /dev/null
+++ b/chrome/browser/extensions/api/api_resource_event_notifier.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_API_API_RESOURCE_EVENT_NOTIFIER_H_
+#define CHROME_BROWSER_EXTENSIONS_API_API_RESOURCE_EVENT_NOTIFIER_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/values.h"
+#include "googleurl/src/gurl.h"
+
+class ExtensionEventRouter;
+class Profile;
+
+namespace extensions {
+
+enum APIResourceEventType {
+ API_RESOURCE_EVENT_CONNECT_COMPLETE,
+ API_RESOURCE_EVENT_DATA_READ,
+ API_RESOURCE_EVENT_WRITE_COMPLETE
+};
+
+extern const char kSrcIdKey[];
+
+// TODO(miket): It's possible that we'll further refactor these new classes in
+// light of some changes that mihaip has suggested. The names might change,
+// too:
+//
+// IOResource
+// IOResourceExtensionFunction
+// IOResourceEventNotifier
+// IOResourceController
+
+// APIResourceEventNotifier knows how to send an event to a specific app's
+// onEvent handler. It handles all platform-API events.
+class APIResourceEventNotifier {
+ public:
+ APIResourceEventNotifier(ExtensionEventRouter* router,
+ Profile* profile,
+ const std::string& src_extension_id, int src_id,
+ const GURL& src_url);
+ virtual ~APIResourceEventNotifier();
+
+ virtual void OnConnectComplete(int result_code);
+ virtual void OnDataRead(int result_code, const std::string& data);
+ virtual void OnWriteComplete(int result_code);
+
+ static std::string APIResourceEventTypeToString(
+ APIResourceEventType event_type);
+
+ private:
+ void DispatchEvent(DictionaryValue* event);
+ DictionaryValue* CreateAPIResourceEvent(APIResourceEventType event_type);
+
+ void SendEventWithResultCode(APIResourceEventType event_type,
+ int result_code);
+
+ ExtensionEventRouter* router_;
+ Profile* profile_;
+ std::string src_extension_id_;
+ int src_id_;
+ GURL src_url_;
+
+ DISALLOW_COPY_AND_ASSIGN(APIResourceEventNotifier);
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_API_API_RESOURCE_EVENT_NOTIFIER_H_
diff --git a/chrome/browser/extensions/api/serial/serial_api.cc b/chrome/browser/extensions/api/serial/serial_api.cc
index c8b5bcb..26b523f 100644
--- a/chrome/browser/extensions/api/serial/serial_api.cc
+++ b/chrome/browser/extensions/api/serial/serial_api.cc
@@ -2,85 +2,124 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "chrome/browser/extensions/api/serial/serial_api.h"
+
#include <string>
-#include "base/bind.h"
#include "base/values.h"
-#include "chrome/browser/extensions/api/serial/serial_api.h"
-#include "content/public/browser/browser_thread.h"
-
-using content::BrowserThread;
+#include "chrome/browser/extensions/api/api_resource_controller.h"
+#include "chrome/browser/extensions/api/serial/serial_connection.h"
namespace extensions {
const char kConnectionIdKey[] = "connectionId";
+const char kMessageKey[] = "message";
+const char kBytesReadKey[] = "bytesRead";
+const char kBytesWrittenKey[] = "bytesWritten";
-SerialOpenFunction::SerialOpenFunction() {
+SerialOpenFunction::SerialOpenFunction()
+ : src_id_(-1) {
}
-SerialOpenFunction::~SerialOpenFunction() {
-}
-
-bool SerialOpenFunction::RunImpl() {
- EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &port_));
-
- bool rv = BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(&SerialOpenFunction::WorkOnIOThread, this));
- DCHECK(rv);
-
+bool SerialOpenFunction::Prepare() {
+ size_t argument_position = 0;
+ EXTENSION_FUNCTION_VALIDATE(args_->GetString(argument_position++, &port_));
+ src_id_ = ExtractSrcId(argument_position);
return true;
}
-void SerialOpenFunction::WorkOnIOThread() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+void SerialOpenFunction::Work() {
+ APIResourceEventNotifier* event_notifier = CreateEventNotifier(src_id_);
+ SerialConnection* serial_connection = new SerialConnection(port_,
+ event_notifier);
+ CHECK(serial_connection);
+ int id = controller()->AddAPIResource(serial_connection);
+ CHECK(id);
+
+ bool open_result = serial_connection->Open();
+ if (!open_result) {
+ serial_connection->Close();
+ controller()->RemoveAPIResource(id);
+ id = -1;
+ }
DictionaryValue* result = new DictionaryValue();
- result->SetInteger(kConnectionIdKey, 42);
+ result->SetInteger(kConnectionIdKey, id);
result_.reset(result);
+}
- bool rv = BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(&SerialOpenFunction::RespondOnUIThread, this));
- DCHECK(rv);
+bool SerialOpenFunction::Respond() {
+ return true;
}
-void SerialOpenFunction::RespondOnUIThread() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- SendResponse(true);
+bool SerialCloseFunction::Prepare() {
+ EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &connection_id_));
+ return true;
}
-SerialCloseFunction::SerialCloseFunction() : connection_id_(0) {
+void SerialCloseFunction::Work() {
+ bool close_result = false;
+ SerialConnection* serial_connection =
+ controller()->GetSerialConnection(connection_id_);
+ if (serial_connection) {
+ serial_connection->Close();
+ controller()->RemoveAPIResource(connection_id_);
+ close_result = true;
+ }
+
+ result_.reset(Value::CreateBooleanValue(close_result));
}
-SerialCloseFunction::~SerialCloseFunction() {
+bool SerialCloseFunction::Respond() {
+ return true;
}
-bool SerialCloseFunction::RunImpl() {
+bool SerialReadFunction::Prepare() {
EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &connection_id_));
+ return true;
+}
- bool rv = BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(&SerialCloseFunction::WorkOnIOThread, this));
- DCHECK(rv);
+void SerialReadFunction::Work() {
+ int bytes_read = -1;
+ std::string message;
+ SerialConnection* serial_connection =
+ controller()->GetSerialConnection(connection_id_);
+ if (serial_connection) {
+ unsigned char byte = '\0';
+ bytes_read = serial_connection->Read(&byte);
+ if (bytes_read == 1)
+ message = byte;
+ }
- return true;
+ DictionaryValue* result = new DictionaryValue();
+ result->SetInteger(kBytesReadKey, bytes_read);
+ result->SetString(kMessageKey, message);
+ result_.reset(result);
}
-void SerialCloseFunction::WorkOnIOThread() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+bool SerialReadFunction::Respond() {
+ return true;
+}
- result_.reset(Value::CreateBooleanValue(true));
+bool SerialWriteFunction::Prepare() {
+ EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &connection_id_));
+ EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &data_));
+ return true;
+}
- bool rv = BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(&SerialCloseFunction::RespondOnUIThread, this));
- DCHECK(rv);
+void SerialWriteFunction::Work() {
+ int bytes_written = -1;
+ SerialConnection* serial_connection =
+ controller()->GetSerialConnection(connection_id_);
+ if (serial_connection)
+ bytes_written = serial_connection->Write(data_);
+ DictionaryValue* result = new DictionaryValue();
+ result->SetInteger(kBytesWrittenKey, bytes_written);
+ result_.reset(result);
}
-void SerialCloseFunction::RespondOnUIThread() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- SendResponse(true);
+bool SerialWriteFunction::Respond() {
+ return true;
}
} // namespace extensions
diff --git a/chrome/browser/extensions/api/serial/serial_api.h b/chrome/browser/extensions/api/serial/serial_api.h
index df4c3e0..f81ad90 100644
--- a/chrome/browser/extensions/api/serial/serial_api.h
+++ b/chrome/browser/extensions/api/serial/serial_api.h
@@ -8,44 +8,63 @@
#include <string>
-#include "chrome/browser/extensions/extension_function.h"
+#include "chrome/browser/extensions/api/api_function.h"
namespace extensions {
extern const char kConnectionIdKey[];
-class SerialOpenFunction : public AsyncExtensionFunction {
+class SerialOpenFunction : public AsyncIOAPIFunction {
public:
SerialOpenFunction();
- virtual ~SerialOpenFunction();
-
- virtual bool RunImpl() OVERRIDE;
protected:
- void WorkOnIOThread();
- void RespondOnUIThread();
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+ virtual bool Respond() OVERRIDE;
private:
+ int src_id_;
std::string port_;
DECLARE_EXTENSION_FUNCTION_NAME("experimental.serial.open")
};
-class SerialCloseFunction : public AsyncExtensionFunction {
- public:
- SerialCloseFunction();
- virtual ~SerialCloseFunction();
+class SerialCloseFunction : public AsyncIOAPIFunction {
+ protected:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+ virtual bool Respond() OVERRIDE;
+
+ private:
+ int connection_id_;
+
+ DECLARE_EXTENSION_FUNCTION_NAME("experimental.serial.close")
+};
+
+class SerialReadFunction : public AsyncIOAPIFunction {
+ protected:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+ virtual bool Respond() OVERRIDE;
+
+ private:
+ int connection_id_;
- virtual bool RunImpl() OVERRIDE;
+ DECLARE_EXTENSION_FUNCTION_NAME("experimental.serial.read")
+};
+class SerialWriteFunction : public AsyncIOAPIFunction {
protected:
- void WorkOnIOThread();
- void RespondOnUIThread();
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+ virtual bool Respond() OVERRIDE;
private:
int connection_id_;
+ std::string data_;
- DECLARE_EXTENSION_FUNCTION_NAME("experimental.serial.close")
+ DECLARE_EXTENSION_FUNCTION_NAME("experimental.serial.write")
};
} // namespace extensions
diff --git a/chrome/browser/extensions/api/serial/serial_apitest.cc b/chrome/browser/extensions/api/serial/serial_apitest.cc
index 7a58d73..b588047 100644
--- a/chrome/browser/extensions/api/serial/serial_apitest.cc
+++ b/chrome/browser/extensions/api/serial/serial_apitest.cc
@@ -7,6 +7,8 @@
#include "chrome/browser/extensions/api/serial/serial_api.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/extension_function_test_utils.h"
+#include "chrome/browser/extensions/extension_test_message_listener.h"
+#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_switches.h"
#include "content/public/browser/browser_thread.h"
@@ -30,5 +32,47 @@ class SerialApiTest : public ExtensionApiTest {
} // namespace
IN_PROC_BROWSER_TEST_F(SerialApiTest, SerialExtension) {
- ASSERT_TRUE(RunExtensionTest("serial/api")) << message_;
+ ResultCatcher catcher;
+ catcher.RestrictToProfile(browser()->profile());
+
+ ExtensionTestMessageListener listener("serial_port", true);
+
+ ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("serial/api")));
+ EXPECT_TRUE(listener.WaitUntilSatisfied());
+
+#if 0
+ // Enable this path only if all the following are true:
+ //
+ // 1. You're running Linux.
+ //
+ // 2. You have an Adafruit ATmega32u4 breakout board attached to your machine
+ // via USB with the Arduino Leonardo bootloader flashed to the board. Other
+ // devices will work; this is the only one tested.
+ //
+ // 3. Your user has permission to read/write the /dev/ttyACM0 device.
+ //
+ // 4. You have uploaded a program to the '32u4 that does a byte-for-byte echo
+ // on the virtual serial port at 57600 bps. Here is an example (built using
+ // the Arduino IDE):
+ //
+ // void setup() {
+ // Serial.begin(57600);
+ // }
+ //
+ // void loop() {
+ // while (true) {
+ // while (Serial.available() > 0) {
+ // Serial.print((char)Serial.read());
+ // }
+ // }
+ // }
+ //
+ // TODO(miket): Enable a more forgiving set of test conditions, specifically
+ // by mocking SerialConnection.
+ listener.Reply("/dev/ttyACM0");
+#else
+ listener.Reply("none");
+#endif
+
+ EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
}
diff --git a/chrome/browser/extensions/api/serial/serial_connection.h b/chrome/browser/extensions/api/serial/serial_connection.h
new file mode 100644
index 0000000..971684a
--- /dev/null
+++ b/chrome/browser/extensions/api/serial/serial_connection.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_API_SERIAL_SERIAL_CONNECTION_H_
+#define CHROME_BROWSER_EXTENSIONS_API_SERIAL_SERIAL_CONNECTION_H_
+#pragma once
+
+#include <string>
+
+#include "chrome/browser/extensions/api/api_resource.h"
+
+namespace extensions {
+
+class APIResourceEventNotifier;
+
+class SerialConnection : public APIResource {
+ public:
+ SerialConnection(const std::string& port,
+ APIResourceEventNotifier* event_notifier);
+ virtual ~SerialConnection();
+
+ bool Open();
+ void Close();
+
+ int Read(unsigned char* byte);
+ int Write(const std::string& data);
+
+ private:
+ std::string port_;
+ int fd_;
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_API_SERIAL_SERIAL_CONNECTION_H_
diff --git a/chrome/browser/extensions/api/serial/serial_connection_posix.cc b/chrome/browser/extensions/api/serial/serial_connection_posix.cc
new file mode 100644
index 0000000..f3d2c68
--- /dev/null
+++ b/chrome/browser/extensions/api/serial/serial_connection_posix.cc
@@ -0,0 +1,45 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/api/serial/serial_connection.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string>
+#include <termios.h>
+#include <unistd.h>
+
+namespace extensions {
+
+SerialConnection::SerialConnection(const std::string& port,
+ APIResourceEventNotifier* event_notifier)
+ : APIResource(APIResource::SerialConnectionResource, event_notifier),
+ port_(port), fd_(0) {
+}
+
+SerialConnection::~SerialConnection() {
+}
+
+bool SerialConnection::Open() {
+ fd_ = open(port_.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
+ return (fd_ > 0);
+}
+
+void SerialConnection::Close() {
+ if (fd_ > 0) {
+ close(fd_);
+ fd_ = 0;
+ }
+}
+
+int SerialConnection::Read(unsigned char* byte) {
+ return read(fd_, byte, 1);
+}
+
+int SerialConnection::Write(const std::string& data) {
+ return write(fd_, data.c_str(), data.length());
+}
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/serial/serial_connection_win.cc b/chrome/browser/extensions/api/serial/serial_connection_win.cc
new file mode 100644
index 0000000..213df5b
--- /dev/null
+++ b/chrome/browser/extensions/api/serial/serial_connection_win.cc
@@ -0,0 +1,39 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/api/serial/serial_connection.h"
+
+#include <string>
+
+namespace extensions {
+
+SerialConnection::SerialConnection(const std::string& port,
+ APIResourceEventNotifier* event_notifier)
+ : APIResource(APIResource::SerialConnectionResource, event_notifier),
+ port_(port), fd_(0) {
+}
+
+SerialConnection::~SerialConnection() {
+}
+
+bool SerialConnection::Open() {
+ // TODO(miket): implement
+ return false;
+}
+
+void SerialConnection::Close() {
+ // TODO(miket): implement
+}
+
+int SerialConnection::Read(unsigned char* byte) {
+ // TODO(miket): implement
+ return -1;
+}
+
+int SerialConnection::Write(const std::string& data) {
+ // TODO(miket): implement
+ return -1;
+}
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/socket/socket.cc b/chrome/browser/extensions/api/socket/socket.cc
index 7944c56..9d5f65c 100644
--- a/chrome/browser/extensions/api/socket/socket.cc
+++ b/chrome/browser/extensions/api/socket/socket.cc
@@ -5,7 +5,7 @@
#include "chrome/browser/extensions/api/socket/socket.h"
#include "base/bind.h"
-#include "chrome/browser/extensions/api/socket/socket_event_notifier.h"
+#include "chrome/browser/extensions/api/api_resource_event_notifier.h"
#include "net/base/io_buffer.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
@@ -13,8 +13,11 @@
namespace extensions {
-Socket::Socket(SocketEventNotifier* event_notifier)
- : event_notifier_(event_notifier),
+Socket::Socket(const std::string& address, int port,
+ APIResourceEventNotifier* event_notifier)
+ : APIResource(APIResource::SocketResource, event_notifier),
+ address_(address),
+ port_(port),
is_connected_(false),
read_buffer_(new net::IOBufferWithSize(kMaxRead)) {
}
@@ -28,15 +31,16 @@ void Socket::OnDataRead(int result) {
std::string message;
if (result >= 0)
message = std::string(read_buffer_->data(), result);
- event_notifier_->OnDataRead(result, message);
+ event_notifier()->OnDataRead(result, message);
}
void Socket::OnWriteComplete(int result) {
- event_notifier_->OnWriteComplete(result);
+ event_notifier()->OnWriteComplete(result);
}
std::string Socket::Read() {
- int result = socket()->Read(read_buffer_, kMaxRead,
+ int result = socket()->Read(
+ read_buffer_, kMaxRead,
base::Bind(&Socket::OnDataRead, base::Unretained(this)));
if (result == net::ERR_IO_PENDING)
return "";
@@ -54,7 +58,8 @@ int Socket::Write(const std::string message) {
int bytes_sent = 0;
while (buffer->BytesRemaining()) {
- int result = socket()->Write(buffer, buffer->BytesRemaining(),
+ int result = socket()->Write(
+ buffer, buffer->BytesRemaining(),
base::Bind(&Socket::OnWriteComplete, base::Unretained(this)));
if (result <= 0)
// We pass all errors, including ERROR_IO_PENDING, back to the caller.
diff --git a/chrome/browser/extensions/api/socket/socket.h b/chrome/browser/extensions/api/socket/socket.h
index 9168b1e..858a9e8 100644
--- a/chrome/browser/extensions/api/socket/socket.h
+++ b/chrome/browser/extensions/api/socket/socket.h
@@ -9,7 +9,7 @@
#include <string>
#include "base/memory/scoped_ptr.h"
-#include "chrome/browser/extensions/api/socket/socket_event_notifier.h"
+#include "chrome/browser/extensions/api/api_resource.h"
#include "net/base/io_buffer.h"
namespace net {
@@ -20,10 +20,13 @@ namespace extensions {
// A Socket wraps a low-level socket and includes housekeeping information that
// we need to manage it in the context of an extension.
-class Socket {
+class Socket : public APIResource {
public:
virtual ~Socket();
+ // Returns true iff the socket was able to properly initialize itself.
+ virtual bool IsValid() = 0;
+
// Returns net::OK if successful, or an error code otherwise.
virtual int Connect() = 0;
virtual void Disconnect() = 0;
@@ -46,10 +49,12 @@ class Socket {
virtual void OnWriteComplete(int result);
protected:
- explicit Socket(SocketEventNotifier* event_notifier);
+ Socket(const std::string& address, int port,
+ APIResourceEventNotifier* event_notifier);
virtual net::Socket* socket() = 0;
- scoped_ptr<SocketEventNotifier> event_notifier_;
+ const std::string address_;
+ int port_;
bool is_connected_;
private:
diff --git a/chrome/browser/extensions/api/socket/socket_api.cc b/chrome/browser/extensions/api/socket/socket_api.cc
index 5674dc3..25387f3 100644
--- a/chrome/browser/extensions/api/socket/socket_api.cc
+++ b/chrome/browser/extensions/api/socket/socket_api.cc
@@ -6,13 +6,11 @@
#include "base/bind.h"
#include "base/values.h"
-#include "chrome/browser/extensions/api/socket/socket_api_controller.h"
-#include "chrome/browser/extensions/api/socket/socket_event_notifier.h"
+#include "chrome/browser/extensions/api/api_resource_controller.h"
+#include "chrome/browser/extensions/api/socket/socket.h"
+#include "chrome/browser/extensions/api/socket/tcp_socket.h"
+#include "chrome/browser/extensions/api/socket/udp_socket.h"
#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/profiles/profile.h"
-#include "content/public/browser/browser_thread.h"
-
-using content::BrowserThread;
namespace extensions {
@@ -22,34 +20,7 @@ const char kSocketIdKey[] = "socketId";
const char kTCPOption[] = "tcp";
const char kUDPOption[] = "udp";
-SocketController* SocketApiFunction::controller() {
- return profile()->GetExtensionService()->socket_controller();
-}
-
-bool SocketApiFunction::RunImpl() {
- if (!Prepare()) {
- return false;
- }
- bool rv = BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(&SocketApiFunction::WorkOnIOThread, this));
- DCHECK(rv);
- return true;
-}
-
-void SocketApiFunction::WorkOnIOThread() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- Work();
- bool rv = BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(&SocketApiFunction::RespondOnUIThread, this));
- DCHECK(rv);
-}
-
-void SocketApiFunction::RespondOnUIThread() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- SendResponse(Respond());
-}
+const char kSocketNotFoundError[] = "Socket not found";
SocketCreateFunction::SocketCreateFunction()
: src_id_(-1) {
@@ -57,22 +28,14 @@ SocketCreateFunction::SocketCreateFunction()
bool SocketCreateFunction::Prepare() {
std::string socket_type_string;
- EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &socket_type_string));
- EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &address_));
- EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(2, &port_));
-
- scoped_ptr<DictionaryValue> options(new DictionaryValue());
- if (args_->GetSize() > 3) {
- DictionaryValue* temp_options = NULL;
- if (args_->GetDictionary(3, &temp_options))
- options.reset(temp_options->DeepCopy());
- }
-
- // If we tacked on a srcId to the options object, pull it out here to provide
- // to the Socket.
- if (options->HasKey(kSrcIdKey)) {
- EXTENSION_FUNCTION_VALIDATE(options->GetInteger(kSrcIdKey, &src_id_));
- }
+ size_t argument_position = 0;
+ EXTENSION_FUNCTION_VALIDATE(args_->GetString(argument_position++,
+ &socket_type_string));
+ EXTENSION_FUNCTION_VALIDATE(args_->GetString(argument_position++,
+ &address_));
+ EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(argument_position++,
+ &port_));
+ src_id_ = ExtractSrcId(argument_position);
if (socket_type_string == kTCPOption)
socket_type_ = kSocketTypeTCP;
@@ -84,15 +47,19 @@ bool SocketCreateFunction::Prepare() {
}
void SocketCreateFunction::Work() {
- SocketEventNotifier* event_notifier(new SocketEventNotifier(
- profile()->GetExtensionEventRouter(), profile(), extension_id(),
- src_id_, source_url()));
- int socket_id = socket_type_ == kSocketTypeTCP ?
- controller()->CreateTCPSocket(address_, port_, event_notifier) :
- controller()->CreateUDPSocket(address_, port_, event_notifier);
+ APIResourceEventNotifier* event_notifier = CreateEventNotifier(src_id_);
+ Socket* socket = NULL;
+ if (socket_type_ == kSocketTypeTCP) {
+ socket = new TCPSocket(address_, port_, event_notifier);
+ } else {
+ socket = new UDPSocket(address_, port_, event_notifier);
+ }
+ DCHECK(socket);
+ DCHECK(socket->IsValid());
+
DictionaryValue* result = new DictionaryValue();
- result->SetInteger(kSocketIdKey, socket_id);
+ result->SetInteger(kSocketIdKey, controller()->AddAPIResource(socket));
result_.reset(result);
}
@@ -106,7 +73,7 @@ bool SocketDestroyFunction::Prepare() {
}
void SocketDestroyFunction::Work() {
- controller()->DestroySocket(socket_id_);
+ controller()->RemoveAPIResource(socket_id_);
}
bool SocketDestroyFunction::Respond() {
@@ -119,7 +86,12 @@ bool SocketConnectFunction::Prepare() {
}
void SocketConnectFunction::Work() {
- int result = controller()->ConnectSocket(socket_id_);
+ int result = -1;
+ Socket* socket = controller()->GetSocket(socket_id_);
+ if (socket)
+ result = socket->Connect();
+ else
+ error_ = kSocketNotFoundError;
result_.reset(Value::CreateIntegerValue(result));
}
@@ -133,7 +105,12 @@ bool SocketDisconnectFunction::Prepare() {
}
void SocketDisconnectFunction::Work() {
- controller()->DisconnectSocket(socket_id_);
+ int result = -1;
+ Socket* socket = controller()->GetSocket(socket_id_);
+ if (socket)
+ result = socket->Connect();
+ else
+ error_ = kSocketNotFoundError;
result_.reset(Value::CreateNullValue());
}
@@ -147,7 +124,10 @@ bool SocketReadFunction::Prepare() {
}
void SocketReadFunction::Work() {
- std::string message = controller()->ReadSocket(socket_id_);
+ std::string message;
+ Socket* socket = controller()->GetSocket(socket_id_);
+ if (socket)
+ message = socket->Read();
DictionaryValue* result = new DictionaryValue();
result->SetString(kMessageKey, message);
@@ -165,7 +145,12 @@ bool SocketWriteFunction::Prepare() {
}
void SocketWriteFunction::Work() {
- int bytes_written = controller()->WriteSocket(socket_id_, message_);
+ int bytes_written = -1;
+ Socket* socket = controller()->GetSocket(socket_id_);
+ if (socket)
+ bytes_written = socket->Write(message_);
+ else
+ error_ = kSocketNotFoundError;
DictionaryValue* result = new DictionaryValue();
result->SetInteger(kBytesWrittenKey, bytes_written);
diff --git a/chrome/browser/extensions/api/socket/socket_api.h b/chrome/browser/extensions/api/socket/socket_api.h
index 2f1616d..259fffc 100644
--- a/chrome/browser/extensions/api/socket/socket_api.h
+++ b/chrome/browser/extensions/api/socket/socket_api.h
@@ -6,45 +6,25 @@
#define CHROME_BROWSER_EXTENSIONS_API_SOCKET_SOCKET_API_H_
#pragma once
-#include "chrome/browser/extensions/extension_function.h"
+#include "chrome/browser/extensions/api/api_function.h"
#include <string>
namespace extensions {
-class SocketController;
+class APIResourceController;
extern const char kBytesWrittenKey[];
extern const char kSocketIdKey[];
extern const char kUdpSocketType[];
-class SocketApiFunction : public AsyncExtensionFunction {
- protected:
- // Set up for work. Guaranteed to happen on UI thread.
- virtual bool Prepare() = 0;
-
- // Do actual work. Guaranteed to happen on IO thread.
- virtual void Work() = 0;
-
- // Respond. Guaranteed to happen on UI thread.
- virtual bool Respond() = 0;
-
- virtual bool RunImpl() OVERRIDE;
-
- SocketController* controller();
-
- private:
- void WorkOnIOThread();
- void RespondOnUIThread();
-};
-
// Many of these socket functions are synchronous in the sense that
// they don't involve blocking operations, but we've made them all
// AsyncExtensionFunctions because the underlying UDPClientSocket
// library wants all operations to happen on the same thread as the
// one that created the socket. Too bad.
-class SocketCreateFunction : public SocketApiFunction {
+class SocketCreateFunction : public AsyncIOAPIFunction {
public:
SocketCreateFunction();
@@ -68,7 +48,7 @@ class SocketCreateFunction : public SocketApiFunction {
DECLARE_EXTENSION_FUNCTION_NAME("experimental.socket.create")
};
-class SocketDestroyFunction : public SocketApiFunction {
+class SocketDestroyFunction : public AsyncIOAPIFunction {
protected:
virtual bool Prepare() OVERRIDE;
virtual void Work() OVERRIDE;
@@ -80,7 +60,7 @@ class SocketDestroyFunction : public SocketApiFunction {
DECLARE_EXTENSION_FUNCTION_NAME("experimental.socket.destroy")
};
-class SocketConnectFunction : public SocketApiFunction {
+class SocketConnectFunction : public AsyncIOAPIFunction {
protected:
virtual bool Prepare() OVERRIDE;
virtual void Work() OVERRIDE;
@@ -92,7 +72,7 @@ class SocketConnectFunction : public SocketApiFunction {
DECLARE_EXTENSION_FUNCTION_NAME("experimental.socket.connect")
};
-class SocketDisconnectFunction : public SocketApiFunction {
+class SocketDisconnectFunction : public AsyncIOAPIFunction {
protected:
virtual bool Prepare() OVERRIDE;
virtual void Work() OVERRIDE;
@@ -104,7 +84,7 @@ class SocketDisconnectFunction : public SocketApiFunction {
DECLARE_EXTENSION_FUNCTION_NAME("experimental.socket.disconnect")
};
-class SocketReadFunction : public SocketApiFunction {
+class SocketReadFunction : public AsyncIOAPIFunction {
protected:
virtual bool Prepare() OVERRIDE;
virtual void Work() OVERRIDE;
@@ -116,7 +96,7 @@ class SocketReadFunction : public SocketApiFunction {
DECLARE_EXTENSION_FUNCTION_NAME("experimental.socket.read")
};
-class SocketWriteFunction : public SocketApiFunction {
+class SocketWriteFunction : public AsyncIOAPIFunction {
protected:
virtual bool Prepare() OVERRIDE;
virtual void Work() OVERRIDE;
diff --git a/chrome/browser/extensions/api/socket/socket_api_controller.cc b/chrome/browser/extensions/api/socket/socket_api_controller.cc
deleted file mode 100644
index 0a628f3..0000000
--- a/chrome/browser/extensions/api/socket/socket_api_controller.cc
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/extensions/api/socket/socket_api_controller.h"
-
-#include "chrome/browser/extensions/api/socket/tcp_socket.h"
-#include "chrome/browser/extensions/api/socket/udp_socket.h"
-#include "chrome/browser/extensions/api/socket/socket_event_notifier.h"
-#include "net/base/address_list.h"
-#include "net/base/net_errors.h"
-#include "net/base/rand_callback.h"
-#include "net/socket/tcp_client_socket.h"
-#include "net/udp/datagram_socket.h"
-#include "net/udp/udp_client_socket.h"
-
-namespace extensions {
-
-SocketController::SocketController() : next_socket_id_(1) {}
-
-SocketController::~SocketController() {}
-
-Socket* SocketController::GetSocket(int socket_id) {
- // TODO(miket): we should verify that the extension asking for the
- // socket is the same one that created it.
- SocketMap::iterator i = socket_map_.find(socket_id);
- if (i != socket_map_.end())
- return i->second.get();
- return NULL;
-}
-
-// TODO(miket): we should consider partitioning the ID space by extension ID
-// to make it harder for extensions to peek into each others' sockets.
-int SocketController::GenerateSocketId() {
- while (next_socket_id_ > 0 && socket_map_.count(next_socket_id_) > 0)
- ++next_socket_id_;
- return next_socket_id_++;
-}
-
-int SocketController::CreateTCPSocket(const std::string& address, int port,
- SocketEventNotifier* event_notifier) {
- net::IPAddressNumber ip_number;
- bool result = net::ParseIPLiteralToNumber(address, &ip_number);
- if (!result)
- return 0;
-
- net::AddressList address_list =
- net::AddressList::CreateFromIPAddress(ip_number, port);
- linked_ptr<Socket> socket(new TCPSocket(
- new net::TCPClientSocket(address_list, NULL, net::NetLog::Source()),
- event_notifier));
- CHECK(socket.get());
- int next_socket_id = GenerateSocketId();
- if (next_socket_id > 0) {
- socket_map_[next_socket_id] = socket;
- return next_socket_id;
- }
- return 0;
-}
-
-int SocketController::CreateUDPSocket(const std::string& address, int port,
- SocketEventNotifier* event_notifier) {
- linked_ptr<Socket> socket(new UDPSocket(
- new net::UDPClientSocket(net::DatagramSocket::DEFAULT_BIND,
- net::RandIntCallback(),
- NULL,
- net::NetLog::Source()),
- address, port, event_notifier));
- CHECK(socket.get());
- int next_socket_id = GenerateSocketId();
- if (next_socket_id > 0) {
- socket_map_[next_socket_id] = socket;
- return next_socket_id;
- }
- return 0;
-}
-
-bool SocketController::DestroySocket(int socket_id) {
- Socket* socket = GetSocket(socket_id);
- if (!socket)
- return false;
- delete socket;
- socket_map_.erase(socket_id);
- return true;
-}
-
-int SocketController::ConnectSocket(int socket_id) {
- Socket* socket = GetSocket(socket_id);
- if (socket)
- return socket->Connect();
- return net::ERR_FILE_NOT_FOUND;
-}
-
-void SocketController::DisconnectSocket(int socket_id) {
- Socket* socket = GetSocket(socket_id);
- if (socket)
- socket->Disconnect();
-}
-
-std::string SocketController::ReadSocket(int socket_id) {
- Socket* socket = GetSocket(socket_id);
- if (!socket)
- return "";
- return socket->Read();
-}
-
-int SocketController::WriteSocket(int socket_id, const std::string& message) {
- Socket* socket = GetSocket(socket_id);
- if (!socket)
- return -1;
- return socket->Write(message);
-}
-
-} // namespace extensions
diff --git a/chrome/browser/extensions/api/socket/socket_api_controller.h b/chrome/browser/extensions/api/socket/socket_api_controller.h
deleted file mode 100644
index e5effb3..0000000
--- a/chrome/browser/extensions/api/socket/socket_api_controller.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_EXTENSIONS_API_SOCKET_SOCKET_API_CONTROLLER_H_
-#define CHROME_BROWSER_EXTENSIONS_API_SOCKET_SOCKET_API_CONTROLLER_H_
-#pragma once
-
-#include <string>
-#include <map>
-
-#include "base/memory/linked_ptr.h"
-#include "base/memory/scoped_ptr.h"
-
-class Profile;
-
-namespace net {
-class IPEndPoint;
-}
-
-namespace extensions {
-
-// kSrcIdKey, or "srcId," binds a socket to the onEvent closure that was
-// optionally passed to the socket.create() method. It's generated by us in
-// schema_generated_bindings.js; the application code is unaware of it.
-extern const char kSrcIdKey[];
-
-class Socket;
-class SocketEventNotifier;
-
-// SocketController keeps track of a collection of Sockets and provides a
-// convenient set of methods to manipulate them.
-class SocketController {
- public:
- SocketController();
- virtual ~SocketController();
-
- // Create/Destroy are a pair. They represent the allocation and deallocation
- // of the Socket object in memory.
- //
- // TODO(miket): aa's suggestion to track lifetime of callbacks associated
- // with each socket, which will then let us clean up when we go out of scope
- // rather than requiring that the app developer remember to call Destroy.
- //
- // Takes ownership of |event_notifier|.
- int CreateTCPSocket(const std::string& address, int port,
- SocketEventNotifier* event_notifier);
- int CreateUDPSocket(const std::string& address, int port,
- SocketEventNotifier* event_notifier);
-
- bool DestroySocket(int socket_id);
-
- // Connect, Disconnect, Read, and Write map to the equivalent methods in the
- // underlying socket.
- int ConnectSocket(int socket_id);
- void DisconnectSocket(int socket_id);
- std::string ReadSocket(int socket_id);
- int WriteSocket(int socket_id, const std::string& message);
-
- private:
- int next_socket_id_;
- typedef std::map<int, linked_ptr<Socket> > SocketMap;
- SocketMap socket_map_;
-
- // Convenience method for accessing SocketMap.
- Socket* GetSocket(int socket_id);
-
- int GenerateSocketId();
-
- DISALLOW_COPY_AND_ASSIGN(SocketController);
-};
-
-} // namespace extensions
-
-#endif // CHROME_BROWSER_EXTENSIONS_API_SOCKET_SOCKET_API_CONTROLLER_H_
diff --git a/chrome/browser/extensions/api/socket/socket_api_controller_unittest.cc b/chrome/browser/extensions/api/socket/socket_api_controller_unittest.cc
deleted file mode 100644
index 5c52bb1..0000000
--- a/chrome/browser/extensions/api/socket/socket_api_controller_unittest.cc
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-#include "base/memory/scoped_ptr.h"
-#include "base/values.h"
-#include "chrome/browser/extensions/api/socket/socket_api.h"
-#include "chrome/browser/extensions/api/socket/socket_api_controller.h"
-#include "chrome/browser/extensions/api/socket/socket_event_notifier.h"
-#include "chrome/common/extensions/extension.h"
-
-namespace extensions {
-
-class SocketApiControllerTest : public testing::Test {
-};
-
-TEST_F(SocketApiControllerTest, TestSocketControllerLifetime) {
- // We want to make sure that killing the controller while a bunch of
- // sockets are alive doesn't crash.
- scoped_ptr<SocketController> controller(new SocketController());
- const int kPort = 38888;
- const std::string address("127.0.0.1");
-
- // Create some sockets but don't do anything with them.
- Profile* profile = NULL;
- std::string extension_id;
- EXPECT_TRUE(Extension::GenerateId("e^(iπ)+1=0", &extension_id));
- const GURL url;
- for (int i = 0; i < 10; ++i) {
- SocketEventNotifier* notifier =
- new SocketEventNotifier(NULL, profile, extension_id, -1, url);
- int socket_id = controller->CreateUDPSocket(address, kPort, notifier);
- ASSERT_TRUE(socket_id != 0);
- }
- for (int i = 0; i < 10; ++i) {
- SocketEventNotifier* notifier =
- new SocketEventNotifier(NULL, profile, extension_id, -1, url);
- int socket_id = controller->CreateTCPSocket(address, kPort, notifier);
- ASSERT_TRUE(socket_id != 0);
- }
-
- // Create some more sockets and connect them. Note that because this is
- // UDP, we can happily "connect" a UDP socket without anyone listening.
- // We skip TCP sockets here because we've already tested what we wanted
- // to test.
- for (int i = 0; i < 10; ++i) {
- SocketEventNotifier* notifier =
- new SocketEventNotifier(NULL, profile, extension_id, -1, url);
- int socket_id = controller->CreateUDPSocket(address, kPort, notifier);
- ASSERT_TRUE(socket_id != 0);
- ASSERT_EQ(0, controller->ConnectSocket(socket_id));
- }
-}
-
-} // namespace extensions
diff --git a/chrome/browser/extensions/api/socket/socket_event_notifier.h b/chrome/browser/extensions/api/socket/socket_event_notifier.h
deleted file mode 100644
index 1a5b9b1..0000000
--- a/chrome/browser/extensions/api/socket/socket_event_notifier.h
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_EXTENSIONS_API_SOCKET_SOCKET_EVENT_NOTIFIER_H_
-#define CHROME_BROWSER_EXTENSIONS_API_SOCKET_SOCKET_EVENT_NOTIFIER_H_
-#pragma once
-
-#include <string>
-
-#include "base/values.h"
-#include "googleurl/src/gurl.h"
-
-class ExtensionEventRouter;
-class Profile;
-
-namespace events {
-extern const char kOnSocketEvent[];
-};
-
-namespace extensions {
-
-enum SocketEventType {
- SOCKET_EVENT_CONNECT_COMPLETE,
- SOCKET_EVENT_DATA_READ,
- SOCKET_EVENT_WRITE_COMPLETE
-};
-
-extern const char kSrcIdKey[];
-
-// Contains the data that a Socket needs to send an event back to the extension
-// that instantiated it.
-class SocketEventNotifier {
- public:
- SocketEventNotifier(ExtensionEventRouter* router,
- Profile* profile,
- const std::string& src_extension_id, int src_id,
- const GURL& src_url);
- virtual ~SocketEventNotifier();
-
- virtual void OnConnectComplete(int result_code);
- virtual void OnDataRead(int result_code, const std::string& data);
- virtual void OnWriteComplete(int result_code);
-
- static std::string SocketEventTypeToString(SocketEventType event_type);
-
- private:
- void DispatchEvent(DictionaryValue* event);
- DictionaryValue* CreateSocketEvent(SocketEventType event_type);
-
- void SendEventWithResultCode(SocketEventType event_type, int result_code);
-
- ExtensionEventRouter* router_;
- Profile* profile_;
- std::string src_extension_id_;
- int src_id_;
- GURL src_url_;
-
- DISALLOW_COPY_AND_ASSIGN(SocketEventNotifier);
-};
-
-} // namespace extensions
-
-#endif // CHROME_BROWSER_EXTENSIONS_API_SOCKET_SOCKET_EVENT_NOTIFIER_H_
diff --git a/chrome/browser/extensions/api/socket/socket_event_notifier_unittest.cc b/chrome/browser/extensions/api/socket/socket_event_notifier_unittest.cc
deleted file mode 100644
index 780747f..0000000
--- a/chrome/browser/extensions/api/socket/socket_event_notifier_unittest.cc
+++ /dev/null
@@ -1,173 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/extensions/api/socket/socket_event_notifier.h"
-
-#include "base/json/json_reader.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/values.h"
-#include "chrome/browser/extensions/extension_event_router.h"
-#include "chrome/common/extensions/extension.h"
-#include "chrome/test/base/testing_profile.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-using testing::_;
-using testing::SaveArg;
-
-namespace extensions {
-
-class MockExtensionEventRouter : public ExtensionEventRouter {
- public:
- explicit MockExtensionEventRouter(Profile* profile) :
- ExtensionEventRouter(profile) {}
-
- MOCK_METHOD5(DispatchEventToExtension, void(const std::string& extension_id,
- const std::string& event_name,
- const std::string& event_args,
- Profile* source_profile,
- const GURL& event_url));
-
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MockExtensionEventRouter);
-};
-
-class MockTestingProfile : public TestingProfile {
- public:
- MockTestingProfile() {}
- MOCK_METHOD0(GetExtensionEventRouter, ExtensionEventRouter*());
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MockTestingProfile);
-};
-
-class SocketEventNotifierTest : public testing::Test {
- public:
- SocketEventNotifierTest()
- : mock_event_router_(
- new MockExtensionEventRouter(&profile_)),
- src_id_(42) {
- EXPECT_TRUE(Extension::GenerateId("chrome fast", &extension_id_));
- event_notifier_.reset(new SocketEventNotifier(mock_event_router_.get(),
- &profile_,
- extension_id_,
- src_id_, url_));
- }
-
- MockTestingProfile profile_;
- scoped_ptr<MockExtensionEventRouter> mock_event_router_;
- int src_id_;
- GURL url_;
- std::string extension_id_;
- scoped_ptr<SocketEventNotifier> event_notifier_;
-};
-
-TEST_F(SocketEventNotifierTest, TestOnConnectCompleteEvent) {
- std::string event_args;
- EXPECT_CALL(*mock_event_router_.get(),
- DispatchEventToExtension(
- extension_id_,
- events::kOnSocketEvent,
- _,
- &profile_,
- url_))
- .Times(1)
- .WillOnce(SaveArg<2>(&event_args));
-
- const int expected_result_code = 777;
- event_notifier_->OnConnectComplete(expected_result_code);
-
- scoped_ptr<Value> result(base::JSONReader::Read(event_args, true));
- Value* value = result.get();
- ASSERT_TRUE(result.get() != NULL);
- ASSERT_EQ(Value::TYPE_LIST, value->GetType());
- ListValue* list = static_cast<ListValue*>(value);
- ASSERT_EQ(1u, list->GetSize());
-
- DictionaryValue* info;
- ASSERT_TRUE(list->GetDictionary(0, &info));
-
- int tmp_src_id = 0;
- ASSERT_TRUE(info->GetInteger("srcId", &tmp_src_id));
- ASSERT_EQ(src_id_, tmp_src_id);
-
- int actual_result_code = 0;
- ASSERT_TRUE(info->GetInteger("resultCode", &actual_result_code));
- ASSERT_EQ(expected_result_code, actual_result_code);
-}
-
-TEST_F(SocketEventNotifierTest, TestOnWriteCompleteEvent) {
- std::string event_args;
- EXPECT_CALL(*mock_event_router_.get(),
- DispatchEventToExtension(
- extension_id_,
- events::kOnSocketEvent,
- _,
- &profile_,
- url_))
- .Times(1)
- .WillOnce(SaveArg<2>(&event_args));
-
- const int expected_result_code = 888;
- event_notifier_->OnWriteComplete(expected_result_code);
-
- scoped_ptr<Value> result(base::JSONReader::Read(event_args, true));
- Value* value = result.get();
- ASSERT_TRUE(result.get() != NULL);
- ASSERT_EQ(Value::TYPE_LIST, value->GetType());
- ListValue* list = static_cast<ListValue*>(value);
- ASSERT_EQ(1u, list->GetSize());
-
- DictionaryValue* info;
- ASSERT_TRUE(list->GetDictionary(0, &info));
-
- int tmp_src_id = 0;
- ASSERT_TRUE(info->GetInteger("srcId", &tmp_src_id));
- ASSERT_EQ(src_id_, tmp_src_id);
-
- int actual_result_code = 0;
- ASSERT_TRUE(info->GetInteger("resultCode", &actual_result_code));
- ASSERT_EQ(expected_result_code, actual_result_code);
-}
-
-TEST_F(SocketEventNotifierTest, TestOnDataReadEvent) {
- std::string event_args;
- EXPECT_CALL(*mock_event_router_.get(),
- DispatchEventToExtension(
- extension_id_,
- events::kOnSocketEvent,
- _,
- &profile_,
- url_))
- .Times(1)
- .WillOnce(SaveArg<2>(&event_args));
-
- const int expected_result_code = 888;
- const std::string result_data("hi");
- event_notifier_->OnDataRead(expected_result_code, result_data);
-
- scoped_ptr<Value> result(base::JSONReader::Read(event_args, true));
- Value* value = result.get();
- ASSERT_TRUE(result.get() != NULL);
- ASSERT_EQ(Value::TYPE_LIST, value->GetType());
- ListValue* list = static_cast<ListValue*>(value);
- ASSERT_EQ(1u, list->GetSize());
-
- DictionaryValue* info;
- ASSERT_TRUE(list->GetDictionary(0, &info));
-
- int tmp_src_id = 0;
- ASSERT_TRUE(info->GetInteger("srcId", &tmp_src_id));
- ASSERT_EQ(src_id_, tmp_src_id);
-
- int actual_result_code = 0;
- ASSERT_TRUE(info->GetInteger("resultCode", &actual_result_code));
- ASSERT_EQ(expected_result_code, actual_result_code);
-
- std::string tmp_result_data;
- ASSERT_TRUE(info->GetString("data", &tmp_result_data));
- ASSERT_EQ(result_data, tmp_result_data);
-}
-
-} // namespace extensions
diff --git a/chrome/browser/extensions/api/socket/tcp_socket.cc b/chrome/browser/extensions/api/socket/tcp_socket.cc
index 44eda1a..8989fd2 100644
--- a/chrome/browser/extensions/api/socket/tcp_socket.cc
+++ b/chrome/browser/extensions/api/socket/tcp_socket.cc
@@ -4,17 +4,42 @@
#include "chrome/browser/extensions/api/socket/tcp_socket.h"
-#include "chrome/browser/extensions/api/socket/socket_event_notifier.h"
+#include "chrome/browser/extensions/api/api_resource.h"
+#include "chrome/browser/extensions/api/api_resource_event_notifier.h"
+#include "net/base/address_list.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
+#include "net/base/rand_callback.h"
#include "net/socket/tcp_client_socket.h"
namespace extensions {
+TCPSocket::TCPSocket(const std::string& address, int port,
+ APIResourceEventNotifier* event_notifier)
+ : Socket(address, port, event_notifier) {
+ net::IPAddressNumber ip_number;
+ if (net::ParseIPLiteralToNumber(address, &ip_number)) {
+ net::AddressList address_list =
+ net::AddressList::CreateFromIPAddress(ip_number, port);
+ socket_.reset(new net::TCPClientSocket(address_list, NULL,
+ net::NetLog::Source()));
+ }
+}
+
+// For testing.
TCPSocket::TCPSocket(net::TCPClientSocket* tcp_client_socket,
- SocketEventNotifier* event_notifier)
-: Socket(event_notifier),
- socket_(tcp_client_socket) {
+ const std::string& address, int port,
+ APIResourceEventNotifier* event_notifier)
+ : Socket(address, port, event_notifier),
+ socket_(tcp_client_socket) {
+}
+
+// static
+TCPSocket* TCPSocket::CreateSocketForTesting(
+ net::TCPClientSocket* tcp_client_socket,
+ const std::string& address, int port,
+ APIResourceEventNotifier* event_notifier) {
+ return new TCPSocket(tcp_client_socket, address, port, event_notifier);
}
TCPSocket::~TCPSocket() {
@@ -23,6 +48,10 @@ TCPSocket::~TCPSocket() {
}
}
+bool TCPSocket::IsValid() {
+ return socket_ != NULL;
+}
+
net::Socket* TCPSocket::socket() {
return socket_.get();
}
@@ -41,7 +70,7 @@ void TCPSocket::Disconnect() {
void TCPSocket::OnConnect(int result) {
is_connected_ = result == net::OK;
- event_notifier_->OnConnectComplete(result);
+ event_notifier()->OnConnectComplete(result);
}
} // namespace extensions
diff --git a/chrome/browser/extensions/api/socket/tcp_socket.h b/chrome/browser/extensions/api/socket/tcp_socket.h
index 48dffc5..051abf4 100644
--- a/chrome/browser/extensions/api/socket/tcp_socket.h
+++ b/chrome/browser/extensions/api/socket/tcp_socket.h
@@ -9,7 +9,6 @@
#include <string>
#include "chrome/browser/extensions/api/socket/socket.h"
-#include "chrome/browser/extensions/api/socket/socket_event_notifier.h"
// This looks like it should be forward-declarable, but it does some tricky
// moves that make it easier to just include it.
@@ -21,21 +20,34 @@ class Socket;
namespace extensions {
+class APIResourceEventNotifier;
+
class TCPSocket : public Socket {
public:
- TCPSocket(net::TCPClientSocket* tcp_client_socket,
- SocketEventNotifier* event_notifier);
+ TCPSocket(const std::string& address, int port,
+ APIResourceEventNotifier* event_notifier);
virtual ~TCPSocket();
+ virtual bool IsValid() OVERRIDE;
+
virtual int Connect() OVERRIDE;
virtual void Disconnect() OVERRIDE;
virtual void OnConnect(int result);
+ static TCPSocket* CreateSocketForTesting(
+ net::TCPClientSocket* tcp_client_socket,
+ const std::string& address, int port,
+ APIResourceEventNotifier* event_notifier);
+
protected:
virtual net::Socket* socket() OVERRIDE;
private:
+ TCPSocket(net::TCPClientSocket* tcp_client_socket,
+ const std::string& address, int port,
+ APIResourceEventNotifier* event_notifier);
+
scoped_ptr<net::TCPClientSocket> socket_;
};
diff --git a/chrome/browser/extensions/api/socket/tcp_socket_unittest.cc b/chrome/browser/extensions/api/socket/tcp_socket_unittest.cc
new file mode 100644
index 0000000..8132e78
--- /dev/null
+++ b/chrome/browser/extensions/api/socket/tcp_socket_unittest.cc
@@ -0,0 +1,99 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/api/socket/tcp_socket.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/extensions/api/api_resource_event_notifier.h"
+#include "net/base/address_list.h"
+#include "net/base/completion_callback.h"
+#include "net/base/net_errors.h"
+#include "net/base/rand_callback.h"
+#include "net/socket/tcp_client_socket.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using testing::_;
+using testing::DoAll;
+using testing::Return;
+using testing::SaveArg;
+
+namespace extensions {
+
+class MockTCPSocket : public net::TCPClientSocket {
+ public:
+ explicit MockTCPSocket(const net::AddressList& address_list)
+ : net::TCPClientSocket(address_list, NULL, net::NetLog::Source()) {
+ }
+
+ MOCK_METHOD3(Read, int(net::IOBuffer* buf, int buf_len,
+ const net::CompletionCallback& callback));
+ MOCK_METHOD3(Write, int(net::IOBuffer* buf, int buf_len,
+ const net::CompletionCallback& callback));
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockTCPSocket);
+};
+
+class MockAPIResourceEventNotifier : public APIResourceEventNotifier {
+ public:
+ MockAPIResourceEventNotifier() : APIResourceEventNotifier(NULL, NULL,
+ std::string(),
+ 0, GURL()) {}
+
+ MOCK_METHOD2(OnReadComplete, void(int result_code,
+ const std::string& message));
+ MOCK_METHOD1(OnWriteComplete, void(int result_code));
+};
+
+TEST(SocketTest, TestTCPSocketRead) {
+ net::AddressList address_list;
+ MockTCPSocket* tcp_client_socket = new MockTCPSocket(address_list);
+ APIResourceEventNotifier* notifier = new MockAPIResourceEventNotifier();
+
+ scoped_ptr<TCPSocket> socket(TCPSocket::CreateSocketForTesting(
+ tcp_client_socket, "1.2.3.4", 1, notifier));
+
+ EXPECT_CALL(*tcp_client_socket, Read(_, _, _))
+ .Times(1);
+
+ std::string message = socket->Read();
+}
+
+TEST(SocketTest, TestTCPSocketWrite) {
+ net::AddressList address_list;
+ MockTCPSocket* tcp_client_socket = new MockTCPSocket(address_list);
+ APIResourceEventNotifier* notifier = new MockAPIResourceEventNotifier();
+
+ scoped_ptr<TCPSocket> socket(TCPSocket::CreateSocketForTesting(
+ tcp_client_socket, "1.2.3.4", 1, notifier));
+
+ EXPECT_CALL(*tcp_client_socket, Write(_, _, _))
+ .Times(1);
+
+ socket->Write("foo");
+}
+
+TEST(SocketTest, TestTCPSocketBlockedWrite) {
+ net::AddressList address_list;
+ MockTCPSocket* tcp_client_socket = new MockTCPSocket(address_list);
+ MockAPIResourceEventNotifier* notifier = new MockAPIResourceEventNotifier();
+
+ scoped_ptr<TCPSocket> socket(TCPSocket::CreateSocketForTesting(
+ tcp_client_socket, "1.2.3.4", 1, notifier));
+
+ net::CompletionCallback callback;
+ EXPECT_CALL(*tcp_client_socket, Write(_, _, _))
+ .Times(1)
+ .WillOnce(testing::DoAll(SaveArg<2>(&callback),
+ Return(net::ERR_IO_PENDING)));
+
+ ASSERT_EQ(net::ERR_IO_PENDING, socket->Write("foo"));
+
+ // Good. Original call came back unable to complete. Now pretend the socket
+ // finished, and confirm that we passed the error back.
+ EXPECT_CALL(*notifier, OnWriteComplete(42))
+ .Times(1);
+ callback.Run(42);
+}
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/socket/udp_socket.cc b/chrome/browser/extensions/api/socket/udp_socket.cc
index 4de8e25..467df66 100644
--- a/chrome/browser/extensions/api/socket/udp_socket.cc
+++ b/chrome/browser/extensions/api/socket/udp_socket.cc
@@ -4,20 +4,38 @@
#include "chrome/browser/extensions/api/socket/udp_socket.h"
-#include "chrome/browser/extensions/api/socket/socket_event_notifier.h"
+#include "chrome/browser/extensions/api/api_resource.h"
+#include "chrome/browser/extensions/api/api_resource_event_notifier.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
#include "net/udp/datagram_socket.h"
+#include "net/udp/udp_client_socket.h"
namespace extensions {
+UDPSocket::UDPSocket(const std::string& address, int port,
+ APIResourceEventNotifier* event_notifier)
+ : Socket(address, port, event_notifier),
+ socket_(new net::UDPClientSocket(net::DatagramSocket::DEFAULT_BIND,
+ net::RandIntCallback(),
+ NULL,
+ net::NetLog::Source())) {
+}
+
+// For testing.
UDPSocket::UDPSocket(net::DatagramClientSocket* datagram_client_socket,
const std::string& address, int port,
- SocketEventNotifier* event_notifier)
-: Socket(event_notifier),
- socket_(datagram_client_socket),
- address_(address),
- port_(port) {
+ APIResourceEventNotifier* event_notifier)
+ : Socket(address, port, event_notifier),
+ socket_(datagram_client_socket) {
+}
+
+// static
+UDPSocket* UDPSocket::CreateSocketForTesting(
+ net::DatagramClientSocket* datagram_client_socket,
+ const std::string& address, int port,
+ APIResourceEventNotifier* event_notifier) {
+ return new UDPSocket(datagram_client_socket, address, port, event_notifier);
}
UDPSocket::~UDPSocket() {
@@ -26,6 +44,10 @@ UDPSocket::~UDPSocket() {
}
}
+bool UDPSocket::IsValid() {
+ return socket_ != NULL;
+}
+
net::Socket* UDPSocket::socket() {
return socket_.get();
}
diff --git a/chrome/browser/extensions/api/socket/udp_socket.h b/chrome/browser/extensions/api/socket/udp_socket.h
index 6511e5d..4cffa98 100644
--- a/chrome/browser/extensions/api/socket/udp_socket.h
+++ b/chrome/browser/extensions/api/socket/udp_socket.h
@@ -9,10 +9,6 @@
#include <string>
#include "chrome/browser/extensions/api/socket/socket.h"
-#include "chrome/browser/extensions/api/socket/socket_event_notifier.h"
-
-// This looks like it should be forward-declarable, but it does some tricky
-// moves that make it easier to just include it.
#include "net/udp/datagram_client_socket.h"
namespace net {
@@ -21,23 +17,34 @@ class Socket;
namespace extensions {
+class APIResourceEventNotifier;
+
class UDPSocket : public Socket {
public:
- UDPSocket(net::DatagramClientSocket* datagram_client_socket,
- const std::string& address, int port,
- SocketEventNotifier* event_notifier);
+ UDPSocket(const std::string& address, int port,
+ APIResourceEventNotifier* event_notifier);
virtual ~UDPSocket();
+ virtual bool IsValid() OVERRIDE;
+
virtual int Connect() OVERRIDE;
virtual void Disconnect() OVERRIDE;
+ static UDPSocket* CreateSocketForTesting(
+ net::DatagramClientSocket* datagram_client_socket,
+ const std::string& address, int port,
+ APIResourceEventNotifier* event_notifier);
+
protected:
virtual net::Socket* socket() OVERRIDE;
private:
+ // Special constructor for testing.
+ UDPSocket(net::DatagramClientSocket* datagram_client_socket,
+ const std::string& address, int port,
+ APIResourceEventNotifier* event_notifier);
+
scoped_ptr<net::DatagramClientSocket> socket_;
- const std::string address_;
- int port_;
};
} // namespace extensions
diff --git a/chrome/browser/extensions/api/socket/socket_unittest.cc b/chrome/browser/extensions/api/socket/udp_socket_unittest.cc
index 511a355..980e3e8 100644
--- a/chrome/browser/extensions/api/socket/socket_unittest.cc
+++ b/chrome/browser/extensions/api/socket/udp_socket_unittest.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/extensions/api/socket/udp_socket.h"
#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/extensions/api/api_resource_event_notifier.h"
#include "net/base/completion_callback.h"
#include "net/base/net_errors.h"
#include "net/base/rand_callback.h"
@@ -18,9 +19,9 @@ using testing::SaveArg;
namespace extensions {
-class MockSocket : public net::UDPClientSocket {
+class MockUDPSocket : public net::UDPClientSocket {
public:
- MockSocket()
+ MockUDPSocket()
: net::UDPClientSocket(net::DatagramSocket::DEFAULT_BIND,
net::RandIntCallback(),
NULL,
@@ -31,25 +32,26 @@ class MockSocket : public net::UDPClientSocket {
MOCK_METHOD3(Write, int(net::IOBuffer* buf, int buf_len,
const net::CompletionCallback& callback));
private:
- DISALLOW_COPY_AND_ASSIGN(MockSocket);
+ DISALLOW_COPY_AND_ASSIGN(MockUDPSocket);
};
-class MockSocketEventNotifier : public SocketEventNotifier {
+class MockAPIResourceEventNotifier : public APIResourceEventNotifier {
public:
- MockSocketEventNotifier() : SocketEventNotifier(NULL, NULL, std::string(),
- 0, GURL()) {}
+ MockAPIResourceEventNotifier() : APIResourceEventNotifier(NULL, NULL,
+ std::string(),
+ 0, GURL()) {}
MOCK_METHOD2(OnReadComplete, void(int result_code,
const std::string& message));
MOCK_METHOD1(OnWriteComplete, void(int result_code));
};
-TEST(SocketTest, TestSocketRead) {
- MockSocket* udp_client_socket = new MockSocket();
- SocketEventNotifier* notifier = new MockSocketEventNotifier();
+TEST(SocketTest, TestUDPSocketRead) {
+ MockUDPSocket* udp_client_socket = new MockUDPSocket();
+ APIResourceEventNotifier* notifier = new MockAPIResourceEventNotifier();
- scoped_ptr<UDPSocket> socket(new UDPSocket(udp_client_socket, "1.2.3.4", 1,
- notifier));
+ scoped_ptr<UDPSocket> socket(UDPSocket::CreateSocketForTesting(
+ udp_client_socket, "1.2.3.4", 1, notifier));
EXPECT_CALL(*udp_client_socket, Read(_, _, _))
.Times(1);
@@ -57,12 +59,12 @@ TEST(SocketTest, TestSocketRead) {
std::string message = socket->Read();
}
-TEST(SocketTest, TestSocketWrite) {
- MockSocket* udp_client_socket = new MockSocket();
- SocketEventNotifier* notifier = new MockSocketEventNotifier();
+TEST(SocketTest, TestUDPSocketWrite) {
+ MockUDPSocket* udp_client_socket = new MockUDPSocket();
+ APIResourceEventNotifier* notifier = new MockAPIResourceEventNotifier();
- scoped_ptr<UDPSocket> socket(new UDPSocket(udp_client_socket, "1.2.3.4", 1,
- notifier));
+ scoped_ptr<UDPSocket> socket(UDPSocket::CreateSocketForTesting(
+ udp_client_socket, "1.2.3.4", 1, notifier));
EXPECT_CALL(*udp_client_socket, Write(_, _, _))
.Times(1);
@@ -70,12 +72,12 @@ TEST(SocketTest, TestSocketWrite) {
socket->Write("foo");
}
-TEST(SocketTest, TestSocketBlockedWrite) {
- MockSocket* udp_client_socket = new MockSocket();
- MockSocketEventNotifier* notifier = new MockSocketEventNotifier();
+TEST(SocketTest, TestUDPSocketBlockedWrite) {
+ MockUDPSocket* udp_client_socket = new MockUDPSocket();
+ MockAPIResourceEventNotifier* notifier = new MockAPIResourceEventNotifier();
- scoped_ptr<UDPSocket> socket(new UDPSocket(udp_client_socket, "1.2.3.4", 1,
- notifier));
+ scoped_ptr<UDPSocket> socket(UDPSocket::CreateSocketForTesting(
+ udp_client_socket, "1.2.3.4", 1, notifier));
net::CompletionCallback callback;
EXPECT_CALL(*udp_client_socket, Write(_, _, _))
diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc
index 920dc53..c664dc2 100644
--- a/chrome/browser/extensions/extension_function_dispatcher.cc
+++ b/chrome/browser/extensions/extension_function_dispatcher.cc
@@ -489,6 +489,8 @@ void FactoryRegistry::ResetFunctions() {
// Serial
RegisterFunction<extensions::SerialOpenFunction>();
RegisterFunction<extensions::SerialCloseFunction>();
+ RegisterFunction<extensions::SerialReadFunction>();
+ RegisterFunction<extensions::SerialWriteFunction>();
// Sockets
RegisterFunction<extensions::SocketCreateFunction>();
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 28a5363..81a6a2f3 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -31,7 +31,7 @@
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_plugin_service_filter.h"
#include "chrome/browser/download/download_extension_api.h"
-#include "chrome/browser/extensions/api/socket/socket_api_controller.h"
+#include "chrome/browser/extensions/api/api_resource_controller.h"
#include "chrome/browser/extensions/app_notification_manager.h"
#include "chrome/browser/extensions/apps_promo.h"
#include "chrome/browser/extensions/component_loader.h"
@@ -387,7 +387,7 @@ ExtensionService::ExtensionService(Profile* profile,
apps_promo_(profile->GetPrefs()),
event_routers_initialized_(false),
extension_warnings_(profile),
- socket_controller_(NULL),
+ api_resource_controller_(NULL),
app_shortcut_manager_(profile) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@@ -484,15 +484,13 @@ ExtensionService::~ExtensionService() {
provider->ServiceShutdown();
}
- // TODO(miket): if we find ourselves adding more and more per-API
- // controllers, we should manage them all with an
- // APIControllerController (still working on that name).
- if (socket_controller_) {
- // If this check failed, then a unit test was using sockets but didn't
- // provide the IO thread message loop needed for those sockets to do their
- // job (including destroying themselves at shutdown).
+ if (api_resource_controller_) {
+ // If this check failed, then a unit test was using an APIResource but
+ // didn't provide the IO thread message loop needed for those resources to
+ // do their job (including destroying themselves at shutdown).
DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::IO));
- BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, socket_controller_);
+ BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE,
+ api_resource_controller_);
}
}
@@ -2645,18 +2643,20 @@ ExtensionService::NaClModuleInfoList::iterator
return nacl_module_list_.end();
}
-extensions::SocketController* ExtensionService::socket_controller() {
- // TODO(miket): Find a better place for SocketController to live. It needs
- // to be scoped such that it can be created and destroyed on the IO thread.
+extensions::APIResourceController*
+ExtensionService::api_resource_controller() {
+ // TODO(miket): Find a better place for this thing to live. It needs to be
+ // scoped such that it can be created and destroyed on the IO thread.
//
// To coexist with certain unit tests that don't have an IO thread message
// loop available at ExtensionService shutdown, we lazy-initialize this
- // object so that those cases neither create nor destroy a SocketController.
+ // object so that those cases neither create nor destroy an
+ // APIResourceController.
CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- if (!socket_controller_) {
- socket_controller_ = new extensions::SocketController();
+ if (!api_resource_controller_) {
+ api_resource_controller_ = new extensions::APIResourceController();
}
- return socket_controller_;
+ return api_resource_controller_;
}
extensions::RulesRegistryService* ExtensionService::GetRulesRegistryService() {
diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h
index eaea412..819dc19 100644
--- a/chrome/browser/extensions/extension_service.h
+++ b/chrome/browser/extensions/extension_service.h
@@ -73,10 +73,10 @@ class ExtensionInputMethodEventRouter;
}
namespace extensions {
+class APIResourceController;
class ComponentLoader;
class RulesRegistryService;
class SettingsFrontend;
-class SocketController;
}
// This is an interface class to encapsulate the dependencies that
@@ -576,7 +576,7 @@ class ExtensionService
}
// Call only from IO thread.
- extensions::SocketController* socket_controller();
+ extensions::APIResourceController* api_resource_controller();
extensions::RulesRegistryService* GetRulesRegistryService();
@@ -833,7 +833,7 @@ class ExtensionService
// We need to control destruction of this object (it needs to happen on the
// IO thread), so we don't get to use any RAII devices with it.
- extensions::SocketController* socket_controller_;
+ extensions::APIResourceController* api_resource_controller_;
extensions::ProcessMap process_map_;